2010-08-08 108 views
22

在我的插件體系結構中,我正在向插件服務傳遞插件名稱(字符串),方法名稱(字符串)和參數(對象數組),以執行指定的方法並返回結果類型T)。將匿名類型作爲方法參數傳遞

插件服務的執行方法可以如下圖所示:

public TResult Execute<TResult>(string pluginName, string operation, params object[] input) { 
    MethodInfo method = null; 
    TResult result = default(TResult); 

    var plugin = _plugins.Enabled().FirstOrDefault(x => x.GetType().Name.Equals(pluginName, StringComparison.InvariantCultureIgnoreCase)); 

    if (plugin != null) { 
     method = plugin.GetType().GetMethods().FirstOrDefault(x => x.Name == operation); 
     if (method != null) { 
      result = (TResult)method.Invoke(plugin, input); 
     } 
    } 
    return result; 
    } 

使用示例:

var url = AppHelper.PluginService.Execute<string>(
    "ImagePlugin", 
    "GetImageUrl", 
    new object[] { image, size }); 

我寧願做的是傳遞一個匿名類型,而不是(因爲我認爲這是更可讀)即

var url = AppHelper.PluginService.Execute<string>(
    "ImagePlugin", 
    "GetImageUrl", 
    new { image = image, targetSize = size }); 

如何更改我的Execute方法來映射匿名t ype屬性到我的插件方法參數?

我曾經考慮在.net 4.0中使用新的動態類型,但我更喜歡在插件方法上定義我的參數,而不是接受一個動態對象。

感謝 本

[更新]

通過ASP.NET MVC的源代碼後,看它似乎很簡單拉匿名類型爲對象字典例如RouteValueDictionary。 在反射的幫助下,linq表達式是動態創建的。雖然它很好的實現,但我並不是真的想要所有這些額外的複雜性。

按照下面的評論,我只能通過指定我的參數在線(無需對象數組聲明)實現的可讀性:

var url = AppHelper.PluginService.Execute<string>("ImagePlugin", "GetImageUrl", image, size); 
+2

由於您正在使用'params'關鍵字,因此您可以執行'image,size'而不是'new object [] {image,size}''。它會使它更具可讀性,並且由於'Invoke'方法接受對象數組,所以我會保留方法簽名。 – Necros 2010-08-09 01:32:27

回答

9

有一些方法可以使這成爲可能,但我不會建議他們中的任何一個。

首先,您可以使用反射,這意味着您必須在PluginService.Execute方法中編寫大量附加(容易出錯)的代碼才能獲取所需的值。其次,如果您知道傳遞給您的方法的匿名類型的參數,則可以使用here中描述的技術。您可以在具有相同屬性的方法中轉換爲另一個匿名類型。 Here是Jon Skeet提供的同樣技術的另一種描述。

第三,您可以使用System.ComponentModel中的類。例如,ASP.NET MVC使用這個。它使用引擎蓋下的反射。但是,在ASP.NET MVC中,屬性名稱是衆所周知的(例如,controlleraction),或者它們的名稱無關緊要,因爲它們按原樣傳遞給控制器​​方法(例如,id)。

+0

Ronald,感謝鏈接。開始認爲我應該堅持我目前的實施。然而,這不是在ASP.NET MVC中廣泛使用的東西,例如在HTML助手和路由中。我確信必須有一個「好」的方式來做到這一點,否則微軟不會這樣做:) – 2010-08-08 12:42:05

+0

好點。我用另一種做你想做的方式更新我的答案。 – 2010-08-08 13:00:02

+0

我已經將答案標記爲答案,因爲它是最完整的答案。但是,在查看MVC源代碼以瞭解它們是如何實現這一點之後,我將堅持使用我的實現,但使用Necros建議來提高可讀性。 – 2010-08-09 10:09:01

1

我這樣做一次。你可以做的是通過反射獲得函數期望的參數。然後,可以通過將參數數組中的名稱與匿名對象的鍵匹配來構建參數數組。

希望幫助:-)。

0

