訓練
.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 = \"{0}\"", g.Field);
Console.WriteLine("SimpleGenericClass.Field.GetType() = {0}", 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 中的泛型:
「泛型類型定義」 (generic type definition),是做為範本的類別、結構或介面宣告,且具有可包含或使用之類型的預留位置。 例如, System.Collections.Generic.Dictionary<TKey,TValue> 類別可包含兩種類型:索引鍵和值。 因為泛型類型定義是只是範本,您無法建立泛型類型定義之類別、結構或介面的執行個體。
「泛型類型參數」(Generic type parameter) 或 「類型參數」(type parameter),是泛型類型或方法定義中的預留位置。 System.Collections.Generic.Dictionary<TKey,TValue> 泛型類型有兩個類型參數,
TKey
和TValue
,分別代表其索引鍵和值的類型。「建構的泛型類型」(constructed generic type) 或 「建構的類型」(constructed type),是為泛型類型定義的泛型類型參數所指定之類型的結果。
「泛型類型引數」 (generic type argument) 是要替換泛型類型參數的所有類型。
一般詞彙「泛型型別」包括建構的型別和泛型型別定義兩者。
泛型型別參數的「共變數」和「反變數」可讓您使用建構的泛型型別,其型別引數比目標建構的型別有更多衍生 (共變數) 或更少衍生 (反變數)。 共變數和反變數合稱為「變異數」。 如需詳細資訊,請參閱共變數和反變數。
「條件約束」 (Constraint),是在泛型類型參數上的限制。 例如,您可以限制類型參數為實作 System.Collections.Generic.IComparer<T> 泛型介面的類型,以確保能夠排序類型的執行個體。 您也可以將型別參數限制為具有特定基底類別的型別,或是具有無參數建構函式的型別,或為參考型別或實值型別。 的泛型類型的使用者無法替換沒有滿足這些條件約束的類型引數。
「泛型方法定義」 (generic method definition),是一種有兩個參數清單的方法:泛型類型參數清單和型式參數清單。 類型參數會顯示為傳回類型或型式參數的類型,如下程式碼所示。
generic<typename T> T MyGenericMethod(T arg) { T temp = arg; //... return temp; }
C#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; } };
C#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^>();
C#LinkedList<string> llist = new LinkedList<string>();
Dim llist As New LinkedList(Of String)()
效能較佳。 泛型集合類型在儲存和管理實值類型上,通常有較好的表現,因為不需要 box 實值類型。
泛型委派讓類型安全回呼不需要建立多個委派類別。 例如, Predicate<T> 泛型委派可讓您建立一種方法,為特定類型實作您自己的搜尋條件,以及讓您搭配 Array 類型的方法 (例如 Find、 FindLast和 FindAll) 使用方法。
泛型簡化了動態產生的程式碼。 當您搭配動態產生的程式碼使用泛型時,不需要產生類型。 這會增加您使用輕量動態方法而非產生整個組件的時機。 如需詳細資訊,請參閱如何:定義與執行動態方法及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.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 提供新的 opcode 及前置詞,以支援 Microsoft 中繼語言 (MSIL) 中的泛型類型,包括 Stelem、 Ldelem、 Unbox_Any、 Constrained和 Readonly。
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++ 使用者的泛型功能,包括泛型和範本之間的差異。 |