2013-04-11 9 views
10

Ich möchte eine MethodInfo einer Methode aus einer generischen Klasse mit einem nur zur Laufzeit bekannten Typparameter erhalten.Wie finde ich MethodInfo für eine Methode einer generischen Klasse mit stark typisierter Reflektion?

Hier ist, wie ich ein MethodInfo für eine generische Methode aus einer nicht-generischen Klasse bekommen würde:

class MyClass 
{ 
    public void MyMethod<T> (T arg) 
    { 
    } 
} 

static MethodInfo Resolve (Type type) 
{ 
    Expression<Action<MyClass, object>> lambda = (c, a) => c.MyMethod (a); 
    MethodCallExpression    call = lambda.Body as MethodCallExpression; 

    return call 
     .Method      // Get MethodInfo for MyClass.MyMethod<object> 
     .GetGenericMethodDefinition() // Get MethodInfo for MyClass.MyMethod<> 
     .MakeGenericMethod (type);  // Get MethodInfo for MyClass.MyMethod<int> 
} 

Resolve (typeof (int)).Invoke (new MyClass(), new object[] {3}); 

Nun, wenn ich etwas ähnliches mit einer generischen Klasse versuchen:

class MyClass<T> 
{ 
    public void MyMethod (T arg) 
    { 
    } 
} 

static MethodInfo Resolve (Type type) 
{ 
    Expression<Action<MyClass<object>, object>> lambda = (c, a) => c.MyMethod (a); 
    MethodCallExpression      call = lambda.Body as MethodCallExpression; 

    return call 
     .Method    // Get MethodInfo for MyClass<object>.MyMethod 
     .SomeMagicMethod(); // FIXME: how can I get a MethodInfo 
          // for MyClass<T>.MyMethod where typeof (T) == type? 
} 

Resolve (typeof (string)).Invoke (new MyClass<string>(), new object[] {"Hello, World!"}); 

Ist es möglich?

+0

, die heikel ist, weil 'C .Meth' und' C .Meth' wäre völlig unabhängig Methoden werden nach dem .NET-Typ-System, weil ihre 'DeclaringType' s sind anders. – usr

+0

Können Sie Ihren Lösungscode reparieren? Sie verwenden die 'method' Variable sowohl als' MethodInfo' als auch als 'MethodInfo []'. Und sogar die Array-Suche nach der gleichen Variablen durchsuchen. Das macht keinen Sinn. – kjbartel

+0

Variable 'Methode' sollte in der Tat ein Array sein, aber der zweite Suchdurchlauf enthält die Lösung, so dass es alles andere als ein Fehler ist. Erwäge, die Frage noch einmal zu lesen. – r3c

Antwort

2
public class MyClass<T> 
{ 
    public void MyMethod(T arg, bool flag) 
    { 
     Console.WriteLine("type: MyClass<{0}>, arg: {1}, flag:{2}", typeof(T), 
      arg.ToString(), flag); 
    } 
    public void MyMethod(T arg) 
    { 
     Console.WriteLine("type: MyClass<{0}>, arg: {1}", typeof(T), arg.ToString()); 
    } 
} 
public class GenericInvokeTest 
{ 
    static MethodInfo Resolve(Type type) 
    { 
     var name = ActionName<object>(x => (o) => x.MyMethod(o)); 
     var genericType = typeof(MyClass<>).MakeGenericType(new[] { type }); 
     MethodInfo genericTypeMyMethodInfo = genericType.GetMethod(name); // "MyMethod"); 
     genericTypeMyMethodInfo = genericType.GetMethod(name, new[] { type, typeof(bool) }); 
     return genericTypeMyMethodInfo; 
    } 
    public static void Test1() 
    { 
     Resolve(typeof(string)) 
      .Invoke(new MyClass<string>(), new object[] { "Hello, World!", true }); 
     // Resolve(typeof(string)) 
      .Invoke(new MyClass<string>(), new object[] { "Hello, World!" }); 
    } 
} 

Um es stark typisierte Sie vereinfachen und anderen Ansatz verwenden sollte:

1) Holen Sie sich das name der Maßnahme/Methode Ausdrücken ...

var name = ActionName<object>(x => (o) => x.MyMethod(o)); 

2) Dann den unvermeidbaren Reflexionsteil

var genericType = typeof(MyClass<>).MakeGenericType(new[] { type }); 
MethodInfo genericTypeMyMethodInfo = genericType.GetMethod(name); // "MyMethod"); 


Wo ActionName einen ähnlichen Ansatz wie z.B. OnPropertyChanged(x => x.Property)

public static string ActionName<T>(Expression<Func<MyClass<T>, Action<T>>> expression) 
{ 
    return GetMemberName(expression.Body); 
} 
public static string GetMemberName(Expression expression) 
{ 
    switch (expression.NodeType) 
    { 
     case ExpressionType.Lambda: 
      var lambdaExpression = (LambdaExpression)expression; 
      return GetMemberName(lambdaExpression.Body); 
     case ExpressionType.MemberAccess: 
      var memberExpression = (MemberExpression)expression; 
      var supername = GetMemberName(memberExpression.Expression); 
      if (String.IsNullOrEmpty(supername)) 
       return memberExpression.Member.Name; 
      return String.Concat(supername, '.', memberExpression.Member.Name); 
     case ExpressionType.Call: 
      var callExpression = (MethodCallExpression)expression; 
      return callExpression.Method.Name; 
     case ExpressionType.Convert: 
      var unaryExpression = (UnaryExpression)expression; 
      return GetMemberName(unaryExpression.Operand); 
     case ExpressionType.Parameter: 
      return String.Empty; 
     default: 
      throw new ArgumentException(
       "The expression is not a member access or method call expression"); 
    } 
} 
+0

Danke, aber der Punkt war, nur stark typisierte Reflexion zu verwenden, nicht "GetMethod" oder eine ähnliche Methode, die ein Refactoring nicht überleben würde. – r3c

+0

Ich habe diesen Punkt nicht erkannt, denke ich :) Ich habe gerade die Antwort aktualisiert. – NSGaga

+1

Gute Idee, aber leider funktioniert es nicht für überladene Methoden: Es wird eine "AmbiguousMatchException" auf den GetMethod Aufruf werfen, und es wäre ziemlich schwierig, es zu lösen, da es die meisten der (gebrochenen) "GetMethod" internen Logik umschreibt. – r3c

1

Arbeitslösung:

static MethodInfo Resolve (Type type) 
{ 
    Expression<Action<MyClass<object>, object>> lambda = (c, a) => c.MyMethod (a); 
    MethodCallExpression      call = lambda.Body as MethodCallExpression; 
    MethodInfo[]        methods; 
    Type          target; 

    target = call 
     .Method // Get MethodInfo for MyClass<object>.MyMethod 
     .DeclaringType // Get typeof (MyClass<object>) 
     .GetGenericTypeDefinition() // Get typeof (MyClass<>) 
     .MakeGenericType (type); // Get typeof (MyClass<T>) where typeof (T) == type 

    methods = target.GetMethods (BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static); // We probably don't need static methods 

    return Array.Find (methods, (m) => m.MetadataToken == method.MetadataToken); // Find MyClass<T>.MyMethod where typeof (T) == type 
} 
Verwandte Themen