簡體   English   中英

IronPython函數具有可變數量的參數作為委托

[英]IronPython function with variable number of arguments as a delegate

在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")

其中addHandler采用單個參數,並以某種方式將其存儲在要調用的方法列表中,並且invoke具有如下簽名:

public virtual void invoke(params object[] tArgs)

因為我想避免使它特定於PythonEngine (以及engine.Operations.Invoke() ),我嘗試了幾種方式來存儲和實現這些事情作為代表,但我認為我的問題的關鍵是我不要知道如何存儲某種與PythonFunction兼容的MulticastDelegate基類型嗎?

也許我想實現自己的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);

您可以將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);
        }
    }
}

注意:

檢查委托 - 參數兼容性的部分是錯誤的,只是為了給你一個想法。

問題是我不知道如何在運行時檢查對象args列表是否與委托簽名兼容......也許我們應該檢查IronPython源代碼:)

我的第一個想法是digEmAll建議但我提出了一個更好的解決方案,不需要轉換為System.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不同,更依賴於平台。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM