C#DynamicObject动态属性

mfc*_*mfc 8 c# dynamicobject

假设我不能使用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;
    }
}
Run Code Online (Sandbox Code Playgroud)

并进一步向下我的类层次结构

class MyNewObject : MyObject {
    public string Name {
        get {
            // do some funky stuff
        }
        set {
            // ditto
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这是非常好的,因为现在我可以做到以下几点: -

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
Run Code Online (Sandbox Code Playgroud)

但是上面假设我在编译时知道属性(例如Age,Name).

假设我直到跑步时才知道它们会是什么.

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

基本上我认为我问的是我如何调用直接调用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;
    }
}
Run Code Online (Sandbox Code Playgroud)

jbt*_*ule 10

虽然c#编译器正在使用字符串名称将动态关键字使用转换为dlr,但如果没有编译器帮助,那些Apis很难直接使用.开源框架Dynamitey(通过nuget作为PCL库提供)封装了dlr API,使其变得简单,以便您可以只调用Impromptu.InvokeSet(目标,名称,值).

using Dynamitey;
...

dynamic o = MyNewObject();

Dynamic.InvokeSet(o,"Age" ,87); 
Dynamic.InvokeSet(o,"Names" ,"Same);   
Run Code Online (Sandbox Code Playgroud)

Getter和Setter是最不复杂的直接使用实际的Microsft API,因此如果您不想使用第三方框架,那么也可以使用源代码.

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");
Run Code Online (Sandbox Code Playgroud)


Jon*_*eet 6

但是上面假设我在编译时知道属性(例如Age,Name).

假设我直到跑步时才知道它们会是什么.

那么C#4中的动态类型根本不能帮助你,你也可以使用它Dictionary<string, object>.

我建议你仔细研究一下你的要求,然后找出你真正想要达到的目标,而不是假设dynamic就是答案.一旦你有了一套明确的要求,就可以更容易地实现它们.

可能会发现你只需要MyObject实现IDictionary<string, object>类似的ExpandoObject实现......虽然问题在于如果你想从其他类派生MyObject并通过字典公开它们的属性,那将会更加棘手.