c#中的泛型,
這篇文章主要來講講c#中的泛型,因為泛型在c#中有很重要的位置,對於寫出高可讀性,高性能的代碼有著關鍵的作用。當我多次看到自己團隊的代碼中包含著大量的非泛型集合,隱式的裝箱和拆箱操作時,我都會建議他們補一補泛型基礎。
1,什麼是泛型
-
- 泛型是c#2中非常重要的一個新特性,它增強了代碼的可讀性,將大量的安全檢查從執行期轉移到編譯期,從而提高代碼的安全性和性能。從根本上來說,泛型實現了類型和方法的參數化。
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
-
- 首先ArrayList是一個非泛型集合,它需要的參數是object,那麼我如果往集合裡面裝配其它的類型,在編譯時也無法判斷錯誤(比如說我裝配一個字符串進去,完全是可以編譯成功的)。
- 由於ArrayList需要的是一個object,那麼在你將值類型裝配到集合中時,會進行隱式裝箱操作,在使用集合中的數據時,需要進行拆箱操作。從而影響應用程序的性能。(關於值類型裝箱請參考c#中的引用類型和值類型)。
- 代碼可以可讀性差,完全看不明白ArrayList應該裝配什麼類型。
下面來看看泛型是如何解決這些問題的:

![]()
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 Code
-
- 由於List<double>只能裝配double,所以當你裝配其它的類型時編譯器會報錯,提高代碼的安全性。
- 由於List<double>只能裝配double類型時,這樣的話就避免了編譯器隱式的裝箱和拆箱操作,提高應用程序的性能。
- 一眼就可以看出集合需要裝配的類型,提高代碼的可讀性。
- NumberSqrt方法在這裡沒什麼用去,如果你要寫一個算法,把NumberSqrt改為泛型方法:NumberSqrt<T>(比如說排序算法),可以實現算法重用。
3,如何使用泛型:語法和規則
上面已經說了什麼是泛型,以及為什麼要用泛型,下面我們來聊聊如何使用泛型
-
- 泛型的語法:泛型主要有泛型方法(例如:static void Swap<T>(ref T lhs, ref T rhs)),泛型接口( 例如: public interface IEnumerable<out T> : IEnumerable),泛型委托( 例如: public delegate void Action<in T>(T obj);)
- 在FCL中內建了許多泛型的接口定義(常用的接口都在System.Collections.Generic),為了使泛型能夠正常的工作,微軟的開發人員將會做如下工作,
- 創建新的IL指令,使之能夠識別類型參數。
- 改變元數據的格式,使之能夠識別泛型參數和泛型方法。
- 開放類型和封閉類型:當為一個泛型類型沒有指定實際的數據類型時,就稱為開放類型,例如List<T>,對於任何開放類型都不能創建該類型的實例,例如下面的代碼在運行是將會報錯未:經處理的異常: System.ArgumentException: 無法創建 ConsoleApplication2.Demo`1[T] 的實例,因為 Type.ContainsGenericParameters 為 True。

![]()
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 }
View Code
如果為泛型類型的所有類型實參傳遞的都是實際數據類型,類型就稱為封閉類型,CLR允許構造封閉類型的實例。
4,泛型在使用過程的注意事項
-
- 泛型不支持協變性
- 缺乏操作符約束或者“數值”約束
- 缺乏泛型屬性,索引器和其它成員類型