.NET 的泛型

泛型可讓您將方法、類別、結構或介面,修改成其發揮作用的精確資料類型。 例如, Hashtable 您可以使用 Dictionary<TKey,TValue> 泛型類別,並指定索引鍵和值所允許的類型,而不是使用類別(可讓索引鍵和值成為任何類型)。 泛型的優點包括加強程式碼的重複使用程度以及類型安全性。

定義和使用泛型

泛型是指一些類別、結構、介面與方法,其具有所儲存或使用之一或多個類型的預留位置 (類型參數)。 泛型集合類別可能會針對所儲存的物件類型,使用類型參數做為預留位置;這些類型參數會顯示為其欄位的類型,和其方法的參數類型。 泛型方法可能會使用其類型參數,做為其傳回值的類型,或其型式參數之一的類型。 下列程式碼將會示範簡單的泛型類別定義。

generic<typename T>
public ref class Generics
{
public:
    T Field;
};
public class Generic<T>
{
    public T Field;
}
Public Class Generic(Of T)
    Public Field As T

End Class

當您建立泛型類別的執行個體時,您會指定實際的類型來替代類型參數。 這會建立新的泛型類別,稱為建構的泛型類別,且在類型參數出現的任何地方,都有您所選擇的替代類型。 此結果是適合您選擇之類型的類型安全類別,如下程式碼所示。

static void Main()
{
    Generics<String^>^ g = gcnew Generics<String^>();
    g->Field = "A string";
    //...
    Console::WriteLine("Generics.Field           = \"{0}\"", g->Field);
    Console::WriteLine("Generics.Field.GetType() = {0}", g->Field->GetType()->FullName);
}
public static void Main()
{
    Generic<string> g = new Generic<string>();
    g.Field = "A string";
    //...
    Console.WriteLine("Generic.Field           = \"{0}\"", g.Field);
    Console.WriteLine("Generic.Field.GetType() = {0}", g.Field.GetType().FullName);
}
Public Shared Sub Main()
    Dim g As New Generic(Of String)
    g.Field = "A string"
    '...
    Console.WriteLine("Generic.Field           = ""{0}""", g.Field)
    Console.WriteLine("Generic.Field.GetType() = {0}", g.Field.GetType().FullName)
End Sub

泛型術語

