C#DynamicObject动态属性

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

class MyObject : DynamicObject { dictionary _properties = dictionary(); 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 

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

假设我不知道它们会在运行时发生什么。

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

基本上我想我要问的是我如何调用直接调用TrySetMember的代码,以便创建新属性或使用getter / setter(如果已定义)。

最终解决方案如下: –

 using System.Dynamic; using Microsoft.CSharp.RuntimeBinder; using System.Runtime.CompilerServices; class MyObject : DynamicObject { Dictionary _properties = new Dictionary(); public object GetMember(string propName) { var binder = Binder.GetMember(CSharpBinderFlags.None, propName, this.GetType(), new List{ CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)}); var callsite = CallSite<Func>.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.Create(CSharpArgumentInfoFlags.None, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)}); var callsite = CallSite<Func>.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; } } 

虽然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); 

Getters和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.Create(CSharpArgumentInfoFlags.None, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }); var callsite = CallSite>.Create(binder); callsite.Target(callsite,o,87); var binder2 =Binder.SetMember(CSharpBinderFlags.None, "Name", typeof(object), new List{ CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }); var callsite2 = CallSite>.Create(binder2); callsite2.Target(callsite2,o,"Sam"); 

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

假设我不知道它们会在运行时发生什么。

那么C#4中的动态类型根本不会对你有所帮助,你也可以使用Dictionary

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

可能会发现你只需要让MyObject也实现像ExpandoObject那样的IDictionary ……虽然问题是如果你想从MyObject 派生其他类并通过字典也暴露它们的属性,那就是会比较棘手。