65.9K
CodeProject 正在变化。 阅读更多。
Home

反思泛型

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.77/5 (20投票s)

2007年12月12日

CPOL

4分钟阅读

viewsIcon

68166

downloadIcon

461

如何使用反射来访问、使用和解释泛型类和方法

引言

你是否曾经不得不这样做?

string typeName = "MyDataType";
Type type = Type.GetType( typeName );
object o = someGenericClass<typeof(type)>();

如果你这样做过,那么你可能也发现这无法编译。好消息是,C# 中可以实现这个功能,但它并不明显。本文旨在澄清围绕反射和泛型类型的迷雾。我们将展示如何使用泛型类来实现这一点(并且,额外赠送,还将展示泛型方法的等效功能)。

背景

你为什么会需要这样做呢?我在创建一个应用程序框架时遇到了这个问题,该框架利用了动态加载的、具有明确定义接口的 .NET 程序集。其中一些接口是泛型的,而另一些则包含泛型方法。

问题出现在实现框架时,当时数据类型在编译时是未知的,而是通过Type对象动态发现的。以任何明显的方式,使用动态加载的数据类型来实例化动态加载的泛型类型是不可能的。

经过大量的互联网搜索和 MSDN 的深入研究,我终于发现了使用泛型反射的秘诀。

类型和泛型

这一切的关键在于理解可以与Generic类关联的两种不同的Type实例。当你定义一个Generic类时,你并没有定义一个或多个成员类型,直到它在代码中的某个地方被使用。这被称为“开放”泛型。当你在代码中声明对这些Generic类之一的引用并提供实际的类型时,你就“完成”了类的定义。这被称为“封闭”泛型。

在我们的所有示例中,我们将使用以下Generic类定义

namespace ReflectGenerics
{
    public class GenericClass<T>
    {
        public GenericClass( T t )
        {
            _t = t;
            Console.WriteLine( "GenericClass<{0}>( {1} ) created", 
                    typeof( T ).FullName, _t.ToString() );
        }
        private T _t = default( T );

        public T GetValue() 
        {
            Console.WriteLine( "GetValue() invoked, returning {0}", _t.ToString() );
            return _t; 
        }

        public static U StaticGetValue<U>( U u )
        {
            Console.WriteLine( "StaticGetValue<{0}>( {1} ) invoked", 
                    typeof( U ).FullName, u.ToString() );
            return u;
        }
    }
}

让我们看一些代码示例

// create an instance of a generic class 
GenericClass<int> t = new GenericClass<int>( 1 );

// get the type of the generic class definition using just the class name
string typeName = "ReflectGenerics.GenericClass";
Type openGenericClass = Type.GetType( typeName );

// get the type of the generic class with the generic parameter defined
Type closedGenericClass = typeof( GenericClass<int> );

如果我们通过调试器单步执行这段代码并检查Type对象,我们可以看到openGenericClassclosedGenericClass之间的一些差异。Type类有一些属性可以帮助我们确定Generic类处于什么状态。相关的属性是:IsGenericTypeIsGenericTypeDefinition。每个属性都返回bool

如果我们检查openGenericClass,我们会发现IsGenericTypetrue,而IsGenericTypeDefinitiontrue。如果我们检查closedGenericClass,我们会发现IsGenericTypetrue,而IsGenericTypeDefinitionfalse。我们可以从中得知,如果一个类是Generic类,那么它关联的Type对象将始终返回trueIsGenericType。但是,如果Type代表一个Generic类,并且它的类型参数未定义,那么IsGenericTypeDefinition也将返回true

要仅从Type定义创建对象实例,我们可以使用Activator类。但是,要实例化一个Generic类型,我们需要一个封闭的Type(即,所有泛型类型参数都已定义)。好的!这很好。但是,如果我们有一个用于泛型类型参数的Type对象和一个开放的泛型Type怎么办?我们如何将它们转换为一个封闭的泛型Type

Type类有一个名为MakeGenericType的辅助方法,它可以将开放类型转换为封闭类型。

// get the type of the generic class from the class definition type
Type dynamicClosedGenericClass = openGenericClass.MakeGenericType( typeof( int ) );

现在我们已经获取了一个开放类型并为其添加了必要的泛型类型参数,我们可以使用这个封闭类型来实例化一个对象。

object o = Activator.CreateInstance( dynamicClosedGenericClass, 1 );
object rv = dynamicClosedGenericClass.InvokeMember
                ( "GetValue", BindingFlags.InvokeMethod, null, o, new object[ 0 ] );
Console.WriteLine( "GetValue() returned {0}", rv.ToString() );

请注意,这段代码在功能上等同于

GenericClass<int> t = new GenericClass<int>( 1 );
Console.WriteLine( "GetValue() returned {0}", t.GetValue() );

泛型方法

我们唯一需要弄清楚的就是泛型方法。我们的示例generic 类有一个static generic方法,用于作为示例。我们学到的关于类开放generic 类型和封闭generic 类型的所有知识也true 地适用于方法,只是我们需要使用MethodInfo类而不是Type类。

// invoke the static template method directly
GenericClass<int>.StaticGetValue( 3 );

// get the open generic method type
MethodInfo openGenericMethod = 
    typeof( GenericClass<> ).GetMethod( "StaticGetValue" );

// get the close generic method type, by supplying the generic parameter type
MethodInfo closedGenericMethod = 
    openGenericMethod.MakeGenericMethod( typeof( int ) );

object o2 = closedGenericMethod.Invoke( null, new object[] { 4 } );

Console.WriteLine( "o2 = {0}", o2.ToString() );

MethodInfo具有IsGenericMethodIsGenericMethodDefinition属性。这些属性与我们正在检查的Type类中的两个属性完全类似。MethodInfo同样可以是“开放”或“封闭”的,如果它引用了一个泛型方法。最后,将开放MethodInfo转换为封闭MethodInfo是通过MakeGenericMethod方法完成的。

摘要

因此,我们已经学会了如何获取一个Type实例并检查它是否是generic的,以及它是开放的还是封闭的。如果是开放的,我们学会了如何使用MakeGenericType方法将其关闭,以便可以使用Activator进行实例化。我们还学会了如何使用MethodInfo类上的MakeGenericMethod方法将开放MethodInfo关闭,从而对泛型方法执行等效的操作。

历史

  • 2007年12月12日:初始版本
© . All rights reserved.