将匿名类型作为方法参数传递
在我的插件架构中,我目前正在将插件名称(字符串),方法名称(字符串)和参数(对象数组)传递给我的插件服务,以执行指定的方法并返回结果(类型为T)。
插件服务的execute方法如下所示:
public TResult Execute(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( "ImagePlugin", "GetImageUrl", new object[] { image, size });
我宁愿做的是传递匿名类型(因为我认为这更具可读性)即
var url = AppHelper.PluginService.Execute( "ImagePlugin", "GetImageUrl", new { image = image, targetSize = size });
如何更改我的Execute方法以将匿名类型属性映射到我的插件方法参数?
我曾考虑在.net 4.0中使用新的动态类型,但我更喜欢在插件方法上定义我的参数而不是接受一个动态对象。
谢谢本
[更新]
在查看ASP.NET MVC源代码之后,将匿名类型拉入对象字典(例如RouteValueDictionary)似乎很简单。 在reflection的帮助下,动态创建linq表达式。 虽然它是一个很好的实现,但我并不是真的想要所有这些额外的复杂性。
根据下面的评论,我只需通过内联指定参数(不需要对象数组声明)就可以实现可读性:
var url = AppHelper.PluginService.Execute("ImagePlugin", "GetImageUrl", image, size);
有一些方法可以实现这一点,虽然我不会建议任何一个。
首先,您可以使用reflection,这意味着您必须在PluginService.Execute
方法中编写许多其他(容易出错的)代码以获取所需的值。
其次,如果您知道要传递给方法的匿名类型的参数,则可以使用此处描述的技术。 您可以在方法内部转换为具有相同属性的另一个匿名类型。 这是Jon Skeet对同一技术的另一种描述。
第三,您可以使用System.ComponentModel
类。 例如,ASP.NET MVC使用它。 它使用引擎盖下的reflection。 但是,在ASP.NET MVC中,属性名称是众所周知的(例如controller
和action
),或者它们的名称无关紧要,因为它们按原样传递给控制器方法(例如id
)。
我最终遇到了这篇文章 ,演示了使用匿名类型作为词典。 使用此方法,您可以将匿名类型作为方法参数(对象)传递并访问其属性。
但是,我还要补充一点,在查看.net 4.0中的新动态function(例如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); }
如果要传递匿名类型,请使用动态对象作为参数。 插件的execute方法应该期望参数对象的某些属性才能工作。 通过使用动态关键字,将指示C#编译器不对参数执行类型检查,并允许在插件代码中使用强类型语法。 属性名称解析将在运行时发生,如果传递的对象没有此类属性,则将抛出exception。
var o = new { FirstName = "John", LastName = "Doe" }; var result = MyMethod(o); string MyMethod(dynamic o) { return o.FirstName + " " + o.LastName; }
阅读此博客文章中的更多内容
此示例将匿名对象转换为字典:
IDictionary AnonymousObjectToDictionary(object propertyBag) { var result = new Dictionary(); 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" });
如果它是来自Linq的自治类型,那么您可以通过传递IEnumerable轻松完成此操作。
这是接收方法的示例
public static void MyMethod(ref IEnumerable 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
当然,既然您正在使用reflection,那么您可能会在较慢的机器上看到性能问题,或者在T中有大的IEnumerable或很多属性。
我这样做了一次。 你可以做的是通过reflection获得函数的预期参数。 然后,您可以通过将参数数组中的名称与匿名对象的键相匹配来构建参数数组。
希望有所帮助:-)。
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)); }
首先,检查System.Addin命名空间,你可能会得到一些帮助。
其次,您可以使用特定的方法名称和参数创建自己的接口,并让插件实现接口。 您可以在可以在应用程序和插件项目中引用的不同项目中定义插件接口。