反射类型和泛型类型
从反射的角度来说,泛型类型和普通类型之间的区别在于泛型类型具有与之关联的一组类型形参(若是泛型类型定义)或类型实参(若是构造类型)。 泛型方法和普通方法以相同方式互不相同。
有两个关键点可了解反射如何处理泛型类型和方法:
泛型类型定义和泛型方法定义的类型参数由 Type 类的实例表示。
注意
Type 对象表示泛型类型参数时, Type 的很多属性和方法具有不同的行为。 这些差异记录在属性和方法文章中。 有关示例,请参阅 IsAutoClass 和 DeclaringType。 此外,某些成员仅在 Type 对象表示泛型类型参数时有效。 有关示例,请参阅 GetGenericTypeDefinition。
如果 Type 的实例表示泛型类型,则它包含表示类型形参(对于泛型类型定义)或类型实参(对于构造类型)的类型数组。 这同样适用于表示泛型方法的 MethodInfo 的类的实例。
反射提供 Type 和 MethodInfo 的方法,它允许你访问类型形参的数组并确定 Type 的实例是表示类型形参还是表示实际类型。
有关演示此处讨论的方法的示例代码,请参阅如何:使用反射检查和实例化泛型类型中提供的其他信息。
以下讨论假定熟悉泛型的术语,例如类型形参和实参之间的差异以及开放式或封闭式构造类型之间的差异。 有关详细信息,请参阅泛型。
这是泛型类型还是方法?
当使用反射来检查未知类型(由 Type的实例表示)时,请使用 IsGenericType 属性来确定未知类型是否为泛型。 如果类型是泛型,它会返回 true
。 同样,当检查未知方法(由 MethodInfo 类的实例表示)时,请使用 IsGenericMethod 属性来确定此方法是否为泛型。
这是泛型类型还是方法定义?
使用 IsGenericTypeDefinition 属性来确定 Type 对象是否表示泛型类型定义,并使用 IsGenericMethodDefinition 方法来确定 MethodInfo 是否表示泛型方法定义。
泛型类型定义和泛型方法定义是一些从中创建可实例化类型的模板。 .NET 库中的泛型类型(例如 Dictionary<TKey,TValue>)是泛型类型定义。
类型或方法是开放式还是封闭式的?
如果可实例化类型都已替换为所有类型形参(包括所有封闭类型的所有类型形参),则泛型类型或方法是封闭式的。 若为封闭式,则只能创建泛型类型的实例。 如果类型为开放式, Type.ContainsGenericParameters 属性将返回 true
。 对于方法, MethodBase.ContainsGenericParameters 方法执行相同的功能。
生成封闭式泛型类型
在具有泛型类型或方法定义之后,请使用 MakeGenericType 方法来创建封闭式泛型类型或者使用 MakeGenericMethod 方法来为封闭式泛型方法创建 MethodInfo 。
获取泛型类型或方法定义
如果你具有开放式泛型类型或方法(非泛型类型或方法定义),则不能创建它的实例,也不能提供缺少的类型形参。 必须具有泛型类型或方法定义。 使用 GetGenericTypeDefinition 方法来获取泛型类型定义或使用 GetGenericMethodDefinition 方法来获取泛型方法定义。
例如,如果你有一个表示 Dictionary<int, 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 属性来确定类型形参是否来自泛型方法。 如果属性值不是空引用,则源是泛型方法。
- 如果源不是泛型方法,则使用 DeclaringType 属性来确定泛型类型形参所属的泛型类型。
如果类型形参属于泛型方法,则 DeclaringType 属性返回声明泛型方法的类型,这是不相关的类型。
泛型参数的位置
在极少数情况下,必须确定类型形参在其声明类的类型形参列表中的位置。 例如,假设你有表示上述示例中的 Type 类型的 B<int, V>
对象。 GetGenericArguments 方法提供类型实参的列表,在检查 V
时即可使用 DeclaringMethod 和 DeclaringType 属性来发现它的来源位置。 然后,可以使用 GenericParameterPosition 属性来确定它在定义它的类型形参列表中的位置。 在此示例中, V
在定义它的类型形参列表中位于位置 0(零)。
基类型和接口约束
使用 GetGenericParameterConstraints 方法来获取类型形参的基类型约束和接口约束。 数组的元素顺序并不重要。 如果元素是接口类型,则它表示接口约束。
泛型参数特性
GenericParameterAttributes 属性获取 GenericParameterAttributes 值,该值指示类型形参的方差(协变或逆变)和特殊约束。
协变和逆变
若要确定类型形参是协变还是逆变,请将 GenericParameterAttributes.VarianceMask 掩码应用到 GenericParameterAttributes 属性返回的 GenericParameterAttributes 值。 如果结果为 GenericParameterAttributes.None,则类型形参不变。 有关详细信息,请参阅协变和逆变。
特殊约束
若要确定类型形参的特殊约束,请将 GenericParameterAttributes.SpecialConstraintMask 掩码应用到 GenericParameterAttributes 属性返回的 GenericParameterAttributes 值。 如果结果为 GenericParameterAttributes.None,则没有任何特殊约束。 可将类型形参约束为引用类型、不可以为 null 的类型以及具有无参数构造函数。
固定协定
有关泛型类型反射中常用术语的固定条件表格,请参阅 Type.IsGenericType。 有关与泛型方法相关的其他术语,请参阅 MethodBase.IsGenericMethod。