C# 4.0 将DynamicObject(IDynamicMetaObjectProvider)用作静态类型的组件会导致无限循环
我正在尝试创建一个动态对象,它可以用作静态对象的组件。这是一个我试图实现的人为的例子 以下是动态组件:C# 4.0 将DynamicObject(IDynamicMetaObjectProvider)用作静态类型的组件会导致无限循环,c#-4.0,dynamic,C# 4.0,Dynamic,我正在尝试创建一个动态对象,它可以用作静态对象的组件。这是一个我试图实现的人为的例子 以下是动态组件: public class DynamicComponent : DynamicObject { public override bool TryInvokeMember( InvokeMemberBinder binder, object[] args, out object result) { result
public class DynamicComponent : DynamicObject
{
public override bool TryInvokeMember(
InvokeMemberBinder binder,
object[] args,
out object result)
{
result = "hello";
return true;
}
}
这里有一个类,其中从DynamicObject
继承不是一个选项……假设有某个第三方类是我被迫从中继承的
public class AStaticComponent : VendorLibraryClass, IDynamicMetaObjectProvider
{
IDynamicMetaObjectProvider component = new DynamicComponent();
public DynamicMetaObject GetMetaObject(Expression parameter)
{
var result = component.GetMetaObject(parameter);
return result;
}
}
直接使用DynamicComponent
可以:
dynamic dynamicComponent = new DynamicComponent();
Assert.AreEqual(dynamicComponent.AMethod(), "hello");
但是,通过AStaticComponent
转发GetMetaObject
会导致某种形式的无限循环
dynamic dynamicComponent = new AStaticComponent();
Assert.AreEqual(dynamicComponent.AMethod(), "hello"); //causes an infinite loop
有人知道为什么会这样吗
如果我无法改变
dynamicbject
的某些固有行为,那么有人能提供一些帮助,帮助我从头开始创建IDynamicMetaObjectProvider
,以实现基于组件的动态对象(只是为了开始工作)?我认为问题在于传递给GetMetaObject
的表达式
参数表示动态调用的目标(即当前对象)。您正在将外部对象传递给对组件.GetMetaObject
的调用,因此返回的meta对象试图在外部对象而不是自身上解析对AMethod
的调用,从而产生无限循环
dynamic dynamicComponent = new AStaticComponent();
Assert.AreEqual(dynamicComponent.AMethod(), "hello"); //causes an infinite loop
您可以创建自己的元对象,在绑定成员调用时将其委托给内部组件:
public class AStaticComponent : VendorLibraryClass, IDynamicMetaObjectProvider
{
IDynamicMetaObjectProvider component = new DynamicComponent();
public DynamicMetaObject GetMetaObject(Expression parameter)
{
return new DelegatingMetaObject(component, parameter, BindingRestrictions.GetTypeRestriction(parameter, this.GetType()), this);
}
private class DelegatingMetaObject : DynamicMetaObject
{
private readonly IDynamicMetaObjectProvider innerProvider;
public DelegatingMetaObject(IDynamicMetaObjectProvider innerProvider, Expression expr, BindingRestrictions restrictions)
: base(expr, restrictions)
{
this.innerProvider = innerProvider;
}
public DelegatingMetaObject(IDynamicMetaObjectProvider innerProvider, Expression expr, BindingRestrictions restrictions, object value)
: base(expr, restrictions, value)
{
this.innerProvider = innerProvider;
}
public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args)
{
var innerMetaObject = innerProvider.GetMetaObject(Expression.Constant(innerProvider));
return innerMetaObject.BindInvokeMember(binder, args);
}
}
}
@李的答案真的很有用,如果没有它,我不知道从哪里开始。但是在生产代码中使用它,我相信它有一个微妙的缺陷 动态调用缓存在调用站点,Lee的代码生成一个
DynamicMetaObject
,它有效地声明内部处理对象是一个常量。如果您在代码中有一个位置,您在AStaticObject
的实例上调用动态方法,然后代码中的同一点在AStaticObject
的不同实例上调用相同的方法(即,因为类型为AStaticObject
的变量现在具有不同的值)然后,代码将进行错误的调用,在代码运行期间,始终从代码中该位置遇到的第一个实例调用处理对象上的方法
这是一个类似的替换,关键区别在于使用Expression.Field
告诉动态调用缓存系统处理对象依赖于父对象:
public class AStaticComponent : VendorLibraryClass, IDynamicMetaObjectProvider
{
IDynamicMetaObjectProvider component = new DynamicComponent();
public DynamicMetaObject GetMetaObject(Expression parameter)
{
return new DelegatingMetaObject(parameter, this, nameof(component));
}
private class DelegatingMetaObject : DynamicMetaObject
{
private readonly DynamicMetaObject innerMetaObject;
public DelegatingMetaObject(Expression expression, object outerObject, string innerFieldName)
: base(expression, BindingRestrictions.Empty, outerObject)
{
FieldInfo innerField = outerObject.GetType().GetField(innerFieldName, BindingFlags.Instance | BindingFlags.NonPublic);
var innerObject = innerField.GetValue(outerObject);
var innerDynamicProvider = innerObject as IDynamicMetaObjectProvider;
innerMetaObject = innerDynamicProvider.GetMetaObject(Expression.Field(Expression.Convert(Expression, LimitType), innerField));
}
public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args)
{
return binder.FallbackInvokeMember(this, args, innerMetaObject.BindInvokeMember(binder, args));
}
}
}
这个问题很好,但我花了很长时间才找到。我想是因为上面写着“静态类”。我完全明白你的意思,静态而不是动态但我的问题是,这与C#通常所指的
static
(例如static类)不同。标题是否可以说“使用包装的DynamicObject(IDynamicMetaObjectProvider)向类添加动态支持会导致无限循环”?我希望我添加的这条评论可以帮助其他人找到它,如果他们正在思考与您相同的问题,就像我一样!:)谢谢你——对答案也是如此,真的很有帮助!旁白/无关:我放弃了使用dynamic
的所有希望。来自.Net开发人员的压力太大了。你是怎么做到的/你在哪个项目中使用它的?谢谢你的反馈。像这样的参考文献帮助我同意这种类型的东西需要住在图书馆里,而不是大多数人必须维护的代码!我正在替换一个提供一些动态方法的库,但我希望在我的版本中有一个父类。你可能会发现我做的这些东西很有用。好运:用法示例: