反射类型和泛型类型

从反射的角度来看,泛型类型和普通类型之间的差异是泛型类型与泛型类型关联了一组类型参数(如果它是泛型类型定义)或类型参数(如果是构造类型)。 泛型方法与普通方法以相同的方式不同。

有两个键可理解反射如何处理泛型类型和方法:

  • 泛型类型定义和泛型方法定义的类型参数由类的 Type 实例表示。

    注释

    Type 的许多属性和方法在 Type 对象表示泛型类型参数时会表现出不同的行为。 这些差异记录在属性和方法文章中。 例如,请参阅 IsAutoClassDeclaringType。 此外,某些成员仅在Type对象表示泛型类型参数时才有效。 有关示例,请参阅 GetGenericTypeDefinition

  • 如果表示泛型类型的实例 Type ,则它包括表示类型参数(泛型类型定义)或类型参数(对于构造类型)的类型数组。 表示泛型方法的 MethodInfo 类实例也是如此。

反射提供了TypeMethodInfo方法,允许你访问类型参数数组,并确定Type是表示类型参数的实例还是实际的类型。

有关演示此处讨论的方法的示例代码,请参阅 如何:使用反射检查和实例化泛型类型

以下讨论假设熟悉泛型的术语,例如类型参数和参数之间的差异以及打开或关闭的构造类型。 有关详细信息,请参阅 泛型

这是泛型类型还是方法?

使用反射检查未知类型(由实例 Type表示)时,请使用该 IsGenericType 属性来确定未知类型是否为泛型类型。 如果类型为泛型,则返回 true 该类型。 同样,在检查由类实例表示的 MethodInfo 未知方法时,请使用 IsGenericMethod 该属性来确定该方法是否为泛型方法。

这是泛型类型还是方法定义?

使用该 IsGenericTypeDefinition 属性可确定对象是否 Type 表示泛型类型定义,并使用 IsGenericMethodDefinition 该方法确定是否 MethodInfo 表示泛型方法定义。

泛型类型和方法定义是创建可实例化类型的模板。 .NET 库中的泛型类型,例如 Dictionary<TKey,TValue>,都是泛型类型定义。

类型或方法是打开还是关闭?

当可实例化的类型被用来替换其所有类型参数,包括所有封闭类型的所有类型参数时,泛型类型或方法是闭合的。 如果已关闭,则只能创建泛型类型的实例。 如果该类型处于打开状态,该 Type.ContainsGenericParameters 属性将 true 返回。 对于方法,该方法 MethodBase.ContainsGenericParameters 执行相同的函数。

生成封闭的泛型类型

获得泛型类型或方法定义后,使用MakeGenericType方法来创建封闭泛型类型,或使用MakeGenericMethod来创建封闭泛型方法的MethodInfo

获取泛型类型或方法定义

如果你有一个不是泛型类型或方法定义的开放泛型类型或方法,则无法创建它的实例,并且无法提供缺少的类型参数。 必须具有泛型类型或方法定义。 使用GetGenericTypeDefinition方法获取泛型类型定义,或使用GetGenericMethodDefinition方法获取泛型方法定义。

例如,如果你有一个 Type 表示 Dictionary<int, string> 对象并且要创建类型 Dictionary<string, MyClass>,则可以使用 GetGenericTypeDefinition 该方法获取 Type 表示 Dictionary<TKey, TValue> ,然后使用 MakeGenericType 该方法生成一个 Type 表示 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

如果获取到一个Type对象,表示D<V, W>,并使用BaseType属性获取它的基类型,那么所得的type B<int, V>是已开放的,但它不是一个泛型类型定义。

泛型参数的源

泛型类型参数可能来自要检查的类型、封闭类型或泛型方法。 可以按如下所示确定泛型类型参数的源:

  • 首先,使用 DeclaringMethod 属性来确定类型参数是否来自泛型方法。 如果属性值不是 null 引用,则源是泛型方法。
  • 如果源不是泛型方法,请使用 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