原文出處:http://www.cnblogs.com/xun126/archive/2011/01/13/1933838.html
泛型是CLR 2.0的一個新特性,在CLR 1.0中,要創建一個靈活的類或方法,但該類或方法在編譯期間不知道使用什麼類,就得以Object類為基礎。而Object在編譯期間沒有類型安全性,因此必須進行強制類型轉換,同時,給值類型使用Object類會有性能損失。泛型類使用泛型類型,並可以根據需要用特定的類型替換泛型類型。這就保證了類型安全性:如果某個類型不支持泛型類,編譯器就會報錯。
一、泛型有以下幾個優點:
1)性能
對值類型使用非泛型集合類,在把值類型轉換為引用類型,和把引用類型轉換為值類型時,需要進行裝箱和拆箱操作。裝箱和拆箱的操作很容易實現,但是性能損失較大。假如使用泛型,就可以避免裝箱和拆箱操作。
1 ArrayList list=new ArrayList(); 2 list.Add(20); //裝箱,list存放的是object類型元素,須將值類型轉化為引用類型 3 int i=(int)list[0]; //拆箱,list[0]的類型是object,要賦值就得把引用類型轉化為值類型如果換成泛型編程,就不會有裝箱和拆箱的性能損失。
1 List<T> list=new List<int>(); 2 list.Add(20); //因為指定了用int來實例化,因此不必裝箱 3 int i=list[0]; //同樣地,訪問時也不需要拆箱
2)類型安全
與ArrayList類一樣,如果使用對象,可以在這個集合中添加任意類型。
如果使用非泛型編程,如下代碼,就有可能在某些情況下會發生異常。
1 ArrayList list=new ArrayList(); 2 list.Add(20); 3 list.Add("string"); 4 list.Add(new MyClass()); 5 6 foreach(int i in list) 7 { 8 Console.WriteLine(i); //這裡會有個異常,因為並不是集合中的所有元素都可以轉化為int 9 }如果該用泛型編程,則可以避免這種異常,讓編譯器檢查出錯誤。
1 List<int> list=new List<int>(); 2 list.Add(20); 3 lsit.Add("string"); //編譯時報錯,只能報整數類型添加到集合中 4 list.Add(new MyClass()); //同上
3)二進制代碼重用
泛型可以定義一次,用許多不同的類型實例化,不需要像C++模板那樣訪問源代碼。泛型可以在一種語言中定義,在另一種.NET語言中使用。
4)代碼的擴展
因為泛型類的定義會放在程序集中,所以用某個類型實例化泛型泛型類不會在IL代碼中復制這些類。但是,在JIT編譯器把泛型類編譯為內部代碼時,會給每個值類型創建一個新類。引用類型共享同一個內部類的所有實現代碼。這是因為引用類型在實例化的泛型類中只需要4字節的內存單元(32位系統),就可以引用一個引用類型。值類型包含在實例化的泛型類的內存中。而每個值類型對內存的要求都不同,所以要為每個值類型實例化一個新類。
二、泛型類的特性
1)默認值
在給類型T初始化時,要注意不能把null賦予泛型類型。因為泛型類型也可以實例化為值類型,而null只能用於引用類型。為了解決這個問題,可以用default關鍵字。通過default關鍵字,將null賦予引用類型,將0賦予值類型。
1 public T GetDoucumet() 2 { 3 T doc=default(T); 4 lock(this) 5 { 6 doc=documentQueue.Dequeue(); 7 } 8 return doc; 9 }補充:default關鍵字根據上下文可以有多種含義。switch語句使用default定義默認情況。在泛型中,根據泛型類型是引用類型還是值類型,default關鍵字用於將泛型類型初始化為null或0。
2)約束
如果泛型類需要調用泛型類型上的方法,就必須添加約束。
1 public class DocumentManager<T> 2 { 3 private readonly Queue<T> documentQueue=new Queue<T>(); 4 5 public void AddDocument(T doc) 6 { 7 lock(this) 8 { 9 documentQueue.Enqueue(doc); 10 } 11 } 12 13 public bool IsDocumentAvailable 14 { 15 get 16 { 17 return documentQueue.Count>0; 18 } 19 } 20 } 21 22 public interface IDocument 23 { 24 string Title{get;set;} 25 string Content{get;set;} 26 } 27 28 public class Document:IDocument 29 { 30 public Document() 31 { 32 } 33 34 public Document(string title,string content) 35 { 36 this.title=title; 37 this.content=content; 38 } 39 40 public string Title{get;set;} 41 public string Content{get;set;} 42 }如果使用DocumentManager<T>類顯示文檔,可以將類型T強制轉換為IDocument接口
1 public void DisplayAllDocument() 2 { 3 foreach(T doc in documentQueue) 4 { 5 Console.WriteLine(((IDocument)doc).Title); 6 } 7 }假如類型T沒有執行IDocument接口,這個類型轉換就會生成一個異常,因此需給DocumentManager<T>類定義一個約束:T必須執行IDocument接口,為了在泛型類型的名稱中指定該要求,將T改為TDocument。wherer子句指定了執行IDocument接口的要求。
1 public class DocumentManager<TDocument>where TDocument:IDocument 2 { 3 .... 4 }這樣編寫foreach語句就可以讓類型T包含Title屬性。
1 public void DisplayAllDocument() 2 { 3 foreach(TDocument doc in documentQueue) 4 { 5 Console.WriteLine(doc.Title); 6 } 7 }在Main()方法中,DocumentManager<T>類用Document類型來實例化,而Document類型執行了需要的IDocument接口。
1 static void Main() 2 { 3 DocumentManager<Document> dm=new DocumentManager<Document>(); 4 dm.AddDocument(new Document("Title A","A")); 5 dm.AddDocument(new Document("Title B","B")); 6 dm.DisplayAllDocument();
除此之外,泛型還有幾種約束類型。如下:
1)where T:struct 使用結構約束。類型T必須是值類型
2)where T:class 類約束指定,類型T必須是引用類型
3)where T:IFoo 指定類型T必須執行接口IFoo
4)where T:Foo 指定類型T必須派生於基類Foo
5)where T:new() 構造函數約束,指定類型T必須有一個默認構造函數
6)where T:U 類型T1派生於泛型類型T2。該約束也成為裸類型約束。
注意:使用泛型類型還可以合並多個約束。where T:IFoo,new()約束和MyClass<T>聲明指定,類型T必須執行IFoo接口,且必須有一個默認構造函數。
1 public class MyClass<T>where t:IFoo,new() 2 { 3 ... 4 }
3)繼承
泛型類型可以執行泛型接口,也可以派生於一個類。泛型類可以派生於泛型基類:
1 public class Base<T> 2 { 3 4 } 5 6 public class Derived<T>:Base<T> 7 { 8 9 }要求必須重復接口的泛型類型,或者必須指定基類的類型。
1 public class Base<T> 2 { 3 4 } 5 6 public class Derived<T>:Base<string> 7 { 8 9 }所以,派生類可以是泛型類或非泛型類。如可以定義一個抽象的泛型基類,它在派生類中用一個具體的類型實現。
1 public abstract class Calc<T> 2 { 3 public abstract T Add(T x,T y); 4 public abstract T Sub(T x,T y); 5 } 6 7 public class SimpleCalc:Calc<int> 8 { 9 public override int Add(int x,int y) 10 { 11 return x+y; 12 } 13 14 public override int Sub(int x,int y) 15 { 16 return x-y; 17 } 18 }
4)靜態成員
泛型類的靜態成員需要特別關注。泛型類的靜態成員只能在類的一個實例中共享。
1 public class StaticDemo<T> 2 { 3 public static int x; 4 }對一個string類型和一個int類型使用了StaticDemo<T>類,所以存在兩組靜態字段:
1 StaticDemo<string>.x=4; 2 StaticDemo<int>.x=5; 3 Console.WrileLine(StaticDemo<string>.x); //將會輸出4