在上篇文章【C# 调用 Go 语言】0x1 Hello Golang  中,我们将 Golang 源码编译为动态链接库(dll),用 C# 调用 Golang 导出的方法并成功的看到了控制台的输出。本篇文章将对 C# 调用 Golang 方法做更详细的介绍,涉及如何对 Golang 方法进行传参、获取返回值以及处理调用过程中的类型转换。

本文源代码可以在  https://gitee.com/coderbusy/golang-with-csharp 找到。

基本的传参与返回值

使用 Golang 编写一个名为 Check 的方法,该方法接收两个整型的参数(i1,i2)并返回一个布尔值,当 i1 > i2 时返回值为 True,否则为 False :

需要一个 make.bat 文件,用于生成动态链接库:

同上篇,将 C# 项目 Golang.Ioc 的目标平台设置为 x86 ,将生成的 Golang.Ioc.Interop.dll 复制到项目中并设置为始终复制:

使用 P/Invoke 调用导出的方法:

运行之后,程序将会产生如下输出,程序行为符合我们的预期:

C、CGO、Golang 与 P/Invoke

C/C++ 经过几十年的发展,已经积累了庞大的软件资产,它们很多久经考验而且性能已经足够优化。Go 语言必须能够站在 C/C++ 这个巨人的肩膀之上,有了海量的 C/C++ 软件资产兜底之后,我们才可以放心愉快地用 Go 语言编程。C 语言作为一个通用语言,很多库会选择提供一个 C 兼容的 API,然后用其他不同的编程语言实现。Go 语言通过自带的一个叫 CGO 的工具来支持 C 语言函数调用,同时我们可以用 Go 语言导出 C 动态库接口给其它语言使用。

Go语言高级编程 》 第二章 CGO编程

P/Invoke 的全称是 Platform Invoke (平台调用) 它实际上是一种函数调用机制,通过 P/Invoke 我们就可以调用非托管 DLL 中的函数。实际上很多 NET 基类库中定义的类型内部调用了从 Kernel32.dll,User32.dll,gdi32.dll 等非托管 DLL 中导出的函数。

之所以可以在 C# 中调用 Golang 程序集是因为 CGO 在中间充当了桥梁。我们的调用顺序应该是 C# -> C -> Golang 。

下表列出了 Windows API 和 C 样式函数中使用的数据类型。许多非托管库包含将这些数据类型作为参数和返回值传递的函数。第三列列出了相应的 .NET Framework 内置值类型或可在托管代码中使用的类。

Windows API 中的非托管类型 非托管 C 语言类型 托管类型 描述
VOID void System.Void 应用于不返回值的函数。
HANDLE void * System.IntPtr 或 System.UIntPtr 在 32 位 Windows 操作系统上为 32 位、在 64 位 Windows 操作系统上为 64 位。
BYTE unsigned char System.Byte 8 位
SHORT short System.Int16 16 位
WORD unsigned short System.UInt16 16 位
INT int System.Int32 32 位
UINT unsigned int System.UInt32 32 位
LONG long System.Int32 32 位
BOOL long System.Boolean 或 System.Int32 32 位
DWORD unsigned long System.UInt32 32 位
ULONG unsigned long System.UInt32 32 位
CHAR char System.Char 使用 ANSI 修饰。
WCHAR wchar_t System.Char 使用 Unicode 修饰。
LPSTR char * System.String 或 System.Text.StringBuilder 使用 ANSI 修饰。
LPCSTR const char * System.String 或 System.Text.StringBuilder 使用 ANSI 修饰。
LPWSTR wchar_t * System.String 或 System.Text.StringBuilder 使用 Unicode 修饰。
LPCWSTR const wchar_t * System.String 或 System.Text.StringBuilder 使用 Unicode 修饰。
FLOAT float System.Single 32 位
DOUBLE double System.Double 64 位

Go语言中数值类型和C语言数据类型基本上是相似的,以下是它们的对应关系表:

C语言类型 CGO类型 Go语言类型
char C.char byte
singed char C.schar int8
unsigned char C.uchar uint8
short C.short int16
unsigned short C.short uint16
int C.int int32
unsigned int C.uint uint32
long C.long int32
unsigned long C.ulong uint32
long long int C.longlong int64
unsigned long long int C.ulonglong uint64
float C.float float32
double C.double float64
size_t C.size_t uint

需要注意的是,虽然在C语言中int、short等类型没有明确定义内存大小,但是在CGO中它们的内存大小是确定的。在CGO中,C语言的int和long类型都是对应4个字节的内存大小,size_t类型可以当作Go语言uint无符号整数类型对待。

在编写完 Golang 代码后,如果不确定对应的 C# 类型,那么可以查看在编译后与 DLL 同时生成的 .h 头文件,对应上面两张表应该就可以找到正确的类型 。

字符串类型参数

如果一个方法需要导出并且参数或返回值涉及到字符串,通常使用 *C.char 来代替 Golang 内置的 string 类型对外导出。可以调用 C.CString 方法将 Golang 的字符串类型转为 *C.char 类型:

需要注意的是:C string 在 C 的堆上使用 malloc 申请。调用者有责任在合适的时候对该字符串进行释放,释放方式可以是调用C.free(调用C.free需包含stdlib.h)。

在 Golang 源码中新增 GetSlogan 方法,该方法接受一个名为 name 的字符串参数,并返回一句为武汉加油的口号。为了可以在返回值使用完成后释放掉由 C.CString 申请的内存,再增加一个 Free 方法:

