2009-07-14 45 views
32

谷歌搜索和SO登陸並具有後讀this other question從MethodInfo構建一個委託?

是否有可能從一個MethodInfo建立一個正確的代表如果你不知道在編譯時參數的數量和類型?

更多內容:這可以在沒有使用Reflection.Emit或類型構建器的情況下優雅地完成嗎?

這對我來說是件壞事,因爲Delegate.CreateDelegate要求我指定正確的委託類型作爲第一個參數,否則它會拋出異常或調用不正確的方法。

我正在建造一些忍者齒輪,這將有助於很多...謝謝!


這裏有一個通用的解決方案:

/// <summary> 
/// Builds a Delegate instance from the supplied MethodInfo object and a target to invoke against. 
/// </summary> 
public static Delegate ToDelegate(MethodInfo mi, object target) 
{ 
    if (mi == null) throw new ArgumentNullException("mi"); 

    Type delegateType; 

    var typeArgs = mi.GetParameters() 
     .Select(p => p.ParameterType) 
     .ToList(); 

    // builds a delegate type 
    if (mi.ReturnType == typeof(void)) { 
     delegateType = Expression.GetActionType(typeArgs.ToArray()); 

    } else { 
     typeArgs.Add(mi.ReturnType); 
     delegateType = Expression.GetFuncType(typeArgs.ToArray()); 
    } 

    // creates a binded delegate if target is supplied 
    var result = (target == null) 
     ? Delegate.CreateDelegate(delegateType, mi) 
     : Delegate.CreateDelegate(delegateType, target, mi); 

    return result; 
} 

注意:我建立一個Silverlight應用程序將取代我在其中有多個內置年同期的JavaScript應用程序調用相同Silverlight [ScriptableMember]方法的Javascript接口。

所有那些傳統的JS接口需要支持以及用於訪問新功能的新接口,所以自動設置JS接口和將委託調用到正確的Silverlight方法的東西將有助於加速工作。

我不能在這裏發佈代碼,所以這是總結。

回答

22

說實話,如果你不知道編譯時的類型,在創建Delegate時沒有太多的好處。你不想使用DynamicInvoke;它會像反思一樣慢。這種情況的主要例外是,當有一個代理類型潛伏在陰影中時,例如訂閱事件時 - 在這種情況下,EventInfo可以提供此功能。

有關信息,在Expression .NET 3.5,有:

Expression.GetActionType(params Type[] typeArgs); 
Expression.GetFuncType(params Type[] typeArgs) 

這可能有助於在一定程度上:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Linq.Expressions; 
using System.Reflection; 
static class Program { 
    static void Main() { 
     DoStuff("Test1"); 
     DoStuff("Test2"); 
    } 
    static void DoStuff(string methodName) { 
     MethodInfo method = typeof(Program).GetMethod(methodName); 
     List<Type> args = new List<Type>(
      method.GetParameters().Select(p => p.ParameterType)); 
     Type delegateType; 
     if (method.ReturnType == typeof(void)) { 
      delegateType = Expression.GetActionType(args.ToArray()); 
     } else { 
      args.Add(method.ReturnType); 
      delegateType = Expression.GetFuncType(args.ToArray()); 
     } 
     Delegate d = Delegate.CreateDelegate(delegateType, null, method); 
     Console.WriteLine(d); 
    } 
    public static void Test1(int i, DateTime when) { } 
    public static float Test2(string x) { return 0; } 
} 
7

如果您事先不知道參數的數量或類型,這可能意味着您不知道要創建的委託類型?

如果是這樣的話,你就陷入絕對一般的情況。

然而,對於大多數普通案件(無REF /輸出參數,幾家有足夠的參數來使用現有的類型之一),你可以逃脫的FuncAction代表之一。 (.NET 4.0有大量參數的Func/Action類型,所以真的只需要擔心out/ref參數。)如果方法有非void返回類型,則使用Func,否則使用Action。根據參數數量確定要使用的類型,例如

static readonly Type[] FuncTypes = { typeof(Func), 
    typeof(Func<>), typeof(Func<,>), typeof(Func<,,>), /* etc */ }; 

使用Type.MakeGenericType使用的參數類型和返回類型得到正確的委託類型,然後Delegate.CreateDelegate應該工作。

我現在沒有時間處理樣本,但是如果您希望我稍後再來,請告訴我。

一個問題:你打算如何使用這個代表?還有一些東西需要知道如何執行它,當然...

+0

ah-ha ...對MakeGenericType + Func很好......這樣做:-) – chakrit 2009-07-14 10:36:43

+4

爲了避免你static Type [],請考慮Expression.GetActionType/Expression.GetFuncType - 請參閱post。我希望**能夠將這些方法擴展到包含新的.NET 4.0變體。 – 2009-07-14 10:44:55

+0

我爲問題添加了「爲什麼」 – chakrit 2009-07-14 10:52:09

6

爲什麼那麼複雜?

public static Delegate CreateDelegate(this MethodInfo method) 
{ 
    return Delegate.CreateDelegate 
    (
     Expression.GetDelegateType 
     (
      method.GetParameters() 
       .Select(p => p.ParameterType) 
       .Concat(new Type[] { method.ReturnType }) 
       .ToArray() 
     ), 
     null, 
     method 
    ); 
} 

[旁註:我在這個方法的前綴「創建...」。 「To ...」令人困惑,因爲它誤導你認爲這是一種轉換。]