[英]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
。 這顯然意味着:
對於第一個問題,我認為你需要編寫自己的“委托調用者”,例如:
//
// 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.