這篇文章主要來講講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 }
下面來看看泛型是如何解決這些問題的:
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 }
3,如何使用泛型:語法和規則
上面已經說了什么是泛型,以及為什么要用泛型,下面我們來聊聊如何使用泛型
1 public class Demo<T> { } 2 class Program 3 { 4 static void Main(string[] args) 5 { 6 object o = null; 7 Type t = typeof(Demo<>); 8 o = Activator.CreateInstance(t); 9 }10 }
如果為泛型類型的所有類型實參傳遞的都是實際數據類型,類型就稱為封閉類型,CLR允許構造封閉類型的實例。
1 // 摘要: 2 // 支持在泛型集合上進行簡單迭代。 3 // 4 // 類型參數: 5 // T: 6 // 要枚舉的對象的類型。 7 public interface IEnumerator<out T> : IDisposable, IEnumerator 8 { 9 // 摘要:10 // 獲取集合中位于枚舉數當前位置的元素。11 //12 // 返回結果:13 // 集合中位于枚舉數當前位置的元素。14 T Current { get; }15 }
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 MyClass<string> myclass = new MyClass<string>(); 6 myclass.ShowInfo("myClass"); 7 } 8 } 9 10 class MyClass<Ta>11 {12 public void ShowInfo(Ta a)13 {14 Type t = typeof(Ta);15 Console.WriteLine(t.FullName + "非泛型方法");16 }17 public void ShowInfo<Ta>(Ta a)18 {19 Type t = typeof(Ta);20 Console.WriteLine(t.FullName + "泛型方法");21 }22 public void ShowInfo<Tb>(Ta a, Tb b)23 {24 Type t = typeof(Tb);25 Console.WriteLine(t.FullName);26 }27 }
泛型方法的類型推斷:c#語法中包含大量"<"和">"符號,所以導致代碼的可讀性和可維護性降低了,所以為了改變這種情況,c#編譯器支持在調用一個方法時進行類型推斷。例如下面的代碼:myclass.ShowInfo("myClass", myclass);會調用ShowInfo<string>(string a, string b);
1 public static Boolean MethodTakingAnyType<T>(T o) {2 T temp = o;3 Console.WriteLine(o.ToString());4 Boolean b = temp.Equals(o);5 return b;6 }
看這個方法里面有一個臨時變量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 }
在編譯時會報如下錯誤(error CS0117:"T"不包含"CompareTo"的定義),因為并不是所有的類型都提供了CompareTo方法。那么在什么情況下T應該是什么類型呢?幸好,編譯器和CLR支持一個稱為約束的機制,可利用它使泛型變得真正有用!
1 public static Boolean MethodTakingAnyType<T>(T o) where T : struct2 {3 T temp = o;4 Console.WriteLine(o.ToString());5 Boolean b = temp.Equals(o);6 return b;7 }
c#的where關鍵字告訴編譯器,為T指定的任何類型都必須是值類型。所以當你為T指定其它類型時,編譯器會報錯,例如你指定為string類型時(MethodTakingAnyType<string>("");)它會報錯誤 1 類型“string”必須是不可以為 null 值的類型才能用作泛型類型或方法
1 public static Boolean MethodTakingAnyType<T, TBase>(T o) where T : TBase2 {3 T temp = o;4 Console.WriteLine(o.ToString());5 Boolean b = temp.Equals(o);6 return b;7 }
T類型參數由TBase類型單數約束,也就是說不管T為什么類型,都必須兼容于TBase指定的類型實參。
1 public static Boolean MethodTakingAnyType<T, TBase>(T o) where T : new()2 {3 T temp = o;4 Console.WriteLine(o.ToString());5 Boolean b = temp.Equals(o);6 return b;7 }
1 public static void MethodTakingAnyType<T, TBase>(T o)2 {3 int x = (int)o;4 string s = (string)o;5 }
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 something5 }6 }
4,將兩個泛型類型變量相互比較:如果T是值類型,下面的代碼就是非法的。
1 public static void MethodTakingAnyType<T>(T o1,T o2)2 {3 if (o1 == o2) { 4 5 }6 }
4,泛型在使用過程的注意事項
新聞熱點
疑難解答