反射类型和泛型类型
从反射的角度来说,泛型类型和普通类型的区别在于,泛型类型与一组类型参数(如果是泛型类型定义)或类型变量(如果是构造的类型)关联。泛型方法与普通方法的区别也在于此。
反射的问题在于提供一种方式来检查类型参数或类型变量的此数组。如果是类型参数,反射还必须提供一种方式来检查约束。本节介绍提供检查泛型类型和方法的能力的 Type 和 MethodInfo 类的方法
概述
要理解反射处理泛型类型和泛型方法的方式,有两点很重要:
- 泛型类型定义和泛型方法定义的类型参数是由 Type 类的实例表示的。
说明:如果 Type 对象表示泛型类型参数,则 Type 的许多属性和方法具有不同的行为。这些不同在属性和方法主题中进行介绍。有关示例,请参见 IsAutoClass 和 DeclaringType。此外,某些成员仅当 Type 对象表示泛型类型参数时才有效。有关示例,请参见 GetGenericTypeDefinition。
- 如果 Type 的实例表示泛型类型,则会包含一组表示类型参数(泛型类型定义)或类型变量(构造类型)的类型。表示泛型方法的 MethodInfo 类的实例也是如此。
反射提供 Type 和 MethodInfo 方法,允许访问类型参数数组,还可以确定 Type 实例表示的是类型参数还是实际类型。
下面的讨论假定已熟悉了泛型术语,如类型参数、类型变量以及开放式或封闭式构造类型之间的差异。有关更多信息,请参见 .NETFramework 中的泛型概述。
是泛型类型还是泛型方法?
使用反射检查 Type 实例表示的未知类型时,请使用 IsGenericType 属性确定未知类型是否为泛型。类似地,检查 MethodInfo 类实例表示的未知方法时,请使用 IsGenericMethod 属性确定方法是否为泛型。
- 是泛型类型定义还是泛型方法定义?
使用 IsGenericTypeDefinition 属性确定 Type 对象是否表示泛型类型定义,使用 IsGenericMethodDefinition 属性确定 MethodInfo 是否表示泛型方法定义。
泛型类型定义和泛型方法定义是一些模板,根据这些模板,可以创建可实例化的类型。.NET 框架类库中的泛型类型(如 Dictionary<TKey, TValue>)是泛型类型定义。
- 类型或方法是开放式的还是封闭式的?
如果可实例化类型的所有类型参数都已替换,包括所有封闭类型的所有类型参数,则泛型类型或泛型方法是封闭式的。如果是封闭式的,则只能创建泛型类型的一个实例。如果类型是开放式的,则 Type.ContainsGenericParameters 属性返回 true。MethodInfo.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> {}
如果获取表示 D<V, W> 的 Type 对象并使用 BaseType 属性获取其基类型,则产生的 typeB<int, V> 是开放式的非泛型类型定义。
- 泛型参数的源
泛型类型参数可能来自要检查的类型、封闭式类型或泛型方法。可以确定泛型类型参数的源,方法如下:
首先,使用 DeclaringMethod 属性确定类型参数是否来自泛型方法。如果该属性值不是空引用(在 Visual Basic 中为 Nothing),则源是泛型方法。
如果源不是泛型方法,则使用 DeclaringType 属性确定泛型类型参数所属的泛型类型。
如果类型参数属于泛型方法,则 DeclaringType 属性返回声明该泛型方法的类型,这已没有意义。
- 泛型参数的位置
在极少情况下,需要确定类型参数在其声明类的类型参数列表中的位置。例如,假定具有一个 Type 对象,该对象表示上一示例中的 B<int, V> 类型。GetGenericArguments 方法提供类型变量列表,在检查 V 时,可以使用 DeclaringMethod 和 DeclaringType 属性获取其来源。然后,可以使用 GenericParameterPosition 属性确定其在定义时在类型参数列表中的位置。在本示例中,V 在进行定义的类型参数列表的位置 0(零)处。
- 基类型约束和接口约束
使用 GetGenericParameterConstraints 方法可获取类型参数的基类型约束和接口约束。数组元素的顺序不重要。如果是接口类型,则元素表示接口约束。
- 特殊约束
GenericParameterAttributes 属性获取一个 GenericParameterAttributes 值,该值指示特殊约束。类型参数可以约束为引用类型、不可为空值类型和具有默认构造函数。
固定条件
有关泛型类型反射中常用术语的固定条件表,请参见 Type.IsGenericType。有关泛型方法的其他相关术语,请参见 MethodInfo.IsGenericMethod。
如何:使用反射检查和实例化泛型类型
与其他类型的信息一样,泛型类型信息的获取方式为:检查表示泛型类型的 Type 对象。主要差异在于,泛型类型具有一组表示其泛型类型参数的 Type 对象。本部分的第一个步骤是检查泛型类型。第二个步骤演示,通过将类型变量绑定到泛型类型定义的类型参数,可以创建表示构造类型的 Type 对象。
检查泛型类型及其类型参数
using System;
using System.Collections.Generic;
using System.Reflection;
namespace CheckGenericType
5: {
class Program
7: {
string[] args)
9: {
typeof(Dictionary<,>);
, t.IsGenericType);
, t.IsGenericTypeDefinition);
13: Type[] typeParameters = t.GetGenericArguments();
, typeParameters.Length);
in typeParameters)
16: {
if (tParam.IsGenericParameter)
18: { DisplayGenericParameter(tParam); }
else
20: {
, tParam);
22: }
23: }
24: Console.ReadKey();
25: }
void DisplayGenericParameter(Type tp)
27: {
, tp.Name, tp.GenericParameterPosition);
in tp.GetGenericParameterConstraints())
30: {
if (iConstraint.IsInterface)
32: {
, iConstraint);
34: }
35: }
null)
37: {
, tp.BaseType);
39: }
else
41: {
);
43: }
44: GenericParameterAttributes sConstraints = tp.GenericParameterAttributes & GenericParameterAttributes.SpecialConstraintMask;
if (sConstraints == GenericParameterAttributes.None)
46: {
);
48: }
else
50: {
if (GenericParameterAttributes.None != (sConstraints & GenericParameterAttributes.DefaultConstructorConstraint))
52: {
);
54: }
if (GenericParameterAttributes.None != (sConstraints & GenericParameterAttributes.ReferenceTypeConstraint))
56: {
);
if (GenericParameterAttributes.None != (sConstraints & GenericParameterAttributes.NotNullableValueTypeConstraint))
59: {
);
61: }
62: }
63: }
64: }
65: }