C#基礎篇——泛型

来源:https://www.cnblogs.com/i3yuan/archive/2020/05/31/12997374.html
-Advertisement-
Play Games

前言 在開發編程中,我們經常會遇到功能非常相似的功能模塊,只是他們的處理的數據不一樣,所以我們會分別採用多個方法來處理不同的數據類型。但是這個時候,我們就會想一個問題,有沒有辦法實現利用同一個方法來傳遞不同種類型的參數呢? 這個時候,泛型也就因運而生,專門來解決這個問題的。 泛型是在C 2.0就推出 ...


前言

在開發編程中,我們經常會遇到功能非常相似的功能模塊,只是他們的處理的數據不一樣,所以我們會分別採用多個方法來處理不同的數據類型。但是這個時候,我們就會想一個問題,有沒有辦法實現利用同一個方法來傳遞不同種類型的參數呢?

這個時候,泛型也就因運而生,專門來解決這個問題的。

泛型是在C#2.0就推出的一個新語法,由框架升級提供的功能。

說明

泛型通過參數化類型實現在同一份代碼上操作多種數據類型。例如使用泛型的類型參數T,定義一個類Stack

可以用Stack、Stack或者Stack實例化它,從而使類Stack可以處理int、string、Person類型數據。這樣可以避免運行時類型轉換或封箱操作的代價和風險。泛型提醒的是將具體的東西模糊化。

同時使用泛型類型可以最大限度地重用代碼、保護類型安全以及提高性能。

可以創建:泛型介面泛型類泛型方法泛型事件泛型委托

開始

泛型類

泛型類封裝不特定於特定數據類型的操作。 泛型類最常見用法是用於鏈接列表、哈希表、堆棧、隊列和樹等集合。 無論存儲數據的類型如何,添加項和從集合刪除項等操作的執行方式基本相同。

    static void Main(string[] args)
    {

        // T是int類型
        GenericClass<int> genericInt = new GenericClass<int>();
        genericInt._T = 123;
        // T是string類型
        GenericClass<string> genericString = new GenericClass<string>();
        genericString._T = "123";

    }

新建一個GenericClass類

    /// <summary>
    /// 泛型類
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class GenericClass<T>
    {
        public T _T;
    }

泛型方法

泛型方法是通過類型參數聲明的方法, 解決用一個方法,滿足不同參數類型

    static void Main(string[] args)
    {
        #region 泛型方法
        Console.WriteLine("************Generic**************");
        int iValue = 123;
        string sValue = "456";
        DateTime dtValue = DateTime.Now;
        object oValue = "MrValue";
        GenericMethod.Show<int>(iValue);//需要指定類型參數
        //GenericMethod.Show<string>(iValue);//必須吻合
        GenericMethod.Show(iValue);//能省略,自動推算
        GenericMethod.Show<string>(sValue);
        GenericMethod.Show<DateTime>(dtValue);
        GenericMethod.Show<object>(oValue);
        #endregion

    }

新建一個GenericMethod

/// <summary>
/// 泛型方法
/// </summary>
public class GenericMethod
{
    /// <summary>
    /// 2.0推出的新語法
    /// 泛型方法解決用一個方法,滿足不同參數類型;做相同的事兒
    /// 沒有寫死參數類型,調用的時候才指定的類型
    /// 延遲聲明:把參數類型的聲明推遲到調用
    /// 推遲一切可以推遲的~~  延遲思想
    /// 不是語法糖,而是2.0由框架升級提供的功能
    /// 需要編譯器支持+JIT支持
    /// </summary>
    /// <typeparam name="T">T/S 不要用關鍵字  也不要跟別的類型衝突 </typeparam>
    /// <param name="tParameter"></param>
    public static void Show<T>(T tParameter)
    {
        Console.WriteLine("This is {0},parameter={1},type={2}",
            typeof(GenericMethod), tParameter.GetType().Name, tParameter.ToString());
    }
}

泛型介面

