如何向DynamicObject添加键值对?为何需要使用Binder?
如何在非动态场景下为DynamicObject实例设置成员值
要在非动态代码中为DynamicObject实例设置键值对,核心是正确调用其TrySetMember方法,这需要实现或使用合适的SetMemberBinder实例。以下是具体解决方案及原理说明:
自定义SetMemberBinder实现
你需要完成RuntimePropertyBinder的FallbackSetMember方法,该方法是回退逻辑入口——当目标DynamicObject的TrySetMember返回false(无法处理当前设置操作)时触发。如果你的场景默认目标对象能处理所有传入的成员设置,直接抛出异常是合理的选择:
class RuntimePropertyBinder(string name) : SetMemberBinder(name, ignoreCase: false) { public override DynamicMetaObject FallbackSetMember( DynamicMetaObject target, DynamicMetaObject value, DynamicMetaObject? errorSuggestion) { // 优先使用传入的错误建议,否则构造抛出异常的逻辑 if (errorSuggestion != null) return errorSuggestion; var exceptionExpr = Expression.Throw( Expression.New(typeof(NotImplementedException).GetConstructor(Type.EmptyTypes)!), typeof(void)); return new DynamicMetaObject(exceptionExpr, BindingRestrictions.Empty); } }
实现AddValue方法
通过DynamicMetaObject的绑定机制,触发目标DynamicObject的TrySetMember逻辑:
void AddValue(DynamicObject obj, string name, object? value) { var binder = new RuntimePropertyBinder(name); var targetMeta = obj.GetMetaObject(Expression.Constant(obj)); // 构造值的DynamicMetaObject var valueExpr = Expression.Constant(value); var valueMeta = new DynamicMetaObject(valueExpr, BindingRestrictions.Empty); // 获取设置成员的执行表达式并执行 var setExpr = targetMeta.BindSetMember(binder, valueMeta); setExpr.Compile().DynamicInvoke(); }
Binder与回退机制的作用
- Binder的角色:
SetMemberBinder是动态绑定的描述器,它定义了"设置成员"操作的元数据(如成员名称、大小写规则),是连接调用逻辑与DynamicObject内部处理逻辑的桥梁。 - 回退机制的用途:
FallbackSetMember是容错逻辑,当目标DynamicObject无法处理当前设置操作(比如成员名称不合法、权限不足)时,会进入这个方法执行替代逻辑。你可以在这里实现自定义的降级处理,比如反射设置公共成员、返回默认值,或者像示例中一样抛出异常明确告知错误。
更简洁的替代方案:使用内置CSharpSetMemberBinder
如果你不想自定义Binder,可以引用Microsoft.CSharp NuGet包,使用.NET内置的CSharpSetMemberBinder,它已经实现了标准C#动态绑定的回退逻辑:
// 需先安装Microsoft.CSharp NuGet包 void AddValue(DynamicObject obj, string name, object? value) { var binder = Microsoft.CSharp.RuntimeBinder.Binder.SetMember( CSharpBinderFlags.None, name, typeof(YourCurrentClass), // 替换为当前方法所在类的类型 new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }); var targetMeta = obj.GetMetaObject(Expression.Constant(obj)); var valueMeta = new DynamicMetaObject(Expression.Constant(value), BindingRestrictions.Empty); var setExpr = targetMeta.BindSetMember((SetMemberBinder)binder, valueMeta); setExpr.Compile().DynamicInvoke(); }
内容的提问来源于stack exchange,提问作者Kyle McClellan
相关产品推荐
相关产品推荐

