.Net自從2.0版本開始就支援泛型。使用泛型類型可以最大限度地重用代碼、保護類型的安全以及提高性能。
性能
var list = new ArrayList();
list.Add(20);//裝箱--将值類型轉換為引用類型
int num = (int)list[0];//拆箱--将引用類型轉換為值類型
foreach (int i in list)//拆箱
{
Console.WriteLine(i);
}
ArrayList存儲對象,是以Add方法會進行裝箱操作,當讀取ArrayList中的值時,需要将其轉換為int類型,需要進行拆箱操作。裝箱、拆箱操作很容易使用,但是性能損失較大,特别是周遊許多項時。
此時,我們可以使用List<T>泛型類來減少裝箱和拆箱操作,類型參數T定義為int類型
var list = new List<int>();
list.Add(20);//不進行裝箱操作
int num = list[0];//不進行拆箱操作
foreach (int i in list)//不進行拆箱操作
{
Console.WriteLine(i);
}
類型安全
1 var list = new ArrayList();
2 list.Add(20);
3 list.Add("abc");
4 foreach (int i in list)//集合中并非所有元素都可以強制轉化為int類型,會出現一個運作異常
5 {
6 Console.WriteLine(i);
7 }
泛型類List<T>中,參數類型T可以定義允許使用的資料類型
1 var list = new List<int>();//參數類型T設定為int類型,隻允許對int類型的數值進行操作
2 list.Add(20);
3 list.Add("abc");//傳入參數資料類型與設定的參數類型不符,會導緻錯誤
代碼複用性
我們看如下代碼,為了實作對不同資料類型的數組進行周遊,我們需要為每種類型都編寫一個方法:
1 class Generic
2 {
3 public void Main()
4 {
5 int[] arr_int = { 1, 2, 3 };
6 string[] arr_string = { "abc", "123" };
7 char[] arr_char = { 'a', 'b', 'c' };
8 PrintArray(arr_int);
9 PrintArray(arr_string);
10 PrintArray(arr_char);
11
12 Console.ReadKey();
13 }
14 //周遊int類型的數組
15 void PrintArray(int[] arr)
16 {
17 foreach (int i in arr)
18 {
19 Console.WriteLine(i);
20 }
21 }
22 //周遊string類型的數組
23 void PrintArray(string[] arr)
24 {
25 foreach (string str in arr)
26 {
27 Console.WriteLine(str);
28 }
29 }
30 //周遊char類型的數組
31 void PrintArray(char[] arr)
32 {
33 foreach (char ch in arr)
34 {
35 Console.WriteLine(ch);
36 }
37 }
38 }
但是如果我們使用泛型的話,代碼就簡單多了
1 class Generic
2 {
3 public void Main()
4 {
5 int[] arr_int = { 1, 2, 3 };
6 string[] arr_string = { "abc", "123" };
7 char[] arr_char = { 'a', 'b', 'c' };
8 //完整的寫法
9 PrintArray<int>(arr_int);
10 PrintArray<string>(arr_string);
11 PrintArray<char>(arr_char);
12 //簡便寫法
13 PrintArray(arr_int);
14 PrintArray(arr_string);
15 PrintArray(arr_char);
16
17 Console.ReadKey();
18 }
19 //周遊數組
20 void PrintArray<T>(T[] arr)
21 {
22 foreach (T item in arr)
23 {
24 Console.WriteLine(item);
25 }
26 }
27 }
命名約定
如果在程式中使用了泛型,遵循泛型的命名規則可以幫助我們區分泛型類型和非泛型類型。
泛型類型的命名規則:
- 泛型類型的名稱用字母T作為字首;
1 class TClassName<T>
2 {
3 //...
4 }
- 如果沒有特殊的要求,泛型類型允許用任意的類型代替,并且隻使用了一個泛型類型,就可以用T作為泛型類型的名稱;
- 如果泛型類型有特定的要求的(例如,他必須實作一個接口或派生自基類),或者使用了兩個或多個泛型類型,就應該為泛型類型使用描述性的名稱
1 class TClassName<TParam1, Tparam2>
2 {
3 //...
4 }
泛型類的預設值
我們看下邊的代碼,我們需要将變量value進行初始化,要求如果value是引用類型就初始化為null,如果value是值類型就初始化為0;但是泛型中并不能确定T的類型是值類型或是引用類型,那我們要如何進行初始化呢?為了解決這個問題,我們可以是會用default關鍵字。default關鍵字會根據T的類型進行初始化,如果T為引用類型就将其設定為null,如果T為值類型就将其設定為0。
1 public T Test<T>()
2 {
3 T value = default(T);
4 //..Do Something
5 return value;
6 }
限制
限制 | 說明 |
where T:struct | 指定類型T必須是值類型 |
where T:class | 指定類型T必須是引用類型 |
where T:IFoo | 指定類型T必須實作接口IFoo |
where T:Foo | 指定類型T必須派生自基類Foo |
where T:new() | 構造函數限制,指定類型T必須有一個預設的無參構造函數(構造函數隻能對無參的構造函數進行限制) |
where T1:T2 | 類型T1派生自泛型類型T2 |