【问题标题】:C# DynamicObject dynamic propertiesC# DynamicObject 动态属性
【发布时间】:2012-08-21 14:49:07
【问题描述】:

假设我不能使用 ExpandoObject 并且必须像这样滚动我自己的:-

class MyObject : DynamicObject {
    dictionary<string, object> _properties = dictionary<string, object>();

    public override bool TryGetMember(GetMemberBinder binder, out object result) {
        string name = binder.Name.ToLower();

        return _properties.TryGetValue(name, out result);
    }

    public override bool TrySetMember(SetMemberBinder binder, object value) {
        _properties[binder.Name.ToLower()] = value;

        return true;
    }
}

以及我拥有的类层次结构

class MyNewObject : MyObject {
    public string Name {
        get {
            // do some funky stuff
        }
        set {
            // ditto
        }
    }
}

这很好,因为现在我可以执行以下操作:-

dynamic o = MyNewObject();

o.Age = 87;     // dynamic property, handled by TrySetMember in MyObject
o.Name = "Sam"; // non dynamic property, handled by the setter defined in MyNewObject

但以上假设我在编译时知道属性(例如年龄、姓名)。

假设直到运行时我才知道它们会是什么。

如何更改以上内容以支持仅在运行时才知道的属性?

基本上我想我要问的是如何调用直接调用 TrySetMember 的代码,以便它要么创建一个新属性,要么在已定义的情况下使用 getter/setter。

最终解决方案如下:-

using System.Dynamic;
using Microsoft.CSharp.RuntimeBinder;
using System.Runtime.CompilerServices;

class MyObject : DynamicObject {
    Dictionary<string, object> _properties = new Dictionary<string, object>();

    public object GetMember(string propName) {
        var binder = Binder.GetMember(CSharpBinderFlags.None,
              propName, this.GetType(),
              new List<CSharpArgumentInfo>{
                       CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)});
        var callsite = CallSite<Func<CallSite, object, object>>.Create(binder);

        return callsite.Target(callsite, this);
    }

    public void SetMember(string propName, object val) {
        var binder = Binder.SetMember(CSharpBinderFlags.None,
               propName, this.GetType(),
               new List<CSharpArgumentInfo>{
                       CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
                       CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)});
        var callsite = CallSite<Func<CallSite, object, object, object>>.Create(binder);

        callsite.Target(callsite, this, val);
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result) {
        string name = binder.Name.ToLower();

        return _properties.TryGetValue(name, out result);
    }

    public override bool TrySetMember(SetMemberBinder binder, object value) {
        _properties[binder.Name.ToLower()] = value;

        return true;
    }
}

【问题讨论】:

  • 你为什么不同时实现IDictionary&lt;string, object&gt;See
  • 非常感谢您提供非基于库的解决方案!

标签: c# dynamicobject


【解决方案1】:

尽管 c# 编译器将动态关键字用法转换为使用字符串名称使用 dlr 调用,但如果没有编译器的帮助,这些 API 很难直接使用。开源框架 Dynamitey(可通过 nuget 作为 PCL 库获得)封装了 dlr API,以便您可以轻松调用 Impromptu.InvokeSet(target,name,value)

using Dynamitey;
...

dynamic o = MyNewObject();

Dynamic.InvokeSet(o,"Age" ,87); 
Dynamic.InvokeSet(o,"Names" ,"Same);   

Getter 和 Setter 是直接使用实际 Microsft API 最简单的方法,因此如果您不想使用第 3 方框架,也可以选择 source

using Microsoft.CSharp.RuntimeBinder;
using System.Runtime.CompilerServices;
...

dynamic o = MyNewObject();
var binder = Binder.SetMember(CSharpBinderFlags.None,
                   "Age",
                   typeof(object),
                   new List<CSharpArgumentInfo>{
                           CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
                           CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
                                               });

  var callsite = CallSite<Func<CallSite, object, object, object>>.Create(binder);

  callsite.Target(callsite,o,87);

  var binder2 =Binder.SetMember(CSharpBinderFlags.None,
                   "Name",
                   typeof(object),
                   new List<CSharpArgumentInfo>{
                           CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
                           CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
                                               });
  var callsite2 = CallSite<Func<CallSite, object, object, object>>.Create(binder2);

  callsite2.Target(callsite2,o,"Sam");

【讨论】:

  • 酷。使用活页夹效果很好。不幸的是,在一个不允许开源或第三方库的地方工作。
【解决方案2】:

但以上假设我在编译时知道属性(例如年龄、姓名)。

假设直到运行时我才知道它们会是什么。

那么 C# 4 中的动态类型对你一点帮助都没有,你还不如直接使用Dictionary&lt;string, object&gt;

而不是假设 dynamic 是答案,我建议您仔细查看您的要求,并找出您真正想要实现的目标。一旦你有了一套明确的要求,实施它们就会变得更容易。

可能发现您只需要让MyObject 也像ExpandoObject 那样实现IDictionary&lt;string, object&gt;...虽然问题是如果您想导出 来自MyObject 的其他类,并且它们的属性也通过字典公开,这将变得更加棘手。

【讨论】:

  • 您好,感谢您的回答。我真的没有任何真正的要求,只是在玩功能。但是我确实想要在这个实例中使用一个真正的类,因为我希望能够为我预先知道的一些属性定义 setter 或 getter,但以后仍然能够在以后动态添加其他未知属性。
猜你喜欢
  • 1970-01-01
  • 2013-08-17
  • 1970-01-01
  • 1970-01-01
  • 2016-08-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-29
相关资源
最近更新 更多