為泛型集合類或表示集合中的項的泛型類定義介面通常很有用處。在c#中,通過尖括弧“<>”將類型參數括起來,表示泛型。聲明泛型介面時,與聲明一般介面的唯一區別是增加了一個。一般來說,聲明泛型介面與聲明非泛型介面遵循相同的規則。

泛型介面定義完成之後,就要定義此介面的子類。定義泛型介面的子類有以下兩種方法。

(1)直接在子類後聲明泛型。

(2)在子類實現的介面中明確的給出泛型類型。

    static void Main(string[] args)
    {
        #region 泛型介面
        CommonInterface commonInterface = new CommonInterface();
        commonInterface.GetT("123");
        #endregion
    }

新建GenericInterface.cs類文件

        /// <summary>
        /// 泛型類
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public class GenericClass<T>
        {
            public T _T;
        }

        /// <summary>
        /// 泛型介面
        /// </summary>
        public interface IGenericInterface<T>
        {
            //泛型類型的返回值
            T GetT(T t);
        }


        /// <summary>
        /// 使用泛型的時候必須指定具體類型,
        /// 這裡的具體類型是int
        /// </summary>
        public class CommonClass : GenericClass<int>
        {

        }

        /// <summary>
        /// 必須指定具體類型
        /// </summary>
        public class CommonInterface : IGenericInterface<string>
        {
            public string GetT(string t)
            {
                return t;
            }
        }

        /// <summary>
        /// 子類也是泛型的,繼承的時候可以不指定具體類型
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public class CommonClassChild<T> : GenericClass<T>
        {

        }

泛型委托

泛型委托主要是想講一下Action和Func兩個委托,因為這兩個在Linq中是經常見到的。

Action只能委托必須是無返回值的方法

Fun只是委托必須有返回值的方法

不管是不是泛型委托,只要是委托委托那能用Lamdba表達式,因為不管Lamdba表達式還是匿名函數其實都是將函數變數化。

下麵簡單的來做的demo說下兩個的用法,這個會了基本linq會了一半了。

    static void Main(string[] args)
    {
        #region 泛型委托
        Action<string> action = s => {
            Console.WriteLine(s);
        };
        action("i3yuan");
        Func<int, int, int> func = (int a, int b) => {
            return a + b;
        };
        Console.WriteLine("sum:{0}", func(1,1));
        Console.ReadLine();
        #endregion
    }

上面其實都是將函數做為變數,這也是委托的思想。action是實例化了一個只有一個字元串參數沒有返回值得函數變數。func是實例化了一個有兩個int類型的參數返回值為int的函數變數。

可以看到通過Lamdba表達式和泛型的結合,算是又方便了開發者們,更加方便實用。

引入委托常用的另一方式

無論是在類定義內還是類定義外,委托可以定義自己的類型參數。引用泛型委托的代碼可以指定類型參數來創建一個封閉構造類型,這和實例化泛型類或調用泛型方法一樣,如下例所示:

public delegate void MyDelegate<T>(T item);
public void Notify(int i){}
//...
 
MyDelegate<int> m = new MyDelegate<int>(Notify);
 
C#2.0版有個新特性稱為方法組轉換(method group conversion),具體代理和泛型代理類型都可以使用。用方法組轉換可以把上面一行寫做簡化語法:
MyDelegate<int> m = Notify;
 
在泛型類中定義的委托,可以與類的方法一樣地使用泛型類的類型參數。
class Stack<T>
{
T[] items;
      int index
//...
public delegate void StackDelegate(T[] items);
}
 
引用委托的代碼必須要指定所在類的類型參數,如下:
 
