轉載:C#中的泛型

来源:http://www.cnblogs.com/whyhappy/archive/2016/03/11/5264124.html
-Advertisement-
Play Games

泛型(generic)是C#語言2.0和通用語言運行時(CLR)的一個新特性。泛型為.NET框架引入了類型參數(type parameters)的概念。類型參數使得設計類和方法時,不必確定一個或多個具體參數,其的具體參數可延遲到客戶代碼中聲明、實現。這意味著使用泛型的類型參數T,寫一個類MyList


泛型(generic)是C#語言2.0和通用語言運行時(CLR)的一個新特性。泛型為.NET框架引入了類型參數(type parameters)的概念。類型參數使得設計類和方法時,不必確定一個或多個具體參數,其的具體參數可延遲到客戶代碼中聲明、實現。這意味著使用泛型的類型參數T,寫一個類MyList<T>,客戶代碼可以這樣調用:MyList<int>, MyList<string>或 MyList<MyClass>。這避免了運行時類型轉換或裝箱操作的代價和風險。

  目錄 C# 中的泛型. 1 一、泛型概述. 2 二、泛型的優點. 5 三、泛型類型參數. 7 四、類型參數的約束. 8 五、泛型類. 11 六、泛型介面. 13 七、泛型方法. 19 八、泛型委托. 21 九、泛型代碼中的default 關鍵字. 23 十、C++ 模板和C# 泛型的區別. 24 十一 、運行時中的泛型. 25 十二 、基礎類庫中的泛型. 27    


 

一、泛型概述     泛型類和泛型方法兼復用性、類型安全和高效率於一身,是與之對應的非泛型的類和方法所不及。泛型廣泛用於容器(collections)和對容器操作的方法中。.NET框架2.0的類庫提供一個新的命名空間System.Collections.Generic,其中包含了一些新的基於泛型的容器類。要查找新的泛型容器類(collection classes)的示例代碼,請參見基礎類庫中的泛型。當然,你也可以創建自己的泛型類和方法,以提供你自己的泛化的方案和設計模式,這是類型安全且高效的。下麵的示例代碼以一個簡單的泛型鏈表類作為示範。(多數情況下,推薦使用由.NET框架類庫提供的List<T>類,而不是創建自己的表。)類型參數T在多處使用,具體類型通常在這些地方來指明表中元素的類型。類型參數T有以下幾種用法: l        在AddHead方法中,作為方法參數的類型。 l        在公共方法GetNext中,以及嵌套類Node的 Data屬性中作為返回值的類型。 l        在嵌套類中,作為私有成員data的類型。   註意一點,T對嵌套的類Node也是有效的。當用一個具體類來實現MyList<T>時——如MyList<int>——每個出現過的T都要用int代替。   using System; using System.Collections.Generic;   public class MyList<T> //type parameter T in angle brackets     {         private Node head; // The nested type is also generic on T.         private class Node                   {             private Node next; //T as private member data type:             private T data;          //T used in non-generic constructor:             public Node(T t)                     {                 next = null;                 data = t;             }             public Node Next             {                 get { return next; }                 set { next = value; }             } //T as return type of property:             public T Data                        {                 get { return data; }                 set { data = value; }             }         }         public MyList()         {             head = null;         } //T as method parameter type:         public void AddHead(T t)             {             Node n = new Node(t);             n.Next = head;             head = n;         }           public IEnumerator<T> GetEnumerator()         {             Node current = head;               while (current != null)             {                 yield return current.Data;                 current = current.Next;             }         }     }   下麵的示例代碼演示了客戶代碼如何使用泛型類MyList<T>,來創建一個整數表。通過簡單地改變參數的類型,很容易改寫下麵的代碼,以創建字元串或其他自定義類型的表。   class Program     {         static void Main(string[] args)         { //int is the type argument.            MyList<int> list = new MyList<int>();             for (int x = 0; x < 10; x++)                 list.AddHead(x);               foreach (int i in list)             {                 Console.WriteLine(i);             }             Console.WriteLine("Done");         }     }


 

