泛型

 
泛型的本質是參數化類型,也就是說所操做的數據類型被指定爲一個參數。這種參數類型能夠用在類、接口和方法的建立中,分別稱爲泛型類、泛型接口、泛型方法。 
應用場景:1.泛型避免裝箱拆箱的性能損耗   2.代碼複用,好比用在ORM中    3.參數的範圍約束
 
泛型(generic)是C#語言2.0和通用語言運行時(CLR)的一個新特性。泛型爲.NET框架引入了類型參數(type parameters)的概念。類型參數使得設計類和方法時,沒必要肯定一個或多個具體參數,其的具體參數可延遲到客戶代碼中聲明、實現。這意味着使用泛型的類型參數T,寫一個類MyList<T>,客戶代碼能夠這樣調用:MyList<int>, MyList<string>或 MyList<MyClass>。這避免了運行時類型轉換或裝箱操做的代價和風險。
 
 
 
 
目錄
C#   中的泛型. 1
1、泛型概述. 2
2、泛型的優勢. 5
3、泛型類型參數. 7
4、類型參數的約束. 8
5、泛型類. 11
6、泛型接口. 13
7、泛型方法. 19
8、泛型委託. 21
9、泛型代碼中的default   關鍵字. 23
10、C++   模板和C#   泛型的區別. 24
十一   、運行時中的泛型. 25
十二   、基礎類庫中的泛型. 27
 
 


 

1、泛型概述
    泛型類和泛型方法兼複用性、類型安全和高效率於一身,是與之對應的非泛型的類和方法所不及。泛型普遍用於容器(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");
        }
    }

 


 

2、泛型的優勢
針對早期版本的通用語言運行時和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更安全,並且明顯地更加快速,尤爲當表中的元素是值類型的時候。


 

3、泛型類型參數
   
    在泛型類型或泛型方法的定義中,類型參數是一個佔位符(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執行替換的詳細信息,請參見運行時中的泛型。


 

4、類型參數的約束
 
若要檢查表中的一個元素,以肯定它是否合法或是否能夠與其餘元素相比較,那麼編譯器必須保證:客戶代碼中可能出現的全部類型參數,都要支持所需調用的操做或方法。這種保證是經過在泛型類的定義中,應用一個或多個約束而獲得的。一個約束類型是一種基類約束,它通知編譯器,只有這個類型的對象或從這個類型派生的對象,可被用做類型參數。一旦編譯器獲得這樣的保證,它就容許在泛型類中調用這個類型的方法。上下文關鍵字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繼承而來,因此帶有無類型約束的泛型類的用途十分有限。當你但願強制兩個類型參數具備繼承關係時,可對泛型類使用無類型約束。


 

5、泛型類
 
 
泛型類封裝了不針對任何特定數據類型的操做。泛型類經常使用於容器類,如鏈表、哈希表、棧、隊列、樹等等。這些類中的操做,如對容器添加、刪除元素,不論所存儲的數據是何種類型,都執行幾乎一樣的操做。
 
對大多數狀況,推薦使用.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){…}


 

6、泛型接口
不管是爲泛型容器類,仍是表示容器中元素的泛型類,定義接口是頗有用的。把泛型接口與泛型類結合使用是更好的用法,好比用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>
 
泛型類、泛型結構,泛型接口都具備一樣方法重載的規則。詳細信息,請參見泛型方法。


 

7、泛型方法
 
泛型方法是聲名了類型參數的方法,以下:
 
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>(){}

 


 
8、泛型委託
不管是在類定義內仍是類定義外,委託能夠定義本身的類型參數。引用泛型委託的代碼能夠指定類型參數來建立一個封閉構造類型,這和實例化泛型類或調用泛型方法同樣,以下例所示:
 
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;

 

 


 

9、泛型代碼中的   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;
        }
}

 

 
、運行時中的泛型
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=705841web

相關文章
相關標籤/搜索