C#泛型


泛型(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;
        }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM