2010-10-02 144 views
3

在IronPython中,我試圖用C#中不同數量的參數調用PythonFunction。例如;具有可變數量參數的IronPython函數作爲代理

我想做的事:

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()),我嘗試了幾種方式來存儲和實現這些東西作爲代表,但我認爲我的問題的癥結在於,我不知道如何去請參考某種MulticastDelegate基本類型,它與PythonFunction兼容?

也許我想實現我自己的DynamicInvoke方法?任何想法和經驗將不勝感激!

想要做到這一點的原因是我想透明地將密封的Javascript引擎的調用映射到通過C#的IronPython。即在JavaScript調用:使用下面的動態事件綁定

def doThing(s, i, d): 
    pass 

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

你的意思是你想讓'invoke()'從你使用'addHandler()'添加的'delegate'列表中選擇正確的'delegate',根據它的簽名(它所需要的類型),然後調用它? – 2010-10-02 15:38:45

+0

討厭,因爲我設法使用'PythonFunction'作爲'Delegate'之前,我只需要找到代碼。 – 2010-10-02 16:26:40

+0

重載是否僅使用'object'作爲參數是否重要? 'void doThing(object)','void doThing(object,object,object)'等等?否則像'def add(a,b):a + b'這樣的東西是不明確的,因爲它可以使用任何數字類型。 – 2010-10-02 16:46:20

回答

0

我的第一個想法是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

+0

謝謝你們的答案(對於延遲迴復你們兩個抱歉)。 – John 2010-10-06 17:42:50

+0

使用此解決方案的問題真的有兩方面。 (1)如果不存儲對PythonFunction的額外引用(不是太多問題),我無法刪除該代理。 (2)它將腳本宿主特定的功能插入到更通用的代碼庫部分中。 – John 2010-10-06 17:45:47

+0

我的妥協方法是編寫一個子類,用於添加具有腳本宿主特定處理能力的DynamicEvent(如上所述),但在每個受支持的腳本宿主中實現並覆蓋。並不理想,因爲我相信你可以想象...但它確實讓我可以在腳本主機之間透明地調用,如果不在它們之間添加/刪除處理程序......仍然看起來令人驚訝的是,這不是默認支持的,因爲一些隱式轉換可調用和委託...唉:) – John 2010-10-06 17:51:50

3

您可以通過鑄造傳遞一個PythonFunction,例如委託Client.doThing("something", 4, {"key:"value"}) 和處理它與蟒蛇一個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. 你可以通過Python函數與最多16個參數(因爲行動支持最多16個PARAMS)

對於第一問題我想你需要編寫自己的「代表調用」,是這樣的:

// 
// 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); 
     } 
    } 
} 

NB :

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

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

+0

你是指'動作[物體,物體,物體](酒吧)'? – 2010-10-02 18:13:55

+0

是的,對不起。編輯謝謝:) – digEmAll 2010-10-02 18:21:05

相關問題