2012-10-28 15 views
9

Si supponga Ho una classe in questo modo:Come arrivare MethodInfo per tipo generico aperto da MethodInfo di tipo chiuso

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

Ora, assumere, ho un'istanza di MyClass<int> e un MethodInfo del suo metodo Foo. La chiamata methodInfo.GetParameters() restituirà un array ParameterInfo con una voce, facendo riferimento al tipo int. Il mio problema è che non riesco a scoprirlo, se quel parametro è stato dichiarato come int nella classe o come T.

Cosa sto cercando di ottenere?
In fase di esecuzione, desidero leggere la documentazione del metodo specificato da MethodInfo dal file Doc XML generato da Visual Studio.
Per il metodo sopra definito, il tasto appare così:

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

La `0 si riferisce al primo parametro di tipo generico della classe dichiarare. Per essere in grado di costruire questa stringa, ho bisogno di ottenere in qualche modo questa informazione.
Ma come?MethodInfo non sembra contenere queste informazioni ...

+0

Le tecniche di riflessione avanzate di solito richiedono il ritorno a IMetaDataImport2. Non così facile da usare da C#. –

+0

@HansPassant: questa interfaccia supporterebbe il mio scenario? –

+0

Quindi, se il tuo metodo fosse "public void Foo (int i, T t, string s)", vorresti ottenere qualcosa come " .MyClass'1.Foo (int,' 0, stringa) "? – user276648

risposta

3

La chiave sembra essere Type.ContainsGenericParameters sul tipo di parametro:

Dato

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

    public void Bar(int i) 
    { 

    } 
} 

Poi

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

uscite

True 
False 

Questo ovviamente necessario più lavoro per sovraccarichi e presto.

+0

Aha! Sembra abbastanza buono. Verificherà più tardi in dettaglio. Grazie! –

+0

Ci scusiamo per aver impiegato così tanto tempo ad accettare la tua risposta. Era perfetto. –

+1

Come posso ottenere il corretto 'MethodInfo' se ho sovraccarico del metodo' Foo'? 'GetMethod' lancia un' AmbiguousMatchException'. 'GetMethod' deve ricevere il tipo di parametri ma non riesco a trovare il parametro generico dal tipo chiuso' MethodInfo' –

1

potrebbe ottenere la definizione della classe generica attraverso Type.GetGenericTypeDefinition Method e trovare lì la definizione per lo stesso metodo, per esempio, in base al nome (e la firma), e poi confrontare 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(); 
    } 
} 
+0

Ci ho pensato anch'io, ma non sono sicuro di come funzionerebbe con metodi sovraccarichi. –

+0

@DanielHilgarth, sono d'accordo che potrebbe essere una cosa complicata trovare sovraccarichi di metodi corrispondenti. Eppure non vedo neanche l'altra via d'uscita. – horgh

1

non so se si è pensato di utilizzare Mono.Cecil al posto della riflessione di .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"); 

poi myMethodDef.FullName rendimenti

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

e classDef.GenericParameters[0].FullName rendimenti

"T" 

Nota che Mono.Cecil utilizza un diverso modo di scrivere farmaci generici, classi annidate e array:

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

Il problema è che ho già un oggetto MethodInfo per il quale ho bisogno di recuperare tali informazioni. –

Problemi correlati