C# 提供一个 ICustomMarshaler 接口,可以用它来对托管内存和非托管内存进行转换。添加一个 CStringMarshaler  实现 ICustomMarshaler 接口,帮我们处理 C# string 和 C.CString 之间的转换过程,并保证内存被正确释放:

测试一下对 GetSlogan 方法的调用:

运行代码后将产生以下输出:

增加代码进行性能测试:

调用 52 万 1 千次后,内存占用仍在 20M 以内,可以证明没有发生内存泄漏问题:

【C# 调用 Go 语言】0x2 参数、返回值与类型转换相关推荐

  1. C#调用存储过程详解(带返回值、参数输入输出等)

    C#调用存储过程详解(带返回值.参数输入输出等) 这篇文章主要介绍了C#调用存储过程的方法,结合实例形式详细分析了各种常用的存储过程调用方法,包括带返回值.参数输入输出等,需要的朋友可以参考下 本文实 ...

  2. java方法带参数返回值_Java方法中的参数太多,第6部分:方法返回

    java方法带参数返回值 在当前的系列文章中,我正在致力于减少调用Java方法和构造函数所需的参数数量,到目前为止,我一直专注于直接影响参数本身的方法( 自定义类型 , 参数对象 , 构建器模式 , ...

  3. 易语言取linux命令返回值,易语言取程序返回值写法

    公告: 为响应国家净网行动,部分内容已经删除,感谢读者理解. 话题:易语言取程序返回值写法回答:在易语言中,程序包括"处理程序"都有六部分组成.一.程序名,就是程序的名称,程序名不 ...

  4. python 调用控制台并获取返回结果_Java调用Python脚本并获取返回值

    在Java程序中有时需要调用Python的程序,这时可以使用一般的PyFunction来调用python的函数并获得返回值,但是采用这种方法有可能出现一些莫名其妙的错误,比如ImportError.在 ...

  5. C++调用python并获取其返回值

    C++调用python并获取其返回值 先上实例代码: C++代码: //初始化py环境 // Py_Initialize();PyRun_SimpleString("import sys&q ...

  6. c语言函数返回值存储,C语言的函数返回值所存放的寄存器

    #include int add(int a, int b) { return a + b; } int asm_compare_one(int a) { _asm { mov edx, a cmp ...

  7. vb获得mysql的值,VB.NET调用MySQL存储过程并获得返回值的方法

    本文实例讲述了VB.NET调用MySQL存储过程并获得返回值的方法.分享给大家供大家参考.具体实现方法如下: Dim myConnectionString As String = "Data ...

  8. java 调用linux 脚本并获取返回值

    大家好,我是烤鸭: 今天分享下java 调用 shell脚本 并获取返回值. 代码实践 String cmd = "df -h"; StringBuffer sb = new St ...

  9. atitit.架构设计---方法调用结果使用异常还是返回值

    atitit.架构设计---方法调用结果使用异常还是返回值 1. 应该返回BOOL类型还是异常 1 2. 最终会有四种状况,抛出异常.返回特殊值.阻塞.超时 1 3. 异常的优缺点点 1 4. jav ...

  10. 学习大数据的第13天——Java面向对象(接口、分析参数返回值的类型不同时如何解决、包以及访问权限修饰符(public、protected、默认、private))

    学习大数据的第13天--Java面向对象(接口.分析参数返回值的类型不同时如何解决.包以及访问权限修饰符(public.protected.默认.private)) 接口 接口的基本定义: 1.1.语 ...

最新文章

  1. 昨天下午面了个哥们,也就问了4个问题,但好像他被我虐了
  2. 几种Linux包管理系统的命令对照
  3. Google I/O 大会上的 Android Things 亮点汇总
  4. 怎样通过css控制table的部分td
  5. 阐述:SIP协议是什么
  6. Qt之解决error: member access into incomplete type ‘UI::XXX‘
  7. 奇葩Bug:IE下表单要提交两次
  8. 发布一个 host 管理插件
  9. 【转】还原一个真实的银行待遇
  10. 64位Ubuntu14.04系统无法解压bin文件的解决方法
  11. 道高一尺,魔高一丈--加密与解密的此消彼长
  12. 网络流24题(部分)
  13. 韩国研发人工智能武器 遭30国专家联名抵制:和你绝交!
  14. win10 下 caffe 的第一个测试程序(附带详细讲解)
  15. IDEA误删文件恢复
  16. 51nod 1631 小鲨鱼在51nod小学 【线段树--】
  17. 微信小程序:各种Tab栏
  18. 网件r6800,r6900v2,r7250刷老毛子(pandvan)教程
  19. 在Unity中实现基础的MVC架构
  20. Oracle APEX 系列文章5:在阿里云上打造属于你自己的APEX完整开发环境 (进一步优化)...

热门文章

  1. [转]Myeclipse9引入easy_ui时,其中的部分js文件报错
  2. 国外网站评出对程序员最具影响的书籍清单
  3. hotmail_在新的Hotmail Wave 4中禁用Messenger
  4. 7.16 10.19-10.22
  5. 9 个使用前必须再三小心的 Linux 命令
  6. 抽象类和接口类的区别
  7. 医疗大数据:商业保险、移动医疗的崛起,正在形成闭环(二)
  8. 人的一生能交多少朋友?
  9. 系统架构师-基础到企业应用架构-服务层
  10. HDU 2896 病毒侵袭【AC自动机】