2012-11-11 114 views
1

我正在構建一個C#WebKit Web瀏覽器,該瀏覽器可以通過IronPython實現自動化以幫助進行質量保證測試。我將使用IronPython創建測試計劃,該計劃將運行許多瀏覽器方法,提供參數並評估結果。從IronPython調用C#方法

IronPython的大部分文檔說明了如何使用C#調用IronPython方法,但我已經想出瞭如何爲方法設置參數,以及如何檢索方法返回值,但不是來自同一方法。在下面的例子中你會注意到,我將一個參數傳遞給一個方法,該方法依次設置一個類成員變量,然後用另一種方法檢索該值。

任何人都可以提出一個更優雅的模式?

using System; 
using System.Windows.Forms; 
using IronPython.Hosting; 
using Microsoft.Scripting; 
using Microsoft.Scripting.Hosting; 

namespace PythonScripting.TestApp 
{ 

public partial class Form1 : Form 
{ 
    private ScriptEngine m_engine = Python.CreateEngine(); 
    private ScriptScope m_scope = null; 

    //used to call funcs by Python that dont need to return vals 
    delegate void VoidFunc(string val); 

    public Form1() 
    { 
     InitializeComponent(); 
    } 

    private void doSomething() 
    { 
     MessageBox.Show("Something Done", "TestApp Result"); 
    } 

    private string _rslt = ""; 

    private string getSomething() 
    { 
     return _rslt; 
    } 

    private void setSomething(string val) 
    { 
     _rslt = val; 
    } 

    private void Form1_Load(object sender, EventArgs e) 
    { 
     m_scope = m_engine.CreateScope(); 

     Func<string> PyGetFunction = new Func<string>(getSomething); 
     VoidFunc PySetFunction = new VoidFunc(setSomething); 

     m_scope.SetVariable("txt", txtBoxTarget); 
     m_scope.SetVariable("get_something", PyGetFunction); 
     m_scope.SetVariable("set_something", PySetFunction);   
    } 

    private void button1_Click(object sender, EventArgs e) 
    { 
     string code = comboBox1.Text.Trim(); 
     ScriptSource source = m_engine.CreateScriptSourceFromString(code, SourceCodeKind.SingleStatement); 

     try 
     { 
      source.Execute(m_scope); 
      Func<string> result = m_scope.GetVariable<Func<string>>("get_something"); 

      MessageBox.Show("Result: " + result(), "TestApp Result"); 
     } 
     catch (Exception ue) 
     { 
      MessageBox.Show("Unrecognized Python Error\n\n" + ue.GetBaseException(), "Python Script Error"); 
     } 
    } 
} 
} 
+0

您定位的是哪個版本的框架?在這裏使用'dynamic'會使所有這些都變得微不足道。 –

+0

謝謝傑夫,我是obv一個IronPython noob-我會研究這個... –

+0

理解這段代碼真的幫助我,對任何感興趣的人:http://thinkingeek.com/2009/03/11/using-ironpython -to-extend-your-net-applications/ –

回答

4

在正常情況下,IronPython的只能訪問公共類和成員的.NET類型。您應該能夠將範圍內的對象設置爲變量,並從腳本訪問該對象上的任何公共成員。然而,你在你的例子中的方法是private因此,你不能真正訪問它們。如果您希望能夠訪問它們,您需要以某種方式公開非公開成員,以便您可以操縱它們或使用反射。

您可以嘗試的模式是添加一個方法,該方法將爲您的對象創建一個代理,該代理將擁有您想要公開的所有方法。然後將該代理對象添加到您的作用域,然後使用該代理來調用您的方法。

public partial class MyForm : Form 
{ 
    private readonly ScriptEngine m_engine; 
    private readonly ScriptScope m_scope; 

    public MyForm() 
    { 
     InitializeComponent(); 
     m_engine = Python.CreateEngine(); 

     dynamic scope = m_scope = m_engine.CreateScope(); 
     // add this form to the scope 
     scope.form = this; 
     // add the proxy to the scope 
     scope.proxy = CreateProxy(); 
    } 

    // public so accessible from a IronPython script 
    public void ShowMessage(string message) 
    { 
     MessageBox.Show(message); 
    } 

    // private so not accessible from a IronPython script 
    private int MyPrivateFunction() 
    { 
     MessageBox.Show("Called MyForm.MyPrivateFunction"); 
     return 42; 
    } 

    private object CreateProxy() 
    { 
     // let's expose all methods we want to access from a script 
     dynamic proxy = new ExpandoObject(); 
     proxy.ShowMessage = new Action<string>(ShowMessage); 
     proxy.MyPrivateFunction = new Func<int>(MyPrivateFunction); 
     return proxy; 
    } 
} 

有了這個,你可以執行腳本通過form變量或通過proxy變量代理訪問你的形式。爲了更好的衡量,您可以輕鬆訪問示波器中的變量和其他對象。

private void DoTests() 
{ 
    // try to call the methods on the form or proxy objects 
    var script = @" 
form.ShowMessage('called form.ShowMessage') 
# formFuncResult = form.MyPrivateFunction() # fail, MyPrivateFunction is not accessible 
proxy.ShowMessage('called proxy.ShowMessage') 
proxyFuncResult = proxy.MyPrivateFunction() # success, MyPrivateFunction on the proxy is accessible 
"; 
    m_engine.Execute(script, m_scope); 

    // access the scope through a dynamic variable 
    dynamic scope = m_scope; 

    // get the proxyFuncResult variable 
    int proxyFuncResult = scope.proxyFuncResult; 
    MessageBox.Show("proxyFuncResult variable: " + proxyFuncResult); 

    // call the the function on the proxy directly 
    int directResult = scope.proxy.MyPrivateFunction(); 
    MessageBox.Show("result of MyPrivateFunction: " + directResult); 
} 
+0

感謝傑夫,我實際做這些改變我張貼了這個時間之間的公衆,當你現在只是回答。這個偉大的東西。該代理看起來是我正在尋找的,但我不知道如何使用表單變量,所以我會挖掘一下。再次感謝。 -G –

+0

如果你看一下我的例子中,構造函數,我添加了一個'form'變量這是'this'形式對象的引用(這是從我可以告訴你想與之交互的對象)的範圍。您還會看到'proxy'變量被設置爲創建的代理對象。它們僅用於演示腳本如何訪問這些對象。 –

+0

傑夫,感謝公司招聘我還通過你的示例 - 澆築誰都希望拿到了動態關鍵字和ExpandoObject加快,這MSDN文章奠定了良好的基礎http://msdn.microsoft.com/ en-us/magazine/gg598922.aspx –

1

我不確定你想達到什麼樣的,但是怎麼樣?

在C#:

m_scope.SetVariable("myAssembly", System.Reflection.Assembly.GetExecutingAssembly()); 

[...] 

var result = (string) m_scope.GetVariable("theOutVar"); 

然後在IronPython的腳本:

import clr 
clr.AddReference(myAssembly) 
import MyNamespace 
theOutVar = MyNamespace.MyClass.MyStaticMethod("hi") 

或許這樣嗎?

在C#:

m_scope.SetVariable("theTestObject", myTestObj); 

而且在IronPython的:

result = myTestObj.DoSomething("hi there", 10, false) 
+0

我想要做的,就是能夠從一個鐵python腳本(設置方法參數)調用C#方法,但在C#應用程序中卻可以輕鬆獲得結果。我的困惑是使用GetVariable方法的正確語法。我需要做的就是將方法返回到IronPython中的變量,如上面所示,result = GetSomething(「my val」)和調用範圍。的getVariable(「結果」) –