首先,檢查System.Addin命名空間,你可能會得到一些幫助。其次,您可以使用特定的方法名稱和參數創建自己的接口,並讓插件實現接口。你可以在不同的項目中定義插件接口,這個項目可以在應用程序和插件項目中引用。

+0

我們已經在使用接口和StructureMap來調用特定的插件類型。這更多的是用戶可以即時創建並想要在UI中使用的插件(例如,他們可能希望將上面的ImagePlugin換成從Amazon S3中抽取圖像的插件) – 2010-08-08 12:39:24

21

我終於遇到了this post,它演示瞭如何使用匿名類型作爲字典。使用此方法,您可以將匿名類型作爲方法參數(對象)傳遞並訪問其屬性。

不過,我還想補充一點,尋找到新的動態特徵後.NET 4.0,如ExpandoObject,感覺乾淨多了傳遞一個動態的對象作爲參數:

 dynamic myobj = new ExpandoObject(); 
     myobj.FirstName = "John"; 
     myobj.LastName = "Smith"; 

     SayHello(myobj); 
     ........... 

     public static void SayHello(dynamic properties) 
     { 
      Console.WriteLine(properties.FirstName + " " + properties.LastName); 
     } 
12

如果您想傳遞匿名類型,請使用動態對象作爲參數。插件的執行方法應該期望參數對象的某些屬性才能工作。通過使用動態關鍵字C#編譯器將被指示不對參數執行類型檢查,並允許在插件代碼中使用強類型語法。屬性名稱解析將在運行時發生,如果傳遞的對象沒有這樣的屬性,則會拋出異常。

var o = new { FirstName = "John", LastName = "Doe" }; 

var result = MyMethod(o); 

string MyMethod(dynamic o) 
{ 
    return o.FirstName + " " + o.LastName; 
} 

Read more in this blog post

7

這個例子匿名對象轉換爲詞典:

IDictionary<string, object> AnonymousObjectToDictionary(object propertyBag) 
{ 
    var result = new Dictionary<string, object>(); 
    if (propertyBag != null) 
    { 
     foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(propertyBag)) 
     { 
      result.Add(property.Name, property.GetValue(propertyBag)); 
     } 
    } 
    return result; 
} 

你可以這樣調用:

AnonymousObjectToDictionary(new { foo = 11, bar = "Be happy" }); 
3

如果它是一個anonomous類型從LINQ中,那麼您可以通過傳遞IEnumerable來輕鬆完成此操作。

這裏有一個接收方法

public static void MyMethod<IEnumType>(ref IEnumerable<IEnumType> ienum) 
    { 
     using (DataTable dt = new DataTable()) 
     { 
      ienum.First(ie => true).GetType().GetProperties().ToList().ForEach(pr => dt.Columns.Add(pr.Name, typeof(string))); //Parallelization not possible since DataTable columns must be in certain order 

      ienum.ToList().ForEach(ie => //Parallelization not possible since DataTable rows not synchronized. 
       { 
        List<object> objAdd = new List<object>(); 
        ie.GetType().GetProperties().ToList().ForEach(pr => objAdd.Add(ie.GetType().InvokeMember(pr.Name, BindingFlags.GetProperty, null, ie, null))); //Parallelization not possible since DataTable columns must be in certain order 
        dt.Rows.Add(objAdd.ToArray()); 
        objAdd.Clear(); 
        objAdd = null; 
       }); 
      //Do something fun with dt 
     } 
    } 

當然的例子,因爲你使用反射,那麼你可能會看到在速度較慢的machiens或者你有一張大的IEnumerable或很多的屬性的性能問題T.

1
public static void ExAnonymousType() 
{ 
    var nguoi = new { Ten = "Vinh", Tuoi = 20 }; 
    Console.WriteLine(nguoi.Ten + " " + nguoi.Tuoi); 
    DoSomeThing(nguoi); 

} 

private static void DoSomeThing(object nguoi) 
{ 
    Console.WriteLine(nguoi.GetType().GetProperty("Ten").GetValue(nguoi,null)); 
}