基于.NET 7 的 AOT 功能加持,C# 可以将程序进行AOT编译,变成纯机器码文件。因此C# 通过AOT 编译的dll 不用依赖运行时,可以被其他语言编写的程序调用。
详细的.NET 7 AOT 生成dll的入门教程可以参考这篇文章 (文章作者 InCerry)
跨语言调用C#代码的新方式-DllExport - InCerry - 博客园 (cnblogs.com)
上述文章中介绍了如何发布dll,传递数值类型,字符串类型的参数,本篇主要讲解如何传递数组类型的参数。
数组类型 属于引用类型,我们并不能在传参时,直接将整个数组丢过去,而是要传递数组的地址(指针)以及数组的长度大小,因此在定义函数形参时,定义一个IntPtr 地址指针 ,一个int 数组大小
using System.Runtime.InteropServices;
namespace testdll
{
public class Class1
{
//声明这是一个仅非托管类型的导出函数
[UnmanagedCallersOnly(EntryPoint = "double_array")]
public static IntPtr double_array(IntPtr array_ptr, int len)
{
// 将指针转换成 double 数组
double[] result = new double[len];
Marshal.Copy(array_ptr, result, 0, len);
// 将数组的每个元素都加一
for(int i = 0; i < len; i++)
{
result[i]++;
}
// 将 double 数组转换为指针
IntPtr arrayPointer = Marshal.AllocHGlobal(sizeof(double) * len);
Marshal.Copy(result, 0, arrayPointer, len);
return arrayPointer;
}
}
}
如果你对上述代码有不理解地方,可以将其复制到CHATGPT中查看代码的详细含义,上述代码也是我慢慢引导chatGPT写出的代码,最终解释权归chatGPT所有(手动狗头
编译之后得到dll文件,放到你的py项目文件夹中,编写python代码:
import ctypes
l1=[2,3,4,5,6.1]
# 加载DLL库
mydll = ctypes.cdll.LoadLibrary("./testdll.dll")
# 定义函数的输入参数和返回值类型
mydll.double_array.restype = ctypes.POINTER(ctypes.c_double)
# 创建一个double数组
input_array = (ctypes.c_double * len(l1))(*l1)
# 调用函数并获取返回结果
result_array_ptr = mydll.double_array(input_array, len(l1))
# 将返回的数组转换为列表
result_list = [result_array_ptr[i] for i in range(len(l1))]
# 打印结果
print(result_list)
同样的该代码的解释权归chatGPT所有
闲话时间:
如果你看到了这里,我希望你继续往下读一下我的废话,
思考一下,你为什么想用python调用动态链接库?能不能采用纯python手段?
其实这东西应用场景不是很多,如果考虑性能的话,完全可以使用numpy进行计算,如果是循环和判断影响了你代码性能,调用dll也不是特别好的方案,因为,上述代码中你也看到了,c数组指针完全就是一个中间数据类型,我们传入数据还是使用返回结果,都转成自己语言的数据类型,数据类型转换也增加了成本,而且我的代码中还涉及到了内存数据复制(python将数组传入c#dll,代码第一行进行的数据复制操作),因为你不能直接操作python数据,这很危险。
Python 调用 外部 dll 可能的应用场景是:
1.你的主要编程语言不是python
2.不想用Python 三方库来做,打包出来的程序太大
3.Python 中没有特别好的你想要的三方库,或学习成本高,恰巧你又掌握了别的语言对应的功能库