Stack<float> s = new Stack<float>();
Stack<float>.StackDelegate myDelegate = StackNotify;
 
 
泛型委托在定義基於典型設計模式的事件時特別有用。因為sender[JX2] ,而再也不用與Object相互轉換。
public void StackEventHandler<T,U>(T sender, U eventArgs);
class Stack<T>
{
    //…
    public class StackEventArgs : EventArgs{...}
    public event StackEventHandler<Stack<T>, StackEventArgs> stackEvent;
    protected virtual void OnStackChanged(StackEventArgs a)
    {
      stackEvent(this, a);
    }
}
class MyClass
{
  public static void HandleStackChange<T>(Stack<T> stack, StackEventArgs args){...};
}
Stack<double> s = new Stack<double>();
MyClass mc = new MyClass();
s.StackEventHandler += mc.HandleStackChange;


泛型約束

所謂的泛型約束,實際上就是約束的類型T。使T必須遵循一定的規則。比如T必須繼承自某個類,或者T必須實現某個介面等等。那麼怎麼給泛型指定約束?其實也很簡單,只需要where關鍵字,加上約束的條件。

定義一個People類,裡面有屬性和方法:

    public interface ISports
    {
        void Pingpang();
    }
    public interface IWork
    {
        void Work();
    }
    public class People
    {
        public int Id { get; set; }
        public string Name { get; set; }
    
        public void Hi()
        {
            Console.WriteLine("Hi");
        }
    
    }
    public class Chinese : People, ISports, IWork
    {
        public void Tradition()
        {
            Console.WriteLine("仁義禮智信,溫良恭儉讓");
        }
        public void SayHi()
        {
            Console.WriteLine("吃了麽?");
        }
    
        public void Pingpang()
        {
            Console.WriteLine("打乒乓球...");
        }
    
        public void Work()
        {
            throw new NotImplementedException();
        }
    } 
    public class Hubei : Chinese
    {
        public Hubei(int version)
        { }
    
        public string Changjiang { get; set; }
        public void Majiang()
        {
            Console.WriteLine("打麻將啦。。");
        }
    }
    public class Japanese : ISports
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public void Hi()
        {
            Console.WriteLine("Hi");
        }
        public void Pingpang()
        {
            Console.WriteLine("打乒乓球...");
        }
    }

列印方法

    /// <summary>
    /// 列印個object值
    /// 1 object類型是一切類型的父類
    /// 2 通過繼承,子類擁有父類的一切屬性和行為;任何父類出現的地方,都可以用子類來代替
    /// object引用類型  加入傳個值類型int  會有裝箱拆箱  性能損失
    /// 類型不安全
    /// </summary>
    /// <param name="oParameter"></param>
    public static void ShowObject(object oParameter)
    {
        Console.WriteLine("This is {0},parameter={1},type={2}",
            typeof(Constraint), oParameter.GetType().Name, oParameter);

        Console.WriteLine($"{((People)oParameter).Id}_{((People)oParameter).Name}");

    }

在main方法中

    static void Main(string[] args)
    {
        #region  Constraint 介面約束
        Console.WriteLine("************Constraint*****************");
        {
            People people = new People()
            {
                Id = 123,
                Name = "走自己的路"
            };
            Chinese chinese = new Chinese()
            {
                Id = 234,
                Name = "晴天"
            };
            Hubei hubei = new Hubei(123)
            {
                Id = 345,
                Name = "流年"
            };
            Japanese japanese = new Japanese()
            {
                Id = 7654,
                Name = "i3yuan"//
            };
            CommonMethod.ShowObject(people);
            CommonMethod.ShowObject(chinese);
            CommonMethod.ShowObject(hubei);
            CommonMethod.ShowObject(japanese);
  
            Console.ReadLine();
        }
        #endregion
    }

泛型約束總共有五種。

約束 說明
T:結構 類型參數必須是值類型
T:類 類型參數必須是引用類型;這一點也適用於任何類、介面、委托或數組類型。
T:new() 類型參數必須具有無參數的公共構造函數。 當與其他約束一起使用時,new() 約束必須最後指定。
T:<基類名> 類型參數必須是指定的基類或派生自指定的基類。
T:<介面名稱> 類型參數必須是指定的介面或實現指定的介面。 可以指定多個介面約束。 約束介面也可以是泛型的。

