将动态对象解析委托给其他实例

时间:2011-01-21 12:16:41

标签: c# .net ironpython dynamic-language-runtime dynamicobject

我目前正在.NET 2.0应用程序中托管IronPython。

我想创建一个类(在C#中),其实例可以被插件实例“扩展”。这意味着,我的实例上无法解析的每个成员访问都应该转发到提供该成员的相应插件实例。我的对象将保存一个包含这些插件实例的私有容器。

AFAICS,去那里的方法是从DynamicObject派生。到目前为止,第一步很简单,只要python代码使用我的实例的“未知”成员,就会调用TryGetMember。我还可以返回可以从python代码中使用的对象和委托。

但是,不知何故,当我尝试使用DLR在插件实例和e上执行“subsearch”时,我遇到了困难。 G.以IronPython期望的方式返回插件实例的方法或属性。

欢迎任何提示!

谢谢!

编辑:我原来的问题不够明确,抱歉。这里有一些观点:

  • 解决方案必须使用普通的.NET 2.0运行,不允许使用.NET 3.5或4.0。
  • 插件列表是每个实例(这意味着每个实例可以有一个不同但不可变的插件对象列表)。
  • 插件对象应该是普通的C#对象,并且映射了所有公共成员(或至少是方法和属性)。
  • 碰撞检测并不重要。

再次感谢。

1 个答案:

答案 0 :(得分:3)

您想要做的是将插件实例键入对象或动态(而不是将它们键入到您有效传递TryGetMember请求的某个界面),然后对另一个对象执行动态绑定。幸运的是,DLR互操作协议允许这种情况!它需要下拉到IDynamicMetaObjectProvider层而不是使用DynamicObject,但它实际上非常简单。我将向您展示一个使用InvokeMember的简单示例,它使用C#4.0进行端到端工作。您需要继续执行其余操作 - 特别是IronPython将使用GetMember而不是InvokeMember。但这是一个直接的过程。

首先解释你是如何做到的。在IDMOP级别,DLR处理元对象和语言请求来自元对象的操作,并且这些元对象返回更多元对象。您还可以要求语言执行默认绑定,最重要的是,您可以向其提供有关出错时应采取的措施的建议。

基于此,您可以要求语言尝试绑定到每个插件和您自己的对象。根据您是希望插件具有优先级还是动态对象,您可以最后执行绑定到自己。此示例演示了基于成员名称的两种技术。

所以没有进一步的延迟,这是:

using System;
using System.Dynamic;
using System.Linq.Expressions;

namespace ConsoleApplication10 {
    class Program {
        static void Main(string[] args) {
            dynamic dynamicObj = new MyDynamicObject(new TestPlugin());
            dynamicObj.Foo();
            dynamicObj.Bar();
            Console.ReadLine();
        }

    }

    public class TestPlugin {
        public void Foo() {
            Console.WriteLine("TestPlugin Foo");
        }

        public void Bar() {
            Console.WriteLine("TestPlugin Bar");
        }
    }

    class MyDynamicObject : IDynamicMetaObjectProvider {
        internal readonly object[] _plugins;

        public void Foo() {
            Console.WriteLine("MyDynamicObject Foo");
        }

        public void Bar() {
            Console.WriteLine("MyDynamicObject Bar");
        }

        public MyDynamicObject(params object[] plugins) {
            _plugins = plugins;
        }

        class Meta : DynamicMetaObject {
            public Meta(Expression parameter, BindingRestrictions restrictions, MyDynamicObject self)
                : base(parameter, restrictions, self) {
            }

            public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args) {                
                // get the default binding the language would return if we weren't involved
                // This will either access a property on MyDynamicObject or it will report
                // an error in a language appropriate manner.
                DynamicMetaObject errorSuggestion = binder.FallbackInvokeMember(this, args);

                // run through the plugins and replace our current rule.  Running through
                // the list forward means the last plugin has the highest precedence because
                // it may throw away the previous rules if it succeeds.
                for (int i = 0; i < Value._plugins.Length; i++) {
                    var pluginDo = DynamicMetaObject.Create(Value._plugins[i],
                        Expression.Call(
                            typeof(MyDynamicObjectOps).GetMethod("GetPlugin"),
                            Expression,
                            Expression.Constant(i)
                        )
                    );

                    errorSuggestion = binder.FallbackInvokeMember(pluginDo, args, errorSuggestion);                    
                }

                // Do we want DynamicMetaObject to have precedence?  If so then we can do
                // one more bind passing what we've produced so far as the rule.  Or if the
                // plugins have precedence we could just return the value.  We'll do that
                // here based upon the member name.

                if (binder.Name == "Foo") {
                    return binder.FallbackInvokeMember(this, args, errorSuggestion);
                }

                return errorSuggestion;
            }

            public new MyDynamicObject Value {
                get {
                    return (MyDynamicObject)base.Value;
                }
            }
        }



        #region IDynamicMetaObjectProvider Members

        public DynamicMetaObject GetMetaObject(System.Linq.Expressions.Expression parameter) {
            return new Meta(parameter, BindingRestrictions.Empty, this);
        }

        #endregion
    }

    public static class MyDynamicObjectOps {
        public static object GetPlugin(object myDo, int index) {
            return ((MyDynamicObject)myDo)._plugins[index];
        }
    }
}

运行此打印:

MyDynamicObject Foo TestPlugin Bar

显示对于Foo成员我们更喜欢绑定实际对象,对于Bar成员,我们更喜欢Bar。如果添加对第三个Baz成员的访问权限,则会产生C#的运行时绑定程序异常。如果从IronPython中调用它,我们会为Python程序生成一个AttributeError(.NET中的MissingMemberException),并且JavaScript实现应该将undefined返回给它们的程序。

因此,您不仅可以获得可扩展的插件系统,还可以轻松地在任何消耗对象的语言中获得正确的行为。