10

假设我有这样的课程:

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

现在,假设,我有一个实例MyClass<int>和一个MethodInfo它的Foo方法。调用methodInfo.GetParameters()将返回一个ParameterInfo包含一个条目的数组,指的是 type int。我的问题是,我似乎无法确定该参数是int在类中声明还是声明为T.

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

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

引用声明类的`0第一个泛型类型参数。为了能够构造这个字符串,我需要以某种方式获取这些信息。
但是怎么做? MethodInfo好像没有那个信息...

4

4 回答 4

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

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

于 2012-11-22T11:16:47.077 回答
1

您能否通过Type.GetGenericTypeDefinition Method获得泛型类的定义,并在那里找到相同方法的定义,例如,按名称(和签名),然后比较Foo(T t)Foo(int t)

MyClass<int> c = new MyClass<int>();

Type concreteType = c.GetType();
Console.Write("Concrete type name:");
Console.WriteLine(concreteType.FullName);
Console.WriteLine();

MethodInfo concreteMethod = concreteType.GetMethod("Foo");
if (concreteMethod != null)
{
    Console.WriteLine(concreteMethod.Name);
    foreach (ParameterInfo pinfo in concreteMethod.GetParameters())
    {
        Console.WriteLine(pinfo.Name);
        Console.WriteLine(pinfo.ParameterType);
        Console.WriteLine();
    }
    Console.WriteLine();
}

if (concreteType.IsGenericType)
{
    Console.Write("Generic type name:");
    Type genericType = concreteType.GetGenericTypeDefinition();
    Console.WriteLine(genericType.FullName);
    Console.WriteLine();

    MethodInfo genericMethod = genericType.GetMethod("Foo");
    if (genericMethod != null)
    {
        Console.WriteLine(genericMethod.Name);
        foreach (ParameterInfo pinfo in genericMethod.GetParameters())
        {
            Console.WriteLine(pinfo.Name);
            Console.WriteLine(pinfo.ParameterType);
            Console.WriteLine();
        }
        Console.WriteLine();
    }
}
于 2012-10-28T12:17:27.103 回答
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...]
于 2012-11-22T09:37:07.210 回答
0

这是一个棘手的问题。如果你有一个(泛型)方法的MethodInfo实例,它是在一个开放的泛型上获得的,你实际上可以使用它来重复关闭该方法(即,为封闭类型使用不同的泛型参数)。 Type

关键是使用晦涩的静态函数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类型句柄(作为第二个参数传入)不仅仅是T您最终想要参数化的类型参数的句柄。相反,您必须首先使用它T来关闭原始Topen(打开)泛型类型,然后使用生成的(关闭)泛型类型的句柄。

换句话说,如果不先显式关闭泛型类型,就无法关闭泛型方法。

于 2020-04-28T22:24:15.047 回答