我正在尝试创建一个可以用作静态对象组件的动态对象。这是我想要完成的一个人为的例子。
以下是动态组件:
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");
然而,转发GetMetaObject
到AStaticComponent
会导致某种形式的无限循环。
dynamic dynamicComponent = new AStaticComponent();
Assert.AreEqual(dynamicComponent.AMethod(), "hello"); //causes an infinite loop
任何人都知道为什么会这样吗?
如果在DynamicObject
的行为中出现了一些我无法改变的事情,那么有人可以提供一些帮助来解决如何从头开始创建IDynamicMetaObjectProvider
以完成基于组件的动态对象(只需获取一些内容)事情开始了吗?
答案 0 :(得分:9)
我认为问题在于传递给Expression
的{{1}}参数表示动态调用的目标(即当前对象)。您正在将外部对象传递给GetMetaObject
上的调用,因此返回的元对象正在尝试解析对外部对象component.GetMetaObject
的调用而不是自身,因此无限循环。
您可以创建自己的元对象,在绑定成员调用时委托给内部组件:
AMethod
答案 1 :(得分:1)
动态调用在调用站点缓存,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));
}
}
}