【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 参数、返回值与类型转换的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: .Net Core 认证系统之基于Ide
- 下一篇: 《ASP.NET Core 微服务实战》