【问题标题】: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 的真正要求。

标签: reflection dynamic .net-4.0 c#-4.0 dynamic-language-runtime


【解决方案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 来介绍如何在运行时创建自己的类型和添加属性——以防其他人正在寻找类似的东西。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-01-12
      • 1970-01-01
      • 1970-01-01
      • 2011-04-25
      • 1970-01-01
      • 2022-11-29
      • 2016-01-07
      相关资源
      最近更新 更多