自從上次參加完俱樂部的聚會後,覺的有必要總結下泛型的用法,雖然腦袋講的 非常仔細,沒有必要再寫,但做為學習者,我喜歡把自己的所學以文章的形式展示出 來,這樣也有我的一部分。我們可以用一個簡單的例子來做實驗:實例化一個 ArrayList和一個List<int>,然後往其中加入成員,最後分別讀取出第一個 成員,進行一個加法操作。
ArrayList _list = new ArrayList(); _list.Add(1); _list.Add("a"); int i = (int)_list[0] + 1; List<int> _list2 = new List<int>(); _list2.Add(1);//正確 int j = (int)_list2[0] + 1;
ArrayList的缺點:
1:處理值類型時,出現裝箱、折箱操作,影響性能。_list.Add(1)時會發生裝 箱。
2:處理引用類型時,雖沒有裝箱和折箱操作,但仍需要類型轉換操作。代碼 _list.Add("a")不會發生裝箱。
3:程序運行時的類型轉換可能引發異常。運行_list[1]時,由於它是一個字符 串,要強制轉換成int就會有異常。
泛型處理過程:泛型用一個通用的數據類型T來代替object,在類實例化時指 定T的類型,CLR自動編譯為本地代碼,並且保證數據類型安全。
泛型優點:
1:類型安全的。例如實例化了int類型的類,就不能處理string類型的數據。 上面的_list2.Add("a"),就會報錯。
2:處理值類型時,無需裝箱和折箱。int j=i+1;i可以直接取,並不需要折箱操 作。
3:無需類型轉換,包含值類型和引用類型。
我們來看下它生成的IL代碼:
// 代碼大小 72 (0x48) .maxstack 2 .locals init ([0] class [mscorlib]System.Collections.ArrayList _list, [1] int32 i, [2] class [mscorlib] System.Collections.Generic.List`1<int32> _list2, [3] int32 j) IL_0000: nop IL_0001: newobj instance void [mscorlib] System.Collections.ArrayList::.ctor() IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: ldc.i4.1 IL_0009: box [mscorlib]System.Int32 IL_000e: callvirt instance int32 [mscorlib] System.Collections.ArrayList::Add(object) IL_0013: pop IL_0014: ldloc.0 IL_0015: ldstr "a" IL_001a: callvirt instance int32 [mscorlib] System.Collections.ArrayList::Add(object) IL_001f: pop IL_0020: ldloc.0 IL_0021: ldc.i4.0 IL_0022: callvirt instance object [mscorlib] System.Collections.ArrayList::get_Item(int32) IL_0027: unbox.any [mscorlib]System.Int32 IL_002c: ldc.i4.1 IL_002d: add IL_002e: stloc.1 IL_002f: newobj instance void class [mscorlib] System.Collections.Generic.List`1<int32>::.ctor() IL_0034: stloc.2 IL_0035: ldloc.2 IL_0036: ldc.i4.1 IL_0037: callvirt instance void class [mscorlib] System.Collections.Generic.List`1<int32>::Add(!0) IL_003c: nop IL_003d: ldloc.2 IL_003e: ldc.i4.0 IL_003f: callvirt instance !0 class [mscorlib] System.Collections.Generic.List`1<int32>::get_Item(int32) IL_0044: ldc.i4.1 IL_0045: add IL_0046: stloc.3 IL_0047: ret
第一:ArrayList與List<T>的構造函數說明了泛型的類型安全優點:
1:實例化ArrayList:
IL_0001: newobj instance void [mscorlib] System.Collections.ArrayList::.ctor()
2:實例化List<T>:
IL_002f: newobj instance void class [mscorlib] System.Collections.Generic.List`1<int32>::.ctor()
第二:IL_0009: box [mscorlib]System.Int32這條語句表 明一個整數被加入到ArrayList時會出現裝箱。而List<int>的IL代碼中並 沒有出現box語句。這點可以證明泛型的優點二。
第三:兩者讀取成員的操作不同可以說明泛型的優點三。
1:下面的語句表明當從ArrayList中讀取一個成員時,需要進行拆箱操作。
IL_0022: callvirt instance object [mscorlib] System.Collections.ArrayList::get_Item(int32) IL_0027: unbox.any [mscorlib]System.Int32
2:下面的代碼表明從List<int>中讀取一個成員時,不需要進行拆箱操 作。
IL_003f: callvirt instance !0 class [mscorlib] System.Collections.Generic.List`1<int32>::get_Item(int32) IL_0044: ldc.i4.1
泛型類實例化的理論:C#泛型類在編譯時,先生成中間代碼IL,通用類型T只 是一個占位符。在實例化類時,根據用戶指定的數據類型代替T並由即時編譯器 (JIT)生成本地代碼,這個本地代碼中已經使用了實際的數據類型。
泛型中的靜態成員變量:在C#1.x中,類的靜態成員變量在不同的類實例間是 共享的,C#2.0中,靜態成員變量在相同封閉類間共享,不同的封閉類間不共享。示 例代碼:
public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { Point<int> _point = new Point<int> (1); Point<int> _point2 = new Point<int> (2); Point<long> _point3 = new Point<long>(3); string width1 = _point.ToString();//結果為2 string width2 = _point2.ToString();//結果為2 string width3 = _point3.ToString();//結果為3 //width1和width2相等,但和width3不等,說明了靜態成 員變量在相同封閉類間共享, 不同的封閉類間不共享。 } } /// <summary> /// 一個泛型類,接受一個T /// </summary> /// <typeparam name="T"></typeparam> public class Point<T> { /// <summary> /// 靜態變量 /// </summary> public static T width; /// <summary> /// 構造函數 /// </summary> /// <param name="_width"></param> public Point(T _width) { width = _width; } /// <summary> /// 輸出width值 /// </summary> /// <returns></returns> public override string ToString() { return width .ToString (); } }
小結:類實例_point和_point2是同一類型,他們之間共享靜態成員變量,但 類實例_point3卻是和_point、_point2完全不同的類型,所以不能和_point、 _point2共享靜態成員變量。
說明:泛型還有許多內容,像泛型委托,泛型接口,泛型方法,泛型類,泛型 約束,其實上面的都是泛型的最基本的知識,其它的應用都建立在這些基礎之上 。