1、基類約束

上面列印的方法約束T類型必須是People類型。

///


/// 基類約束:約束T必須是People類型或者是People的子類
/// 1 可以使用基類的一切屬性方法---權利
/// 2 強制保證T一定是People或者People的子類---義務

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="tParameter"></param>
        public static void Show<T>(T tParameter) where T : People
        {
            Console.WriteLine($"{tParameter.Id}_{tParameter.Name}");
            tParameter.Hi();
        }

註意:

基類約束時,基類不能是密封類,即不能是sealed類。sealed類表示該類不能被繼承,在這裡用作約束就無任何意義,因為sealed類沒有子類。

2、介面約束

        /// <summary>
        /// 介面約束
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        /// <returns></returns>
        public static T Get<T>(T t) where T : ISports
        {
            t.Pingpang();
            return t;
        }

3、引用類型約束 class

引用類型約束保證T一定是引用類型的。

        /// 引用類型約束
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        /// <returns></returns>
        public static T Get<T>(T t) where T : class
        {
            return t;
        }

4、值類型約束 struct

值類型約束保證T一定是值類型的。

        /// 值類型類型約束
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        /// <returns></returns>
        public static T Get<T>(T t) where T : struct
        {
            return t;
        }

5、無參數構造函數約束 new()

        /// new()約束
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        /// <returns></returns>
        public static T Get<T>(T t) where T : new()
        {
            return t;
        }

泛型約束也可以同時約束多個,例如:

        /// <summary>
        ///  泛型:不同的參數類型都能進來;任何類型都能過來,你知道我是誰?
        /// 沒有約束,也就沒有自由
        ///  泛型約束--基類約束(不能是sealed):
        /// 1 可以使用基類的一切屬性方法---權利
        /// 2  強制保證T一定是People或者People的子類---義務
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="tParameter"></param>
        public static void Show<T>(T tParameter)
        where T : People, ISports, IWork, new()
        {
            Console.WriteLine($"{tParameter.Id}_{tParameter.Name}");
            tParameter.Hi();
            tParameter.Pingpang();
            tParameter.Work();
        }

註意:有多個泛型約束時,new()約束一定是在最後。

泛型的協變和逆變

    public class Animal
    {
        public int Id { get; set; }
    }

    public class Cat : Animal
    {
        public string Name { get; set; }
    }
    

 static void Main(string[] args)
 {
    #region 協變和逆變

    // 直接聲明Animal類
    Animal animal = new Animal();
    // 直接聲明Cat類
    Cat cat = new Cat();
    // 聲明子類對象指向父類
    Animal animal2 = new Cat();
    // 聲明Animal類的集合
    List<Animal> listAnimal = new List<Animal>();
    // 聲明Cat類的集合
    List<Cat> listCat = new List<Cat>();

    #endregion
 }

那麼問題來了:下麵的一句代碼是不是正確的呢?

1 List<Animal> list = new List<Cat>();

可能有人會認為是正確的:因為一隻Cat屬於Animal,那麼一群Cat也應該屬於Animal啊。但是實際上這樣聲明是錯誤的:因為List和List之間沒有父子關係。

image-2020053023015097

這時就可以用到協變和逆變了。

1 // 協變
2 IEnumerable<Animal> List1 = new List<Animal>();
3 IEnumerable<Animal> List2 = new List<Cat>();

F12查看定義:

可以看到,在泛型介面的T前面有一個out關鍵字修飾,而且T只能是返回值類型,不能作為參數類型,這就是協變。使用了協變以後,左邊聲明的是基類,右邊可以聲明基類或者基類的子類。

協變除了可以用在介面上面,也可以用在委托上面:

 Func<Animal> func = new Func<Cat>(() => null);

