.NET 中的泛型
泛型可讓您針對其作用的精確數據類型量身打造方法、類別、結構或介面。 例如,與其使用允許索引鍵和值為任何類型的 Hashtable 類別,您可以使用 Dictionary<TKey,TValue> 泛型類別,並指定索引鍵和值允許的類型。 泛型的優點之一是增加程式碼重複使用性和類型安全性。
定義和使用泛型
泛型是類別、結構、介面和方法,這些泛型具有一或多個其儲存或使用的類型佔位元(類型參數)。 泛型集合類別可能會使用類型參數作為其儲存物件型別的佔位符。 類型參數會顯示為其字段的類型,以及其方法的參數類型。 泛型方法可能會使用其型別參數作為其傳回值的型別,或作為其一個形式參數的型別。
下列程式代碼說明簡單的泛型類別定義。
generic<typename T>
public ref class SimpleGenericClass
{
public:
T Field;
};
public class SimpleGenericClass<T>
{
public T Field;
}
Public Class SimpleGenericClass(Of T)
Public Field As T
End Class
當您建立泛型類別的實例時,您可以指定要替代類型參數的實際類型。 這會建立一個新的泛型類別,稱為建構的泛型類別,將您選擇的類型替換所有出現的類型參數。 結果為類型安全類別,專為您選擇的類型量身打造,如下列程式代碼所示。
static void Main()
{
SimpleGenericClass<String^>^ g = gcnew SimpleGenericClass<String^>();
g->Field = "A string";
//...
Console::WriteLine("SimpleGenericClass.Field = \"{0}\"", g->Field);
Console::WriteLine("SimpleGenericClass.Field.GetType() = {0}", g->Field->GetType()->FullName);
}
public static void Main()
{
SimpleGenericClass<string> g = new SimpleGenericClass<string>();
g.Field = "A string";
//...
Console.WriteLine($"SimpleGenericClass.Field = \"{g.Field}\"");
Console.WriteLine($"SimpleGenericClass.Field.GetType() = {g.Field.GetType().FullName}");
}
Public Shared Sub Main()
Dim g As New SimpleGenericClass(Of String)
g.Field = "A string"
'...
Console.WriteLine("SimpleGenericClass.Field = ""{0}""", g.Field)
Console.WriteLine("SimpleGenericClass.Field.GetType() = {0}", g.Field.GetType().FullName)
End Sub
術語
下列詞彙可用來討論 .NET 中的泛型:
泛型型別定義 是一種類別、結構或介面宣告,可作為範本,其可包含或使用的類型佔位元。 例如,System.Collections.Generic.Dictionary<TKey,TValue> 類別可以包含兩種類型:索引鍵和值。 因為泛型型別定義只是範本,所以您無法建立屬於泛型型別定義的類別、結構或介面實例。
泛型型別參數或 類型參數,是泛型類型或方法定義中的佔位元。 System.Collections.Generic.Dictionary<TKey,TValue> 泛型型別有兩個類型參數,
TKey
和TValue
,代表其索引鍵和值的型別。構建的泛型型別或 構建型別,是透過指定泛型型別定義的泛型參數類型來完成的。
泛型型別自變數 是替代泛型型別參數的任何類型。
一般詞彙 範型類型 包含建構類型和範型類型定義。
泛型型別參數的共變性 和 反變性,使您能夠使用其類型參數比目標建構型別更衍生(共變性)或更少衍生(反變性)的建構泛型型別。 共變數和反變數統稱為 變異數。 如需詳細資訊,請參閱 共變數和反變數。
條件約束 是泛型型別參數的限制。 例如,您可以將類型參數限制為實作 System.Collections.Generic.IComparer<T> 泛型介面的類型,以確保可以排序型別的實例。 您也可以將類型參數限制為具有特定基類、具有無參數建構函式或參考型別或實值型別的類型。 泛型類型的用戶無法取代不符合條件約束的類型自變數。
泛型方法定義 是具有兩個參數清單的方法:泛型型別參數清單和正式參數清單。 型別參數可以顯示為傳回型別或正式參數的類型,如下列程式代碼所示。
generic<typename T> T MyGenericMethod(T arg) { T temp = arg; //... return temp; }
T MyGenericMethod<T>(T arg) { T temp = arg; //... return temp; }
Function MyGenericMethod(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 MyGenericClass { T M(T arg) { T temp = arg; //... return temp; } };
class A { T G<T>(T arg) { T temp = arg; //... return temp; } } class MyGenericClass<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 MyGenericClass(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)()
更好的效能。 泛型集合類型通常在儲存和處理值類型時表現更佳,因為不需要將值類型裝箱。
泛型委派可啟用具型別安全的回呼函數,而無需建立多個委派類別。 例如,Predicate<T> 泛型委派可讓您建立方法來實作特定類型的搜尋準則,並將您的方法用於 Array 類型的方法中,例如 Find、FindLast和 FindAll。
泛型可簡化動態產生的程序代碼。 當您搭配動態產生的程式代碼使用泛型時,不需要產生類型。 這增加了您可以使用輕量型動態方法而不是產生整個程序集的情境數。 如需詳細資訊,請參閱 如何:定義和執行動態方法 和 DynamicMethod。
以下是泛型的一些限制:
泛型型別可以衍生自大多數基類,例如 MarshalByRefObject (而且條件約束可用來要求泛型型別參數衍生自基類,例如 MarshalByRefObject)。 不過,.NET 不支援內容系結泛型型別。 泛型型別可以衍生自 ContextBoundObject,但嘗試建立該類型的實例會導致 TypeLoadException。
列舉不能有泛型型別參數。 列舉只能在某些情況下成為泛型(例如,因為它嵌套在由 Visual Basic、C# 或 C++ 定義的泛型類型中)。 如需詳細資訊,請參閱 通用類型系統中的「列舉」。
輕量型動態方法不可為泛型。
在 Visual Basic、C# 和 C++ 中,巢狀類型如果封閉於泛型型別內,必須先為所有封閉型別的類型參數指定類型,否則無法具現化。 另一種表達方式是,在程式設計中的反射機制中,使用這些程式語言定義的巢狀類型包含所有封閉類型的類型參數。 這可讓封入型別的類型參數用於巢狀類型的成員定義中。 如需詳細資訊,請參閱 MakeGenericType中的「巢狀型別」。
備註
巢狀類型可以透過發出程式代碼至動態組件或使用 Ilasm.exe(IL 組件裝配器) 來定義;它不需要包含封閉型別的類型參數,然而,如果不包含,這些類型參數將不在巢狀類別的範疇中。
如需詳細資訊,請參閱 MakeGenericType中的「巢狀類型」。
類別庫和語言支援
.NET 提供下列命名空間中的一些泛型集合類別:
System.Collections.Generic 命名空間包含 .NET 所提供的大部分泛型集合類型,例如 List<T> 和 Dictionary<TKey,TValue> 泛型類別。
System.Collections.ObjectModel 命名空間包含其他泛型集合類型,例如 ReadOnlyCollection<T> 泛型類別,對於向類別的使用者公開物件模型很有用。
實作排序和相等比較的泛型介面會在 System 命名空間中提供,以及事件處理程式、轉換和搜尋述詞的泛型委派類型。
System.Numerics 命名空間提供數學功能的泛型介面(適用於 .NET 7 和更新版本)。 如需詳細資訊,請參閱 泛型數學。
泛型的支援已新增至 System.Reflection 命名空間,以檢查泛型類型和泛型方法、System.Reflection.Emit 發出包含泛型類型和方法的動態元件,以及產生包含泛型的來源圖形 System.CodeDom。
Common Language Runtime 提供新的操作碼和前置碼,以支援通用中繼語言 (CIL) 的泛型類型,包括 Stelem、Ldelem、Unbox_Any、Constrained和 Readonly。
Visual C++、C# 和 Visual Basic 都提供定義和使用泛型的完整支援。 如需語言支援的詳細資訊,請參閱 Visual Basic 中的泛型類型、泛型簡介 和 Visual C++中的泛型概觀。
巢狀類型和泛型
嵌套在泛型型別中的類型可以依賴於封閉泛型型別的類型參數。 Common Language Runtime 會將巢狀類型視為泛型,即使它們本身沒有泛型類型參數也一樣。 當您建立巢狀類型的實體時,您必須為所有封入泛型類型指定類型自變數。
相關文章
標題 | 說明 |
---|---|
.NET 中的泛型集合 | 描述 .NET 中的泛型集合類別和其他泛型型別。 |
用來操作陣列和列表的泛型委派 | 描述用於轉換、搜尋謂詞,以及在陣列或集合元素上執行的操作的泛型委派。 |
泛型數學 | 描述如何一般執行數學運算。 |
泛型介面 | 描述泛型介面,這些介面可在泛型型別系列之間提供通用功能。 |
共變數和反變數 | 描述泛型類型參數中的共變數和反變數。 |
常用的集合類型 | 提供 .NET 中集合類型特性和使用案例的摘要資訊,包括泛型型別。 |
何時使用泛型集合 | 描述判斷何時使用泛型集合類型的一般規則。 |
如何:使用 Reflection Emit 定義泛型類型 | 說明如何產生包含泛型類型和方法的動態元件。 |
在 Visual Basic 中泛型類型 | 描述 Visual Basic 使用者的泛型特性,包括如何使用和定義泛型類型的相關教學主題。 |
泛型簡介 | 提供 C# 使用者關於定義和使用泛型型別的概觀。 |
Visual C++ 中的泛型概觀 | 描述C++使用者的泛型功能,包括泛型與範本之間的差異。 |