在C#中嵌入Golang函数的实现方法
在C#中嵌入Golang函数的实现方法 不确定这是否是正确的地方 🤔,因为我不确定这涉及 Go 还是 C#。
无论如何,我需要将我的 Go 应用程序嵌入到一个大型 C# 项目中。 我能够嵌入大部分函数,例如:
//PrintHello :
//export PrintHello
func PrintHello(s string) {
fmt.Println(s)
}
然后在 C# 中我会这样使用:
[DllImport("main", EntryPoint = "PrintHello")]
extern static void PrintHello(string s);
PrintHello("Go rules!!!!");
这工作得很好,但是当我的 Go 函数接收一个切片作为输入,而从 C# 传入一个数组时,我遇到了问题。 例如:
//Sum :
//export Sum
func Sum(a []int) int {
return a[0] + a[1]
}
[DllImport("main", EntryPoint = "Sum")]
extern static int Sum(int[] array);
var numlis = new List<int>();
numlis.Add(1);
numlis.Add(5);
int[] array = numlis.ToArray();
int c = Sum(array);
Console.WriteLine("Call Go func Sum, result is " + c);
我得到:
程序 ‘[15300] InitialConnect.exe’ 已退出,代码为 2 (0x2)。
我做错了什么?
更多关于在C#中嵌入Golang函数的实现方法的实战教程也可以访问 https://www.itying.com/category-94-b0.html
这可能没有帮助,但或许可以尝试在你的 DllImportAttribute 中指定调用约定:
[DllImport("main", EntryPoint = "Sum", CallingConvention=CallingConvention.Cdecl)]
extern static int Sum(int[] array);
更多关于在C#中嵌入Golang函数的实现方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
与你的导入问题无关(看起来你已经得到了帮助),但你可以简化这段代码:
var numlis = new List<int>();
numlis.Add(1);
numlis.Add(5);
int[] array = numlis.ToArray();
…为单行代码:
int[] array = { 1, 5 };
除非你确实需要,否则没有必要创建一个列表。
我刚刚做了一个小测试,.h 文件中包含了以下代码:
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
extern __declspec(dllexport) void DoubleSlice(GoSlice s);
另一种方法可能是,与其在 Go 代码中创建切片,不如在你的 C# 代码中创建 GoSlice 结构体。无论哪种方式,这都不会是透明/自动的。
你生成了 .h 文件吗?如果生成了,请检查其中 Sum 函数的签名。C# 不太可能自动生成正确的结构体来表示 Go 的切片。我曾经制作过一个供 Python 调用的共享库,那时我不得不将那些操作切片的函数进行包装,包装后的函数接受指针和长度作为参数,就像在 C 语言中通常做的那样。这些包装函数在调用原本接受切片参数的原始函数之前,会使用指针和长度来创建切片头,就像这样:
header := (*reflect.SliceHeader)(unsafe.Pointer(&out))
header.Data = uintptr(unsafe.Pointer(ptr))
header.Len = n
header.Cap = n
这里的 out 是切片变量。
感谢您的提示,这帮助我解决了问题。 确实,我必须创建一个 C# 结构体来容纳 Go 切片。
extern static int Sum(GoSlice vals);
struct GoSlice
{
public IntPtr data;
public Int64 len;
public Int64 cap;
}
经过一番研究,并借助这里的帮助,我得以完成任务。这允许将数组作为 Go 切片传递给 Go 函数。
GCHandle h = GCHandle.Alloc(array, GCHandleType.Pinned);
GoSlice gs = new GoSlice
{
data = h.AddrOfPinnedObject(),
cap = n,
len = n
};
在C#中调用接收切片的Go函数时,需要理解Go的切片在C接口中的内存布局。Go的切片实际上是一个包含三个字段的结构体:
type slice struct {
data uintptr
len int
cap int
}
你需要传递这个结构体,而不是直接传递C#数组。以下是正确的实现方法:
Go代码:
package main
import "C"
import "fmt"
//export PrintHello
func PrintHello(s string) {
fmt.Println(s)
}
// SumSlice 接收Go切片结构体
//export SumSlice
func SumSlice(ptr *C.int, length C.int) C.int {
// 将C指针转换为Go切片
slice := (*[1 << 30]C.int)(unsafe.Pointer(ptr))[:length:length]
var sum C.int = 0
for i := 0; i < int(length); i++ {
sum += slice[i]
}
return sum
}
// Sum 接收完整的切片描述符(用于演示)
//export Sum
func Sum(ptr unsafe.Pointer, len int) int {
// 创建Go切片
slice := (*[1 << 30]int)(ptr)[:len:len]
sum := 0
for _, v := range slice {
sum += v
}
return sum
}
func main() {}
C#代码:
using System;
using System.Runtime.InteropServices;
class Program
{
// 简单字符串函数
[DllImport("main", EntryPoint = "PrintHello", CharSet = CharSet.Ansi)]
extern static void PrintHello(string s);
// 方法1:传递指针和长度
[DllImport("main", EntryPoint = "SumSlice")]
extern static int SumSlice(IntPtr arrayPtr, int length);
// 方法2:使用结构体传递完整的切片描述符
[StructLayout(LayoutKind.Sequential)]
struct GoSlice
{
public IntPtr data; // 指向数据的指针
public long len; // 长度
public long cap; // 容量
}
[DllImport("main", EntryPoint = "Sum")]
extern static int Sum(GoSlice slice);
static void Main()
{
PrintHello("Go rules!!!!");
// 方法1:传递指针和长度
int[] array1 = { 1, 5, 3, 7 };
IntPtr ptr = Marshal.AllocHGlobal(array1.Length * sizeof(int));
Marshal.Copy(array1, 0, ptr, array1.Length);
int result1 = SumSlice(ptr, array1.Length);
Console.WriteLine($"SumSlice result: {result1}");
Marshal.FreeHGlobal(ptr);
// 方法2:使用GoSlice结构体
int[] array2 = { 1, 5, 3, 7 };
IntPtr ptr2 = Marshal.AllocHGlobal(array2.Length * sizeof(int));
Marshal.Copy(array2, 0, ptr2, array2.Length);
GoSlice slice = new GoSlice
{
data = ptr2,
len = array2.Length,
cap = array2.Length
};
int result2 = Sum(slice);
Console.WriteLine($"Sum result: {result2}");
Marshal.FreeHGlobal(ptr2);
}
}
编译Go代码:
go build -buildmode=c-shared -o main.dll main.go
关键点:
- Go切片在C接口中不是简单的指针,而是包含指针、长度和容量的结构体
- 需要在C#中定义匹配的
GoSlice结构体 - 或者更简单的方法是分别传递数据指针和长度
- 必须正确管理内存分配和释放
对于你的具体问题,错误代码2通常表示访问了无效的内存地址,这是因为C#数组没有被正确转换为Go切片结构体。使用上述方法可以解决这个问题。