除了使用.NET框架定義好的以為,我們還可以自定義協變,例如:

    /// <summary>
    /// out 協變 只能是返回結果
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public interface ICustomerListOut<out T>
    {
        T Get();
    }

    public class CustomerListOut<T> : ICustomerListOut<T>
    {
        public T Get()
        {
            return default(T);
        }
    }

使用自定義的協變:

 // 使用自定義協變
 ICustomerListOut<Animal> customerList1 = new CustomerListOut<Animal>();
 ICustomerListOut<Animal> customerList2 = new CustomerListOut<Cat>();

在來看看逆變。

在泛型介面的T前面有一個In關鍵字修飾,而且T只能方法參數,不能作為返回值類型,這就是逆變。請看下麵的自定義逆變:

    /// <summary>
    /// 逆變 只能是方法參數
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public interface ICustomerListIn<in T>
    {
        void Show(T t);
    }

    public class CustomerListIn<T> : ICustomerListIn<T>
    {
        public void Show(T t)
        {
        }
    }

使用自定義逆變:

 // 使用自定義逆變
 ICustomerListIn<Cat> customerListCat1 = new CustomerListIn<Cat>();
 ICustomerListIn<Cat> customerListCat2 = new CustomerListIn<Animal>();

協變和逆變也可以同時使用,看看下麵的例子:

    /// <summary>
    /// inT 逆變
    /// outT 協變
    /// </summary>
    /// <typeparam name="inT"></typeparam>
    /// <typeparam name="outT"></typeparam>
    public interface IMyList<in inT, out outT>
    {
        void Show(inT t);
        outT Get();
        outT Do(inT t);
    }

    public class MyList<T1, T2> : IMyList<T1, T2>
    {

        public void Show(T1 t)
        {
            Console.WriteLine(t.GetType().Name);
        }

        public T2 Get()
        {
            Console.WriteLine(typeof(T2).Name);
            return default(T2);
        }

        public T2 Do(T1 t)
        {
            Console.WriteLine(t.GetType().Name);
            Console.WriteLine(typeof(T2).Name);
            return default(T2);
        }
    }

使用:

 IMyList<Cat, Animal> myList1 = new MyList<Cat, Animal>();
 IMyList<Cat, Animal> myList2 = new MyList<Cat, Cat>();//協變
 IMyList<Cat, Animal> myList3 = new MyList<Animal, Animal>();//逆變
 IMyList<Cat, Animal> myList4 = new MyList<Animal, Cat>();//逆變+協變

有關可變性的註意事項

  • 變化只適用於引用類型,因為不能直接從值類型派生其他類型
  • 顯示變化使用in和out關鍵字只適用於委托和介面,不適用於類、結構和方法
  • 不包括in和out關鍵字的委托和介面類型參數叫做不變

泛型緩存

在前面我們學習過,類中的靜態類型無論實例化多少次,在記憶體中只會有一個。靜態構造函數只會執行一次。在泛型類中,T類型不同,每個不同的T類型,都會產生一個不同的副本,所以會產生不同的靜態屬性、不同的靜態構造函數,請看下麵的例子:

public class GenericCache<T>
{
    static GenericCache()
    {
        Console.WriteLine("This is GenericCache 靜態構造函數");
        _TypeTime = string.Format("{0}_{1}", typeof(T).FullName, DateTime.Now.ToString("yyyyMMddHHmmss.fff"));
    }

    private static string _TypeTime = "";

    public static string GetCache()
    {
        return _TypeTime;
    }
}
public class GenericCacheTest
{
    public static void Show()
    {
        for (int i = 0; i < 5; i++)
        {
            Console.WriteLine(GenericCache<int>.GetCache());
            Thread.Sleep(10);
            Console.WriteLine(GenericCache<long>.GetCache());
            Thread.Sleep(10);
            Console.WriteLine(GenericCache<DateTime>.GetCache());
            Thread.Sleep(10);
            Console.WriteLine(GenericCache<string>.GetCache());
            Thread.Sleep(10);
            Console.WriteLine(GenericCache<GenericCacheTest>.GetCache());
            Thread.Sleep(10);
        }
    }
}

