反映和泛型概觀
更新:2007 年 11 月
瞭解反映 (Reflection) 如何處理泛型型別和方法時,要注意兩個要點。
泛型型別定義和泛型方法定義的型別參數是由 Type 類別的執行個體所表示。
注意事項:
當 Type 物件表示泛型型別參數時,Type 的許多屬性和方法都有不同的行為;這些差異會記錄在這些屬性和方法的主題中。例如,請參閱 IsAutoClass 和 DeclaringType。此外,某些成員只有在 Type 物件表示泛型型別參數時,才會是有效的。如需範例,請參閱 GetGenericTypeDefinition。
如果 Type 的執行個體表示泛型型別,則它會包含表示型別參數 (若是泛型型別定義) 或型別引數 (若是建構的型別) 的型別之陣列。表示泛型方法的 MethodInfo 類別之執行個體,也是同樣的情況。
反映提供了 Type 和 MethodInfo 的方法,可讓您存取型別參數的陣列,以及判斷 Type 的執行個體是否表示型別參數或實際型別。
如需示範這裡所討論之方法的範例程式碼,請參閱 HOW TO:使用反映檢視和執行個體化泛型型別。
下列討論內容假設您熟悉泛型的用語,例如,型別參數和引數以及開放式或封閉式建構的型別之間的差異。如需詳細資訊,請參閱 .NET Framework 中的泛型概觀。
這是泛型型別還是方法?
當您使用反映來檢查由 Type 之執行個體表示的未知型別時,請使用 IsGenericType 屬性來判斷此未知型別是否為泛型。如果此型別是泛型,則會傳回 true。同樣地,當您檢查由 MethodInfo 類別之執行個體所表示的未知方法時,請使用 IsGenericMethod 屬性來判斷此方法是否為泛型。
這是泛型型別還是方法定義?
使用 IsGenericTypeDefinition 屬性來判斷 Type 物件是否表示泛型型別定義,並使用 IsGenericMethodDefinition 方法來判斷 MethodInfo 是否表示泛型方法定義。
泛型型別和方法定義都是建立可執行個體化之型別時所根據的樣板;.NET Framework 類別庫中的泛型型別 (例如 Dictionary<TKey, TValue>) 就是泛型型別定義。
此型別或方法為開放式還是封閉式?
如果可執行個體化之型別已經用來替代泛型型別或方法的所有型別參數 (包括所有封入型別的所有型別參數),則此泛型型別或方法為封閉式。您只能在泛型型別為封閉式時,建立它的執行個體。如果型別為開放式,Type.ContainsGenericParameters 屬性會傳回 true。如果是方法,則 MethodInfo.ContainsGenericParameters 方法會執行相同的功能。
產生封閉式泛型型別
一旦您有泛型型別或方法定義之後,請使用 MakeGenericType 方法來建立封閉式泛型型別,或使用 MakeGenericMethod 方法為封閉式泛型方法建立 MethodInfo。
取得泛型型別或方法定義
如果您擁有的開放式泛型型別或方法不是泛型型別或方法定義,您將無法建立它的執行個體,也無法提供遺漏的型別參數。您必須要有泛型型別或方法定義;請使用 GetGenericTypeDefinition 方法來取得泛型型別定義,或使用 GetGenericMethodDefinition 方法來取得泛型方法定義。
例如,如果您有一個表示 Dictionary<int, string> (Visual Basic 中為 Dictionary(Of Integer, String)) 的 Type 物件,而且您想要建立型別 Dictionary<string, MyClass>,您可使用 GetGenericTypeDefinition 方法來取得表示 Dictionary<TKey, TValue> 的 Type,並使用 MakeGenericType 方法來產生表示 Dictionary<int, MyClass> 的 Type。
如需非泛型型別之開放式泛型型別的範例,請參閱這個主題之後的<型別參數或型別引數>。
檢查型別引數和型別參數
使用 Type.GetGenericArguments 方法來取得表示泛型型別之型別參數或型別引數的 Type 物件之陣列,並使用 MethodInfo.GetGenericArguments 方法來為泛型方法執行相同的處理。
一旦您知道 Type 物件表示型別參數之後,反映還可以解答許多其他的問題。您可以判斷型別參數的來源、它的位置及它的條件約束。
型別參數或型別引數
若要判斷特定的陣列元素為型別參數或型別引數,請使用 IsGenericParameter 屬性。如果此元素為型別參數,則 IsGenericParameter 屬性為 true。
泛型型別不需要是泛型型別定義,也可以是開放式,此時,它會混合型別引數和型別參數。例如在下列程式碼中,類別 D 是衍生自一個型別,而此型別的建立方式,是將 D 的第一個型別參數替代 B 的第二個型別參數。
class B<T, U> {}
class D<V, W> : B<int, V> {}
Class B(Of T, U)
End Class
Class D(Of V, W)
Inherits B(Of Integer, V)
End Class
generic<typename T, typename U> ref class B {};
generic<typename V, typename W> ref class D : B<int, V> {};
如果您取得一個表示 D<V, W> 的 Type 物件,並使用 BaseType 屬性來取得它的基底型別,則產生的 type B<int, V> 會是開放式,但不是泛型型別定義。
泛型參數的來源
泛型型別參數可能來自於您正在檢查的型別、封入型別或泛型方法。您可利用下列方式判斷泛型型別參數的來源:
首先,使用 DeclaringMethod 屬性來判斷型別參數是否來自於泛型方法。如果此屬性值不是 null 參考 (Visual Basic 中為 Nothing),則來源為泛型方法。
如果來源不是泛型方法,請使用 DeclaringType 屬性來判斷泛型型別參數所屬的泛型型別。
如果此型別參數屬於泛型方法,則 DeclaringType 屬性會傳回宣告此泛型方法的型別 (它並沒有什麼重要性)。
泛型參數的位置
在罕見的情況下,必須判斷某個型別參數在它的宣告類別之型別參數清單中的位置。例如,假設您有一個 Type 物件,其表示上一個範例中的 B<int, V> 型別。GetGenericArguments 方法會為您提供型別參數清單,而當您檢查 V 時,您可使用 DeclaringMethod 和 DeclaringType 屬性來探查它的來源。然後,您可使用 GenericParameterPosition 屬性來判斷它在型別參數清單中定義所在的位置。在此範例中,V 在型別參數清單中定義所在的位置為 0 (零)。
基底型別和介面的條件約束
使用 GetGenericParameterConstraints 方法來取得型別參數的基底型別條件約束和介面條件約束。陣列的元素順序並不是很重要,如果某個元素為介面型別,則該元素表示介面條件約束。
特殊條件約束
GenericParameterAttributes 屬性會取得一個指示特殊條件約束的 GenericParameterAttributes 值。型別參數可以限制為參考型別、不可為 null 的實值型別,以及擁有預設建構函式。
不變項目
如需泛型型別反映中常用詞彙的不變條件之表格,且參閱 Type.IsGenericType。如需與泛型方法有關的詞彙,請參閱 MethodInfo.IsGenericMethod。