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开发人员的压力太大了。你是怎么做到的/你在哪个项目中使用它的?谢谢你的反馈。像这样的参考文献帮助我同意这种类型的东西需要住在图书馆里,而不是大多数人必须维护的代码!我正在替换一个提供一些动态方法的库,但我希望在我的版本中有一个父类。你可能会发现我做的这些东西很有用。好运:用法示例: