与其他类型的信息一样,泛型类型的信息的获取方式为:检查表示泛型类型的 Type 对象。主要的差异在于,泛型类型具有一组表示其泛型类型参数的 Type 对象。本部分的第一个步骤是检查泛型类型。
通过将类型变量绑定到泛型类型定义的类型参数,可以创建表示构造类型的 Type 对象。第二个步骤演示此过程。
检查泛型类型及其类型参数
-
获取表示泛型类型的 Type 实例。在下面的代码中,使用 C# 的 typeof 运算符(在 Visual Basic 中为GetType,在 Visual C++ 中为 typeid)获取类型。有关获取 Type 对象的其他方法,请参见 Type 类主题。注意,余下的步骤中,类型包含在名为 t 的方法参数中。
C#
Type d1 = typeof(Dictionary<,>);
-
使用 IsGenericType 属性确定类型是否为泛型,然后使用 IsGenericTypeDefinition 属性确定类型是否为泛型类型定义。
C#
Console.WriteLine( " Is this a generic type? {0}",
t.IsGenericType);
Console.WriteLine( " Is this a generic type definition? {0}",
t.IsGenericTypeDefinition);
-
使用 GetGenericArguments 方法获取包含泛型类型参数的数组。
C#
Type[] typeParameters = t.GetGenericArguments();
-
对每个类型变量,使用 IsGenericParameter 属性确定其是不是类型参数(例如,在泛型类型定义中),是不是已为类型参数指定的类型(例如,在构造类型中)。
C#
Console.WriteLine( " List {0} type arguments:",
typeParameters.Length);
foreach( Type tParam in typeParameters )
{
if (tParam.IsGenericParameter)
{
DisplayGenericParameter(tParam);
}
else
{
Console.WriteLine( " Type argument: {0}",
tParam);
}
}
-
在类型系统中,和普通类型一样,泛型类型参数是由 Type 的实例表示的。下面的代码演示表示泛型类型参数的 Type 对象的名称和参数位置。此处,参数位置无足轻重;它在检查类型参数(用作其他泛型类型的类型变量)时更有价值。
C#
private static void DisplayGenericParameter(Type tp)
{
Console.WriteLine( " Type parameter: {0} position {1}",
tp.Name, tp.GenericParameterPosition);
-
通过使用 GetGenericParameterConstraints 方法获取单个数组中的所有约束,确定泛型类型参数的基类型约束和接口约束。不保证约束处于任何特定顺序。
C#
Type classConstraint = null;
foreach(Type iConstraint in tp.GetGenericParameterConstraints())
{
if (iConstraint.IsInterface)
{
Console.WriteLine( " Interface constraint: {0}",
iConstraint);
}
}
if (classConstraint != null)
{
Console.WriteLine( " Base type constraint: {0}",
tp.BaseType);
}
else
Console.WriteLine( " Base type constraint: None");
-
使用 GenericParameterAttributes 属性获取类型参数的特殊约束,如要求其为引用类型。该属性还包含表示方差的值,该值可屏蔽,如下面的代码所示。
C#
GenericParameterAttributes sConstraints =
tp.GenericParameterAttributes &
GenericParameterAttributes.SpecialConstraintMask;
-
特殊约束的属性为标志,表示没有任何特殊约束的标志 (System.Reflection.GenericParameterAttributes.None) 还表示没有协变或逆变。因此,若要测试其中一个条件,必须使用适当的屏蔽。在此情况下,请使用 System.Reflection.GenericParameterAttributes.SpecialConstraintMask 隔离特殊约束标志。
C#
if (sConstraints == GenericParameterAttributes.None)
{
Console.WriteLine( " No special constraints.");
}
else
{
if (GenericParameterAttributes.None != (sConstraints &
GenericParameterAttributes.DefaultConstructorConstraint))
{
Console.WriteLine( " Must have a parameterless constructor.");
}
if (GenericParameterAttributes.None != (sConstraints &
GenericParameterAttributes.ReferenceTypeConstraint))
{
Console.WriteLine( " Must be a reference type.");
}
if (GenericParameterAttributes.None != (sConstraints &
GenericParameterAttributes.NotNullableValueTypeConstraint))
{
Console.WriteLine( " Must be a non-nullable value type.");
}
}
构造泛型类型的实例
泛型类型和模板类似。除非指定其泛型类型参数的实际类型,否则不能创建泛型类型的实例。若要在运行时使用反射创建实例,需要使用 MakeGenericType 方法。
构造泛型类型的实例
-
获取表示泛型类型的 Type 对象。下面的代码以两种不同方式获取泛型类型 Dictionary:一种方法使用 System.Type.GetType(System.String) 方法重载和描述类型的字符串,另一种方法调用构造类型 Dictionary<String, Example>(在 Visual Basic 中为 Dictionary(Of String, Example))的 GetGenericTypeDefinition 方法。MakeGenericType 方法需要泛型类型定义。
C#
// Use the typeof operator to create the generic type
// definition directly. To specify the generic type definition,
// omit the type arguments but retain the comma that separates
// them.
Type d1 = typeof(Dictionary<,>);
// You can also obtain the generic type definition from a
// constructed class. In this case, the constructed class
// is a dictionary of Example objects, with String keys.
Dictionary< string, Example> d2 = new Dictionary< string, Example>();
// Get a Type object that represents the constructed type,
// and from that get the generic type definition. The
// variables d1 and d4 contain the same type.
Type d3 = d2.GetType();
Type d4 = d3.GetGenericTypeDefinition();
-
构造一组用于替换类型参数的类型变量。数组必须包含正确数目的 Type 对象,并且顺序和对象在类型参数列表中的顺序相同。在这种情况下,键(第一个类型参数)的类型为 String,字典中的值是名为 Example 的类的实例。
C#Type[] typeArgs = { typeof( string), typeof(Example)};
-
调用 MakeGenericType 方法将类型变量绑定到类型参数,然后构造类型。
C#
Type constructed = d1.MakeGenericType(typeArgs);
-
使用 CreateInstance 方法重载来创建构造类型的对象。下面的代码在生成的 Dictionary<String, Example> 对象中存储 Example 类的两个实例。
C#
object o = Activator.CreateInstance(constructed);
示例
下面的代码示例定义 DisplayGenericType 方法来检查泛型类型定义和代码中使用的构造类型,并显示它们的信息。DisplayGenericType 方法演示如何使用 IsGenericType、IsGenericParameter 和 GenericParameterPosition 属性以及 GetGenericArguments 方法。
该示例还定义 DisplayGenericParameter 方法来检查泛型类型参数并显示其约束。
代码示例定义一组测试类型,包括说明类型参数约束的泛型类型,并演示如何显示这些类型的信息。
示例通过创建一组类型参数并调用 MakeGenericType 方法,从 Dictionary 类构造类型。程序对使用 MakeGenericType 构造的 Type 对象和使用 typeof(Visual Basic 中为 GetType)获取的 Type 对象进行比较,演示这两个对象是相同的。类似地,程序使用 GetGenericTypeDefinition 方法获取构造类型的泛型类型定义,并将其与表示 Dictionary 类的 Type 对象进行比较。
using System.Reflection;
using System.Collections.Generic;
using System.Security.Permissions;
// Define an example interface.
public interface ITestArgument {}
// Define an example base class.
public class TestBase {}
// Define a generic class with one parameter. The parameter
// has three constraints: It must inherit TestBase, it must
// implement ITestArgument, and it must have a parameterless
// constructor.
public class Test<T> where T : TestBase, ITestArgument, new() {}
// Define a class that meets the constraints on the type
// parameter of class Test.
public class TestArgument : TestBase, ITestArgument
{
public TestArgument() {}
}
public class Example
{
// The following method displays information about a generic
// type.
private static void DisplayGenericType(Type t)
{
Console.WriteLine( "\r\n {0}", t);
Console.WriteLine( " Is this a generic type? {0}",
t.IsGenericType);
Console.WriteLine( " Is this a generic type definition? {0}",
t.IsGenericTypeDefinition);
// Get the generic type parameters or type arguments.
Type[] typeParameters = t.GetGenericArguments();
Console.WriteLine( " List {0} type arguments:",
typeParameters.Length);
foreach( Type tParam in typeParameters )
{
if (tParam.IsGenericParameter)
{
DisplayGenericParameter(tParam);
}
else
{
Console.WriteLine( " Type argument: {0}",
tParam);
}
}
}
// The following method displays information about a generic
// type parameter. Generic type parameters are represented by
// instances of System.Type, just like ordinary types.
private static void DisplayGenericParameter(Type tp)
{
Console.WriteLine( " Type parameter: {0} position {1}",
tp.Name, tp.GenericParameterPosition);
Type classConstraint = null;
foreach(Type iConstraint in tp.GetGenericParameterConstraints())
{
if (iConstraint.IsInterface)
{
Console.WriteLine( " Interface constraint: {0}",
iConstraint);
}
}
if (classConstraint != null)
{
Console.WriteLine( " Base type constraint: {0}",
tp.BaseType);
}
else
Console.WriteLine( " Base type constraint: None");
GenericParameterAttributes sConstraints =
tp.GenericParameterAttributes &
GenericParameterAttributes.SpecialConstraintMask;
if (sConstraints == GenericParameterAttributes.None)
{
Console.WriteLine( " No special constraints.");
}
else
{
if (GenericParameterAttributes.None != (sConstraints &
GenericParameterAttributes.DefaultConstructorConstraint))
{
Console.WriteLine( " Must have a parameterless constructor.");
}
if (GenericParameterAttributes.None != (sConstraints &
GenericParameterAttributes.ReferenceTypeConstraint))
{
Console.WriteLine( " Must be a reference type.");
}
if (GenericParameterAttributes.None != (sConstraints &
GenericParameterAttributes.NotNullableValueTypeConstraint))
{
Console.WriteLine( " Must be a non-nullable value type.");
}
}
}
[PermissionSetAttribute(SecurityAction.Demand, Name= "FullTrust")]
public static void Main()
{
// Two ways to get a Type object that represents the generic
// type definition of the Dictionary class.
//
// Use the typeof operator to create the generic type
// definition directly. To specify the generic type definition,
// omit the type arguments but retain the comma that separates
// them.
Type d1 = typeof(Dictionary<,>);
// You can also obtain the generic type definition from a
// constructed class. In this case, the constructed class
// is a dictionary of Example objects, with String keys.
Dictionary< string, Example> d2 = new Dictionary< string, Example>();
// Get a Type object that represents the constructed type,
// and from that get the generic type definition. The
// variables d1 and d4 contain the same type.
Type d3 = d2.GetType();
Type d4 = d3.GetGenericTypeDefinition();
// Display information for the generic type definition, and
// for the constructed type Dictionary<String, Example>.
DisplayGenericType(d1);
DisplayGenericType(d2.GetType());
// Construct an array of type arguments to substitute for
// the type parameters of the generic Dictionary class.
// The array must contain the correct number of types, in
// the same order that they appear in the type parameter
// list of Dictionary. The key (first type parameter)
// is of type string, and the type to be contained in the
// dictionary is Example.
Type[] typeArgs = { typeof( string), typeof(Example)};
// Construct the type Dictionary<String, Example>.
Type constructed = d1.MakeGenericType(typeArgs);
DisplayGenericType(constructed);
object o = Activator.CreateInstance(constructed);
Console.WriteLine( "\r\nCompare types obtained by different methods:");
Console.WriteLine( " Are the constructed types equal? {0}",
(d2.GetType()==constructed));
Console.WriteLine( " Are the generic definitions equal? {0}",
(d1==constructed.GetGenericTypeDefinition()));
// Demonstrate the DisplayGenericType and
// DisplayGenericParameter methods with the Test class
// defined above. This shows base, interface, and special
// constraints.
DisplayGenericType( typeof(Test<>));
}
}