二、泛型的優點 針對早期版本的通用語言運行時和C#語言的局限,泛型提供了一個解決方案。以前類型的泛化(generalization)是靠類型與全局基類System.Object的相互轉換來實現。.NET框架基礎類庫的ArrayList容器類,就是這種局限的一個例子。ArrayList是一個很方便的容器類,使用中無需更改就可以存儲任何引用類型或值類型。   //The .NET Framework 1.1 way of creating a list ArrayList list1 = new ArrayList();  list1.Add(3); list1.Add(105); //... ArrayList list2 = new ArrayList(); list2.Add(“It is raining in Redmond.”); list2.Add("It is snowing in the mountains."); //...   但是這種便利是有代價的,這需要把任何一個加入ArrayList的引用類型或值類型都隱式地向上轉換成System.Object。如果這些元素是值類型,那麼當加入到列表中時,它們必須被裝箱;當重新取回它們時,要拆箱。類型轉換和裝箱、拆箱的操作都降低了性能;在必須迭代(iterate)大容器的情況下,裝箱和拆箱的影響可能十分顯著。   另一個局限是缺乏編譯時的類型檢查,當一個ArrayList把任何類型都轉換為Object,就無法在編譯時預防客戶代碼類似這樣的操作:   ArrayList list = new ArrayList();  //Okay.   list.Add(3);  //Okay, but did you really want to do this? list.Add(.“It is raining in Redmond.”);   int t = 0; //This causes an InvalidCastException to be returned.     foreach(int x in list) {   t += x; }   雖然這樣完全合法,並且有時是有意這樣創建一個包含不同類型元素的容器,但是把string和int變數放在一個ArrayList中,幾乎是在製造錯誤,而這個錯誤直到運行的時候才會被髮現。   在1.0版和1.1版的C#語言中,你只有通過編寫自己的特定類型容器,才能避免.NET框架類庫的容器類中泛化代碼(generalized code)的危險。當然,因為這樣的類無法被其他的數據類型復用,也就失去泛型的優點,你必須為每個需要存儲的類型重寫該類。   ArrayList和其他相似的類真正需要的是一種途徑,能讓客戶代碼在實例化之前指定所需的特定數據類型。這樣就不需要向上類型轉換為Object,而且編譯器可以同時進行類型檢查。換句話說,ArrayList需要一個類型參數。這正是泛型所提供的。在System.Collections.Generic命名空間中的泛型List<T>容器里,同樣是把元素加入容器的操作,類似這樣: The .NET Framework 2.0 way of creating a list List<int> list1 = new List<int>(); //No boxing, no casting: list1.Add(3); //Compile-time error: list1.Add("It is raining in Redmond.");   與ArrayList相比,在客戶代碼中唯一增加的List<T>語法是聲明和實例化中的類型參數。代碼略微複雜的回報是,你創建的表不僅比ArrayList更安全,而且明顯地更加快速,尤其當表中的元素是值類型的時候。


 

三、泛型類型參數         在泛型類型或泛型方法的定義中,類型參數是一個占位符(placeholder),通常為一個大寫字母,如T。在客戶代碼聲明、實例化該類型的變數時,把T替換為客戶代碼所指定的數據類型。泛型類,如泛型概述中給出的MyList<T>類,不能用作as-is,原因在於它不是一個真正的類型,而更像是一個類型的藍圖。要使用MyList<T>,客戶代碼必須在尖括弧內指定一個類型參數,來聲明並實例化一個已構造類型(constructed type)。這個特定類的類型參數可以是編譯器識別的任何類型。可以創建任意數量的已構造類型實例,每個使用不同的類型參數,如下:   MyList<MyClass> list1  = new MyList<MyClass>(); MyList<float> list2 = new MyList<float>(); MyList<SomeStruct> list3 = new MyList<SomeStruct>();       在這些MyList<T>的實例中,類中出現的每個T都將在運行的時候被類型參數所取代。依靠這樣的替換,我們僅用定義類的代碼,就創建了三個獨立的類型安全且高效的對象。有關CLR執行替換的詳細信息,請參見運行時中的泛型。


 