Main()方法裡面調用:

 static void Main(string[] args)
 {
    #region 泛型緩存
	GenericCacheTest.Show();
    #endregion
 }

結果:

20200530232600809

從上面的截圖中可以看出,泛型會為不同的類型都創建一個副本,所以靜態構造函數會執行5次。 而且每次靜態屬性的值都是一樣的。利用泛型的這一特性,可以實現緩存。

註意:只能為不同的類型緩存一次。泛型緩存比字典緩存效率高。泛型緩存不能主動釋放。

註意

1.泛型代碼中的 default 關鍵字

在泛型類和泛型方法中會出現的一個問題是,如何把預設值賦給參數化類型,此時無法預先知道以下兩點:

  • T將是值類型還是引用類型

  • 如果T是值類型,那麼T將是數值還是結構

對於一個參數化類型T的變數t,僅當T是引用類型時,t = null語句才是合法的; t = 0只對數值的有效,而對結構則不行。這個問題的解決辦法是用default關鍵字,它對引用類型返回空,對值類型的數值型返回零。而對於結構,它將返回結構每個成員,並根據成員是值類型還是引用類型,返回零或空。下麵GenericList類的例子顯示瞭如何使用default關鍵字。

    static void Main(string[] args)
    {
        #region 泛型代碼預設關鍵字default
        // 使用非空的整數列表進行測試.
        GenericList<int> gll = new GenericList<int>();
        gll.AddNode(5);
        gll.AddNode(4);
        gll.AddNode(3);
        int intVal = gll.GetLast();
        // 下麵一行顯示5.
        Console.WriteLine(intVal);

        // 用一個空的整數列表進行測試.
        GenericList<int> gll2 = new GenericList<int>();
        intVal = gll2.GetLast();
        // 下麵一行顯示0.
        Console.WriteLine(intVal);

        // 使用非空字元串列表進行測試.
        GenericList<string> gll3 = new GenericList<string>();
        gll3.AddNode("five");
        gll3.AddNode("four");
        string sVal = gll3.GetLast();
        // 下麵一行顯示five.
        Console.WriteLine(sVal);

        // 使用一個空字元串列表進行測試.
        GenericList<string> gll4 = new GenericList<string>();
        sVal = gll4.GetLast();
        // 下麵一行顯示一條空白行.
        Console.WriteLine(sVal);
        #endregion
        Console.ReadKey();
    }
    public class GenericList<T>
    {
        private class Node
        {
            // 每個節點都有一個指向列表中的下一個節點的引用.
            public Node Next;
            // 每個節點都有一個T類型的值.
            public T Data;
        }

        // 這個列表最初是空的.
        private Node head = null;

        // 在列表開始的時候添加一個節點,用t作為它的數據值.
        public void AddNode(T t)
        {
            Node newNode = new Node();
            newNode.Next = head;
            newNode.Data = t;
            head = newNode;
        }

        // 下麵的方法返回存儲在最後一個節點中的數據值列表. 如果列表是空的, 返回類型T的預設值.
        public T GetLast()
        {
            // 臨時變數的值作為方法的值返回. 
            // 下麵的聲明初始化了臨時的溫度 
            // 類型T的預設值. 如果該列表為空返回預設值.
            T temp = default(T);

            Node current = head;
            while (current != null)
            {
                temp = current.Data;
                current = current.Next;
            }
            return temp;
        }
    }

2.泛型集合

通常情況下,建議您使用泛型集合,因為這樣可以獲得類型安全的直接優點而不需要從基集合類型派生並實現類型特定的成員。下麵的泛型類型對應於現有的集合類型:

