2012-12-26 27 views
4

我試圖使用Delegate.CreateDelegate[MSDN link]綁定到靜態泛型方法,但綁定失敗。 這裏是在PoC代碼:Delegate.CreateDelegate無法綁定到靜態泛型方法

public static class CreateDelegateTest { 
    public static void Main() { 
     Action actionMethod = CreateDelegateTest.GetActionDelegate(); 
     Action<int> intActionMethod = CreateDelegateTest.GetActionDelegate<int>(); 
     Func<int> intFunctionMethod = CreateDelegateTest.GetFunctionDelegate<int>(); 
    } 

    public static Action GetActionDelegate() { 
     return (Action)Delegate.CreateDelegate(typeof(Action), typeof(CreateDelegateTest), "ActionMethod"); 
    } 

    public static Action<T> GetActionDelegate<T>() { 
     return (Action<T>)Delegate.CreateDelegate(typeof(Action<T>), typeof(CreateDelegateTest), "GenericActionMethod"); 
    } 

    public static Func<TResult> GetFunctionDelegate<TResult>() { 
     return (Func<TResult>)Delegate.CreateDelegate(typeof(Func<TResult>), typeof(CreateDelegateTest), "GenericFunctionMethod"); 
    } 

    public static void ActionMethod() { } 

    public static void GenericActionMethod<T>(T arg) { } 

    public static TResult GenericFunctionMethod<TResult>() { 
     return default(TResult); 
    } 
} 

actionMethod正確創建,但intActionMethodintFunctionMethod創造罰球。

爲什麼CreateDelegate無法綁定到泛型方法?如何綁定到他們?

我已經提交了Microsoft Connect的錯誤[link]。如果您認爲這是一個錯誤,請投票。

更新2:我錯了,認爲綁定到非函數泛型方法成功。原來,任何泛型方法都無法綁定。

+0

這不是一個錯誤。通常您會依賴編譯器的類型推斷來創建要調用的特定方法的實例,即處理特定類型的方法。 CreateDelegate()不會爲你做這件事,你必須幫助並明確地創建該方法。 MethodInfo.MakeGenericMethod()是必需的。 –

+0

@HansPassant原來我誤解了綁定工作的非函數泛型方法。這實際上發生在所有通用方法上。 –

回答

2

試試這個(我不能讓你的GetActionDelegate()版本也工作):

public class CreateDelegateTest 
{ 
    public static Func<TResult> GetFunctionDelegate<TResult>() 
    { 
     var methodInfo = typeof(CreateDelegateTest).GetMethod("FunctionMethod") 
                .MakeGenericMethod(typeof(TResult)); 
     return (Func<TResult>)Delegate.CreateDelegate(typeof(Func<TResult>), methodInfo); 
    } 

    public static Action<T> GetActionDelegate<T>() 
    { 
     var methodInfo = typeof(CreateDelegateTest).GetMethod("ActionMethod") 
                .MakeGenericMethod(typeof(T)); 
     return (Action<T>)Delegate.CreateDelegate(typeof(Action<T>), methodInfo); 
    } 
} 

我不知道爲什麼CreateDelegate(Type, Type, string)超載未能做到這一點,只是這種方式來實現。

更新:

它仍然可以使用同樣的方法與任何委託類型。主要想法是爲MakeGenericMethod()調用找到正確的參數。它是如何做到快速例如:

public static Delegate CreateDelegate(Type delegateType, Type objectType, string methodName) 
{ 
    var delegateMethod  = delegateType.GetMethod("Invoke"); 
    var delegateReturn  = delegateMethod.ReturnType; 
    var delegateParameters = delegateMethod.GetParameters(); 
    var methods    = objectType.GetMethods(); 
    MethodInfo method = null; 
    ParameterInfo[] methodParameters = null; 
    Type methodReturn = null; 
    // find correct method by argument count 
    foreach(var methodInfo in methods) 
    { 
     if(methodInfo.Name != methodName) 
     { 
      continue; 
     } 
     methodParameters = methodInfo.GetParameters(); 
     methodReturn = methodInfo.ReturnType; 
     if(methodParameters.Length != delegateParameters.Length) 
     { 
      continue; 
     } 
     method = methodInfo; 
    } 
    if(method == null) 
    { 
     throw new Exception("Method not found"); 
    } 
    if(method.IsGenericMethodDefinition) 
    { 
     var genericArguments = method.GetGenericArguments(); 
     var genericParameters = new Type[genericArguments.Length]; 

     int genericArgumentIndex = Array.IndexOf(genericArguments, methodReturn); 
     if(genericArgumentIndex != -1) 
     { 
      genericParameters[genericArgumentIndex] = delegateReturn; 
     } 

     for(int i = 0; i < methodParameters.Length; ++i) 
     { 
      var methodParameter = methodParameters[i]; 
      genericArgumentIndex = Array.IndexOf(genericArguments, methodParameter.ParameterType); 
      if(genericArgumentIndex == -1) continue; 
      genericParameters[genericArgumentIndex] = delegateParameters[i].ParameterType; 
     } 

     if(Array.IndexOf(genericParameters, null) != -1) 
     { 
      throw new Exception("Failed to resolve some generic parameters."); 
     } 

     var concreteMethod = method.MakeGenericMethod(genericParameters); 
     return Delegate.CreateDelegate(delegateType, concreteMethod); 
    } 
    else 
    { 
     return Delegate.CreateDelegate(delegateType, method); 
    } 
} 

注1:我已經極度簡化重載的方法解決在這個例子 - 它依賴的論點只算。

注2:它仍然可以寫一個canot被這樣包裹在委託的方法,例如,int Method<T>(string arg)(任何不引用泛型參數在參數列表或返回值,這是一個不管怎樣,不好的做法)。

+0

不幸的是,我無法通過這種方式解決我的真實問題。在我的真實情況下,我只需要一個'TDelegate'泛型類型參數,我希望找到一個靜態方法。從中提取所需的信息並不那麼容易。 –

+0

添加我的真實代碼的問題。 –

+0

我不認爲我理解你的代碼的目的(或者至少我確信應該有其他方法來做你想做的事情),但是仍然可以稍微改進'CreateDelegate()'的泛型方法。答案已更新。 – max

相關問題