四、類型參數的約束   若要檢查表中的一個元素,以確定它是否合法或是否可以與其他元素相比較,那麼編譯器必須保證:客戶代碼中可能出現的所有類型參數,都要支持所需調用的操作或方法。這種保證是通過在泛型類的定義中,應用一個或多個約束而得到的。一個約束類型是一種基類約束,它通知編譯器,只有這個類型的對象或從這個類型派生的對象,可被用作類型參數。一旦編譯器得到這樣的保證,它就允許在泛型類中調用這個類型的方法。上下文關鍵字where用以實現約束。下麵的示例代碼說明瞭應用基類約束,為MyList<T>類增加功能。   public class Employee {  public class Employee     {         private string name;         private int id;         public Employee(string s, int i)         {             name = s;             id = i;         }           public string Name         {             get { return name; }             set { name = value; }         }         public int ID         {             get { return id; }             set { id = value; }         }       } } class MyList<T> where T: Employee {  //Rest of class as before.   public T FindFirstOccurrence(string s)   {    T t = null;    Reset();    while (HasItems())    {       if (current != null)       { //The constraint enables this:          if (current.Data.Name == s)          {             t = current.Data;             break;          }          else          {             current = current.Next;          }       } //end if    } // end while   return t;   } }   約束使得泛型類能夠使用Employee.Name屬性,因為所有為類型T的元素,都是一個Employee對象或是一個繼承自Employee的對象。   同一個類型參數可應用多個約束。約束自身也可以是泛型類,如下:   class MyList<T> where T: Employee, IEmployee,  IComparable<T>,  new() {…}       下表列出了五類約束:

 

約束 描述
where T: struct 類型參數必須為值類型。
where T : class 類型參數必須為類型。
where T : new() 類型參數必須有一個公有、無參的構造函數。當於其它約束聯合使用時,new()約束必須放在最後。
where T : <base class name> 類型參數必須是指定的基類型或是派生自指定的基類型。
where T : <interface name> 類型參數必須是指定的介面或是指定介面的實現。可以指定多個介面約束。介面約束也可以是泛型的。
    類型參數的約束,增加了可調用的操作和方法的數量。這些操作和方法受約束類型及其派生層次中的類型的支持。因此,設計泛型類或方法時,如果對泛型成員執行任何賦值以外的操作,或者是調用System.Object中所沒有的方法,就需要在類型參數上使用約束。   無限制類型參數的一般用法 沒有約束的類型參數,如公有類MyClass<T>{...}中的T, 被稱為無限制類型參數(unbounded type parameters)。無限制類型參數有以下規則: l        不能使用運算符 != 和 == ,因為無法保證具體的類型參數能夠支持這些運算符。 l        它們可以與System.Object相互轉換,也可顯式地轉換成任何介面類型。 l        可以與null比較。如果一個無限制類型參數與null比較,當此類型參數為值類型時,比較的結果總為false。     無類型約束 當約束是一個泛型類型參數時,它就叫無類型約束(Naked type constraints)。當一個有類型參數成員方法,要把它的參數約束為其所在類的類型參數時,無類型約束很有用。如下例所示:   class List<T> {       //...     void Add<U>(List<U> items) where U:T {…} }   在上面的示例中, Add方法的上下文中的T,就是一個無類型約束;而List類的上下文中的T,則是一個無限制類型參數。   無類型約束也可以用在泛型類的定義中。註意,無類型約束一定也要和其它類型參數一起在尖括弧中聲明: //naked type constraint public class MyClass<T,U,V> where T : V   因為編譯器只認為無類型約束是從System.Object繼承而來,所以帶有無類型約束的泛型類的用途十分有限。當你希望強制兩個類型參數具有繼承關係時,可對泛型類使用無類型約束。


 

五、泛型類     泛型類封裝了不針對任何特定數據類型的操作。泛型類常用於容器類,如鏈表、哈希表、棧、隊列、樹等等。這些類中的操作,如對容器添加、刪除元素,不論所存儲的數據是何種類型,都執行幾乎同樣的操作。   對大多數情況,推薦使用.NET框架2.0類庫中所提供的容器類。有關使用這些類的詳細信息,請參見基礎類庫中的泛型。   通常,從一個已有的具體類來創建泛型類,並每次把一個類型改為類型參數,直至達到一般性和可用性的最佳平衡。當創建你自己的泛型類時,需要重點考慮的事項有: l        哪些類型應泛化為類型參數。一般的規律是,用參數表示的類型越多,代碼的靈活性和復用性也就越大。過多的泛化會導致代碼難以被其它的開發人員理解。 l        如果有約束,那麼類型參數需要什麼樣約束。一個良好的習慣是,儘可能使用最大的約束,同時保證可以處理所有需要處理的類型。例如,如果你知道你的泛型類只打算使用引用類型,那麼就應用這個類的約束。這樣可以防止無意中使用值類型,同時可以對T使用as運算符,並且檢查空引用。 l        把泛型行為放在基類中還是子類中。泛型類可以做基類。同樣非泛型類的設計中也應考慮這一點。泛型基類的繼承規則     。 l        是否實現一個或多個泛型介面。例如,要設計一個在基於泛型的容器中創建元素的類,可能需要實現類似IComparable<T>的介面,其中T是該類的參數。   泛型概述中有一個簡單泛型類的例子。   類型參數和約束的規則對於泛型類的行為(behavior)有一些潛在的影響,——尤其是對於繼承和成員可訪問性。在說明這個問題前,理解一些術語十分重要。對於一個泛型類Node<T>,客戶代碼既可以通過指定一個類型參數來創建一個封閉構造類型(Node<int>),也可以保留類型參數未指定,例如指定一個泛型基類來創建開放構造類型(Node<T>)。泛型類可以繼承自具體類、封閉構造類型或開放構造類型:   // concrete type class Node<T> : BaseNode //closed constructed type class Node<T> : BaseNode<int> //open constructed type class Node<T> : BaseNode<T>   非泛型的具體類可以繼承自封閉構造基類,但不能繼承自開放構造基類。這是因為客戶代碼無法提供基類所需的類型參數。   //No error. class Node : BaseNode<int> //Generates an error. class Node : BaseNode<T>   泛型的具體類可以繼承自開放構造類型。除了與子類共用的類型參數外,必須為所有的類型參數指定類型,如下代碼所示: //Generates an error. class Node<T> : BaseNode<T, U> {…} //Okay. class Node<T> : BaseNode<T, int>{…}   繼承自開放結構類型的泛型類,必須指定: Generic classes that inherit from open constructed types must specify must specify constraints that are a superset of, or imply, the constraints on the base type:   class NodeItem<T> where T : IComparable<T>, new() {…} class MyNodeItem<T> : NodeItem<T> where T : IComparable<T> , new(){…}     泛型類型可以使用多種類型參數和約束,如下: class KeyType<K,V>{…} class SuperKeyType<K,V,U> where U : IComparable<U>, where V : new(){…}   開放結構和封閉構造類型型可以用作方法的參數: void Swap<T>(List<T> list1, List<T> list2){…} void Swap(List<int> list1, List<int> list2){…}


 

六、泛型介面 不論是為泛型容器類,還是表示容器中元素的泛型類,定義介面是很有用的。把泛型介面與泛型類結合使用是更好的用法,比如用IComparable<T>而非IComparable,以避免值類型上的裝箱和拆箱操作。.NET框架2.0類庫定義了幾個新的泛型介面,以配合System.Collections.Generic中新容器類的使用。       當一個介面被指定為類型參數的約束時,只有實現該介面的類型可被用作類型參數。下麵的示例代碼顯示了一個從MyList<T>派生的SortedList<T>類。更多信息,請參見泛型概述。SortedList<T>增加了約束where T : IComparable<T>。 這使得SortedList<T>中的BubbleSort方法可以使用表中的元素的IComparable<T>.CompareTo方法。在這個例子中,表中的元素是簡單類——實現IComparable<Person>的Person類。   using System; using System.Collections.Generic;   //Type parameter T in angle brackets. public class MyList<T> {     protected Node head;     protected Node current = null;   // Nested type is also generic on T     protected class Node              {         public Node next; //T as private member datatype.         private T data;          //T used in non-generic constructor.         public Node(T t)                 {             next = null;             data = t;         }         public Node Next         {             get { return next; }             set { next = value; }         } //T as return type of property.         public T Data                    {             get { return data; }             set { data = value; }         }     }     public MyList()     {         head = null;     } //T as method parameter type.     public void AddHead(T t)         {         Node n = new Node(t);         n.Next = head;         head = n;        }     // Implement IEnumerator<T> to enable foreach     // iteration of our list. Note that in C# 2.0     // you are not required to implment Current and     // GetNext. The compiler does that for you.     public IEnumerator<T> GetEnumerator()     {         Node current = head;           while (current != null)         {             yield return current.Data;             current = current.Next;         }     } }     public class SortedList<T> : MyList<T> where T : IComparable<T> {     // A simple, unoptimized sort algorithm that     // orders list elements from lowest to highest:   public void BubbleSort()     {           if (null == head || null == head.Next)             return;         bool swapped;           do         {             Node previous = null;             Node current = head;             swapped = false;               while (current.next != null)             {                 //  Because we need to call this method, the SortedList                 //  class is constrained on IEnumerable<T>                 if (current.Data.CompareTo(current.next.Data) > 0)                 {                     Node tmp = current.next;                     current.next = current.next.next;                     tmp.next = current;                       if (previous == null)                     {                         head = tmp;                     }                     else                     {                         previous.next = tmp;                     }                     previous = tmp;                     swapped = true;                 }                   else                 {                     previous = current;                     current = current.next;                 }               }// end while         } while (swapped);     }   }   // A simple class that implements IComparable<T> // using itself as the type argument. This is a // common design pattern in objects that are // stored in generic lists. public class Person : IComparable<Person> {     string name;     int age;     public Person(string s, int i)     {         name = s;         age = i;     }     // This will cause list elements     // to be sorted on age values.     public int CompareTo(Person p)     {         return age - p.age;     }     public override string ToString()     {         return name + ":" + age;     }     // Must implement Equals.     public bool Equals(Person p)     {         return (this.age == p.age);     } }   class Program {     static void Main(string[] args)     {         //Declare and instantiate a new generic SortedList class.         //Person is the type argument.         SortedList<Person> list = new SortedList<Person>();           //Create name and age values to initialize Person objects.         string[] names = new string[]{"Franscoise", "Bill", "Li", "Sandra", "Gunnar", "Alok", "Hiroyuki", "Maria", "Alessandro", "Raul"};         int[] ages = new int[]{45, 19, 28, 23, 18, 9, 108, 72, 30, 35};           //Populate the list.         for (int x = 0; x < 10; x++)         {             list.AddHead(new Person(names[x], ages[x]));         }         //Print out unsorted list.         foreach (Person p in list)         {             Console.WriteLine(p.ToString());         }           //Sort the list.         list.BubbleSort();           //Print out sorted list.         foreach (Person p in list)         {             Console.WriteLine(p.ToString());         }           Console.WriteLine("Done");     } }     可以在一個類型指定多個介面作為約束,如下:   class Stack<T> where T : IComparable<T>, IMyStack1<T>{}     一個介面可以定義多個類型參數,如下:   IDictionary<K,V>   介面和類的繼承規則相同: //Okay. IMyInterface : IBaseInterface<int> //Okay. IMyInterface<T> : IBaseInterface<T>   //Okay. IMyInterface<T>: IBaseInterface<int> //Error. IMyInterface<T> : IBaseInterface2<T, U>   具體類可以實現封閉構造介面,如下: class MyClass : IBaseInterface<string>   泛型類可以實現泛型介面或封閉構造介面,只要類的參數列表提供了介面需要的所有參數,如下: //Okay. class MyClass<T> : IBaseInterface<T> //Okay. class MyClass<T> : IBaseInterface<T, string>   泛型類、泛型結構,泛型介面都具有同樣方法重載的規則。詳細信息,請參見泛型方法。


 

七、泛型方法   泛型方法是聲名了類型參數的方法,如下:   void Swap<T>( ref T lhs, ref T rhs) {   T temp;   temp = lhs;   lhs = rhs;   rhs = temp; }     下麵的示例代碼顯示了一個以int作為類型參數,來調用方法的例子:   int a = 1; int b = 2; //… Swap<int>(a, b);   也可以忽略類型參數,編譯器會去推斷它。下麵調用Swap的代碼與上面的例子等價: Swap(a, b);     靜態方法和實例方法有著同樣的類型推斷規則。編譯器能夠根據傳入的方法參數來推斷類型參數;而無法單獨根據約束或返回值來判斷。因此類型推斷對沒有參數的方法是無效的。類型推斷發生在編譯的時候,且在編譯器解析重載方法標誌之前。編譯器對所有同名的泛型方法應用類型推斷邏輯。在決定(resolution)重載的階段,編譯器只包含那些類型推斷成功的泛型類。更多信息,請參見C# 2.0規範,20.6.4類型參數推斷   在泛型方法中,非泛型方法能訪問所在類中的類型參數,如下: class MyClass<T> {   //…   void Swap (ref T lhs, ref T rhs){…} }   [JX1] 定義一個泛型方法,和其所在的類具有相同的類型參數;試圖這樣做,編譯器會產生警告CS0693。   class MyList<T> { // CS0693     void MyMethod<T>{...}    }   class MyList<T> { //This is okay, but not common.     void SomeMethod<U>(){...}    }   使用約束可以在方法中使用更多的類型參數的特定方法。這個版本的Swap<T>稱為SwapIfGreater<T>,它只能使用實現了IComparable<T>的類型參數。 void SwapIfGreater<T>( ref T lhs, ref T rhs) where T: IComparable<T> {   T temp;   if(lhs.CompareTo(rhs) > 0)     {       temp = lhs;       lhs = rhs;       rhs = temp;     } }   泛型方法通過多個類型參數來重載。例如,下麵的這些方法可以放在同一個類中: void DoSomething(){} void DoSomething<T>(){} void DoSomething<T,U>(){}  


 

八、泛型委托 無論是在類定義內還是類定義外,委托可以定義自己的類型參數。引用泛型委托的代碼可以指定類型參數來創建一個封閉構造類型,這和實例化泛型類或調用泛型方法一樣,如下例所示:   public delegate void MyDelegate<T>(T item); public void Notify(int i){} //...   MyDelegate<int> m = new MyDelegate<int>(Notify);   C#2.0版有個新特性稱為方法組轉換(method group conversion),具體代理和泛型代理類型都可以使用。用方法組轉換可以把上面一行寫做簡化語法: MyDelegate<int> m = Notify;   在泛型類中定義的委托,可以與類的方法一樣地使用泛型類的類型參數。   class Stack<T> { T[] items;       int index //... public delegate void StackDelegate(T[] items); }   引用委托的代碼必須要指定所在類的類型參數,如下:   Stack<float> s = new Stack<float>(); Stack<float>.StackDelegate myDelegate = StackNotify;     泛型委托在定義基於典型設計模式的事件時特別有用。因為sender[JX2] ,而再也不用與Object相互轉換。   public void StackEventHandler<T,U>(T sender, U eventArgs); class Stack<T> {     //…     public class StackEventArgs : EventArgs{...}     public event StackEventHandler<Stack<T>, StackEventArgs> stackEvent;     protected virtual void OnStackChanged(StackEventArgs a)     {       stackEvent(this, a);     } } class MyClass {   public static void HandleStackChange<T>(Stack<T> stack, StackEventArgs args){...}; } Stack<double> s = new Stack<double>(); MyClass mc = new MyClass(); s.StackEventHandler += mc.HandleStackChange;  


 

九、泛型代碼中的 default 關鍵字   在泛型類和泛型方法中會出現的一個問題是,如何把預設值賦給參數化類型,此時無法預先知道以下兩點: l        T將是值類型還是引用類型 l        如果T是值類型,那麼T將是數值還是結構   對於一個參數化類型T的變數t,僅當T是引用類型時,t = null語句才是合法的; t = 0只對數值的有效,而對結構則不行。這個問題的解決辦法是用default關鍵字,它對引用類型返回空,對值類型的數值型返回零。而對於結構,它將返回結構每個成員,並根據成員是值類型還是引用類型,返回零或空。下麵MyList<T>類的例子顯示瞭如何使用default關鍵字。更多信息,請參見泛型概述。   public class MyList<T> {     //...         public T GetNext()         {             T temp = default(T);             if (current != null)             {                 temp = current.Data;                 current = current.Next;             }             return temp;         } }


 

十、 C++ 模板和 C# 泛型的區別 (未翻譯)   C# Generics and C++ templates are both language features that provide support for parameterized types. However, there are many differences between the two. At the syntax level, C# generics are a simpler approach to parameterized types without the complexity of C++ templates. In addition, C# does not attempt to provide all of the functionality that C++ templates provide. At the implementation level, the primary difference is that C# generic type substitutions are performed at runtime and generic type information is thereby preserved for instantiated objects. For more information, see Generics in the Runtime.   The following are the key differences between C# Generics and C++ templates: ·                     C# generics do not provide the same amount of flexibility as C++ templates. For example, it is not possible to call arithmetic operators in a C# generic class, although it is possible to call user defined operators. ·                     C# does not allow non-type template parameters, such as template C<int i> {}. ·                     C# does not support explicit specialization; that is, a custom implementation of a template for a specific type. ·                     C# does not support partial specialization: a custom implementation for a subset of the type arguments. ·                     C# does not allow the type parameter to be used as the base class for the generic type. ·                     C# does not allow type parameters to have default types. ·                     In C#, a generic type parameter cannot itself be a generic, although constructed types can be used as generics. C++ does allow template parameters. ·                     C++ allows code that might not be valid for all type parameters in the template, which is then checked for the specific type used as the type parameter. C# requires code in a class to be written in such a way that it will work with any type that satisfies the constraints. For example, in C++ it is possible to write a function that uses the arithmetic operators + and - on objects of the type parameter, which will produce an error at the time of instantiation of the template with a type that does not support these operators. C# disallows this; the only language constructs allowed are those that can be deduced from the constraints.


 

十一 、運行時中的泛型 Specialized generic types are created once for each unique value type used as a parameter.   當泛型類或泛型方法被編譯為微軟中間語言(MSIL)後,它所包含的元數據定義了它的類型參數。根據所給的類型參數是值類型還是引用類型,對泛型類型所用的MSIL也是不同的。     當第一次以值類型作為參數來構造一個泛型類型,運行時用所提供的參數或在MSIL中適當位置被替換的參數,來創建一個專用的泛型類型。[JX3]        例如,假設你的程式代碼聲名一個由整型構成的棧,如:   Stack<int> stack;   此時,運行時用整型恰當地替換了它的類型參數,生成一個專用版本的棧。此後,程式代碼再用到整型棧時,運行時復用已創建的專用的棧。下麵的例子創建了兩個整型棧的實例,它們共用一個Stack<int>代碼實例:   Stack<int> stackOne = new Stack<int>(); Stack<int> stackTwo = new Stack<int>();       然而,如果由另一種值類型——如長整型或用戶自定義的結構——作為參數,在代碼的其他地方創建另一個棧,那麼運行時會生成另一個版本的泛型類型。這次是把長整型替換到MSIL中的適當的位置。由於每個專用泛型類原本就包含值類型,因此不需要再轉換。       對於引用類型,泛型的工作略有不同。當第一次用任何引用類型構造泛型類時,運行時在MSIL中創建一個專用泛型類,其中的參數被對象引用所替換。之後,每當用一個引用類型作為參數來實例化一個已構造類型時,就忽略其類型,運行時復用先前創建的專用版本的泛型類。這可能是由於所有的引用的大小都相同。       例如,假如你有兩個引用類型,一個Customer類和一個Order類;進一步假設你創建了一個Customer的棧:   Stack<Customer> customers;       此時,運行時生成一個專用版本的棧,用於稍後存儲對象的引用,而不是存儲數據。假如下一行代碼創建了一個另一種引用類型的棧,名為Order:   Stack<Order> orders = new Stack<Order>();       和值類型不同,運行時並沒有為Order類型創建另一個棧的專用版本。相反,運行時創建了一個專用版本棧實例,並且變數orders指向這個實例。如果之後是一行創建Customer類型的棧的代碼:   customers = new Stack<Customer>();   和之前以Order類型創建的棧一樣,創建了專用棧的另一個實例,並且其中所包含的指針指向一塊大小與Customer類一致的記憶體。由於不同程式間引用類型的數量差別很大,而編譯器只為引用類型的泛型類創建一個專用類,因此C#對泛型的實現極大地降低了代碼膨脹。     此外,當用類型參數實現一個泛型C#類時,想知道它是指類型還是引用類型,可以在運行時通過反射確定它的真實類型和它的類型參數。    


 

十二 、基礎類庫中的泛型     2.0版的.NET框架類庫提供了一個新的命名空間,System.Collections.Generic,其中包含了一些已經可以使用的泛型容器類和相關的介面。和早期版本的.NET框架提供的非泛型容器類相比,這些類和介面更高效且是類型安全的。在設計、實現自定義的容器類之前,請你考慮是否使用或繼承所列出類中的一個。       下麵的表格列出了新的泛型類和介面,旁邊是對應的非泛型類和介面。在一些地方要特別註意,如List<T>和Dictionary<T>,新泛型類的行為(behavior)與它們所替換的非泛型類有些不同,也不完全相容。更詳細的內容,請參考System.Collections.Generic的文檔  

 

 

泛型類或介面 描述 對應的非泛型類型
Collection<T> ICollection<T> 為泛型容器提供基類 CollectionBase ICollection
Comparer<T> IComparer<T> IComparable<T> 比較兩個相同泛型類型的對象是否相等、可排序。 Comparer IComparer IComparable
Dictionary<K, V> IDictionary<K,V> 表示用鍵組織的鍵/值對集合。 Hashtable IDictionary
Dictionary<K, V>.KeyCollection 表示Dictionary<K, V>中鍵的集合。 None.
Dictionary<K, V>.ValueCollection 表示Dictionary<K, V>中值的集合。 None.
IEnumerable<T> IEnumerator<T> 表示可以使用foreach 迭代的集合。 IEnumerable IEnumerator
KeyedCollection<T, U> 表示有鍵值的集合。 KeyedCollection
LinkedList<T> 表示雙向鏈表。 None.
LinkedListNode<T> 表示LinkedList<T>中的節點。 None.
List<T> IList<T> 使用大小可按需動態增加的數組實現 IList 介面 ArrayList IList
Queue<T> 表示對象的先進先出集合。 Queue
ReadOnlyCollection<T> 為泛型只讀容器提供基類。 ReadOnlyCollectionBase
SortedDictionary<K, V>  表示鍵/值對的集合,這些鍵和值按鍵排序並可按照鍵訪問,實現IComparer<T>介面。 SortedList
Stack<T> 表示對象的簡單的後進先出集合。 Stack

 

                       Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=705841

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 自從2013 Google I/O大會之後,筆者就將android ide開發工具從eclipse遷移到Android Studio了,android studio一直在更新完善,為了與時俱進,我們當然要將工具更新到最新版本啦!其實更新本來是很簡單,只要從Android Studio Help菜單中
  • 項目為愛鮮蜂,一款電商APP,使用語言Swift2.0,開發工具Xcode7.0.1. 項目為純代碼開發,沒有使用XIB和StoryBoard.開發周期大概為2個月左右(工作閑暇之餘). 數據都是本地數據,輔助開發軟體:PhotoShop CS6(圖片處理),Charles(抓包工具). 寫的比較匆
  • oracle基礎
  • 有些時候,有些作業遇到問題執行時間過長,因此我寫了一個腳本可以根據歷史記錄,找出執行時間過長的作業,在監控中就可以及時發現這些作業並儘早解決,代碼如下: SELECT sj.name , sja.start_execution_date,DATEDIFF (SECOND ,sja.start_exe...
  • 五、bash運算及啟動腳本01.使用bash的命令歷史#history……#set(顯示所有的變數) | grep HISHISTFILE=/root/.bash_historyHISTFILESIZE=1000(歷史文件個數)HISTSIZE=1000(文件的歷史大小)#vi /root/.bas
  • 序列化是將一個對象轉換成位元組流以達到將其長期保存在記憶體、資料庫或文件中的處理過程。它的主要目的是保存對象的狀態以便以後需要的時候使用。與其相反的過程叫做反序列化。 序列化一個對象 為了序列化一個對象,我們需要一個被序列化的對象,一個容納被序列化了的對象的(位元組)流和一個格式化器。進行序列化之前我們先
  • 註冊用戶有一段時間了,一直很忙,到現在還沒有寫一篇,忽然覺的一定要花點時間記錄和總結一些東西。好吧,就從這裡開始了。 今天客戶提出要點擊菜單(TreeView實現的)的父級節點時,展開節點。心想這個應該是很常見的功能吧,特意google了一下,發現大部分是將的不是js實現的,有些js實現的寫的麻煩,
  • 它們只是不起眼的小技巧。日積月累,它們讓我們的工作、學習更有效率,讓我們更加專註於邏輯本身,它們是.NET程式員的好朋友,它們是Visual Studio的小技巧……我們,真的認識它們嗎? 如果想儘快掌握這些技巧,請打開Visual Studio親自試一下這些技巧,希望找到你喜歡的技巧的。 (圖片來
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...