前提
介绍 go 中的反射之前,我们需要先介绍一下 interface。go 中的 interface 分类两种,eface 和 iface。
eface
go 中所有的类型的数据都可以转成 eface ,对应 reflect 中的 emptyInterface
type eface struct {
_type *_type // 类型信息
data unsafe.Pointer // 指向的值
}
type _type struct {
size uintptr // 类型占用内容大小
ptrdata uintptr // 包含所有指针的内存前缀的大小
hash uint32 // 类型 hash
tflag tflag // 标记位,主要用于反射
align uint8 // 对齐字段
fieldAlign uint8 // 当前结构字段的对齐字节数
kind uint8 // 基础类型枚举值
equal func(unsafe.Pointer, unsafe.Pointer) bool // 比较两个形参对应对象的类型是否相等
gcdata *byte // GC 类型的数据
str nameOff // 类型名称字符串在二进制文件段中的偏移量
ptrToThis typeOff // 类型元信息指针在二进制文件段中的偏移量
}
rutime.runtime2.go 文件
eface 主要包括类型信息和值的指针,非常好理解。其中 data 指针指向的内存包含了类型和值的信息,也就是说 data 指针指向了 eface 本身
iface
iface 主要用来表示实现了 interface 的数据,对应 reflect 中的 nonEmptyInterface
type iface struct {
tab *itab
data unsafe.Pointer
}
type itab struct {
inter *interfacetype // 接口静态类型, 接口的抽象表示,也就是静态的接口,不是实际的 struct
_type *_type // 实际类型
hash uint32 // copy of _type.hash. Used for type switches. 和 _type 中的 hash 一样,用来类型断言
_ [4]byte
fun [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter. 接口实现的函数,跟接口类型保持一致
}
type interfacetype struct {
typ _type // interface 的类型
pkgpath name // 包信息
mhdr []imethod // interface 的 method 抽象,不是实际的被实现的 method
}
runtime.runtime2.go 文件
iface 类型字段中包含类型的动态和静态信息,以及包和一些函数信息。
eface 和 iface 的转换
我们知道 任何指针都可以转换成 unsafe.Pointer, unsafe.Pointer 也可以转换成任意的指针
在使用 unsafe.Pointer 进行转换时,不会进行类型检查,只是粗暴的把类型替换,指针的地址和实际的值都不会变化。
假如使用 unsafe.Pointer 进行转换的两种数据类型差异很大,转换之后使用的时候也会出错。这也是叫 unsafe 的原因。
对于 eface 和 iface:假如 eface 的类型是 Interface,那么 eface 中的 _type 字段和 iface 中的 inter 是可以通过 unsafe.Pointer 进行转换的。
首先,两者都是指针,所以可以转成 unsafe.Pointer。其次如果我们仔细观察,会发现 inter 的第一个字段类型是 _type
所以可以进行转换,并且转换之后 interfacetype 的其他字段也是有值的。说明 eface 的 type 类型并不是只有显示定义的字段,还存在一些隐藏的字段,根据类型的不同,拥有不同的字段。
reflect.Type
首先现看一下 reflect.TypeOf 代码,Type 是 interface , *rtype 实现了 Type
func TypeOf(i interface{}) Type {
// 转成 eface
eface := *(*emptyInterface)(unsafe.Pointer(&i))
return toType(eface.typ)
}
type emptyInterface struct {
typ *rtype
word unsafe.Pointer
}
上面我们介绍 eface 的时候说了, runtime.eface 和 reflect.emptyInterface 是一样的,所以这里直接通过 unsafe.Pointer 转换成 emptyInterface ,然后取其中的 typ 属性。 reflect.Type 相关的操作都是基于 *rtype
go 中的基本类型总共 26 种,在反射中也有枚举体现
type Kind uint
const (
Invalid Kind = iota
Bool
Int
Int8
Int16
Int32
Int64
Uint
Uint8
Uint16
Uint32
Uint64
Uintptr
Float32
Float64
Complex64
Complex128
Array
Chan
Func
Interface
Map
Ptr
Slice
String
Struct
UnsafePointer
)
在操作 reflect.Type 时很多类型都有对应的专属 type,不过只有 Interface 类型的数据会有一些特殊逻辑。
下面是各种不同类型对应的不同结构体
type arrayType struct {
rtype
elem *rtype // array element type
slice *rtype // slice type
len uintptr
}
type chanType struct {
rtype
elem *rtype // channel element type
dir uintptr // channel direction (ChanDir) chan 的方向
}
type funcType struct {
rtype
inCount uint16 // 输入参数
outCount uint16 // top bit is set if last input parameter is ... 输出参数
}
type structType struct {
rtype
pkgPath name
fields []structField // sorted by offset
}
type ptrType struct {
rtype // 指针的类型
elem *rtype // pointer element (pointed at) type 指针指向的元素的类型(静态类型)
}
type sliceType struct {
rtype
elem *rtype // slice element type
}
type mapType struct {
rtype
key *rtype // map key type
elem *rtype // map element (value) type
bucket *rtype // internal bucket structure
// function for hashing keys (ptr to key, seed) -> hash
hasher func(unsafe.Pointer, uintptr) uintptr
keysize uint8 // size of key slot
valuesize uint8 // size of value slot
bucketsize uint16 // size of bucket
flags uint32
}
type interfaceType struct {
rtype
pkgPath name // import path
methods []imethod // sorted by hash
}
以上所有的类型都可以通过 *rtype 转换
下面我们通过分析 reflect.Type 的几个函数,窥一斑而见全豹,来聊一聊反射是怎么取 _type 中的数据,进一步了解各个类型的不同之处。
Method
获取函数需要区分是否为 Interface 类型,假如是 Interface 类型,把 *rtype 转成 *interfaceType 。因为 interfaceType 类型中包含了函数信息字段,所以直接根据索引取一下就行了,这块逻辑是 easy case。
func (t *interfaceType) Method(i int) (m Method) {
if i < 0 || i >= len(t.methods) {
return
}
p := &t.methods[i]
// 构造 method
pname := t.nameOff(p.name)
m.Name = pname.name()
if !pname.isExported() {
m.PkgPath = pname.pkgPath()
if m.PkgPath == "" {
m.PkgPath = t.pkgPath.name()
}
}
m.Type = toType(t.typeOff(p.typ))
m.Index = i
return
}
type imethod struct {
name nameOff // name of method
typ typeOff // .(*FuncType) underneath 指向 method 的 type 类型
}
interface 类型取 method 的代码
假如是其他类型,就需要导出函数信息了。因为函数信息并没有显示的以字段的形式,所以需要组成临时的结构体取一下
func (t *rtype) exportedMethods() []method {
// 获取存储函数列表的 uncommon
ut := t.uncommon()
if ut == nil {
return nil
}
// 获取导出的函数列表
return ut.exportedMethods()
}
func (t *rtype) uncommon() *uncommonType {
if t.tflag&tflagUncommon == 0 {
return nil
}
switch t.Kind() {
case Struct:
return &(*structTypeUncommon)(unsafe.Pointer(t)).u
case Ptr:
type u struct {
ptrType
u uncommonType
}
return &(*u)(unsafe.Pointer(t)).u
case Func:
type u struct {
funcType
u uncommonType
}
return &(*u)(unsafe.Pointer(t)).u
case Slice:
type u struct {
sliceType
u uncommonType
}
return &(*u)(unsafe.Pointer(t)).u
case Array:
type u struct {
arrayType
u uncommonType
}
return &(*u)(unsafe.Pointer(t)).u
case Chan:
type u struct {
chanType
u uncommonType
}
return &(*u)(unsafe.Pointer(t)).u
case Map:
type u struct {
mapType
u uncommonType
}
return &(*u)(unsafe.Pointer(t)).u
case Interface:
type u struct {
interfaceType
u uncommonType
}
return &(*u)(unsafe.Pointer(t)).u
default:
type u struct {
rtype
u uncommonType
}
return &(*u)(unsafe.Pointer(t)).u
}
}
func (t *uncommonType) exportedMethods() []method {
if t.xcount == 0 {
return nil
}
return (*[1 << 16]method)(add(unsafe.Pointer(t), uintptr(t.moff), "t.xcount > 0"))[:t.xcount:t.xcount]
}
uncommon() 函数
上面的 uncommonType 是存储函数信息的结构体
// 储存函数的类型
type uncommonType struct {
pkgPath nameOff // import path; empty for built-in types like int, string
mcount uint16 // number of methods
xcount uint16 // number of exported methods 可导出的函数数量
moff uint32 // offset from this uncommontype to [mcount]method 函数数组的偏移量
_ uint32 // unused
}
type method struct {
name nameOff // name of method
mtyp typeOff // method type (without receiver) 函数类型
ifn textOff // fn used in interface call (one-word receiver)
tfn textOff // fn used for normal method call
}
uncommonType 类型
有了 uncommonType 就可以获取函数的类型信息了
func (t *rtype) Method(i int) (m Method) {
// 假如是 interface
if t.Kind() == Interface {
tt := (*interfaceType)(unsafe.Pointer(t))
return tt.Method(i)
}
// 获取导出的函数列表
methods := t.exportedMethods()
if i < 0 || i >= len(methods) {
panic("reflect: Method index out of range")
}
p := methods[i]
// 取函数的名字
pname := t.nameOff(p.name)
m.Name = pname.name()
fl := flag(Func)
mtyp := t.typeOff(p.mtyp)
ft := (*funcType)(unsafe.Pointer(mtyp))
in := make([]Type, 0, 1+len(ft.in()))
// 先把当前类型加到 in 里
in = append(in, t)
// 再把参数类型加到 in 里
for _, arg := range ft.in() {
in = append(in, arg)
}
// 返回值类型数组
out := make([]Type, 0, len(ft.out()))
for _, ret := range ft.out() {
out = append(out, ret)
}
// 构建函数
mt := FuncOf(in, out, ft.IsVariadic())
m.Type = mt
tfn := t.textOff(p.tfn)
fn := unsafe.Pointer(&tfn)
m.Func = Value{mt.(*rtype), fn, fl}
m.Index = i
return m
}
*rtype 的 Method 方法
从上面的代码可以看出来,在取到 method 之后,会根据 method 转成 Method,而 Method 会把接收者当做第一个入参,所以需要 FuncOf 重新组建一个 *rtype, 当做 Method 的 Type。所以假如想使用 Method.Func 调用 Call 方法时,需要把接收者当做第一个参数传入。
FuncOf 方法比较长,这里就不贴代码了。简单说明一下主要逻辑
- 新建一个
funcType - 赋值入参和出参,以及
hash值(夹杂着可变函数的判断) - 从缓存中找相同类型的 funcType,有就返回
- 从链接器等中查找,不管有没有都会放进缓存然后返回
Field
获取 Field 的类型必须是 Struct
func (t *rtype) Field(i int) StructField {
if t.Kind() != Struct {
panic("reflect: Field of non-struct type " + t.String())
}
// 转成 structType
tt := (*structType)(unsafe.Pointer(t))
return tt.Field(i)
}
*rtype 的 Field 方法
可以看到先判断类型,然后转成 *structType
type structType struct {
rtype
pkgPath name
fields []structField // sorted by offset
}
structType 结构体
structType获取 Field 的方法非常简单
func (t *structType) Field(i int) (f StructField) {
if i < 0 || i >= len(t.fields) {
panic("reflect: Field index out of bounds")
}
p := &t.fields[i]
f.Type = toType(p.typ)
f.Name = p.name.name()
f.Anonymous = p.embedded()
if !p.name.isExported() {
f.PkgPath = t.pkgPath.name()
}
if tag := p.name.tag(); tag != "" {
f.Tag = StructTag(tag)
}
f.Offset = p.offset()
f.Index = []int{i}
return
}
Implements
func (t *rtype) Implements(u Type) bool {
if u == nil {
panic("reflect: nil type passed to Type.Implements")
}
if u.Kind() != Interface {
panic("reflect: non-interface type passed to Type.Implements")
}
return implements(u.(*rtype), t)
}
*rtype 的 Implements 方法
这个方法含义是:t 是否实现了 u
上面要求 u 的类型必须是 Interface,在 go 中,我们一般这么获取 u 这种类型: reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
声明一个空的 interface 指针,然后取它的 Elem。为什么这种方法可以取到呢,就需要看一下 Elem 方法了
func (t *rtype) Elem() Type {
switch t.Kind() {
case Array:
tt := (*arrayType)(unsafe.Pointer(t))
return toType(tt.elem)
case Chan:
tt := (*chanType)(unsafe.Pointer(t))
return toType(tt.elem)
case Map:
tt := (*mapType)(unsafe.Pointer(t))
return toType(tt.elem)
case Ptr:
tt := (*ptrType)(unsafe.Pointer(t))
return toType(tt.elem)
case Slice:
tt := (*sliceType)(unsafe.Pointer(t))
return toType(tt.elem)
}
panic("reflect: Elem of invalid type " + t.String())
}
*rtype 的 Elem 方法
按照上面的代码,我们知道 t 现在的 Kind 是指针,所以转成了 ptrType,然后取 ptrType.elem 字段作为类型。所以 elem字段指向的应该是静态类型。以上 Array、Chan 等等的 elem 都是静态类型。
接着 implements 的逻辑。implements 的方法主要有两个分支判断,参数顺序有点变化,下面代码里的 V 其实 t,下面代码里的 t 其实是 u
-
假如 t 是
Interface类型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) // 函数名称和类型一样就行 // 得保证相同的入参和出参的函数用的一个 rtype 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 } -
假如不是
Interface类型// 不是 interface,判断实际的函数 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 } } }
我们可以看到上面两块代码的逻辑一致且简单,都是判断类型和名称是否一致,假如是非导出的函数需要再判断包名是否一致
AssignableTo
func (t *rtype) AssignableTo(u Type) bool {
if u == nil {
panic("reflect: nil type passed to Type.AssignableTo")
}
uu := u.(*rtype)
// 判断类型
return directlyAssignable(uu, t) || implements(uu, t)
}
*rtype 的 AssignableTo 方法
含义:t 是否可以赋值给 u
是否实现的逻辑我们已经看过了,直接看 directlyAssignable 的逻辑
func directlyAssignable(T, V *rtype) bool {
// x's type V is identical to T?
if T == V {
return true
}
// Otherwise at least one of T and V must not be defined
// and they must have the same kind.
if T.hasName() && V.hasName() || T.Kind() != V.Kind() {
return false
}
if T.Kind() == Chan && specialChannelAssignability(T, V) {
return true
}
// x's type T and V must have identical underlying types.
return haveIdenticalUnderlyingType(T, V, true)
}
reflect.type.go 中的 directlyAssignable 方法
解释一下上面的逻辑:
*rtype相等,可赋值Kind必须相等,并且其中一个类型的 name 必须为空- 类型为
chan时,需要判断Elem和chan的方向
关于类型的名称,有一些基本类型是有名称,一些没有
- bool - Complex128 为 1-16 有name
- array 17 无 name
- chan 50-18 无 name
- func 为 51-19 无 name
- interface 20 有 name
- map 53-21 无 name
- ptr 54-22 无 name
- slice 23 无 name
- string 24 有 name
- struct 25 匿名的无 name,其他的有
- unsafe.Pointer 58-26 有
以 type 关键字声明的类型都是有 name 的,所以 struct 类型的匿名数据才会没有 name
接着看 haveIdenticalUnderlyingType 方法
if Bool <= kind && kind <= Complex128 || kind == String || kind == UnsafePointer {
return true
}
// Composite types.
switch kind {
case Array:
return T.Len() == V.Len() && haveIdenticalType(T.Elem(), V.Elem(), cmpTags)
case Chan:
return V.ChanDir() == T.ChanDir() && haveIdenticalType(T.Elem(), V.Elem(), cmpTags)
case Func:
t := (*funcType)(unsafe.Pointer(T))
v := (*funcType)(unsafe.Pointer(V))
if t.outCount != v.outCount || t.inCount != v.inCount {
return false
}
for i := 0; i < t.NumIn(); i++ {
if !haveIdenticalType(t.In(i), v.In(i), cmpTags) {
return false
}
}
for i := 0; i < t.NumOut(); i++ {
if !haveIdenticalType(t.Out(i), v.Out(i), cmpTags) {
return false
}
}
return true
case Interface:
t := (*interfaceType)(unsafe.Pointer(T))
v := (*interfaceType)(unsafe.Pointer(V))
if len(t.methods) == 0 && len(v.methods) == 0 {
return true
}
return false
case Map:
return haveIdenticalType(T.Key(), V.Key(), cmpTags) && haveIdenticalType(T.Elem(), V.Elem(), cmpTags)
case Ptr, Slice:
return haveIdenticalType(T.Elem(), V.Elem(), cmpTags)
case Struct:
t := (*structType)(unsafe.Pointer(T))
v := (*structType)(unsafe.Pointer(V))
if len(t.fields) != len(v.fields) {
return false
}
if t.pkgPath.name() != v.pkgPath.name() {
return false
}
for i := range t.fields {
tf := &t.fields[i]
vf := &v.fields[i]
if tf.name.name() != vf.name.name() {
return false
}
if !haveIdenticalType(tf.typ, vf.typ, cmpTags) {
return false
}
if cmpTags && tf.name.tag() != vf.name.tag() {
return false
}
if tf.offsetEmbed != vf.offsetEmbed {
return false
}
}
return true
}
其中简单类型只要 kind 一致就可以,复杂类型各有各的判断,不过逻辑都很简单
reflect.Value
reflect.Value 主要通过 reflect.ValueOf 获取。reflect.ValueOf 主要是调用 unpackEface方法开箱成 Value
func unpackEface(i interface{}) Value {
// 转成 eface
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}
}
func ifaceIndir(t *rtype) bool {
return t.kind&kindDirectIface == 0
}
kindDirectIface = 1 << 5 // 32
unpackEface 函数
跟 reflect.TypeOf 很像,都是先转成 emptyInterface。这里有一个逻辑 if ifaceIndir(t) { f |= flagIndir},假如 t 的原始 kind 位于 32 等于 0,那么就是间接的。大部分类型都是间接的,只有以下几种类型为直接的:
- map
- chan
- func
- ptr
- unsafe.Pointer
对于直接或者间接的类型,在装箱成 Interface 的时候(也就是调用 Interface() 方法时)会有不同的处理逻辑。
同分析 reflect.Type 一样,我们分析 reflect.Value 也通过几个关键方法,来看一看 reflect.Value 的实现
Method
获取 method 的 Value
func (v Value) Method(i int) Value {
if v.typ == nil {
panic(&ValueError{"reflect.Value.Method", Invalid})
}
// 本身就是个 method
if v.flag&flagMethod != 0 || uint(i) >= uint(v.typ.NumMethod()) {
panic("reflect: Method index out of range")
}
if v.typ.Kind() == Interface && v.IsNil() {
panic("reflect: Method on nil interface value")
}
// 加上函数相关的 flag 补码
fl := v.flag & (flagStickyRO | flagIndir) // Clear flagEmbedRO
fl |= flag(Func)
fl |= flag(i)<<flagMethodShift | flagMethod
// 这里的 type 并不是 Method 的 type, 而是方法接收者的 type
return Value{v.typ, v.ptr, fl}
}
Value 的 Method 方法
Method 方法的代码很简单,就是把 flag 加上一些标志位。type 和 ptr 都没有变化,这么做的意义会 Call 方法中了解到。
Call
func (v Value) Call(in []Value) []Value {
v.mustBe(Func)
v.mustBeExported()
return v.call("Call", in)
}
Value 的 Call 方法
我们看到 Call 的实现主要是依赖 call 方法, call 方法刚开始的逻辑是这样的:
func (v Value) call(op string, in []Value) []Value {
// 不管 v.type 是啥,先转成 funcType,反正可以转
t := (*funcType)(unsafe.Pointer(v.typ))
var (
fn unsafe.Pointer
rcvr Value
rcvrtype *rtype
)
// v 其实是接收者
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
}
假如是使用 Method 方法获取到的 Value,需要 methodReceiver 方法处理一下。
func methodReceiver(op string, v Value, methodIndex int) (rcvrtype *rtype, t *funcType, fn unsafe.Pointer) {
i := methodIndex
if v.typ.Kind() == Interface {
// 先转成 接口类型
tt := (*interfaceType)(unsafe.Pointer(v.typ))
if uint(i) >= uint(len(tt.methods)) {
panic("reflect: internal error: invalid method index")
}
// 取接口里的 method
m := &tt.methods[i]
if !tt.nameOff(m.name).isExported() {
panic("reflect: " + op + " of unexported method")
}
// 转成 iface
iface := (*nonEmptyInterface)(v.ptr)
if iface.itab == nil {
panic("reflect: " + op + " of method on nil interface value")
}
// 动态类型的 type
rcvrtype = iface.itab.typ
// 动态类型指向的 fn 指针
fn = unsafe.Pointer(&iface.itab.fun[i])
// 静态类型的 funcType, 因为和动态类型的一样
t = (*funcType)(unsafe.Pointer(tt.typeOff(m.typ)))
} else {
rcvrtype = v.typ
// 可导出的方法集合
ms := v.typ.exportedMethods()
if uint(i) >= uint(len(ms)) {
panic("reflect: internal error: invalid method index")
}
m := ms[i]
if !v.typ.nameOff(m.name).isExported() {
panic("reflect: " + op + " of unexported method")
}
ifn := v.typ.textOff(m.ifn)
fn = unsafe.Pointer(&ifn)
t = (*funcType)(unsafe.Pointer(v.typ.typeOff(m.mtyp)))
}
return
}
type method struct {
name nameOff // name of method
mtyp typeOff // method type (without receiver) 函数类型
ifn textOff // fn used in interface call (one-word receiver)
tfn textOff // fn used for normal method call
}
methodReceiver 方法
methodReceiver 会获取对应 method 的类型、指针以及接收者的类型。
call 方法接着会做一些可变参数的判断以及组合传入的参数值,然后调用 runtime.call (实际是 runtime.reflectcall)
call(frametype, fn, args, uint32(frametype.size), uint32(retOffset))
上面的方法才会真正的调用函数。然后把返回值组合成 []Value 返回,返回值的组合逻辑主要涉及指针地址的位移操作,这里就不累述了。
Convert
func (v Value) Convert(t Type) Value {
if v.flag&flagMethod != 0 {
v = makeMethodValue("Convert", v)
}
op := convertOp(t.common(), v.typ)
if op == nil {
panic("reflect.Value.Convert: value of type " + v.typ.String() + " cannot be converted to type " + t.String())
}
return op(v, t)
}
Value 的 Convert 函数
表示 v 转成类型为 t 的 Value。reflect.TypeOf().ConvertibleTo 也是调用 convertOp 来实现的。包括 brom 中 model下字段的反射赋值也是调用的这个方法。但是这个方法有在数字转成字符串的时候会有问题。
func convertOp(dst, src *rtype) func(Value, Type) Value {
switch src.Kind() {
case Int, Int8, Int16, Int32, Int64:
switch dst.Kind() {
case Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
return cvtInt
case Float32, Float64:
return cvtIntFloat
case String:
return cvtIntString
}
case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
switch dst.Kind() {
case Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
return cvtUint
case Float32, Float64:
return cvtUintFloat
case String:
return cvtUintString
}
case Float32, Float64:
switch dst.Kind() {
case Int, Int8, Int16, Int32, Int64:
return cvtFloatInt
case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
return cvtFloatUint
case Float32, Float64:
return cvtFloat
}
case Complex64, Complex128:
switch dst.Kind() {
case Complex64, Complex128:
return cvtComplex
}
case String:
if dst.Kind() == Slice && dst.Elem().PkgPath() == "" {
switch dst.Elem().Kind() {
case Uint8:
return cvtStringBytes
case Int32:
return cvtStringRunes
}
}
case Slice:
if dst.Kind() == String && src.Elem().PkgPath() == "" {
switch src.Elem().Kind() {
case Uint8:
return cvtBytesString
case Int32:
return cvtRunesString
}
}
case Chan:
if dst.Kind() == Chan && specialChannelAssignability(dst, src) {
return cvtDirect
}
}
// dst and src have same underlying type.
if haveIdenticalUnderlyingType(dst, src, false) {
return cvtDirect
}
// dst and src are non-defined pointer types with same underlying base type.
if dst.Kind() == Ptr && dst.Name() == "" &&
src.Kind() == Ptr && src.Name() == "" &&
haveIdenticalUnderlyingType(dst.Elem().common(), src.Elem().common(), false) {
return cvtDirect
}
if implements(dst, src) {
if src.Kind() == Interface {
return cvtI2I
}
return cvtT2I
}
return nil
}
我们先看数字转成字符串,拿其中的 cvtIntString 看一下
func cvtIntString(v Value, t Type) Value {
return makeString(v.flag.ro(), string(v.Int()), t)
}
func makeString(f flag, v string, t Type) Value {
ret := New(t).Elem()
ret.SetString(v)
ret.flag = ret.flag&^flagAddr | f
return ret
}
这里直接把 v.Int() 转成了 string,很明显是错误的,在 go 中是不能把 int 直接转成 string 的,我们一般都是调用 strconv.Itoa() 转成十进制的数字。有兴趣的可以看一下 strconv.Itoa(),相当于把数字,一点一点拆成 string
其他类型的转换应该都是正常的,先判断类型能否转换,然后返回不同的转换函数
Elem
接下来看一下 Elem 方法,这个方法主要针对 ptr 和 interface
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() // 害怕在类型转换的时候转成 eface,丢失了一些类型信息
})(v.ptr))
}
x := unpackEface(eface)
if x.flag != 0 {
x.flag |= v.flag.ro()
}
// interface 实际的类型
return x
case Ptr:
ptr := v.ptr
// 指针的指针, 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
// 更改 flag
fl := v.flag&flagRO | flagIndir | flagAddr
fl |= flag(typ.Kind())
return Value{typ, ptr, fl}
}
panic(&ValueError{"reflect.Value.Elem", v.kind()})
}
我们假如类型为 ptr 的逻辑:
- 先转成
ptrType - 取
ptrType.elem元素类型作为新Value的类型,我们知道elem指向的是元素静态类型 - 然后
flag位或上一些标志位
这里有一个 if 判断,if v.flag&flagIndir != 0 {ptr = *(*unsafe.Pointer)(ptr)} 我们没有说到。什么时候会进入到这个 if 逻辑里呢?
现在假如数据为指针的指针,然后调用 Elem 之后,flag 上就带上了 flagIndir 的标志位,我们发现只有 typ 和 flag 变了, ptr 没变,也就是说 ptr 现在还是指向指针的指针。假如现在把调用过 Elem 的 Value 再调用一次,这个时候就会进入到上面的 if 逻辑块里了。
if 逻辑块里做了什么呢?把 ptr 转成指针的指针,然后取值,就获取到了指针。也就是说现在 ptr 是一个普通的指向值的指针了。记住这块逻辑,在 Value 装箱成 interface 的时候,我们会介绍到。
假如类型为 Interface 的逻辑:
- 转成空的
interface,假如有函数,需要转成一个带M()方法的空interface,这里应该是害怕丢失掉类型信息(只能这么解释了) - 把空的
interface开箱成Value,这里的Value就变成动态类型了
Interface
Interface 主要返回 Value 的实际的值
func (v Value) Interface() (i interface{}) {
return valueInterface(v, true)
}
func valueInterface(v Value, safe bool) interface{} {
if v.flag == 0 {
panic(&ValueError{"reflect.Value.Interface", Invalid})
}
if safe && v.flag&flagRO != 0 {
panic("reflect.Value.Interface: cannot return value obtained from unexported field or method")
}
if v.flag&flagMethod != 0 {
v = makeMethodValue("Interface", v)
}
if v.kind() == Interface {
// Special case: return the element inside the interface.
// Empty interface has one layout, all interfaces with
// methods have a second layout.
if v.NumMethod() == 0 {
return *(*interface{})(v.ptr)
}
return *(*interface {
M()
})(v.ptr)
}
return packEface(v)
}
主要是调用 packEface 方法,把 Value 装箱成 Interface
func packEface(v Value) interface{} {
t := v.typ
var i interface{}
e := (*emptyInterface)(unsafe.Pointer(&i))
switch {
// 间接内存
case ifaceIndir(t):
if v.flag&flagIndir == 0 {
panic("bad indir")
}
ptr := v.ptr
// 说明可寻址
if v.flag&flagAddr != 0 {
c := unsafe_New(t)
typedmemmove(t, c, ptr)
// 搞一个新的地址赋值
ptr = c
}
// 不可寻址,说明就是个值的副本, 直接赋值就行
e.word = ptr
case v.flag&flagIndir != 0:
e.word = *(*unsafe.Pointer)(v.ptr)
default:
// Value is direct, and so is the interface. 就是个指针
e.word = v.ptr
}
e.typ = t
return i
}
假如是间接地址:
- 可寻址,
new一个新指针,把ptr的值拷贝到新指针,然后赋值给e.word。更改返回的interface不会影响到原值 - 不可寻址,说明就是个值复本,直接赋值
e.word
直接地址,并且标志位里含有 flagIndir 间接标志:
- 转成指针的指针,然后取值成指针,赋值给
e.word
这里有一个问题,直接地址的类型,比如 map。我们声明一个 map 的指针指针 A,然后对 A 做 B:=reflect.ValueOf(A).Elem().Elem() 操作,根据 Elem 代码,我们了解到现在的 B 的标志位里有 flagIndir 标志,并且 B 现在的 ptr 是指向 map 的指针,因为第二次 Elem 的时候,已经把指针的指针转成指针了。然后我们调用 B.Interface() ,这个时候我们发现:
B是map,是直接类型B的标志位里有flagIndir间接标志- 执行
e.word = *(*unsafe.Pointer)(v.ptr)操作
但是 B.ptr 是指针,虽然 unsafe.Pointer 可以转成任意的指针,但是对指针按照指针的指针来操作,获取到的指针地址肯定是会变的。
但是!但是!这块代码可以正常执行!!也就是说这块代码最终指向了正确的 map 值
那我们就猜测一下,对于直接类型,指针是可以转成指针的指针的。但是下面的单测却 panic 了,没法证明这个逻辑:
a := map[string]string{"cc": "dd"}
c := unsafe.Pointer(&a)
fmt.Println(c)
d := *(*unsafe.Pointer)(c)
fmt.Println(d)
f := *(*map[string]string)(d)
fmt.Println(f)
这是一块俺没法弄明白的逻辑!