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

时间:2010-08-08 09:56:23

标签: c# reflection anonymous-types

在我的插件架构中,我正在将插件名称(字符串),方法名称(字符串)和参数(对象数组)传递给我的插件服务,以执行指定的方法并返回结果(类型为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方法以将匿名类型属性映射到我的插件方法参数?

我曾考虑在.net 4.0中使用新的动态类型,但我更喜欢在插件方法上定义我的参数,而不是接受一个动态对象。

由于 本

[更新]

在查看ASP.NET MVC源代码之后,将匿名类型拉入对象字典中似乎很简单,例如RouteValueDictionary。 在反射的帮助下,动态创建linq表达式。虽然它是一个很好的实现,但我并不是真的想要所有这些额外的复杂性。

根据下面的评论,我可以通过指定内联参数(不需要对象数组声明)来实现可读性:

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

10 个答案:

答案 0 :(得分: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);
        }

答案 1 :(得分:12)

如果要传递匿名类型,请使用动态对象作为参数。插件的execute方法应该期望参数对象的某些属性才能工作。通过使用动态关键字,将指示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

答案 2 :(得分:9)

有一些方法可以实现这一点,尽管我不建议任何一种方法。

首先,您可以使用反射,这意味着您必须在PluginService.Execute方法中编写许多其他(容易出错的)代码才能获得所需的值。

其次,如果您知道要传递给方法的匿名类型的参数,则可以使用here所述的技术。您可以在方法内部转换为具有相同属性的另一个匿名类型。 Here是Jon Skeet对同一技术的另一种描述。

第三,您可以使用System.ComponentModel中的类。例如,ASP.NET MVC使用它。它使用引擎盖下的反射。但是,在ASP.NET MVC中,属性名称是众所周知的(例如controlleraction)或者它们的名称无关紧要,因为它们按原样传递给控制器​​方法({例如{1}}。

答案 3 :(得分: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" });

答案 4 :(得分:3)

如果它是来自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
        }
    }

当然,既然您正在使用反射,那么您可能会在较慢的机器上看到性能问题,或者在T中有大量IEnumerable或很多属性。

答案 5 :(得分:2)

您好我写了一篇关于它的帖子:

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

我希望有所帮助。

答案 6 :(得分:1)

我做过一次。你可以做的是通过反射获得函数的预期参数。然后,您可以通过将参数数组中的名称与匿名对象的键相匹配来构建参数数组。

希望有所帮助:-)。

答案 7 :(得分: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));
}

答案 8 :(得分:0)

首先,检查System.Addin命名空间,你可能会得到一些帮助。

其次,您可以使用特定的方法名称和参数创建自己的接口,并让插件实现接口。您可以在可以在应用程序和插件项目中引用的不同项目中定义插件接口。

答案 9 :(得分:0)

使用c#7.0可以使用元组。 https://docs.microsoft.com/en-us/dotnet/csharp/tuples

这是一个例子:

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

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

希望有帮助。