将成员动态添加到动态对象

Posted

技术标签:

【中文标题】将成员动态添加到动态对象【英文标题】:Dynamically adding members to a dynamic object 【发布时间】:2011-01-05 23:55:45 【问题描述】:

我正在寻找一种的方法。好的,我想需要澄清一下...

当你这样做时:

dynamic foo = new ExpandoObject();
foo.Bar = 42;

Bar 属性将在运行时动态添加。但是代码仍然“静态地”引用 Bar(名称“Bar”是硬编码的)......如果我想在运行时添加一个属性而不知道它在编译时的名称怎么办?

我知道如何使用DynamicObject 类的方法使用自定义动态对象(实际上是几个月前的blogged about it)来做到这一点,但是我该如何使用any 动态对象?

我可能可以使用IDynamicMetaObjectProvider 接口,但我不明白如何使用它。例如,我应该将什么参数传递给 GetMetaObject 方法? (它需要一个Expression

顺便问一下,你如何对动态对象进行反射? “常规”反射和TypeDescriptor 不显示动态成员...

任何见解将不胜感激!

【问题讨论】:

在 C# 6.0 中,可能是你可以写成foo.$Bar = 42; :) 不确定是否允许动态... @nawfal,实际上,该功能已被删除......但无论如何,foo.$Bar 只是foo["Bar"] 的简写 Thomas,不知道这个功能被删除了(我很高兴),但是哦,是的,有一刻我忽略了你的 q 的真正要求。 【参考方案1】:

你想要的类似于 Python 的 getattr/setattr 函数。在 C# 或 VB.NET 中没有内置的等效方法可以做到这一点。 DLR 的外层(在 Microsoft.Scripting.dll 中附带 IronPython 和 IronRuby)包括一组托管 API,其中包括具有 GetMember/SetMember 方法的 ObjectOperations API。你可以使用这些,但你需要 DLR 的额外依赖和基于 DLR 的语言。

可能最简单的方法是使用现有 C# 绑定器之一创建一个 CallSite。您可以通过查看 ildasm 或反射器中“foo.Bar = 42”的结果来获取此代码。但一个简单的例子是:

object x = new ExpandoObject();
CallSite<Func<CallSite, object, object, object>> site = CallSite<Func<CallSite, object, object, object>>.Create(
            Binder.SetMember(
                Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags.None,
                "Foo",
                null,
                new[]  CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) 
            )
        );
site.Target(site, x, 42);
Console.WriteLine(((dynamic)x).Foo);

【讨论】:

我需要花一些时间来确保我真的理解这段代码在做什么,但无论如何它工作正常......谢谢! 是否可以使用 DLR 执行此操作 上面的方法应该不能正常工作。 Setter 调用站点需要提供 2 个参数信息而不是 1 个。正确的定义是:new[] CSharpArgumentInfoFlags.None, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) 【参考方案2】:

ExpandoObject 实现了 IDictionary 尽管是显式的。这意味着您可以简单地将 ExpandoObject 转换为 IDictionary 并操作字典。

dynamic foo = new ExpandoObject();
foo.Bar = 42;
food = (IDictionary<string,object>)foo;
food["Baz"] = 54

【讨论】:

谢谢!正如我后来意识到的那样,ExpandoObject 确实实现了 IDictionary,因此在这种情况下它显然是最简单的解决方案。然而,我的问题范围更广:我正在寻找一种适用于任何动态对象的解决方案,而不仅仅是 ExpandoObject【参考方案3】:

开源框架Dynamitey 将执行此操作(可通过nuget 获得)。它封装同时仍然缓存@Dino-Viehland 使用的调用站点和绑定代码。

Dynamic.InvokeSet(foo,"Bar",42);

也可以调用多个other kinds of c# binder too。

【讨论】:

【参考方案4】:

我知道这是一篇相当老的帖子,但我想我会传递Miron Abramson solution 来介绍如何在运行时创建自己的类型和添加属性——以防其他人正在寻找类似的东西。

【讨论】:

以上是关于将成员动态添加到动态对象的主要内容,如果未能解决你的问题,请参考以下文章

如何复制动态分配的对象(具有一个类的 const 成员)

将 jQuery 添加到动态 JS-DOM 对象

在运行时(动态)将 EditorAttribute 添加到对象的特殊属性

delphi 动态添加系统菜单

ASP.NET MVC3 - 将项目动态添加到对象的数组中

从android中的其他类动态地将对象添加到listview