Go语言版本
入口
reflect.TypeOf和reflect.ValueOf两个函数会分别返回Type和Value两个结构,顾名思义,分别代表一个Go数据的类型和值。通过查看Type和Value的方法可以得出反射可以实现的功能。
1 | type Value struct { |
Value结构体中也包括Type,因此可以通过
1 | (v Value) Type() Type |
方法直接返回Type。Value结构体中ptr指向的是实际数据
结构体
Type接口中关于结构体有如下几个方法:
1 | type StructField struct { |
结构体通过TypeOf函数之后实际内存布局为如下结构:
1 | type structType struct { |
其中rtype为实现了Type接口的结构体,pkgPath为包名,fields为一个包括所有结构体字段的切片,因此NumField()返回len(fields)即可。同理,Field(i int)根据索引查找该字段并且生成一个StructField结构并且返回
继续查看Value类型中对struct的操作
1 | func (v Value) Field(i int) Value { |
通过值加字段偏移量并且生成一个新的Value然后返回
对struct的反射操作示例,通过json.Marshal看看如何将一个结构体转换为json,重点函数如下:
1 | func newStructEncoder(t reflect.Type) encoderFunc { |
生成一个struct的encoderFunc,通过对一个结构体的反射,遍历机构体,将json tag取出,然后通过如下函数生成json
1 | func (se structEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { |
方法
一个struct如果有方法,则内存布局变为:
1 | type structTypeUncommon struct { |
一个结构体的Value值加上uncommonType中的moff就是结构体的方法数组。Value的MethodByName通过一个方法名称可以返回一个方法
1 | func (t *rtype) MethodByName(name string) (m Method, ok bool) { |
然后通过调用Call可以调用该方法,Call签名如下:
1 | func (v Value) Call(in []Value) []Value { |
我们看一个通过反射实现继承的例子:
1 | package main |
例子中相当于T1、T2继承了T,T1重载了Geeks方法,T2使用父类的方法
总结
文中介绍了结构体的反射,其他类型未详细描述,需要可以自行查询文档或者代码。
关于需要反射的其他场景,可以参考如下文章:
https://golangbot.com/reflection/