如何从封闭类型的MethodInfo获取开放泛型类型的MethodInfo

时间:2012-10-28 11:44:42

标签: c# .net reflection

假设我有一个类似的课程:

public class MyClass<T>
{
    public void Foo(T t)
    {
    }
}

现在,假设我有一个MyClass<int>的实例和MethodInfo方法的Foo。 调用methodInfo.GetParameters()将返回带有一个条目的ParameterInfo数组,引用类型int。我的问题是,如果该参数在类中声明为intT,我似乎无法找到。

我想要实现的目标是什么?
在运行时,我想从Visual Studio生成的XML Doc文件中读取MethodInfo指定的方法的文档。
对于上面定义的方法,键如下所示:

<namespace>.MyClass`1.Foo(`0)

`0是指声明类的第一个泛型类型参数。为了能够构造这个字符串,我需要以某种方式获取这些信息 但是如何? MethodInfo似乎不包含该信息...

4 个答案:

答案 0 :(得分:3)

参数类型上的键似乎是Type.ContainsGenericParameters

鉴于

public class MyClass<T>
{
    public void Foo(T t)
    {
    }

    public void Bar(int i)
    {

    }
}

然后

class Program
{
    static void Main(string[] args)
    {
        var obj = new MyClass<int>();

        // Closed type
        var closedType = obj.GetType();

        // Open generic (typeof(MyClass<>))
        var openType = closedType.GetGenericTypeDefinition();

        // Methods on open type
        var fooT = openType.GetMethod("Foo");
        var barint = openType.GetMethod("Bar");

        // Parameter types
        var tInFoo = fooT.GetParameters()[0].ParameterType;
        var iInBar = barint.GetParameters()[0].ParameterType;

        // Are they generic?
        var tInFooIsGeneric = tInFoo.ContainsGenericParameters;
        var iInBarIsGeneric = iInBar.ContainsGenericParameters;

        Console.WriteLine(tInFooIsGeneric);
        Console.WriteLine(iInBarIsGeneric);

        Console.ReadKey();
    }
}

输出

True
False

这显然需要更多的工作来处理重载等问题。

答案 1 :(得分:1)

您是否可以通过Type.GetGenericTypeDefinition Method获取泛型类的定义,并找到相同方法的定义,例如,按名称(和签名),然后比较Foo(T t)和{{1 }}:

Foo(int t)

答案 2 :(得分:1)

我不知道你是否考虑过使用Mono.Cecil而不是.Net的反思。

// Gets the AssemblyDefinition (similar to .Net's Assembly).
Type testType = typeof(MyClass<>);
AssemblyDefinition assemblyDef = AssemblyDefinition.ReadAssembly(new Uri(testType.Assembly.CodeBase).LocalPath);
// Gets the TypeDefinition (similar to .Net's Type).
TypeDefinition classDef = assemblyDef.MainModule.Types.Single(typeDef => typeDef.Name == testType.Name);
// Gets the MethodDefinition (similar to .Net's MethodInfo).
MethodDefinition myMethodDef = classDef.Methods.Single(methDef => methDef.Name == "Foo");

然后myMethodDef.FullName返回

"System.Void MyNamespace.MyClass`1::Foo(System.Int32,T,System.String)"

classDef.GenericParameters[0].FullName返回

"T"

请注意,Mono.Cecil使用不同的方式编写泛型,嵌套类和数组:

List[T] => List<T>
MyClass+MyNestedClass => MyClass/MyNestedClass
int[,] => int[0...,0...]

答案 3 :(得分:0)

这是一个棘手的问题。如果您有( non- 通用)方法的MethodInfo实例,该实例是通过 open通用获取的> Type,您实际上可以 使用它来重复关闭该方法(即,对于 enclosing 类型使用不同的通用参数)。

关键是使用模糊的静态函数MethodInfo.GetMethodFromHandle(...)

完整的工作示例:

static class SomeType<T>
{
    public static int NonGenericMethod() => typeof(T).MetadataToken;
};

使用方法:

static void demo()
{
    var Topen = typeof(SomeType<>);
    var mi_nope = Topen.GetMethod("NonGenericMethod");  // cannot 'Invoke' this.

    /// ...later perhaps...

    var Tclosed = Topen.MakeGenericType(typeof(ushort));
    var mi_ok = MethodInfo.GetMethodFromHandle(mi_nope.MethodHandle, Tclosed.TypeHandle);
    var ret = (int)mi_ok.Invoke(null, null);     // --> 0x02000150

    /// ...again with a different generic arg, reusing 'mi_wontInvoke' (and Topen)...

    Tclosed = Topen.MakeGenericType(typeof(Guid));
    mi_ok = MethodInfo.GetMethodFromHandle(mi_nope.MethodHandle, Tclosed.TypeHandle);
    ret = (int)mi_ok.Invoke(null, null);         // --> 0x020000eb
}

请注意,Tclosed类型的句柄(作为第二个arg传入)不仅仅是您最终希望对其进行参数化的类型参数T的句柄。相反,您必须先使用该T来关闭原始的Topen(打开)泛型类型,然后使用生成的(关闭)泛型类型的句柄。

换句话说,您必须先明确关闭其通用类型,才能关闭该通用方法。