2012-01-12 23 views
3

我想在C#中這樣寫:C#非常動態調用

SomeUnknownType x; 

SuperDuperInvoke(x, "MethodName", param1, param2, param3); 

SuperDuperInvoke2(x, "MethodName", "param1String", "param2String", "param3String"); 

得到一些對象我什麼都不知道,一個方法名和參數列表,並調用方法。 SuperDuperInvoke2假定參數可以從字符串轉換。

我認爲這樣的事情是可能使用動態框架......我只是找不到如何...

我知道我可以用反射做到這一點,但它的醜陋和討厭...


我會解釋一下自己。

我想用它來進行一些業務服務器的集成測試。 服務器有很多不同的組件可以處理請求,所有組件都被加載到一個IoC容器中。 我需要公開一些thees組件,主要是爲了測試,所以我只想接收組件的名稱,我應該用什麼參數調用什麼方法,然後調用它。

+1

這看起來像是反射的東西,但我不認爲動態會解決這個問題。讓我們等待一個答案,證明我錯了,所以也許我也學到了一些東西:) – 2012-01-12 13:46:12

+3

你有什麼可能的原因想要做到這一點? – cadrell0 2012-01-12 13:46:44

+1

目前還不清楚你的目標是什麼......可能你需要解決.NET中的動態類型問題,也可以查看OptionalAttribute類...(http://msdn.microsoft.com/zh-cn/library /dd264736.aspx) – 2012-01-12 13:47:54

回答

4

我知道你寫過你不喜歡反思,但這真的很醜嗎?

var result = x.GetType().GetMethod("MethodName").Invoke(x, new object[] { methodParams }); 

如果該方法可能過載,你可以叫不上名字去只還需要知道你要多少參數與調用它。像這樣的東西

var method = x.GetType() 
       .GetMethods() 
       .First(m => m.Name == "MethodName" && m.GetParameters().Length == 2); 
var result = method.Invoke(x, new object[] { methodParams }); 

如果您還需要按類型匹配您的methodParams,這將不起作用。

+0

重疊呢? – 2012-01-12 14:07:04

+0

然後它變得更棘手一點。 – 2012-01-12 14:44:04

+0

我最終走向了相同的方向...... 雖然很糟糕,但真的是跳得更好一些 – 2012-01-12 22:37:54

1

你說你不喜歡使用反射,但是,由於你提到你只知道methodname是一個字符串,所以只有一種方法:反射。這不像你想象的那麼難。這裏有一種方法:

編輯:代碼已更新。它現在可以處理重載的成員和任意數量的參數。如果您知道參數的類型,或者參數已正確初始化爲它們各自的類型,則這將起作用。

public static object InvokeMethod(object o, string MethodName, object[] parameters) 
{ 
    // get the types of the params 
    List<Type> paramTypes = new List<Type>(); 
    foreach (object p in parameters) 
    { 
     paramTypes.Add(p.GetType()); 
    } 

    try 
    { 
     // get the method, equal to the parameter types 
     // considering overloading 
     MethodInfo methodInfo = o.GetType().GetMethod(MethodName, paramTypes.ToArray()); 

     // and invoke the method with your parameters 
     return methodInfo.Invoke(o, parameters); 
    } 
    catch (MissingMethodException e) 
    { 
     // discard or do something 
    } 
} 

注意:你的問題文本你提到的所有PARAMS都轉換爲字符串,並與您傳遞的值作爲字符串。但是,如果您有類似Calc(int)Calc(long)的方法,並且只有一個字符串值可以轉換爲這兩種方法之一,那麼您將需要有關您的方法的更多信息,因爲無法知道應該調用哪些方法所有參數值都被字符串化。

+1

這很難描述,參數的數量,它們的類型等......這是非常可行的,我只是不想自己做 – 2012-01-12 14:05:59

+0

@Hellfrost,我描述的是工作代碼,但我不能判斷你認爲困難,當然,如果我的例子過於簡化,我很抱歉。我擴大了一點,以適應「事先不知道的參數列表」的事實。但是,如果你想要做這樣的事情,我擔心你會終究不得不自己「自己動手」,因爲這種語言只有一種解決方式:反思......對不起:( – Abel 2012-01-12 14:21:25

+0

+1)關於「note」我想要一些類似於動態調用的東西,如果是兩個convirtables,那應該沒關係。 – 2012-01-12 22:42:36

0

沒有動態,你可以這樣做:

public static SuperDuperInvoke(object o, string methodName, params object parameters) 
{ 
    Type t = o.GetType(); 
    MethodInfo mi = t.GetMethod(methodName); 
    if (mi == null) throw new Exception("no such method: " + methodName); 
    mi.invoke(mi, o, parameters.Length == 0 ? null : parameters); 
} 

現在,這是相當幼稚的,因爲它不是尋找一個特定的方法。最好的辦法是取而代之,獲取該名稱的所有方法,並過濾掉具有正確數字參數的方法,然後過濾掉那些具有可從給定參數類型分配的類型的方法,然後選擇具有最少upcast的方法。

+0

我對Reflections API非常熟悉,但是必須要是一個更簡單的方法... – 2012-01-12 14:02:40

1

當使用dynamic關鍵字時,您需要知道編譯時的方法名稱,但實際編譯的內容是DLR API調用,其中包含方法名稱的字符串常量。自己調用這些函數當然是可能的,但它變得棘手的一點是,dlr的性能取決於在這些api調用中創建的靜態緩存站點。

開放源代碼框架ImpromptuInterface(在Nuget中找到)包裝DLR api與一些static invoke methods。它對緩存網站進行了散列處理,因此它不如動態關鍵字快,但速度至少比反射快2倍。唯一的技巧是,如果你試圖調用一個期望值的void返回方法,它會在嘗試綁定時引發異常。示例實現:

public static dynamic SuperDuperInvoke(object target, string methodName, params object[] args){ 
    try{ 
     return Impromptu.InvokeMember(target, methodName, args); 
    }catch(Microsoft.CSharp.RuntimeBinder.RuntimeBinderException){ 
     Impromptu.InvokeMemberAction(target, methodName, args); 
     return null; 
    } 
} 

因爲它是開源的(Apache許可證),你可以經常去source of InvokeMember etc來幫助實現你SuperDuperInvoke,如果你不想依賴你。

SuperDuperInvoke2是比較困難的,因爲DLR會嘗試和匹配基礎上的參數類型的方法,它會考慮到隱式轉換,但只有靜態定義的人(TryConvertDynamicObject將無法​​正常工作),所以你然後需要一個代理,該代理具有靜態定義將隱式轉換爲所有預期的類型,這可能是危險的小心方法超載,他們可能會模糊到SuperDuperInvoke2

public static dynamic SuperDuperInvoke2(object target, string methodName, params ConvertableProxy[] args){ 
    return SuperDuperInvoke(target,methodName,args); 
} 

public class ConvertableProxy{ 
    private IConvertible _value; 
    public ConvertableProxy(IConvertible value){ 
     _value =value; 
    } 

    //Automatically convert strings to proxy 
    public static implicit operator ConvertableProxy(string value){ 
     return new ConvertableProxy(value); 
    } 

    public static implicit operator bool(ConvertableProxy proxy) 
    { 
     return proxy._value.ToBoolean(null); 
    } 

    public static implicit operator int(ConvertableProxy proxy) 
    { 
     return proxy._value.ToInt32(null); 
    } 

    public static implicit operator string(ConvertableProxy proxy) 
    { 
     return proxy._value.ToString(null); 
    } 

    //.. Add Char, DateTime, etc. 


}