2

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

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

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

但是,不知何故,当我尝试使用 DLR 对插件实例和 e 执行“子搜索”时,我被卡住了。G. 以 IronPython 期望的方式返回插件实例的方法或属性。

欢迎任何提示!

谢谢!

编辑:对不起,我最初的问题表述得不够清楚。这里有几点:

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

再次感谢。

4

1 回答 1

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 测试插件栏

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

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

于 2011-01-22T05:13:43.147 回答