c# - 从 IronPython 调用 C# 方法

标签 c# ironpython

我正在构建一个 C# WebKit Web 浏览器,该浏览器可以使用 IronPython 自动化以帮助进行质量保证测试。我将使用 IronPython 创建测试计划,该计划将运行许多浏览器方法、提供参数并评估结果。

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

最佳答案

在正常情况下,IronPython 只能访问 .Net 类型上的公共(public)类和成员。您应该能够将您的对象设置为范围内的变量,并从您的脚本访问该对象上的任何公共(public)成员。但是,您在示例中使用的方法是 private 因此您无法真正访问它们。如果您希望能够访问它们,则需要以某种方式公开非公共(public)成员,以便您可以操纵它们或使用反射。

您可以尝试的一种模式是添加一个方法,该方法将为您的对象创建一个代理,该代理将拥有您想要公开的所有方法。然后将该代理对象添加到您的作用域,然后使用该代理调用您的方法。

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

关于c# - 从 IronPython 调用 C# 方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13333367/

相关文章:

python - IronPython 中 urllib 的 PROTOCOL_TLS 错误

c# - 具有逆变类型参数和 Unity 容器的泛型

c# - 按元素长度对字符串数组进行排序

c# - 这可以创建 GTM.js 服务器端逻辑吗?

c# - 将 C# 类对象传递给 Python 文件

python - 有没有一个好的 IDE 可以用 Python 构建 GUI 应用程序

C#/C++ pInvoke 技巧

c# - 在 .NET 自定义操作中检测静默安装

c# - 在 C# Web 应用程序中嵌入 IronPython 2 的问题

python - 如何在 Visual Studio 2008 的 Python 中使用制表符作为空格?