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方法?任何想法和经验将不胜感激!

想要这样做的原因是我想通过 C# 透明地将来自密封 Javascript 引擎的调用映射到 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 函数(因为 Action 最多支持 16 个参数)

对于第一个问题,我认为您需要编写自己的“委托调用程序”,例如:

//
// 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

我的第一个想法是digEmAll建议的,但我想出了一个更好的解决方案,它不需要强制转换System.Action或任何调用时类型检查或分支。只需重载addHandleraPythonFunction和 a 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});

但它比 using 更依赖于平台Engine.Operations.Invoke(),并且Delegate签名与 'regular' 不同Delegate

于 2010-10-02T17:46:58.813 回答