假设我不能使用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
但上面假设我在编译时知道属性(例如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,null),CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None,object>>.Create(binder); callsite.Target(callsite,this,val); } public override bool TryGetMember(GetMemberBinder binder,object value) { _properties[binder.Name.ToLower()] = value; return true; } }
解决方法
虽然c#编译器正在使用字符串名称将动态关键字使用转换为dlr,但如果没有编译器帮助,那些Apis很难直接使用.开源框架
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);
Getters和Setter是直接使用实际Microsft API最简单的,所以如果你不想使用第三方框架,那么source也是一个选项.
using Microsoft.CSharp.RuntimeBinder; using System.Runtime.CompilerServices; ... dynamic o = MyNewObject(); var binder = Binder.SetMember(CSharpBinderFlags.None,typeof(object),new List<CSharpArgumentInfo>{ CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None,null) }); var callsite = CallSite<Func<CallSite,object>>.Create(binder); callsite.Target(callsite,o,87); var binder2 =Binder.SetMember(CSharpBinderFlags.None,"Name",null) }); var callsite2 = CallSite<Func<CallSite,object>>.Create(binder2); callsite2.Target(callsite2,"Sam");