Infinite Games

Less is More.

标准库: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 包抽象出两个最基本的类型表示对象的信息:

  1. Type Interface
  2. 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 就是 rtypetoType 只做了一个类型转换:

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
}

参考资料