2013-04-11 83 views
10

我想從一個只有在運行時才知道類型參數的泛型類中獲得方法的MethodInfo如何查找使用強類型反射的泛型類方法的MethodInfo?

這是我如何會得到MethodInfo爲一個通用的方法從非通用類:

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

現在,如果我想嘗試用一個通用類類似的東西:

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!"}); 

可能嗎?

+0

因爲'C .Meth'和'C .Meth'是風馬牛不相及方法根據.NET類型系統,因爲他們的'DeclaringType'這是棘手s是不同的。 – usr 2013-04-11 21:44:11

+0

你能解決你的解決方案代碼嗎?您正在使用'method'變量作爲'MethodInfo'和'MethodInfo []'。甚至搜索數組匹配相同的變量。這是沒有意義的。 – kjbartel 2015-09-03 04:36:08

+0

變量'method'確實應該是一個數組,但第二個搜索遍歷包含解決方案,所以它只是一個錯誤。考慮再次閱讀問題。 – r3c 2015-09-04 07:04:23

回答

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!" }); 
    } 
} 

要使它強類型的你應該簡化,並使用不同的方法:

1)使用表達式獲取動作/方法的name ...

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

2 )然後做不可避免的反射部分

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


其中 ActionName正在採用類似的方法。 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

謝謝,但重點僅僅是使用強類型的反射,而不是「GetMethod」或類似的方法,它不會在重構中生存。 – r3c 2013-04-12 08:08:57

+0

我沒有看到這一點我猜:)我剛剛更新了答案。 – NSGaga 2013-04-12 12:16:41

+1

好主意,但不幸的是,它不適用於重載方法:它會在GetMethod調用中拋出一個「AmbiguousMatchException」,解決它將非常困難,因爲它意味着重寫大部分(破壞的)「GetMethod」內部邏輯。 – r3c 2013-04-12 12:30:03

1

工作液:

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 
} 
相關問題