這篇文章主要來講講c#中的泛型,因為泛型在c#中有很重要的位置,對於寫出高可讀性,高性能的代碼有著關鍵的作用。當我多次看到自己團隊的代碼中包含著大量的非泛型集合,隱式的裝箱和拆箱操作時,我都會建議他們補一補泛型基礎。
1,什麼是泛型
2,為什麼要使用泛型,泛型解決了什麼問題
我們先來看看下面的代碼(代碼只是為了演示泛型,沒有實際的意義),看看有什麼問題?
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 ArrayList array = new ArrayList(); 6 array.Add(1); 7 array.Add(2); 8 array.Add(3); 9 ArrayList resultArray = NumberSqrt(array); 10 foreach (var item in resultArray) 11 { 12 Console.WriteLine(item); 13 } 14 } 15 public static ArrayList NumberSqrt(ArrayList array) 16 { 17 ArrayList sqrtArray = new ArrayList(); 18 foreach (var item in array) 19 { 20 sqrtArray.Add(Math.Sqrt((double)(int)item)); 21 } 22 return sqrtArray; 23 24 } 25 } View Code下面來看看泛型是如何解決這些問題的:
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 List<double> array = new List<double>(); 6 array.Add(1); 7 array.Add(2); 8 array.Add(3); 9 List<double> resultArray = NumberSqrt(array); 10 foreach (var item in resultArray) 11 { 12 Console.WriteLine(item); 13 } 14 } 15 public static List<double> NumberSqrt(List<double> array) 16 { 17 List<double> sqrtArray = new List<double>(); 18 foreach (var item in array) 19 { 20 sqrtArray.Add(Math.Sqrt((double)(int)item)); 21 } 22 return sqrtArray; 23 24 } 25 26 } View Code3,如何使用泛型:語法和規則
上面已經說了什麼是泛型,以及為什麼要用泛型,下面我們來聊聊如何使用泛型
如果為泛型類型的所有類型實參傳遞的都是實際數據類型,類型就稱為封閉類型,CLR允許構造封閉類型的實例。
泛型方法的類型推斷:c#語法中包含大量"<"和">"符號,所以導致代碼的可讀性和可維護性降低了,所以為了改變這種情況,c#編譯器支持在調用一個方法時進行類型推斷。例如下面的代碼:myclass.ShowInfo("myClass", myclass);會調用ShowInfo<string>(string a, string b);
看這個方法裡面有一個臨時變量temp。方法裡面執行兩次變量賦值和幾次方法調用,無論T是值類型還是引用類型還是接口類型或者是委托類型這個方法都能工作。這個方法適用於當前存在的所以類型,也適用於將來可能定義的任何類型。比如你再看一下下面這個方法:
1 public static T MethodTakingAnyType<T>(T o1,T o2) { 2 if (o1.CompareTo(o2) < 0) 3 { 4 return o1; 5 } 6 else { 7 return o2; 8 } 9 } View Code在編譯時會報如下錯誤(error CS0117:"T"不包含"CompareTo"的定義),因為並不是所有的類型都提供了CompareTo方法。那麼在什麼情況下T應該是什麼類型呢?幸好,編譯器和CLR支持一個稱為約束的機制,可利用它使泛型變得真正有用!
c#的where關鍵字告訴編譯器,為T指定的任何類型都必須是值類型。所以當你為T指定其它類型時,編譯器會報錯,例如你指定為string類型時(MethodTakingAnyType<string>("");)它會報錯誤 1 類型“string”必須是不可以為 null 值的類型才能用作泛型類型或方法
T類型參數由TBase類型單數約束,也就是說不管T為什麼類型,都必須兼容於TBase指定的類型實參。
2,將一個泛型類型變量設置為默認值:o = default(T);這樣的話,不管T為值類型還是引用類型都可以成功,如果T為引用類型時就設置為null,如果T為值類型時將默認值設為0;
3,將一個泛型類型變量與Null進行比較:使用==或者=!將一個泛型類型變量於null進行比較都是合法的。但是如果T為值類型時,o永遠都不會為null查看下面的代碼: 1 public static void MethodTakingAnyType<T, TBase>(T o) 2 { 3 if (o == null) { 4 //do something 5 } 6 } View Code4,將兩個泛型類型變量相互比較:如果T是值類型,下面的代碼就是非法的。
1 public static void MethodTakingAnyType<T>(T o1,T o2) 2 { 3 if (o1 == o2) { 4 5 } 6 } View Code4,泛型在使用過程的注意事項