如何把Python项目封装成可在C#中直接调用的动态库(.dll)

作者:袖梨 2026-07-01

python 项目无法直接编译为 windows 原生 .dll 供 c# p/invoke 调用,但可通过 python.net 实现无缝集成:在 c# 进程内嵌入 python 运行时,直接调用 python 类、函数及第三方库(如 opencv、pandas),无需进程间通信或 exe 封装。

python 项目无法直接编译为 windows 原生 .dll 供 c# p/invoke 调用,但可通过 python.net 实现无缝集成:在 c# 进程内嵌入 python 运行时,直接调用 python 类、函数及第三方库(如 opencv、pandas),无需进程间通信或 exe 封装。

将 Python 项目“转换”为 C# 可直接引用的 .dll 并非字面意义上的编译(Python 是解释型语言,无原生 .NET IL 输出能力),但可通过 Python.NET 这一成熟桥梁实现等效效果:它允许 C# 程序在同一个进程中加载并执行 Python 代码,访问 Python 对象(包括自定义类、NumPy 数组、OpenCV cv2 模块、Pandas DataFrame 等),完全规避了 Process.Start() 的启动开销、标准流解析错误和跨进程数据序列化难题。

✅ 正确实践:使用 Python.NET 替代 EXE 调用

  1. 安装 Python.NET NuGet 包
    在 C# 项目中执行:

    dotnet add package Python.Runtime

    或通过 Visual Studio NuGet 包管理器安装 Python.Runtime。

  2. 准备 Python 模块(无需打包)
    保留原始 .py 文件(如 math_operation.py),确保其路径可被 Python 运行时发现(推荐放在项目 Resources 目录或指定路径):

    # math_operation.pyclass A:    def add(self, a, b):        return int(a + b)  # 显式转为 int,避免类型不匹配    def subtract(self, a, b):        return int(a - b)
  3. C# 中直接调用 Python 类
    初始化 Python 运行时后,即可导入模块、实例化类、调用方法:

    using Python.Runtime;class Program{    static void Main(string[] args)    {        // 初始化 Python 运行时(需指定 Python 安装路径,或让其自动发现)        // 注意:Python 版本需与 Python.NET 兼容(推荐 3.8–3.11)        PythonEngine.Initialize();        // 设置 Python 模块搜索路径(可选)        using (Py.GIL()) // 获取全局解释器锁        {            // 导入 Python 模块            dynamic mathModule = Py.Import("math_operation");            // 实例化 Python 类            dynamic pyObj = mathModule.A();            // 直接调用方法(参数自动转换,返回值自动映射)            int result1 = pyObj.add(3, 4);            int result2 = pyObj.subtract(10, 6);            Console.WriteLine($"The sum of 3 and 4 is: {result1}");            Console.WriteLine($"The difference between 10 and 6 is: {result2}");        }        PythonEngine.Shutdown(); // 清理资源(可选,进程退出时自动释放)    }}

⚠️ 关键注意事项

  • Python 环境依赖:Python.NET 需要系统已安装对应版本的 Python(如 python39.dll),且 C# 应用需与 Python 架构一致(x64/x86)。建议在部署时将 Python 运行时(如嵌入式 Python)与应用一同分发。
  • 第三方库支持:OpenCV、Pandas 等 C 扩展库可正常工作,但需确保其 DLL 路径已加入 PATH 或通过 PythonEngine.SetPythonHome() 显式指定 Python 环境根目录。
  • 线程安全:每次调用 Python API 前必须 using (Py.GIL()) 获取全局解释器锁;多线程场景下需谨慎管理 GIL。
  • 异常处理:Python 异常会以 PythonException 形式抛出到 C#,应使用 try-catch 捕获并检查 PyErr_Occurred()。
  • 性能考量:相比纯 C#,首次调用有初始化开销,但后续调用接近原生速度(无进程创建、IPC、JSON/XML 序列化等瓶颈)。

✅ 总结

放弃 PyInstaller → EXE → Process.Start() 的低效方案,改用 Python.NET 是将 Python 逻辑深度集成进 C# 应用的工业级标准做法。它不仅解决了 null 返回、类型转换失败、环境隔离等问题,更赋予 C# 直接操作 Python 对象的能力——无论是调用 cv2.imread() 加载图像,还是将 pandas.DataFrame 转为 DataTable,均可在统一内存空间内完成。只需正确配置环境、合理管理 GIL,并注意异常与资源释放,即可构建高性能、易维护的混合应用。

相关文章

精彩推荐