2012-10-28 5 views
9

bekommen Angenommen, ich habe eine Klasse wie folgt:Wie Method für offene generische Art von Method von geschlossenem Typ

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

Es sei nun angenommen, ich habe eine Instanz von MyClass<int> und MethodInfo seiner Foo Methode. Aufruf methodInfo.GetParameters() wird ein Array ParameterInfo mit einem Eintrag zurückgeben, bezogen auf Typ int. Mein Problem ist, dass ich nicht herausfinden kann, ob dieser Parameter als int in der Klasse oder als T deklariert wurde.

Was versuche ich zu erreichen?
Zur Laufzeit möchte ich die Dokumentation der von MethodInfo angegebenen Methode aus der von Visual Studio generierten XML-Doc-Datei lesen.
Für das obige Verfahren definiert sind, sieht der Schlüssel wie folgt aus:

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

Die `0 zur ersten generischen Typparameter der deklarierte Klasse bezieht. Um diese Saite konstruieren zu können, muss ich diese Information irgendwie bekommen.
Aber wie?MethodInfo scheint nicht, dass Informationen zu enthalten ...

+0

Fortgeschrittene Reflexionstechniken erfordern normalerweise einen Rückfall auf IMetaDataImport2. Nicht so einfach von C# zu verwenden. –

+0

@HansPassant: Würde diese Schnittstelle mein Szenario unterstützen? –

+0

Also, wenn Ihre Methode "öffentliche void Foo (int i, T t, String s)" wäre, möchten Sie etwas wie " .MyClass'1.Foo (int,' 0, String) "? – user276648

Antwort

3

Der Schlüssel scheint Type.ContainsGenericParameters auf dem Parameter-Typ zu sein:

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

    public void Bar(int i) 
    { 

    } 
} 

Dann

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

Ausgänge

True 
False 

Dies ist offensichtlich für Überlastungen Bei mehr Arbeit benötigen und bald.

+0

Aha! Das sieht ziemlich gut aus. Werde es später im Detail überprüfen. Vielen Dank! –

+0

Entschuldigen Sie, dass Sie so lange brauchen, um Ihre Antwort zu akzeptieren. Es war genau richtig. –

+1

Wie kann ich die richtige 'MethodInfo' erhalten, wenn ich die 'Foo' Methode überladen habe? 'GetMethod' löst eine' AmbiguousMatchException' aus. 'GetMethod' muss den Parameter type erhalten, aber ich kann den generischen Parameter nicht vom geschlossenen 'MethodInfo'-Typ –

1

Könnten Sie die Definition der allgemeinen Klasse durch Type.GetGenericTypeDefinition Method erhalten und dort die Definition für die gleiche Methode, sagen wir, nach Namen (und die Unterschrift) finden, und dann vergleichen Foo(T t) und 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

Ich dachte auch darüber nach, aber ich bin nicht wirklich sicher, wie das mit überladenen Methoden funktionieren würde. –

+0

@DanielHilgarth, stimme zu, dass es schwierig sein könnte, die Überladungen der entsprechenden Methoden zu finden. Aber ich sehe auch den anderen Ausweg nicht. – horgh

1

ich weiß nicht, ob Sie von .Net Reflexion mit Mono.Cecil betrachtet haben, statt.

// 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"); 

dann myMethodDef.FullName kehrt

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

und classDef.GenericParameters[0].FullName kehrt

"T" 

Beachten Sie, dass Mono.Cecil verwendet eine andere Art und Weise Generika zu schreiben, verschachtelte Klassen und Arrays:

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

Das Problem ist, dass ich bereits ein MethodInfo-Objekt habe, für das ich diese Informationen abrufen muss. –