2

私は現在、.NET 2.0 アプリケーションで IronPython をホストしています。

プラグイン インスタンスによってインスタンスを「拡張」できるクラスを (C# で) 作成したいと考えています。つまり、インスタンスで解決できない各メンバー アクセスは、そのメンバーを提供する適切なプラグイン インスタンスに転送する必要があります。私のオブジェクトは、それらのプラグイン インスタンスを含むプライベート コンテナを保持します。

AFAICS、そこに行く方法は、DynamicObject から派生することです。これまでの最初のステップは簡単で、Python コードがインスタンスの「不明な」メンバーを使用するたびに TryGetMember が呼び出されました。また、Python コードから使用できるオブジェクトとデリゲートを返すこともできました。

しかし、どういうわけか、DLR を使用してプラグイン インスタンスで「サブサーチ」を実行しようとしたときに行き詰まりました。G. IronPython が期待する方法で、プラグイン インスタンスのメソッドまたはプロパティを返します。

どんなヒントでも大歓迎です!

ありがとう!

編集:私の最初の質問は十分に明確に定式化されていませんでした。申し訳ありません。ここでいくつかのポイント:

  • ソリューションはプレーンな .NET 2.0 で実行する必要があり、.NET 3.5 または 4.0 は許可されていません。
  • プラグイン リストはインスタンスごとです (つまり、各インスタンスは、異なるが不変のプラグイン オブジェクトのリストを持つことができます)。
  • プラグイン オブジェクトは、すべてのパブリック メンバー (または少なくともメソッドとプロパティ) がマップされたプレーンな C# オブジェクトである必要があります。
  • 衝突検出は重要ではありません。

再度、感謝します。

4

1 に答える 1

3

プラグインインスタンスをオブジェクトまたは動的に入力してから(TryGetMemberリクエストを効果的に通過させるインターフェイスに入力するのではなく)、別のオブジェクトに対して動的バインディングを実行します。幸いなことに、DLR相互運用プロトコルはまさにこのシナリオを可能にします!DynamicObjectを使用する代わりにIDynamicMetaObjectProviderレイヤーにドロップダウンする必要がありますが、実際には非常に単純です。C#4.0でエンドツーエンドで動作するInvokeMemberを使用した簡単な例を示します。残りの操作を実行する必要があります。特に、IronPythonはInvokeMemberの代わりにGetMemberを使用します。しかし、それは簡単なプロセスです。

まず、それをどのように行うかについて説明します。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];
        }
    }
}

このプリントを実行する:

MyDynamicObjectFooTestPluginバー

Fooメンバーの場合は実際のオブジェクトのバインディングを好み、Barメンバーの場合はBarを好みます。3番目のBazメンバーへのアクセスを追加すると、C#のランタイムバインダー例外が発生します。これがIronPythonから呼び出された場合、PythonプログラムへのAttributeError(.NETではMissingMemberException)が生成され、JavaScript実装はそれらのプログラムに未定義を返す必要があります。

したがって、拡張可能なプラグインシステムを取得するだけでなく、オブジェクトを消費するあらゆる言語で正しい動作を簡単に取得できます。

于 2011-01-22T05:13:43.147 に答える