Como obter MethodInfo para abrir tipo genérico de MethodInfo do tipo fechado

9

Suponha que eu tenha uma aula assim:

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

Agora, suponha que eu tenha uma instância de MyClass<int> e MethodInfo de seu método Foo . Chamar methodInfo.GetParameters() retornará um array ParameterInfo com uma entrada, referindo-se ao tipo int . Meu problema é que não consigo descobrir se esse parâmetro foi declarado como int na classe ou como T .

O que estou tentando alcançar?
Em tempo de execução, desejo ler a documentação do método especificado por MethodInfo do arquivo XML Doc gerado pelo Visual Studio.
Para o método acima definido, a chave é assim:

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

O '0 refere-se ao primeiro parâmetro de tipo genérico da classe declarante. Para ser capaz de construir essa string, eu preciso de alguma forma obter essa informação.
Mas como? MethodInfo não parece conter essa informação ...

    
por Daniel Hilgarth 28.10.2012 в 12:44
fonte

3 respostas

3

A chave parece ser Type.ContainsGenericParameters no tipo de parâmetro:

Dado

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

    public void Bar(int i)
    {

    }
}

Então

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();
    }
}

saídas

True
False

Isso obviamente precisará de mais trabalho para sobrecargas e assim por diante.

    
por AakashM 22.11.2012 / 12:16
fonte
1

Você poderia obter a definição da classe genérica por meio do método Type.GetGenericTypeDefinition e encontre a definição para o mesmo método, digamos, por nome (e a assinatura) e, em seguida, compare Foo(T t) e 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();
    }
}
    
por horgh 28.10.2012 / 13:17
fonte
1

Não sei se você considerou usar o Mono.Cecil em vez do reflexo do .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");

então myMethodDef.FullName retorna

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

e classDef.GenericParameters[0].FullName retornam

"T"

Observe que o Mono.Cecil usa uma maneira diferente de escrever genéricos, classes aninhadas e matrizes:

List[T] => List<T>
MyClass+MyNestedClass => MyClass/MyNestedClass
int[,] => int[0...,0...]
    
por user276648 22.11.2012 / 10:37
fonte