标准库:reflect
官方定义:Package reflect implements run-time reflection, allowing a program to manipulate objects with arbitrary types. The typical use is to take a value with static type interface{} and extract its dynamic type information by calling TypeOf, which returns a Type.
reflect 实现运行时(runtime)反射,允许程序操作任意类型的对象,在运行时访问、检测和修改对象。
go 内部缺少泛型支持,只能通过反射来支持一些动态特性,例如 fmt
等标准库都十分依赖反射机制。
reflect 包抽象出两个最基本的类型表示对象的信息:
Type Interface
Value Struct
API
TypeOf()
使用 TypeOf
函数生成 Type
接口实例,可以看到参数是 interface{}
,直接把空接口转换成 emptyInterface
类型:
func TypeOf(i interface{}) Type {
eface := *(*emptyInterface)(unsafe.Pointer(&i))
return toType(eface.typ)
}
emptyInterface
对应空接口的数据结构,里面都是已经熟悉的 _type
和 data 指针:
type emptyInterface struct {
typ *rtype
word unsafe.Pointer
}
eface.typ
就是 rtype
,toType
只做了一个类型转换:
func toType(t *rtype) Type {
if t == nil {
return nil
}
return t
}
使用 rtype
实现 Type 接口,隐藏的语法是 var x Type = t
。
TypeOf
函数只解析了所有类型公共的 rtype
部分,所有类型都包含 rtype
,如果是 ArrayType 等类型会有一些额外的信息在内存中,反射包(reflect/type.go
)中有相关的定义,所有的基本类型都能找到:
// arrayType represents a fixed array type.
type arrayType struct {
rtype
elem *rtype // array element type
slice *rtype // slice type
len uintptr
}
type funcType struct {
rtype
inCount uint16
outCount uint16 // top bit is set if last input parameter is ...
}
// chanType represents a channel type.
type chanType struct {
rtype
elem *rtype // channel element type
dir uintptr // channel direction (ChanDir)
}
...
ValueOf()
使用 ValueOf
函数生成 Value
实例,参数也是 interface{}
:
func ValueOf(i interface{}) Value {
if i == nil {
return Value{}
}
return unpackEface(i)
}
主要功能都在 unpackEface 函数中:
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.
// 在我们不知道 data 是不是指针之前,不要读取 data
t := e.typ
if t == nil {
return Value{}
}
f := flag(t.Kind())
if ifaceIndir(t) { // 判断是不是指针,kind 必须大于 16,第 5 位才是 1
f |= flagIndir
}
return Value{t, e.word, f}
}
Value 中存储 rtype、word 和标志位 flag。
flag
先看 flag 定义:
type flag uintptr
const (
flagKindWidth = 5 // there are 27 kinds // 用 5 个 bit 表示类型,总共有 27 种类型,5 个 bit 能表示 32 种类型
flagKindMask flag = 1<<flagKindWidth - 1 // 11111
flagStickyRO flag = 1 << 5 // 100000
flagEmbedRO flag = 1 << 6 // 1000000
flagIndir flag = 1 << 7 // 10000000 // 间接位
flagAddr flag = 1 << 8 // 100000000
flagMethod flag = 1 << 9 // 1000000000
flagMethodShift = 10 // 1010
flagRO flag = flagStickyRO | flagEmbedRO // 1100000
)
先计算通过 f := flag(t.Kind())
计算 f,获取的是底层 Kind 的对应数字:
func (t *rtype) Kind() Kind { return Kind(t.kind & kindMask) }
计算 rtype.kind & kindMask 保留前 5 位。
kindMask = (1 << 5) - 1 // 11111 取出前 5 位就是类型
t.kind = 54(110110) & kindMask(11111) 运算结果为二进制 `10110`(2 + 4 + 16) 等于十进制 22 等于 Kind 类型中的 Ptr
ifaceIndir
ifaceIndir()
函数返回类型 t 是直接(Direct)值还是间接(Indirect)值,延伸阅读 Value Parts
- 指针是 Direct
- int 是 Indirect
// ifaceIndir reports whether t is stored indirectly in an interface value.
func ifaceIndir(t *rtype) bool {
return t.kind&kindDirectIface == 0
}
kindDirectIface = 1 << 5 // 100000
指针的 t.kind
是 54 二进制表示为 110110
。
100000
&
110110
=
100000 == 0 // false,不执行 f |= flagIndir
字符串的 t.kind
是 24 二进制表示为 11000
100000
&
011000
=
0 == 0 // true,执行 f |= flagIndir
如果返回第 6 位是 1(数字大于 32),设置 flagIndir
标志位到 flag:
f |= flagIndir
flagIndir 是 flag = 1 << 7 // 10000000
设置 f 的第 8 位为 1,表示当前值是 InDir。
Type Interface
先看 Type 的定义:
type Type interface {
Align() int // 类型对齐后变量在内存中占有的字节数
FieldAlign() int // 如果是 struct 的字段,对齐后占有的字节数
Method(int) Method // 返回方法集里的第 i 个方法
MethodByName(string) (Method, bool) // 通过名称获取方法集中的方法
NumMethod() int // 方法集中导出方法的个数
Name() string // 类型名称
PkgPath() string // 类型所在的路径,例如 encoding/base64
Size() uintptr // 返回类型的大小,与 unsafe.Sizeof 类似
String() string // 类型的字符串表示,实现 fmt.Stringer 接口
Kind() Kind // 返回底层类型
Implements(u Type) bool // 类型是否实现接口 u
AssignableTo(u Type) bool // 类型是否可以赋值给另一个类型 u
ConvertibleTo(u Type) bool // 类型是否可以转换成类型 u
Comparable() bool // 类型是否可以比较
// 下面的函数只有特定类型可以调用
// Methods applicable only to some types, depending on Kind.
// The methods allowed for each kind are:
//
// Int*, Uint*, Float*, Complex*: Bits
// Array: Elem, Len
// Chan: ChanDir, Elem
// Func: In, NumIn, Out, NumOut, IsVariadic.
// Map: Key, Elem
// Ptr: Elem
// Slice: Elem
// Struct: Field, FieldByIndex, FieldByName, FieldByNameFunc, NumField
// Bits returns the size of the type in bits.
// It panics if the type's Kind is not one of the
// sized or unsized Int, Uint, Float, or Complex kinds.
Bits() int // 类型所占据的位数
ChanDir() ChanDir // 返回 channel 的方向
IsVariadic() bool // 返回函数类型是否有可变参数
Elem() Type // 返回内部子元素的类型
Field(i int) StructField // 返回结构体类型的第 i 个字段
FieldByIndex(index []int) StructField // 返回嵌套的结构体字段 []int{1, 1}
FieldByName(name string) (StructField, bool) // 通过字段名获取字段
FieldByNameFunc(match func(string) bool) (StructField, bool) // 返回名称符号 func 函数的字段
In(i int) Type // 获取函数类型第 i 个参数的类型
Key() Type // 返回 map 的 key 类型
Len() int // 返回 Array 的长度,只能 Array 调用
NumField() int // 返回结构体的字段数量,只能 Struct 调用
NumIn() int // 返回函数类型输入参数个数
NumOut() int // 返回函数类型返回值个数
Out(i int) Type // 返回函数类型第 i 个参数的类型
common() *rtype // 表示公共的类型信息
uncommon() *uncommonType // 表示独有的类型信息,例如 Array 有自己独特的类型信息,下面会细讲
}
定义中有很多函数签名,反应类型系统的方方面面,在 reflect
包内部的非导出结构体 rtype
实现 Type
接口,其结构与 runtime/type.go
中的 _type
结构体字段保持一致。
rtype
// rtype must be kept in sync with ../runtime/type.go:/^type._type.
type rtype struct {
size uintptr
ptrdata uintptr // number of bytes in the type that can contain pointers
hash uint32 // hash of type; avoids computation in hash tables
tflag tflag // extra type information flags
align uint8 // alignment of variable with this type
fieldAlign uint8 // alignment of struct field with this type
kind uint8 // enumeration for C
alg *typeAlg // algorithm table
gcdata *byte // garbage collection data
str nameOff // string form
ptrToThis typeOff // type for pointer to this type, may be zero
}
Type 接口实现 String()
函数,满足 fmt.stringer
接口,在使用 fmt.Pringln
打印时,会调用 String
函数输出结果。
Elem()
返回内部元素的类型,如果不是容器类型或指针会报错。
// Elem returns a type's element type.
// It panics if the type's Kind is not Array, Chan, Map, Ptr, or Slice.
返回类型对应的元素类型,例如数组等容器中元素的类型,指针指向的元素类型。
Implements()
ConvertibleTo()
AssignableTo()
Value Struct
Value
表示 interface{}
中存储的实际变量,提供实际变量的各种信息。
type Value struct {
typ *rtype
ptr unsafe.Pointer // 数据指针
flag
}
Value
也存了一份 rtype 存储对象的类型信息。
CanAddr()
// CanAddr reports whether the value's address can be obtained with Addr.
// Such values are called addressable. A value is addressable if it is
// an element of a slice, an element of an addressable array,
// a field of an addressable struct, or the result of dereferencing a pointer.
// If CanAddr returns false, calling Addr will panic.
func (v Value) CanAddr() bool {
// flag & 100000000 != 0
return v.flag&flagAddr != 0
}
判断值的地址能不能通过 Addr()
获取,这样的值称为 addressable
,只有 slice
中的元素,数组中的元素,struct
的字段,指针指向的实例(Elem
函数读取的值),才是 addressable
的,
简单理解就是能不能获取一个实例的地址,下面来试一试。
i := User{Name: "test"}
fmt.Println(reflect.ValueOf(i).CanAddr()) // false
// 指针
fmt.Printf("%p\n", &i) // 0xc000092000
fmt.Println(reflect.ValueOf(&i).CanAddr()) // false
fmt.Println(reflect.ValueOf(&i).Elem().CanAddr()) // true
fmt.Printf("%p\n", reflect.ValueOf(&i).Elem().Addr().Interface()) // 0xc000092000,重新取出值的地址,可以看到地址是一样的
fmt.Println(reflect.ValueOf(&i).Elem().Field(1).CanAddr()) // true
fmt.Println(reflect.ValueOf(&i).Elem().Field(1).Addr()) // 0xc000092010
可以看到,在通过 Elem()
取到真实的实例后,通过 Addr()
可以拿到实例的地址。
Addr()
// Addr returns a pointer value representing the address of v.
// It panics if CanAddr() returns false.
// Addr is typically used to obtain a pointer to a struct field or slice element in order to call a method that requires a pointer receiver.
func (v Value) Addr() Value {
if v.flag&flagAddr == 0 {
panic("reflect.Value.Addr of unaddressable value")
}
return Value{v.typ.ptrTo(), v.ptr, v.flag.ro() | flag(Ptr)}
}
返回 Value 的指针地址:必须是结构体字段、slice 元素、数组元素或指针值。
Indirect()
返回 Value
指针指向的真实值,本质上就是返回指向的内存区域。
// Indirect returns the value that v points to.
// 如果 v 是空指针,返回零值 Value
// 如果不是指针类型,直接返回
func Indirect(v Value) Value {
if v.Kind() != Ptr {
return v
}
return v.Elem()
}
为什么需要 Indirect?
Elem()
Elem 返回 Value 包含指针指向的内存区域对应的值。
Elem 返回 Value 中包含的值(也就是 eface 中的 data),如果类型不是 Interface 或者指针,会 panic
// Elem returns the value that the interface v contains
// or that the pointer v points to.
// It panics if v's Kind is not Interface or Ptr.
// It returns the zero Value if v is nil.
func (v Value) Elem() Value {
k := v.kind()
switch k {
case Interface: // 目测很少有情况会进这个 case,现在还不太明确,使用的地方也很少(绝大多数接口都有动态值,kind 都不是 interface)
var eface interface{} // 初始化 eface
if v.typ.NumMethod() == 0 {
eface = *(*interface{})(v.ptr)
} else {
eface = (interface{})(*(*interface {
M()
})(v.ptr))
}
x := unpackEface(eface) // unpackEface converts the empty interface i to a Value.
if x.flag != 0 {
x.flag |= v.flag.ro() // ???
}
return x
case Ptr: // 主要关注 Ptr Case
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 // 取出指向类型的 type
fl := v.flag&flagRO | flagIndir | flagAddr // 设置标志位 flagRO(1100000) 情况前 5 位 kind,设置第 8 位 flagIndir(10000000) 和第 9 位 flagAddr(100000000) 表示可取址 CanAddr
fl |= flag(typ.Kind()) // 设置前 5 位 kind
return Value{typ, ptr, fl} // 返回新的 Value 实例
}
panic(&ValueError{"reflect.Value.Elem", v.kind()})
}
测试:
i := User{Name: "test"}
fmt.Println(reflect.ValueOf(&i).Elem()) // {test}
fmt.Println(reflect.ValueOf(&i).Elem().Elem()) // panic: reflect: call of reflect.Value.Elem on struct Value
Call()
调用函数,本身需要是函数类型
Type()
IsValid()
报告 v 是否为零值,此时返回 false。
func (v Value) IsValid() bool {
return v.flag != 0
}