Go-深入理解Reflect
深入理解Reflect
Go是一门静态语言,我们可以使用反射对不同类型的对象进行操作。
Type
我们可以通过TypeOf将一个对象转换成Type
t := reflect.TypeOf(1)
fmt.Println(t) // int
首先我们看下Type的组成(下图中我们省略了一些字段)
type Type interface {
// 返回第i个方法,超过范围,则panic
Method(int) Method
// 通过名称获取方法
MethodByName(string) (Method, bool)
// 方法数量
NumMethod() int
// 类型名
Name() string
// 返回包路径(完全路径)
PkgPath() string
// 类似unsafe.Sizeof
Size() uintptr
// 字符串表示,可能包含短包名
String() string
// 类型
Kind() Kind
// 如果该类型实现了u的接口,则返回true
Implements(u Type) bool
// 返回元素类型,如果kind不是slice,channel,map,pointer, 则painc
Elem() Type
// 第几个字段
Field(i int) StructField
FieldByIndex(index []int) StructField
// 根据name返回字段细腻系
FieldByName(name string) (StructField, bool)
FieldByNameFunc(match func(string) bool) (StructField, bool)
// 返回第i个参数类型
In(i int) Type
// 返回array类型的长度,如果不是,则panic
Len() int
// 返回field的数量
NumField() int
// 返回参数的数量
NumIn() int
}
可以看到Type是interface,接下来我们看下TypeOf
func TypeOf(i interface{}) Type {
eface := *(*emptyInterface)(unsafe.Pointer(&i))
return toType(eface.typ)
}
func toType(t *rtype) Type {
if t == nil {
return nil
}
return t
}
golang里面接口类型都是通过emptyInterface来表示的,其中rtype表示变量的类型,word指向数据的地址
type emptyInterface struct {
typ *rtype
word unsafe.Pointer
}
Value
我们可以通过ValueOf将一个类型转换成Value类型
t := reflect.ValueOf(1)
fmt.Println(t) // 1
Value是一个结构体
type Value struct {
// value的type类型
typ *rtype
// 指针指向数据
ptr unsafe.Pointer
flag
}
ValueOf
func ValueOf(i interface{}) Value {
if i == nil {
return Value{}
}
// 从栈逃逸到堆上面
escapes(i)
return unpackEface(i)
}
func unpackEface(i interface{}) Value {
e := (*emptyInterface)(unsafe.Pointer(&i))
// NOTE: don't read e.word until we know whether it is really a pointer or not.
t := e.typ
if t == nil {
return Value{}
}
f := flag(t.Kind())
if ifaceIndir(t) {
f |= flagIndir
}
return Value{t, e.word, f}
}
可以看到ValueOf实际上也是先转成emptyInterface,然后再生成Value
用法
下面我们通过reflect的用法来具体分析下
类型转换
下面的转换是int => interface{} => emptyInterface => Value => Interface => int
v := reflect.ValueOf(1)
vv := v.Interface().(int)
修改数据
x := 1
vv := reflect.ValueOf(&x)
p := vv.Elem()
fmt.Println(p.CanSet()) // true
p.SetInt(2)
fmt.Println(x) // 2
Elem
Elem返回v指针指向的实际变量,如果不是接口或者指针类型,会Panic
func (v Value) Elem() Value {
k := v.kind()
switch k {
case Interface:
var eface interface{}
if v.typ.NumMethod() == 0 {
eface = *(*interface{})(v.ptr)
} else {
eface = (interface{})(*(*interface {
M()
})(v.ptr))
}
x := unpackEface(eface)
if x.flag != 0 {
x.flag |= v.flag.ro()
}
return x
case Ptr:
ptr := v.ptr
if v.flag&flagIndir != 0 {
ptr = *(*unsafe.Pointer)(ptr)
}
// The returned value's address is v's value.
if ptr == nil {
return Value{}
}
tt := (*ptrType)(unsafe.Pointer(v.typ))
typ := tt.elem
fl := v.flag&flagRO | flagIndir | flagAddr
fl |= flag(typ.Kind())
return Value{typ, ptr, fl}
}
panic(&ValueError{"reflect.Value.Elem", v.kind()})
}
Implements
我们可以通过Implements方法判断某些类型是否实现了全部的方法
type Demo struct{}
func (d *Demo) Error() string {
return "demo error"
}
func main() {
r := reflect.TypeOf((*error)(nil)).Elem() // 获取接口的类型
dPtr := reflect.TypeOf(&Demo{})
d := reflect.TypeOf(Demo{})
fmt.Println(dPtr.Implements(r)) // true
fmt.Println(d.Implements(r)) // false
}
具体方法
func (t *rtype) Implements(u Type) bool {
// 如果是nil,则会panic
if u == nil {
panic("reflect: nil type passed to Type.Implements")
}
// 如果不是指针类型,会panic
if u.Kind() != Interface {
panic("reflect: non-interface type passed to Type.Implements")
}
return implements(u.(*rtype), t)
}
func implements(T, V *rtype) bool {
if T.Kind() != Interface {
return false
}
t := (*interfaceType)(unsafe.Pointer(T))
// 如果u的方法数量为0,则返回true
if len(t.methods) == 0 {
return true
}
// 两种方式都一样,通过遍历V和T中的方法来判断是否满足条件,methods是有序的
if V.Kind() == Interface {
v := (*interfaceType)(unsafe.Pointer(V))
i := 0
for j := 0; j < len(v.methods); j++ {
tm := &t.methods[i]
tmName := t.nameOff(tm.name)
vm := &v.methods[j]
vmName := V.nameOff(vm.name)
if vmName.name() == tmName.name() && V.typeOff(vm.typ) == t.typeOff(tm.typ) {
if !tmName.isExported() {
tmPkgPath := tmName.pkgPath()
if tmPkgPath == "" {
tmPkgPath = t.pkgPath.name()
}
vmPkgPath := vmName.pkgPath()
if vmPkgPath == "" {
vmPkgPath = v.pkgPath.name()
}
if tmPkgPath != vmPkgPath {
continue
}
}
if i++; i >= len(t.methods) {
return true
}
}
}
return false
}
v := V.uncommon()
if v == nil {
return false
}
i := 0
vmethods := v.methods()
for j := 0; j < int(v.mcount); j++ {
tm := &t.methods[i]
tmName := t.nameOff(tm.name)
vm := vmethods[j]
vmName := V.nameOff(vm.name)
if vmName.name() == tmName.name() && V.typeOff(vm.mtyp) == t.typeOff(tm.typ) {
if !tmName.isExported() {
tmPkgPath := tmName.pkgPath()
if tmPkgPath == "" {
tmPkgPath = t.pkgPath.name()
}
vmPkgPath := vmName.pkgPath()
if vmPkgPath == "" {
vmPkgPath = V.nameOff(v.pkgPath).name()
}
if tmPkgPath != vmPkgPath {
continue
}
}
if i++; i >= len(t.methods) {
return true
}
}
}
return false
}
方法调用
我们如何通过反射去调用方法
func Sum(a, b int) int {
return a + b
}
func main() {
v := reflect.ValueOf(Sum)
fmt.Println(v.Kind()) // func
fmt.Println(v.Type().NumIn()) // 参数数量2
result := v.Call([]reflect.Value{reflect.ValueOf(1), reflect.ValueOf(2)})
fmt.Println(result[0].Int()) // 3
}
Call
func (v Value) Call(in []Value) []Value {
v.mustBe(Func) // 必须是盘数
v.mustBeExported() // 必须是可导出的
return v.call("Call", in)
}
func (v Value) call(op string, in []Value) []Value {
// 获取type的值
t := (*funcType)(unsafe.Pointer(v.typ))
var (
fn unsafe.Pointer
rcvr Value
rcvrtype *rtype
)
if v.flag&flagMethod != 0 {
rcvr = v
rcvrtype, t, fn = methodReceiver(op, v, int(v.flag)>>flagMethodShift)
} else if v.flag&flagIndir != 0 {
fn = *(*unsafe.Pointer)(v.ptr)
} else {
fn = v.ptr
}
// 检查是否nil
if fn == nil {
panic("reflect.Value.Call: call of nil function")
}
// 判断是否是CallSlice,比如len(arr), in是arr[0], arr[1], arr[2]...
isSlice := op == "CallSlice"
n := t.NumIn()
if isSlice {
if !t.IsVariadic() {
panic("reflect: CallSlice of non-variadic function")
}
if len(in) < n {
panic("reflect: CallSlice with too few input arguments")
}
if len(in) > n {
panic("reflect: CallSlice with too many input arguments")
}
} else {
if t.IsVariadic() {
n--
}
if len(in) < n {
panic("reflect: Call with too few input arguments")
}
if !t.IsVariadic() && len(in) > n {
panic("reflect: Call with too many input arguments")
}
}
for _, x := range in {
if x.Kind() == Invalid {
panic("reflect: " + op + " using zero Value argument")
}
}
for i := 0; i < n; i++ {
if xt, targ := in[i].Type(), t.In(i); !xt.AssignableTo(targ) {
panic("reflect: " + op + " using " + xt.String() + " as type " + targ.String())
}
}
if !isSlice && t.IsVariadic() {
// prepare slice for remaining values
m := len(in) - n
slice := MakeSlice(t.In(n), m, m)
elem := t.In(n).Elem()
for i := 0; i < m; i++ {
x := in[n+i]
if xt := x.Type(); !xt.AssignableTo(elem) {
panic("reflect: cannot use " + xt.String() + " as type " + elem.String() + " in " + op)
}
slice.Index(i).Set(x)
}
origIn := in
in = make([]Value, n+1)
copy(in[:n], origIn)
in[n] = slice
}
nin := len(in)
// 检查参数
if nin != t.NumIn() {
panic("reflect.Value.Call: wrong argument count")
}
// 开始进入准备阶段
nout := t.NumOut()
// 计算参数和返回值锁需要占的空间大小
frametype, _, retOffset, _, framePool := funcLayout(t, rcvrtype)
// Allocate a chunk of memory for frame.
var args unsafe.Pointer
if nout == 0 {
args = framePool.Get().(unsafe.Pointer)
} else {
// 如果有返回参数就不能使用pool,需要单独分配args
args = unsafe_New(frametype)
}
off := uintptr(0)
// 拷贝输入到args
if rcvrtype != nil {
storeRcvr(rcvr, args)
off = ptrSize
}
for i, v := range in {
v.mustBeExported()
targ := t.In(i).(*rtype)
a := uintptr(targ.align)
off = (off + a - 1) &^ (a - 1)
n := targ.size
if n == 0 {
// Not safe to compute args+off pointing at 0 bytes,
// because that might point beyond the end of the frame,
// but we still need to call assignTo to check assignability.
v.assignTo("reflect.Value.Call", targ, nil)
continue
}
addr := add(args, off, "n > 0")
v = v.assignTo("reflect.Value.Call", targ, addr)
if v.flag&flagIndir != 0 {
typedmemmove(targ, addr, v.ptr)
} else {
*(*unsafe.Pointer)(addr) = v.ptr
}
off += n
}
// 开始调用
call(frametype, fn, args, uint32(frametype.size), uint32(retOffset))
// For testing; see TestCallMethodJump.
if callGC {
runtime.GC()
}
var ret []Value
if nout == 0 {
// 清除args,将args放回到pool中
typedmemclr(frametype, args)
framePool.Put(args)
} else {
// Zero the now unused input area of args,
// because the Values returned by this function contain pointers to the args object,
// and will thus keep the args object alive indefinitely.
typedmemclrpartial(frametype, args, 0, retOffset)
// 将返回值写入到ret中
ret = make([]Value, nout)
off = retOffset
for i := 0; i < nout; i++ {
tv := t.Out(i)
a := uintptr(tv.Align())
off = (off + a - 1) &^ (a - 1)
if tv.Size() != 0 {
fl := flagIndir | flag(tv.Kind())
ret[i] = Value{tv.common(), add(args, off, "tv.Size() != 0"), fl}
// Note: this does introduce false sharing between results -
// if any result is live, they are all live.
// (And the space for the args is live as well, but as we've
// cleared that space it isn't as big a deal.)
} else {
// For zero-sized return value, args+off may point to the next object.
// In this case, return the zero value instead.
ret[i] = Zero(tv)
}
off += tv.Size()
}
}
return ret
}
访问结构体私有属性
引入其他包中结构体是无法直接访问其私有属性的,我们可以通过reflect进行访问
// 这个结构体在demo/demo.go中
type Demo struct {
name string
age int8
}
// main.go
package main
import (
"demo"
"fmt"
"reflect"
"unsafe"
)
func main() {
var d demo.Demo
v := reflect.ValueOf(&d)
elem := v.Elem()
fmt.Println(elem.Field(0), elem.Field(1)) // "", 0
}
如果我们想修改私有变量的值怎么变
func change(d *demo.Demo) {
dd := unsafe.Pointer(d)
v := uintptr(0) // 第一个字段,offset为0
p := (*string)(unsafe.Pointer(uintptr(dd) + v))
*p = "a" // 这时name就变成a了
// uintptr(16) 是因为name是string类型,需要占两个字节
age := (*int)(unsafe.Pointer(uintptr(dd) + uintptr(16)))
*age = 11 // age就变成 11了
}