【问题标题】:Delegating dynamic object resolution to other instances将动态对象解析委托给其他实例
【发布时间】:2011-01-21 12:16:41
【问题描述】:

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

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

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

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

欢迎任何提示!

谢谢!

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

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

再次感谢。

【问题讨论】:

  • 我不确定我是否理解这个问题。让我确定我做对了。您有一些继承自 DynamicObject 的类 Foo。 Foo 没有属性“Bar”,所以在您的 TryGetMember 中您想返回插件提供的值? “子搜索”是什么意思?
  • 嗨,yodaj007!你的理解是正确的。我的类 Foo 应该是由第 3 方提供的插件对象“可扩展”的。应该在插件对象实例上搜索在我的类上找不到的方法和属性。如果这些插件类可以是任意 .NET 对象,则加分。从 DynamicObject 继承似乎是我要走的路。与此同时,我还阅读了有关该主题的更多内容,也许我会努力创建自己的 CallSite...

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


【解决方案1】:

听起来您想要做的是将插件实例类型化为对象或动态(而不是将它们类型化到您有效地通过 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 给他们的程序。

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

【讨论】:

  • 仅供参考:我使用 IronPython 2.6.2,文件版本 2.6.1008.2,版本 2.6.10920.0。 (我在第一次测试中确实使用了 2.6.1,因为 ironpython.net 宣布 2.6.1 为当前版本,但似乎更新没有改变任何东西。)
  • 在清理 GAC(将 .NET 2.0 程序集注入到我的 .NET 4.0 测试解决方案中,导致“有趣”效果)之后,我可以告诉以下内容:通过 C# 4.0 调用动态作品如广告所示,但通过 IronPython 调用不起作用 - 我的 BindGetMember 方法(与您的 BindInvokeMember 1:1 对齐)始终绑定到插件方法,它找不到主对象提供的方法。结果与纯 .NET 2.0 解决方案以及 Mono (linux) 下的结果相同。
  • 所以目前的状态是该解决方案不适用于 .NET 4.0、2.0 和 Mono 中的 IronPython。由于绑定到插件实例似乎工作正常,我目前的解决方法是以这种插件的形式提供甚至是默认功能,这不是我想要的,但我现在能想出的唯一解决方案。我仍然对更好的解决方案感兴趣,并将自行调查,但它“暂时”有效。感谢你付出的努力! (我还在这里清理了一些旧的,现在已经过时的 cmets。)
  • 使用 IronPython 2.7 可能会有更好的运气。 IronPython 中修复了几个与正确回退相关的错误,这可能是它不适合您的原因。
  • 嗯,AFAICS IronPython 2.7 仅适用于 .NET 4。所以要么我尝试“向后移植”修复程序,要么我必须坚持我的解决方法。谢谢!
猜你喜欢
  • 2019-05-17
  • 2023-03-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-23
  • 1970-01-01
  • 2018-05-07
  • 1970-01-01
相关资源
最近更新 更多