【C# 调用 Go 语言】0x2 参数、返回值与类型转换
在上篇文章【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 参数、返回值与类型转换相关推荐
- C#调用存储过程详解(带返回值、参数输入输出等)
C#调用存储过程详解(带返回值.参数输入输出等) 这篇文章主要介绍了C#调用存储过程的方法,结合实例形式详细分析了各种常用的存储过程调用方法,包括带返回值.参数输入输出等,需要的朋友可以参考下 本文实 ...
- java方法带参数返回值_Java方法中的参数太多,第6部分:方法返回
java方法带参数返回值 在当前的系列文章中,我正在致力于减少调用Java方法和构造函数所需的参数数量,到目前为止,我一直专注于直接影响参数本身的方法( 自定义类型 , 参数对象 , 构建器模式 , ...
- 易语言取linux命令返回值,易语言取程序返回值写法
公告: 为响应国家净网行动,部分内容已经删除,感谢读者理解. 话题:易语言取程序返回值写法回答:在易语言中,程序包括"处理程序"都有六部分组成.一.程序名,就是程序的名称,程序名不 ...
- python 调用控制台并获取返回结果_Java调用Python脚本并获取返回值
在Java程序中有时需要调用Python的程序,这时可以使用一般的PyFunction来调用python的函数并获得返回值,但是采用这种方法有可能出现一些莫名其妙的错误,比如ImportError.在 ...
- C++调用python并获取其返回值
C++调用python并获取其返回值 先上实例代码: C++代码: //初始化py环境 // Py_Initialize();PyRun_SimpleString("import sys&q ...
- c语言函数返回值存储,C语言的函数返回值所存放的寄存器
#include int add(int a, int b) { return a + b; } int asm_compare_one(int a) { _asm { mov edx, a cmp ...
- vb获得mysql的值,VB.NET调用MySQL存储过程并获得返回值的方法
本文实例讲述了VB.NET调用MySQL存储过程并获得返回值的方法.分享给大家供大家参考.具体实现方法如下: Dim myConnectionString As String = "Data ...
- java 调用linux 脚本并获取返回值
大家好,我是烤鸭: 今天分享下java 调用 shell脚本 并获取返回值. 代码实践 String cmd = "df -h"; StringBuffer sb = new St ...
- atitit.架构设计---方法调用结果使用异常还是返回值
atitit.架构设计---方法调用结果使用异常还是返回值 1. 应该返回BOOL类型还是异常 1 2. 最终会有四种状况,抛出异常.返回特殊值.阻塞.超时 1 3. 异常的优缺点点 1 4. jav ...
- 学习大数据的第13天——Java面向对象(接口、分析参数返回值的类型不同时如何解决、包以及访问权限修饰符(public、protected、默认、private))
学习大数据的第13天--Java面向对象(接口.分析参数返回值的类型不同时如何解决.包以及访问权限修饰符(public.protected.默认.private)) 接口 接口的基本定义: 1.1.语 ...
最新文章
- 昨天下午面了个哥们,也就问了4个问题,但好像他被我虐了
- 几种Linux包管理系统的命令对照
- Google I/O 大会上的 Android Things 亮点汇总
- 怎样通过css控制table的部分td
- 阐述:SIP协议是什么
- Qt之解决error: member access into incomplete type ‘UI::XXX‘
- 奇葩Bug:IE下表单要提交两次
- 发布一个 host 管理插件
- 【转】还原一个真实的银行待遇
- 64位Ubuntu14.04系统无法解压bin文件的解决方法
- 道高一尺,魔高一丈--加密与解密的此消彼长
- 网络流24题(部分)
- 韩国研发人工智能武器 遭30国专家联名抵制:和你绝交!
- win10 下 caffe 的第一个测试程序(附带详细讲解)
- IDEA误删文件恢复
- 51nod 1631 小鲨鱼在51nod小学 【线段树--】
- 微信小程序:各种Tab栏
- 网件r6800,r6900v2,r7250刷老毛子(pandvan)教程
- 在Unity中实现基础的MVC架构
- Oracle APEX 系列文章5:在阿里云上打造属于你自己的APEX完整开发环境 (进一步优化)...