我正在构建一个可以与IronPython自动化的C#WebKit Web浏览器,以帮助进行质量保证测试。我将使用IronPython创建测试计划,该计划将运行许多浏览器方法,提供参数并评估结果。
IronPython的大多数文档都说明了如何使用C#调用IronPython方法,但是我已经弄清楚了如何为方法设置参数,以及如何检索方法的返回值,但不能从同一方法中返回。您将在下面的示例中注意到,我将参数传递给一个方法,该方法依次设置一个类成员变量,然后使用另一个方法检索该值。
谁能建议一个更优雅的图案?
Run Code Online (Sandbox Code Playgroud)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"); } } } }
通常情况下,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;
}
}
Run Code Online (Sandbox Code Playgroud)
这样,您就可以执行脚本来通过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);
}
Run Code Online (Sandbox Code Playgroud)