假设我不能使用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,
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;
}
}
答案 0 :(得分:11)
虽然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,
"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");
答案 1 :(得分:6)
但是上面假设我在编译时知道属性(例如Age,Name)。
假设我在运行时间之前不知道它们会是什么。
然后,C#4中的动态输入根本不能帮助你,你也可以使用Dictionary<string, object>
。
而不是假设 dynamic
是答案,我建议你仔细看看你的要求,并找出你真正想要实现的目标。一旦你有了一套明确的要求,就可以更容易地实现它们。
你可能发现你只需要让MyObject
同时IDictionary<string, object>
实施ExpandoObject
......虽然问题在于如果你想要的话从<{1>} 派生其他类,并通过字典公开其属性,这将更加棘手。