3

IronPythonでは、C#とは異なる数の引数を使用してPythonFunctionを呼び出そうとしています。例えば;

私はやってみたいです:

def foo(a, b):
     print a, b

def bar(a, b, c = None):
     print a, b, c

p = App.DynamicEvent()
p.addHandler(foo)
p.addHandler(bar)
p.invoke("Not", "Working")

whereaddHandlerは単一の引数を取り、それを呼び出されるメソッドのリストに格納し、次のinvokeようなシグネチャを持ちます。

public virtual void invoke(params object[] tArgs)

PythonEngine(したがって)に固有のものにしたくないのでengine.Operations.Invoke()、これらをデリゲートとして保存および実装するいくつかの方法を試しましたが、私の問題の核心は、ある種の保存方法がわからないことだと思います?MulticastDelegateと互換性のあるベースタイプPythonFunction

おそらく私は自分のDynamicInvokeメソッドを実装したいですか?どんな考えや経験でも大歓迎です!

これを実行する理由は、封印されたJavascriptエンジンからの呼び出しをC#経由でIronPythonに透過的にマッピングするためです。つまり、Javascript呼び出しで:Client.doThing("something", 4, {"key:"value"}) そしてPythonでそれを処理します:

def doThing(s, i, d):
    pass

次の動的イベントバインディングを使用します。

doThingEvent = App.DynamicEvent()
doThingEvent.addHandler(doThing)
WebBrowser.handleMethod("doThing", doThingEvent);
4

2 に答える 2

3

たとえば、PythonFunctionをデリゲートとして渡すには、Action<...>

つまり、Pythonでは次のようなことを行います。

import sys
import clr
import System
from System import *

def foo(a, b):
     print a, b

def bar(a, b, c = "N.A."):
     print a, b, c

p = App.DynamicEvent()
p.AddHandler( Action[object,object](foo) )
p.AddHandler( Action[object,object,object](bar) )
p.DynamicInvoke("Not", "Working")

このようにあなたpはすることができますMulticastDelegate。明らかに、これは次のことを意味します。

  1. 渡されたすべてのデリゲートは同じ署名を持っている必要があります
  2. 最大16個のパラメーターでPython関数を渡すことができます(アクションは最大16個のパラメーターをサポートするため)

最初の問題については、次のような独自の「Delegatesinvoker」を作成する必要があると思います。

//
// WARNING: very very rough code here !!
//
public class DynamicEvent
{
    List<Delegate> delegates;

    public DynamicEvent()
    {
        delegates = new List<Delegate>();
    }
    public void AddHandler(Delegate dlgt)
    {
        delegates.Add(dlgt);
    }
    public void Invoke(params object[] args)
    {
        foreach (var del in delegates)
        {
            var parameters = del.Method.GetParameters();
            // check parameters number
            if (args.Length != parameters.Length)
                continue; // go to next param

            // check parameters assignability
            bool assignable = true;
            for (int i = 0; i < args.Length; i++)
            {
                if (!parameters[i].ParameterType.IsInstanceOfType(args[i]))
                {
                    assignable = false;
                    break; // stop looping on parameters
                }
            }
            if (!assignable)
                continue; // go to next param

            // OK it seems compatible, let's invoke
            del.DynamicInvoke(args);
        }
    }
}

注意:

デリゲートとパラメータの互換性をチェックする部分が間違っているのは、あなたにアイデアを与えるためだけです。

問題は、実行時にオブジェクト引数のリストがデリゲート署名と互換性があるかどうかを確認する方法がわからないことです...多分IronPythonソースコードを確認する必要があります:)

于 2010-10-02T16:57:38.807 に答える
0

私の最初のアイデアはdigEmAllSystem.Actionが提案したとおりでしたが、キャストや呼び出し時の型のチェックや分岐を必要としない、より良いソリューションを思いつきました。オーバーロードaddHandlerするだけで、PythonFunctionと同様にDelegate、元のコードを使用できることを意味します。

def foo(a, b):
     print a, b

def bar(a, b, c = None):
     print a, b, c

p = DynamicEvent(engine)
p.addHandler(foo)
p.addHandler(bar)

p.invoke("a", "b")
p.invoke("a", "b", "c")

このC#コードで:

public class DynamicEvent
{
    private Dictionary<int, Action<object[]>> delegates = new Dictionary<int, Action<object[]>>();
    public ScriptEngine Engine { get; set; }


    public DynamicEvent(ScriptEngine engine)
    {
        Engine = engine;
    }

    public void addHandler(PythonFunction pythonFunction)
    {
        int args = (int) pythonFunction.func_code.co_nlocals;
        delegates.Add(args, a => Engine.Operations.Invoke(pythonFunction, a));
    }

    public void addHandler(Delegate d)
    {
        int args = d.Method.GetParameters().Length;
        delegates.Add(args, a => d.DynamicInvoke(a));
    }

    public void invoke(params object[] args)
    {
        Action<object[]> action;
        if(!delegates.TryGetValue(args.Length, out action))
            throw new ArgumentException("There is no handler that takes " + args.Length + " arguments!");

        action(args);
    }
}

engineコンストラクターで使用できるように、スクリプトのスコープにを追加する必要があることに注意してください。

お役に立てば幸いです。


注:を取得して、次のようDelegateに実行することができます。PythonFunction

Delegate target = pythonFunction.__code__.Target;
var result = target.DynamicInvoke(new object[] {pythonFunction, args});

ただし、使用するよりもプラットフォームに依存しEngine.Operations.Invoke()Delegate署名は「通常」とは異なりDelegateます。

于 2010-10-02T17:46:58.813 に答える