将 IronPython 嵌入 C# 应用程序






4.61/5 (11投票s)
在 C# 表单应用程序中嵌入 IronPython 引擎的示例。
引言
本文(和代码)演示了如何在 C# Windows Forms 应用程序中使用 IronPython 引擎。演示了几种不同类型的交互(从 Python 访问 C# 中的类,从 C# 访问 Python 中的类等)。
背景
IronPython 是 Python 语言的一个实现,它运行在 .NET Framework 上。也就是说,它是一组 .NET 程序集,允许您将 Python 代码编译并执行为 C#、Visual Basic 等 .NET 语言。截至当前版本 (2.6),您不能使用许多现有的 Python(称为 cPython)库,因为它们实际上是二进制库(或依赖于它们),并且无法与 IronPython 接口。另一方面,您可以访问任何兼容的 .NET 程序集。这为您提供了一个(非常)大的空间,您可以从中获取 IronPython 用于粘合和解决手头任务的功能。
使用 IronPython 主要有三种方式。第一种是您可以单独使用它来创建整个应用程序。第二种是在 .NET 语言(例如 C#)中有一个主要的实现作为您的主程序逻辑,然后使用 IronPython 引擎(和 Python 代码)来处理动态的细节。第三种是用于快速“一次性”脚本,用于执行系统操作或小型简单任务。我强烈建议避免第一种选择。在争论开始之前,我被标记为异端,您应该认识到,虽然 IronPython 作为 .NET 语言执行,但它在 Visual Studio 中的支持充其量是微乎其微的。用于在 VS 中使用它的一个附加组件 IronPython Studio 已有 2 年未更新,对其的评价充其量是褒贬不一的。如果您正在使用 .NET,并且您曾使用过其他任何东西进行比较,您就会知道 VS 中的智能感知、重构和 GUI 开发支持都非常好。没有它,您就需要猜测导入、类、函数的名称,手动添加回调,修改代码(并重新编译)以使 GUI 恰到好处;这很麻烦,更重要的是,浪费了宝贵的时间。第二种选择,即本文重点介绍的选项,似乎是 IronPython 的一个非常好的用途。考虑以下场景:
- 您正在向最终用户交付一个业务应用程序。他们每个人都有自己喜欢配置应用程序的方式。或者他们每个人都有自定义的处理需求(例如,业务逻辑)。您的应用程序是包含并执行数据主要操作的引擎。您可以使用 IronPython 为每个客户创建自定义脚本,以处理他们特定的配置和业务逻辑。当他们需要更新或修改时,您会向他们发送更新的 Python 脚本。
- 您有一个基于服务器的应用程序,用户在他们的桌面上有一个“客户端”。客户端应用程序允许他们向服务器拉取/推送数据。您以脚本的形式提供额外的处理功能,用户可以创建或从您的组织获取这些脚本。
- 你想写一个视频游戏。在视频游戏中,使用脚本引擎非常普遍。开发过程中不断变化的各种“操作”和“功能”(初始化、对话、情节、胜利条件、人工智能、关卡实例化等)都会被脚本化,并且可以交给非程序员。Python 的语法相对简单,而且每次更改代码时都不必重新编译引擎。
第三个选项是“按需”的。对于这类操作,我通常坚持使用 cPython;库在那里,现有 Python 工具可以识别现有包而没有任何麻烦。
示例
我们首先要做的是问自己 IronPython 将如何使用。我创建了一个操作/情况列表,我希望为此创建一个示例,以便我能够理解使用 IronPython 的限制(和语法)。
- 在 C# 中定义一个类,从 Python 访问它。
- 在 Python 中定义一个类,创建实例并从 C# 调用成员函数。
- 执行一个 Python 函数(与类不同)。
- 将 Python 变量定义为模块级变量,从 C# 访问和操作它们。
- 将函数指针(委托)从 C# 传递给 Python。允许 Python 带参数调用它。
- 在 Python 中执行“yield”操作。
- 捕获 Python 脚本编译错误。
- 捕获 Python 脚本执行错误。
我将讨论其中一些,但大多数应该相当明显(代码中的注释应该足以将它们全部联系起来)。
环境
- 整个应用程序代码是用 Visual Studio 2008 专业版编写的。该代码也应该在 Express 版中运行,没有任何问题。
- 我使用了 IronPython 2.6 版,可以在这里找到。
测试夹具
为了执行这些测试,我创建了一个新项目,它是一个简单的 C# Windows Forms 应用程序。完成产品的屏幕截图如下。

该程序只是一个带按钮的表单,用于执行测试,一个用于记录消息的“SimpleLogger
”类,一个用于更新记录器的计时器,以及用于执行测试本身的代码。“Setup Python”按钮初始化 IronPython 引擎和作用域(您可以通过“作用域”来分割变量和数据)。我保留了它,以便您可以看到初始化引擎需要非平凡的时间。
由于整个代码库都可以下载,我在这里将很少包含代码,除非这样做有附加价值。我将逐步讲解第一个示例,即从 Python 执行 C# 类,以便您了解执行流程。
基础结构
以下语句包含 IronPython 和 Microsoft 动态语言运行时 (DLR) 命名空间。
using IronPython.Hosting;
using IronPython.Runtime;
using Microsoft.Scripting;
using Microsoft.Scripting.Hosting;
我还将以下声明包含在主窗体类的顶部:
private ScriptEngine pyEngine = null;
private ScriptRuntime pyRuntime = null;
private ScriptScope pyScope = null;
private SimpleLogger _logger = new SimpleLogger();
SimpleLogger
类包含在源代码中。它是一个线程安全的工具,用于创建日志条目,并且可以从 C# 端和 Python 端工作。
初始化
以下代码段初始化引擎并设置“范围”。ScriptScope
是一个容器,它包含您希望“分组”在一起的所有变量、函数和类定义。当引擎执行时,它会针对一个范围执行。
if (pyEngine == null)
{
pyEngine = Python.CreateEngine();
pyScope = pyEngine.CreateScope();
pyScope.SetVariable("log", _logger);
_logger.AddInfo("Python Initialized");
}
请注意,_logger 对象通过 pyScope.SetVariable(...)
传递到 Python 作用域中。现在 Python 拥有 SimpleLogger
实例的引用,并且可以引用它公开的方法。
我还创建了一个简单的函数,用于获取字符串(Python 代码语句),编译它们并在定义的范围内执行它们。
private void CompileSourceAndExecute(String code)
{
ScriptSource source = pyEngine.CreateScriptSourceFromString
(code, SourceCodeKind.Statements);
CompiledCode compiled = source.Compile();
// Executes in the scope of Python
compiled.Execute(pyScope);
}
现在您所要做的就是使用字符串构建 Python 代码,将其交给函数,然后它会自动执行。
对于第一个测试,我们希望从 Python 向 _logger
添加一个元素。首先,我们需要创建 Python 代码。我们可以通过从文件加载它,或者只是传入字符串来完成。对于这个项目,我正在寻找一个“自包含”的解决方案,所以 Python 代码将直接由 C# 创建。
private String CreatePythonScript1()
{
String result = "";
string[] lines =
{
@"def DoIt1(logObj):",
@" logObj.AddInfo
('Executed in a function call using log object input.')",
@"",
};
result = String.Join("\r", lines);
return result;
}
我在这里有点吹毛求疵,使用“@”来表示原始字符串。在 Python 中,`string` 可以用单引号或双引号(甚至三引号)括起来,但我将坚持使用单引号,这样编辑器就不会开始着色/取消着色并让我感到困惑。这是一个简单的 Python 函数“`DoIt1(logObj)`”的声明,它接受一个参数 `logObj`。
当您点击“测试 1”按钮时,以下代码将被执行:
private void btnTest1_Click(object sender, EventArgs e)
{
CompileSourceAndExecute(CreatePythonScript1());
CompileSourceAndExecute("DoIt1(log)");
}
第一行编译 Python 函数。第二行将 `_logger` 对象作为“`log`”传递到 Python 作用域中。然后 GUI 上的列表“神奇地”更新了。
其余代码包含剩余的示例。除非您正在寻找一些相当奇特的东西,否则我认为我已经涵盖了大多数我认为对实际应用程序有用的用法示例。如果您有任何问题或需要澄清,请随时留下反馈。
历史
- 2010 年 1 月 21 日:首次发布