2010-08-08 107 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)); 
}