简体   繁体   English

C#DynamicObject动态属性

[英]C# DynamicObject dynamic properties

Assuming I cannot use an ExpandoObject and have to roll my own like so :- 假设我不能使用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;
    }
}

and further down the class hierarchy I have 并进一步向下我的类层次结构

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

which is quite nice as now I can do the follow :- 这是非常好的,因为现在我可以做到以下几点: -

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

But the above assumes I know the properties (eg Age, Name) at compile time. 但是上面假设我在编译时知道属性(例如Age,Name)。

Suppose I don't know what they will be until run time. 假设我直到跑步时才知道它们会是什么。

How can I change the above to support properties I will only know at run time? 如何更改上面的内容以支持我在运行时才知道的属性?

Basically I think I am asking is how I can call the code that calls TrySetMember directly so that it will either create a new property or use a getter/setter if one has been defined. 基本上我想我要问的是我如何调用直接调用TrySetMember的代码,这样它就可以创建一个新属性,或者如果已经定义了一个getter / setter。

Final Solution as follows :- 最终解决方案如下: -

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;
    }
}

Although the c# compiler is translating dynamic keyword usage to invocation with the dlr using a string name, those Apis are difficult to use directly without the compilers help. 虽然c#编译器正在使用字符串名称将动态关键字使用转换为dlr,但如果没有编译器帮助,那些Apis很难直接使用。 The open source framework Dynamitey (available through nuget as a PCL library) encapsulates the dlr API, to make it easy so that you can just call Impromptu.InvokeSet(target,name,value) . 开源框架Dynamitey (通过nuget作为PCL库提供)封装了dlr API,使其变得简单,以便您可以只调用Impromptu.InvokeSet(目标,名称,值)

using Dynamitey;
...

dynamic o = MyNewObject();

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

Getters and Setters are the least complicated to use the actual Microsft API directly, so if you don't want to use the 3rd party framework going to the source is an option too. 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");

But the above assumes I know the properties (eg Age, Name) at compile time. 但是上面假设我在编译时知道属性(例如Age,Name)。

Suppose I don't know what they will be until run time. 假设我直到跑步时才知道它们会是什么。

Then the dynamic typing in C# 4 doesn't really help you at all, and you might as well just use Dictionary<string, object> . 那么C#4中的动态类型根本不会对你有所帮助,你也可以使用Dictionary<string, object>

Rather than assume that dynamic is the answer, I suggest you take a close look at your requirements, and work out what you're really trying to achieve. 我建议你仔细研究一下你的要求,然后找出你真正想要实现的目标,而不是假设 dynamic是答案。 Once you've got a well-specified set of requirements, it's going to be easier to implement them. 一旦你有了一套明确的要求,就可以更容易地实现它们。

You may find that you just need to make MyObject also implement IDictionary<string, object> like ExpandoObject does... although the problem there is that if you want to derive other classes from MyObject and have their properties exposed via the dictionary too, that's going to be trickier. 可能会发现你只需要让MyObject也像ExpandoObject那样实现IDictionary<string, object> ......虽然问题在于如果你想从MyObject 派生其他类并通过字典也暴露它们的属性,那就是会比较棘手。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM