简体   繁体   English

将匿名类型作为方法参数传递

[英]Passing anonymous type as method parameters

In my plugin architecture I am currently passing a plugin name (string), method name (string) and parameters (object array) to my plugin service to execute the specified method and return the result (of type T).在我的插件架构中,我目前正在将插件名称(字符串)、方法名称(字符串)和参数(对象数组)传递给我的插件服务以执行指定的方法并返回结果(类型 T)。

The plugin service's execute method can be seen below:插件服务的execute方法见下图:

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;
  }

An example usage:示例用法:

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

What I would rather do is pass in an anonymous type instead (as I think this is more readable) ie我宁愿做的是传入一个匿名类型(因为我认为这更具可读性)即

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

How would I change my Execute method to map the anonymous type properties to my plugin method parameters?我将如何更改我的 Execute 方法以将匿名类型属性映射到我的插件方法参数?

I had considered using the new dynamic type in .net 4.0 but I prefer to define my parameters on the plugin method rather than accepting one dynamic object.我曾考虑在 .net 4.0 中使用新的动态类型,但我更喜欢在插件方法上定义我的参数,而不是接受一个动态对象。

Thanks Ben谢谢本

[Update] [更新]

After looking through the ASP.NET MVC source code it seems simple enough to pull the anonymous type into an object dictionary eg RouteValueDictionary.在查看 ASP.NET MVC 源代码后,将匿名类型拉入对象字典(例如 RouteValueDictionary)似乎很简单。 With the help of reflection a linq expression is created dynamically.在反射的帮助下,一个 linq 表达式是动态创建的。 Although its a good implementation, I didn't really want all this extra complexity.虽然它是一个很好的实现,但我并不真正想要所有这些额外的复杂性。

As per the comment below, I can achieve readability just by specifying my parameters inline (no need for the object array declaration):根据下面的评论,我可以通过内联指定我的参数来实现可读性(不需要对象数组声明):

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

I did eventually come across this post that demonstrates using anonymous types as dictionaries.我最终确实遇到了这篇演示使用匿名类型作为字典的帖子 Using this method you could pass the anonymous type as a method parameter (object) and access it's properties.使用此方法,您可以将匿名类型作为方法参数(对象)传递并访问它的属性。

However, I would also add that after looking into the new dynamic features in .net 4.0 such as the ExpandoObject, it feels much cleaner to pass a dynamic object as a parameter:但是,我还要补充一点,在研究了 .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);
        }

Use dynamic object for parameters if you want to pass an anonymous type.如果要传递匿名类型,请使用动态对象作为参数。 The execute method of a plugin should expect certain properties of a parameter object in order to work.插件的 execute 方法应该期望参数对象的某些属性才能工作。 By using dynamic keyword C# compiler will be instructed to not perform type check on a parameter and will allow to use strongly-typed syntax in the plugin code.通过使用动态关键字 C# 编译器将被指示不对参数执行类型检查,并允许在插件代码中使用强类型语法。 The properties name resolution will happen in run-time and if a passed object did not have such properties an exception will be thrown.属性名称解析将在运行时发生,如果传递的对象没有这样的属性,则会引发异常。

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 在这篇博文中阅读更多内容

There are some ways to make this possible although I wouldn't advice any of them.有一些方法可以使这成为可能,尽管我不会建议其中任何一个。

First, you can use reflection which means you have to write a lot of additional (error-prone) code in your PluginService.Execute method to get the values you want.首先,您可以使用反射,这意味着您必须在您的PluginService.Execute方法中编写大量额外的(容易出错的)代码来获取您想要的值。

Second, if you know the parameters of the anonymous type you are passing to your method you can use the technique described here .其次,如果您知道要传递给方法的匿名类型的参数,则可以使用此处描述的技术。 You can cast to another anonymous type inside your method that has the same properties.您可以在具有相同属性的方法中强制转换为另一个匿名类型。 Here is another description of the same technique from Jon Skeet. 是 Jon Skeet 对相同技术的另一种描述。

Third, you can use classes from the System.ComponentModel .第三,您可以使用System.ComponentModel类。 For example, ASP.NET MVC uses this.例如,ASP.NET MVC 使用这个。 It uses reflection under the hood.它在引擎盖下使用反射。 However, in ASP.NET MVC either the property names are well-known ( controller and action for example) or their names don't matter because they are passed as-is to a controller method ( id for example).但是,在 ASP.NET MVC 中,要么属性名称众所周知(例如controlleraction ),要么它们的名称无关紧要,因为它们按原样传递给控制器​​方法(例如id )。

This example converts anonymous object to a dictionary:此示例将匿名对象转换为字典:

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;
}

You can call it like this:你可以这样称呼它:

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

If it's an anonomous type from Linq, then you can easily do this by passing IEnumerable.如果它是来自 Linq 的匿名类型,那么您可以通过传递 IEnumerable 轻松完成此操作。

Here's an example of a receiving method这是一个接收方法的例子

    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
        }
    }

Of course, since you're using reflection then you may see performance issues on slower machiens or where you have either a large IEnumerable or a lot of properties in T.当然,由于您使用的是反射,因此您可能会在较慢的机器上看到性能问题,或者在 T 中有大量 IEnumerable 或许多属性的地方。

Hi I have written a post about it:嗨,我写了一篇关于它的帖子:

http://blog.jorgef.net/2011/06/converting-any-object-to-dynamic.html http://blog.jorgef.net/2011/06/converting-any-object-to-dynamic.html

I hope that helps.我希望这有帮助。

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));
}

I did this once.我做过一次。 What you can do is get the parameters expected from the function through reflection.您可以做的是通过反射从函数中获取期望的参数。 Then, you can build up your array of parameters by matching the names in the array of parameters with the keys of the anonymous object.然后,您可以通过将参数数组中的名称与匿名对象的键进行匹配来构建参数数组。

Hope that helps :-).希望有帮助:-)。

First of all, check System.Addin namespace, you might get some help there.首先,检查 System.Addin 命名空间,您可能会在那里得到一些帮助。

Second, you can create an interface of your own with specific method name and parameters, and let the plugin implement the interface.其次,您可以创建自己的具有特定方法名称和参数的接口,并让插件实现该接口。 You can define plugin interface in a different project that can be referenced in both application as well as plugin project.您可以在不同的项目中定义插件接口,可以在应用程序和插件项目中引用。

With c# 7.0 you can use tuples.在 c# 7.0 中,您可以使用元组。 https://docs.microsoft.com/en-us/dotnet/csharp/tuples https://docs.microsoft.com/en-us/dotnet/csharp/tuples

Here's an example:下面是一个例子:

TestMetchod(("String 1", 342));

private void TestMetchod((string Param1, int Param2) p)
{
    //do something
}

Hope that helps.希望有帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM