反射是在运行期间获取类的各种信息。
Go语言的反射需要理解两个概念Type和Value,它们也是Go语言中reflect空间里最重要的两个类型,举例如下:
type MyReader struct {
Name string
}
func (r MyReader)Read(p []byte) (n int, err error) {
// 实现自己的Read方法
}
var reader io.Reader
reader = &MyReader{"a.txt"}
对所有接口进行反射,都可以得到一个包含Type和Value的信息结构。比如对上例的reader进行反射,也将得到一个Type和Value,Type为io.Reader,Value为MyReader{“a.txt”}。顾名思义,Type主要表达的是被反射的这个变量本身的类型信息,而Value则为该变量实例本身的信息。
语言交互性自c语言诞生以来,后面有无数的新的语言实现,但是大部分的语言底层都封装了本地方法调用底层的C语言的方法,比如java提供了JNI机制来调用那些C代码库,Go语言也提供了对C语言的调用,称为Cgo,举例如下:
package main
import "fmt"
/*
#include
*/
import "C"
func Random() int {
return int(C.random())
}
func Seed(i int) {
C.srandom(C.uint(i))
}
func main() {
Seed(100)
fmt.Println("Random:", Random())
}
类型映射
类型映射是指两种语言交互,两种语言之间变量的转换关系,比如Go语言的string类型需要跟c语言中的字符数组进行对应,并且要保证映射到C语言的对象的生命周期足够长,以避免在C语言执行过程中该对象就已经被垃圾回收。对于C语言的原生类型,Cgo都会将其映射为Go语言中的类型,知道有这种对应关系就行。
字符串映射因为Go语言中有明确的string原生类型,而C语言中用字符数组表示,Cgo提供了一系列函数来提供支持:C.CString、C.GoString和C.GoStringN。注意的是string每次的转换都是一次内存复制,字符串其实是不能修改的,比如java中的string两个值的拼接,不是修改原来的string的的值,而是新申请内存空间去存相加后的值,所有用到C.CString的代码大致都可以写成如下的风格:
var gostr string
// ... 初始化gostr
cstr := C.CString(gostr)
defer C.free(unsafe.Pointer(cstr))
// 接下来大胆地使用cstr吧,因为保证可以被释放掉了
// C.sprintf(cstr, "content is: %d", 123)
C程序
需要在Go语言调用任何C代码都可以在注释里面写C语言的代码直接调用如下:
package hello
/*
#include
void hello() {
printf("Hello, Cgo! -- From C world.\n");
}
*/
import "C"
func Hello() int {
return int(C.hello())
}
主要是/*
和*/
之间的内容是写c语言的代码块,如果需要在这个c语言代码块调用非c语言的需求,Cgo提供了#cgo一种伪C文法,让开发者有机会指定依赖的第三方库和编译选项如下:
// #cgo CFLAGS: -DPNG_DEBUG=1
// #cgo linux CFLAGS: -DLINUX=1
// #cgo LDFLAGS: -lpng
// #include
import "C"
函数调用
常规的调用直接函数名加上需要传递的参数就好,比如有错误的返回值的调用为:
n, err := C.atoi("a234")
在传递数组类型的参数时需要注意,在Go语言中将第一个元素的地址作为整个数组的起始地址传入,这一点就不如C语言本身直接传入数组名字那么方便了。如下:
n, err := C.f(&array[0]) // 需要显示指定第一个元素的地址
编译Cgo
编译Cgo代码非常容易,不需要做任何特殊的处理。Go安装后,会自带一个cgo命令行工具,它用于处理所有带有Cgo代码的Go文件,生成Go语言版本的调用封装代码。而Go工具集对cgo命令行工具再次进行了良好的封装,使构建过程能够自动识别和处理带有Cgo代码的Go源代码文件,完全不给用户增加额外的工作负担。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)