Как получить MethodInfo для открытого универсального типа из MethodInfo закрытого типа

Предположим у меня есть класс следующим образом:

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

теперь предположим, что у меня есть экземпляр MyClass<int> и MethodInfo его Foo метод. Зову methodInfo.GetParameters() вернет ParameterInfo массив с одной записью, относящейся к типу int. Моя проблема в том, что я не могу узнать, был ли этот параметр объявлен как int в классе или как T.

чего я пытаюсь достичь?
Во время выполнения, я хочу прочитать документацию метод, указанный MethodInfo из XML-файла Doc, созданного Visual Studio.
Для указанного выше метода ключ выглядит следующим образом:

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

на `0 относится к первому параметру универсального типа объявляющего класса. Чтобы построить эту строку, мне нужно как-то получить эту информацию.
но как? MethodInfo не содержит этой информации...

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

это, очевидно, потребуется больше работы для перегрузок и так далее.


не могли бы вы сделать определение универсального класса через тип.GetGenericTypeDefinition Метод и найти там определение для того же метода, скажем, по имени (и подписи), а затем сравнить 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();
    }
}

Я не знаю, рассматривали ли вы использование Mono.Сесил вместо отражения .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.Сесил использует другой способ написания дженериков, вложенных классов и массивов:

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