下列詞彙會用於討論在 .NET 中的泛型:

  • 「泛型類型定義」 (generic type definition),是做為範本的類別、結構或介面宣告,且具有可包含或使用之類型的預留位置。 例如, System.Collections.Generic.Dictionary<TKey,TValue> 類別可包含兩種類型:索引鍵和值。 因為泛型類型定義是只是範本,您無法建立泛型類型定義之類別、結構或介面的執行個體。

  • 「泛型類型參數」(Generic type parameter) 或 「類型參數」(type parameter),是泛型類型或方法定義中的預留位置。 System.Collections.Generic.Dictionary<TKey,TValue> 泛型類型有兩個類型參數, TKeyTValue,分別代表其索引鍵和值的類型。

  • 「建構的泛型類型」(constructed generic type) 或 「建構的類型」(constructed type),是為泛型類型定義的泛型類型參數所指定之類型的結果。

  • 「泛型類型引數」 (generic type argument) 是要替換泛型類型參數的所有類型。

  • 一般詞彙「泛型型別」包括建構的型別和泛型型別定義兩者。

  • 泛型型別參數的「共變數」和「反變數」可讓您使用建構的泛型型別,其型別引數比目標建構的型別有更多衍生 (共變數) 或更少衍生 (反變數)。 共變數和反變數合稱為「變異數」。 如需詳細資訊,請參閱 Covariance and Contravariance (共變數和反變數 (C# 和 Visual Basic))。

  • 「條件約束」 (Constraint),是在泛型類型參數上的限制。 例如,您可以限制類型參數為實作 System.Collections.Generic.IComparer<T> 泛型介面的類型,以確保能夠排序類型的執行個體。 您也可以將型別參數限制為具有特定基底類別的型別,或是具有無參數建構函式的型別,或為參考型別或實值型別。 的泛型類型的使用者無法替換沒有滿足這些條件約束的類型引數。

  • 「泛型方法定義」 (generic method definition),是一種有兩個參數清單的方法:泛型類型參數清單和型式參數清單。 類型參數會顯示為傳回類型或型式參數的類型,如下程式碼所示。

generic<typename T>
T Generic(T arg)
{
    T temp = arg;
    //...
    return temp;
}
T Generic<T>(T arg)
{
    T temp = arg;
    //...
    return temp;
}
Function Generic(Of T)(ByVal arg As T) As T
    Dim temp As T = arg
    '...
    Return temp
End Function

泛型方法可能會出現在泛型或非泛型類型上。 請務必注意,不會只因為某個方法屬於泛型類型,或只是因為它型式參數的類型是封入類型的泛用參數,此方法就成為泛型。 只有當方法有它自己的類型參數清單時,它才會是泛型。 在下列程式碼中,只有方法 G 是泛型。

ref class A
{
    generic<typename T>
    T G(T arg)
    {
        T temp = arg;
        //...
        return temp;
    }
};
generic<typename T>
ref class Generic
{
    T M(T arg)
    {
        T temp = arg;
        //...
        return temp;
    }
};
class A
{
    T G<T>(T arg)
    {
        T temp = arg;
        //...
        return temp;
    }
}
class Generic<T>
{
    T M(T arg)
    {
        T temp = arg;
        //...
        return temp;
    }
}
Class A
    Function G(Of T)(ByVal arg As T) As T
        Dim temp As T = arg
        '...
        Return temp
    End Function
End Class
Class Generic(Of T)
    Function M(ByVal arg As T) As T
        Dim temp As T = arg
        '...
        Return temp
    End Function
End Class

泛型的優缺點

使用泛型集合和委派有許多優點:

  • 類型安全。 泛型會將類型安全的負擔轉移給編譯器。 並不需要撰寫程式碼以測試是否為正確的資料類型,因為它在編譯時期會強制執行。 降低了類型轉換的需求以及執行階段錯誤的可能性。

  • 程式碼較少且更容易重複使用程式碼。 沒有需要從基底類型繼承,並覆寫成員。 例如, LinkedList<T> 可以立即使用。 例如,您可以下列變數宣告,建立字串的連結清單:

    LinkedList<String^>^ llist = gcnew LinkedList<String^>();
    
    LinkedList<string> llist = new LinkedList<string>();
    
    Dim llist As New LinkedList(Of String)()
    
  • 效能較佳。 泛型集合類型在儲存和管理實值類型上,通常有較好的表現,因為不需要 box 實值類型。

  • 泛型委派讓類型安全回呼不需要建立多個委派類別。 例如, Predicate<T> 泛型委派可讓您建立一種方法,為特定類型實作您自己的搜尋條件,以及讓您搭配 Array 類型的方法 (例如 FindFindLastFindAll) 使用方法。

  • 泛型簡化了動態產生的程式碼。 當您搭配動態產生的程式碼使用泛型時,不需要產生類型。 這會增加您使用輕量動態方法而非產生整個組件的時機。 如需詳細資訊,請參閱如何:定義與執行動態方法DynamicMethod

下列是泛型的一些限制:

  • 泛型類型可以從大部分的基底類別衍生,例如 MarshalByRefObject (且可使用條件約束要求泛型類型參數衍生自基底類別,例如 MarshalByRefObject)。 不過,.NET 不支援內容系結的泛型型別。 泛型類型可以衍生自 ContextBoundObject,但嘗試建立該類型的執行個體會導致 TypeLoadException

  • 列舉不能有泛型類型參數。 列舉只能偶爾做為泛型 (例如,因為它位於使用 Visual Basic、C# 或 C++ 所定義的泛型類型中)。 如需詳細資訊,請參閱 Common Type System中的<列舉>。

  • 輕量動態方法不可為泛型。

  • 在 Visual Basic、C# 和 C++ 中,括在泛型類型中的巢狀類型無法具現化,除非已將指派給類型所有封入類型的類型參數。 另一個說法是,在反映中,已定義使用這些語言的巢狀類型,包括了其所有封入類型的類型參數。 這讓封入類型的類型參數,可用於巢狀類型的成員定義中。 如需詳細資訊,請參閱 MakeGenericType中的「巢狀類型」。

    注意

    透過發出動態組件中的程式碼或使用 Ilasm.exe (IL Assembler) 所定義的巢狀型別,不一定要包含其封入型別的型別參數;不過,如果它不包含這些,型別參數就不在巢狀類別的範圍中。

    如需詳細資訊,請參閱 MakeGenericType中的「巢狀類型」。

類別庫和語言支援

.NET 在下列命名空間中提供數個泛型集合類別:

實作排序及相等比較的泛型介面,和事件處理常式、轉換及搜尋述詞的泛型委派類型,一起提供於 System 命名空間中。

對泛型的支援已加入 System.Reflection 命名空間,以檢查泛型類型和泛型方法;也加入 System.Reflection.Emit ,以發出包含泛型類型和方法的動態組件;同時還加入 System.CodeDom ,以產生包含泛型的來源圖形。

Common Language Runtime 提供新的 opcode 及前置詞,以支援 Microsoft 中繼語言 (MSIL) 中的泛型類型,包括 StelemLdelemUnbox_AnyConstrainedReadonly

Visual C++、C# 和 Visual Basic 均提供定義及使用泛型的完整支援。 如需語言支援的詳細資訊,請參閱 Visual Basic 中的泛型型別泛型簡介Visual C++ 中的泛型概觀

巢狀類型和泛型

泛型類型中的巢狀類型,可取決於封入泛型類型的類型參數。 Common Language Runtime 會將巢狀類型視為泛型,即使它們沒有自己的泛型類型參數。 當您建立巢狀類型的執行個體時,必須為所有封入泛型類型指定類型引數。

標題 描述
.NET 中的泛型集合 描述 .NET 中的泛型集合類別以及其他泛型類型。
用於運算元組和清單的泛型委派 描述轉換、搜尋述詞以及要在陣列或集合的元素上採取之動作的泛型委派。
泛型介面 描述提供泛型類型系列中常見功能的泛型介面。
共變數和反變數 描述泛型類型參數的共變數和反變數。
常用的集合類型 提供 .NET 中集合類型的特性和使用方式案例的摘要資訊,包括泛型類型。
使用泛型集合的時機 描述一般的規則,以判斷何時使用泛型集合類型。
作法:使用反映發出定義泛型型別 說明如何產生包括泛型類型和方法的動態組件。
Generic Types in Visual Basic 為 Visual Basic 使用者描述泛型功能,包括使用及定義泛型類型的「操作說明」主題。
泛型簡介 為 C# 使用者提供定義和使用泛型類型的概觀。
Visual C++ 中的泛型總覽 描述 C++ 使用者的泛型功能,包括泛型和範本之間的差異。

參考

System.Collections.Generic

System.Collections.ObjectModel

System.Reflection.Emit.OpCodes