
通过 go 的 c-shared 构建模式生成动态链接库(.so/.dll),再利用 c# 的 p/invoke 机制,即可在 .net 环境中高效、零进程开销地调用 go 函数。
通过 go 的 c-shared 构建模式生成动态链接库(.so/.dll),再利用 c# 的 p/invoke 机制,即可在 .net 环境中高效、零进程开销地调用 go 函数。
Go 语言原生支持跨语言互操作,其 buildmode=c-shared 模式可将 Go 代码编译为标准 C 兼容的动态库(Linux 下为 .so,Windows 下为 .dll,macOS 下为 .dylib),导出的函数能被任何支持 C ABI 的语言(包括 C#)直接调用,无需启动子进程或依赖中间服务。
✅ 基本实现步骤
-
编写可导出的 Go 函数
使用 //export 注释标记需暴露的函数,并确保包名为 main,且包含空的 main() 函数(仅用于构建):
// hello.go
package main
import "C"
import "fmt"
//export Add
func Add(a, b int) int {
return a + b
}
//export SayHello
func SayHello(name *C.char) *C.char {
goStr := fmt.Sprintf("Hello, %s!", C.GoString(name))
return C.CString(goStr)
}
// 必须存在,但内容为空
func main() {}-
构建共享库
执行以下命令(以 Windows 为例,Linux/macOS 类似):
go build -buildmode=c-shared -o hello.dll hello.go
将生成 hello.dll 和 hello.h(头文件,含函数签名,供参考)。
-
C# 中 P/Invoke 调用
在 C# 项目中声明外部函数,注意字符串需手动释放内存(Go 分配的 C 字符串需由 Go 的 C.free 释放):
using System;
using System.Runtime.InteropServices;
class Program
{
const string LibPath = "hello.dll"; // 确保路径正确,或置于 PATH / 输出目录
[DllImport(LibPath, CallingConvention = CallingConvention.StdCall)]
public static extern int Add(int a, int b);
[DllImport(LibPath, CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr SayHello(IntPtr name);
[DllImport("msvcrt.dll", EntryPoint = "free")]
private static extern void Free(IntPtr ptr);
static void Main()
{
Console.WriteLine($"Add(3, 5) = {Add(3, 5)}"); // 输出: 8
var namePtr = Marshal.StringToHGlobalAnsi("World");
try
{
var resultPtr = SayHello(namePtr);
string result = Marshal.PtrToStringAnsi(resultPtr);
Console.WriteLine(result); // 输出: Hello, World!
// ⚠️ 必须释放 Go 分配的内存,否则内存泄漏!
Free(resultPtr);
}
finally
{
Marshal.FreeHGlobal(namePtr);
}
}
}⚠️ 关键注意事项
- 调用约定:Go 默认使用 StdCall(Windows)或 Cdecl(Unix),需在 [DllImport] 中显式指定(Windows 推荐 CallingConvention.StdCall;Linux/macOS 用 Cdecl)。
- 内存管理:所有由 Go 通过 C.CString 分配的内存,必须由 Go 提供的 C.free 释放(如上例调用 msvcrt.dll 的 free 仅作示意;更健壮的做法是 Go 导出一个 FreeCString 函数)。
- 线程安全:Go 运行时默认启用,但若 C# 多线程频繁调用,建议在 Go 侧初始化 runtime.LockOSThread() 或确保 Go 函数无状态、无全局副作用。
- 平台兼容性:需为目标平台(x64/x86)分别构建对应架构的 DLL/SO,并确保 C# 项目平台匹配(如 x64 项目引用 x64 DLL)。
✅ 总结
相比进程间通信(IPC)或 HTTP API,c-shared + P/Invoke 是性能最优、集成最紧密的互操作方案。它规避了序列化开销与进程启动延迟,真正实现 Go 逻辑与 C# 应用的无缝协同。只要遵循 C ABI 规范、妥善管理内存和调用约定,即可稳定应用于生产环境。


