1、List 是對應於 ArrayList 的泛型類。
2、Dictionary 是對應於 Hashtable 的泛型類。
3、Collection 是對應於 CollectionBase 的泛型類。
4、ReadOnlyCollection 是對應於 ReadOnlyCollectionBase 的泛型類。
5、QueueStackSortedList 泛型類分別對應於與其同名的非泛型類。
6、LinkedList 是一個通用鏈接列表,它提供運算複雜度為 O(1) 的插入和移除操作。
7、SortedDictionary 是一個排序的字典,其插入和檢索操作的運算複雜度為 O(log n),這使得它成為 SortedList 的十分有用的替代類型。
8、KeyedCollection 是介於列表和字典之間的混合類型,它提供了一種存儲包含自己鍵的對象的方法。

總結

  1. 作為一個開發人員,當我們程式代碼有相同的邏輯,有可能是方法、介面、類或者委托,只是某些參數類型不同,我們希望代碼可以通用、復用,甚至是說為了偷懶,也可以說是在不確定類型的情況下,就應該考慮用泛型的思維去實現。
  2. 在非泛型編程中,雖然所有的東西都可以作為Object傳遞,但是在傳遞的過程中免不了要進行類型轉換。而類型轉換在運行時是不安全的。使用泛型編程將可以減少不必要的類型轉換,從而提高安全性。不僅是值類型,引用類型也存在這樣的問題,因此有必要的儘量的去使用泛型集合。
  3. 在非泛型編程中,將簡單類型作為Object傳遞時會引起裝箱和拆箱的操作,這兩個過程都是具有很大開銷的。使用泛型編程就不必進行裝箱和拆箱操作了。

參考 文檔 《C#圖解教程》

註:搜索關註公眾號【DotNet技術谷】--回覆【C#圖解】,可獲取 C#圖解教程文件


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 註釋是為了使別人能看懂你寫的程式,也為了使你在若幹年後還能看得懂你曾經寫的程式而設定的。 註釋是寫給程式員看的,不是寫給電腦看的。所以註釋的內容,C語言編譯器在編譯時會被自動忽略,不會執行註釋的代碼! 方法一:使用// #include "stdafx.h" //main函數 :程式的入口函數,就好 ...
  • 有很多剛學習軟體測試的小伙伴,都會在網路上找尋各種學習資料,去提升自己的專業技能水平。因此,我決定定期分享我整理收集的一些軟體測試的測試工具下載、面試寶典、視頻教學合集。都整理好了,有需要的可以關註我(獲取方式在文末) 軟體測試的學習,不止是基礎理論,還需要學習測試工具的用法,如介面工具Postma ...
  • 女程式員是這麼徵婚的: SELECT * FROM 男人們 WHERE 未婚=true and 同性戀=false and 有房=true and 有車=true and 條件 in (帥氣,紳士,大度,氣質,智慧,溫柔,體貼,會浪漫,活潑,可愛,最好還能帶孩子) and 年齡 between(24 ...
  • API 概述 API(Application Programming Interface),應用程式編程介面。 Java API是一本程式員的 字典 ,是JDK中提供給我們使用的類的說明文檔。 這些類將底層的代碼實現封裝了起來,我們不需要關心這些類是如何實現的,只需要學習這些類如何使用即可。 所以我 ...
  • 0. 前言 該項目使用Maven進行管理和構建,所以需要預先配置好Maven。嗯,在這個系列里就不做過多的介紹了。 1. 創建項目 先創建一個pom.xml 文件,添加以下內容: <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http: ...
  • Data StructureThere're two types of variables in C#, reference type and value type.Enum:enum Color{Red=0,Green=1}//equals to enum Color{Red,//start fr... ...
  • 出庫單的功能。能學習了出庫單管理之後,WMS的 主體功能算是完成了。當然一個成熟的WMS還包括了盤點,報表,策略規則,移庫功能及與其他系統(ERP、TMS等)的介面,實現無縫集成,打破信息孤島,讓數據實時、準確和同步。 ...
  • 本文章主要用於介紹在Asp.Net Mvc(C#)中使用Fleck製作一個Html5的即時聊天室,含有完整代碼和演示Demo。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...