GoLang之Go 语言如何实现反射(3)

1.前言

interface

2.types 和 interface

int, float64, []int

Go 官方博客里就举了一个例子:

type MyInt int

var i int
var j MyInt
MyInt

反射主要与 interface{} 类型相关。关于 interface 的底层结构,可以参考前面有关 interface 章节的内容,这里复习一下。

type iface struct {
	tab  *itab
	data unsafe.Pointer
}

type itab struct {
	inter  *interfacetype
	_type  *_type
	link   *itab
	hash   uint32
	bad    bool
	inhash bool
	unused [2]byte
	fun    [1]uintptr
}
itab_typeinterfacetype_typeinterfacetype
eface“实现了”
type eface struct {
    _type *_type
    data  unsafe.Pointer
}
ifaceeface_typedata

还是用 Go 官方关于反射的博客里的例子,当然,我会用图形来详细解释,结合两者来看会更清楚。顺便提一下,搞技术的不要害怕英文资料,要想成为技术专家,读英文原始资料是技术提高的一条必经之路。

先明确一点:接口变量可以存储任何实现了接口定义的所有方法的变量。

ReaderWriter
type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}

接下来,就是接口之间的各种转换和赋值了:

var r io.Reader
tty, err := os.OpenFile("/Users/qcrao/Desktop/test", os.O_RDWR, 0)
if err != nil {
    return nil, err
}
r = tty
rio.Readerrnilnilr = ttyr*os.File
funRead*os.FileWrite*os.Fileio.Writer
var w io.Writer
w = r.(io.Writer)
rio.Readerio.Writerr
rio.Writerw.Write()w
rfunRead -> Write

最后,再来一个赋值:

var empty interface{}
empty = w
empty
_type*dataitab

这一节的最后,展示一个技巧:

“伪装”
type iface struct {
	tab  *itab
	data unsafe.Pointer
}
type itab struct {
	inter uintptr
	_type uintptr
	link uintptr
	hash  uint32
	_     [4]byte
	fun   [1]uintptr
}

type eface struct {
	_type uintptr
	data unsafe.Pointer
}

接着,将接口变量占据的内存内容强制解释成上面定义的类型,再打印出来:

package main

import (
	"os"
	"fmt"
	"io"
	"unsafe"
)

func main() {
	var r io.Reader
	fmt.Printf("initial r: %T, %v\n", r, r)

	tty, _ := os.OpenFile("/Users/qcrao/Desktop/test", os.O_RDWR, 0)
	fmt.Printf("tty: %T, %v\n", tty, tty)

	// 给 r 赋值
	r = tty
	fmt.Printf("r: %T, %v\n", r, r)

	rIface := (*iface)(unsafe.Pointer(&r))
	fmt.Printf("r: iface.tab._type = %#x, iface.data = %#x\n", rIface.tab._type, rIface.data)

	// 给 w 赋值
	var w io.Writer
	w = r.(io.Writer)
	fmt.Printf("w: %T, %v\n", w, w)

	wIface := (*iface)(unsafe.Pointer(&w))
	fmt.Printf("w: iface.tab._type = %#x, iface.data = %#x\n", wIface.tab._type, wIface.data)

	// 给 empty 赋值
	var empty interface{}
	empty = w
	fmt.Printf("empty: %T, %v\n", empty, empty)

	emptyEface := (*eface)(unsafe.Pointer(&empty))
	fmt.Printf("empty: eface._type = %#x, eface.data = %#x\n", emptyEface._type, emptyEface.data)
}

运行结果:

initial r: <nil>, <nil>
tty: *os.File, &{0xc4200820f0}
r: *os.File, &{0xc4200820f0}
r: iface.tab._type = 0x10bfcc0, iface.data = 0xc420080020
w: *os.File, &{0xc4200820f0}
w: iface.tab._type = 0x10bfcc0, iface.data = 0xc420080020
empty: *os.File, &{0xc4200820f0}
empty: eface._type = 0x10bfcc0, eface.data = 0xc420080020
r,w,empty

3.反射的基本函数

reflect.Typereflect.Value
reflect.Type_typereflect.Value_typedata

reflect 包中提供了两个基础的关于反射的函数来获取上述的接口和结构体:

func TypeOf(i interface{}) Type 
func ValueOf(i interface{}) Value
TypeOfinterface{}interface{}interface{}

看下源码:

func TypeOf(i interface{}) Type {
	eface := *(*emptyInterface)(unsafe.Pointer(&i))
	return toType(eface.typ)
}
emptyInterfaceefacereflectruntimeeface.typ
type emptyInterface struct {
	typ  *rtype
	word unsafe.Pointer
}
toType
func toType(t *rtype) Type {
	if t == nil {
		return nil
	}
	return t
}
Type*rtypeType
type Type interface {
    // 所有的类型都可以调用下面这些函数

	// 此类型的变量对齐后所占用的字节数
	Align() int
	
	// 如果是 struct 的字段,对齐后占用的字节数
	FieldAlign() int

	// 返回类型方法集里的第 `i` (传入的参数)个方法
	Method(int) Method

	// 通过名称获取方法
	MethodByName(string) (Method, bool)

	// 获取类型方法集里导出的方法个数
	NumMethod() int

	// 类型名称
	Name() string

	// 返回类型所在的路径,如:encoding/base64
	PkgPath() string

	// 返回类型的大小,和 unsafe.Sizeof 功能类似
	Size() uintptr

	// 返回类型的字符串表示形式
	String() string

	// 返回类型的类型值
	Kind() Kind

	// 类型是否实现了接口 u
	Implements(u Type) bool

	// 是否可以赋值给 u
	AssignableTo(u Type) bool

	// 是否可以类型转换成 u
	ConvertibleTo(u Type) bool

	// 类型是否可以比较
	Comparable() bool

	// 下面这些函数只有特定类型可以调用
	// 如:Key, Elem 两个方法就只能是 Map 类型才能调用
	
	// 类型所占据的位数
	Bits() int

	// 返回通道的方向,只能是 chan 类型调用
	ChanDir() ChanDir

	// 返回类型是否是可变参数,只能是 func 类型调用
	// 比如 t 是类型 func(x int, y ... float64)
	// 那么 t.IsVariadic() == true
	IsVariadic() bool

	// 返回内部子元素类型,只能由类型 Array, Chan, Map, Ptr, or Slice 调用
	Elem() Type

	// 返回结构体类型的第 i 个字段,只能是结构体类型调用
	// 如果 i 超过了总字段数,就会 panic
	Field(i int) StructField

	// 返回嵌套的结构体的字段
	FieldByIndex(index []int) StructField

	// 通过字段名称获取字段
	FieldByName(name string) (StructField, bool)

	// FieldByNameFunc returns the struct field with a name
	// 返回名称符合 func 函数的字段
	FieldByNameFunc(match func(string) bool) (StructField, bool)

	// 获取函数类型的第 i 个参数的类型
	In(i int) Type

	// 返回 map 的 key 类型,只能由类型 map 调用
	Key() Type

	// 返回 Array 的长度,只能由类型 Array 调用
	Len() int

	// 返回类型字段的数量,只能由类型 Struct 调用
	NumField() int

	// 返回函数类型的输入参数个数
	NumIn() int

	// 返回函数类型的返回值个数
	NumOut() int

	// 返回函数类型的第 i 个值的类型
	Out(i int) Type

    // 返回类型结构体的相同部分
	common() *rtype
	
	// 返回类型结构体的不同部分
	uncommon() *uncommonType
}
Type
Typecommonrtype_type
 // rtype must be kept in sync with ../runtime/type.go:/^type._type.
type rtype struct {
	size       uintptr
	ptrdata    uintptr
	hash       uint32
	tflag      tflag
	align      uint8
	fieldAlign uint8
	kind       uint8
	alg        *typeAlg
	gcdata     *byte
	str        nameOff
	ptrToThis  typeOff
}
rtype
arrayTypechanTyperytpedir
// arrayType represents a fixed array type.
type arrayType struct {
	rtype `reflect:"array"`
	elem  *rtype // array element type
	slice *rtype // slice type
	len   uintptr
}

// chanType represents a channel type.
type chanType struct {
	rtype `reflect:"chan"`
	elem  *rtype  // channel element type
	dir   uintptr // channel direction (ChanDir)
}
TypeString()fmt.Stringerfmt.PrintlnString()fmt.Printf()%Treflect.TypeOf
fmt.Printf("%T", 3) // int
TypeOfValueOfreflect.Valueinterface{}*data

源码如下:

func ValueOf(i interface{}) Value {
	if i == nil {
		return Value{}
	}
	
   // ……
	return unpackEface(i)
}

// 分解 eface
func unpackEface(i interface{}) Value {
	e := (*emptyInterface)(unsafe.Pointer(&i))

	t := e.typ
	if t == nil {
		return Value{}
	}
	
	f := flag(t.Kind())
	if ifaceIndir(t) {
		f |= flagIndir
	}
	return Value{t, e.word, f}
}
i*emptyInterfacetypwordValueValueOf

Value 结构体定义了很多方法,通过这些方法可以直接操作 Value 字段 ptr 所指向的实际数据:

// 设置切片的 len 字段,如果类型不是切片,就会panic
 func (v Value) SetLen(n int)
 
 // 设置切片的 cap 字段
 func (v Value) SetCap(n int)
 
 // 设置字典的 kv
 func (v Value) SetMapIndex(key, val Value)

 // 返回切片、字符串、数组的索引 i 处的值
 func (v Value) Index(i int) Value
 
 // 根据名称获取结构体的内部字段值
 func (v Value) FieldByName(name string) Value
 
 // ……
Value
// 用来获取 int 类型的值
func (v Value) Int() int64

// 用来获取结构体字段(成员)数量
func (v Value) NumField() int

// 尝试向通道发送数据(不会阻塞)
func (v Value) TrySend(x reflect.Value) bool

// 通过参数列表 in 调用 v 值所代表的函数(或方法
func (v Value) Call(in []Value) (r []Value) 

// 调用变参长度可变的函数
func (v Value) CallSlice(in []Value) []Value 
src/reflect/value.gofunc (v Value)
Type()Interface()interfaceTypeValue
TypeOf()ValueOf()

用一张图来串一下:

rtyeType

4.反射的三大定律

根据 Go 官方关于反射的博客,反射有三大定律:

1.Reflection goes from interface value to reflection object.

2.Reflection goes from reflection object to interface value.

3.To modify a reflection object, the value must be settable.

interfaceTypeOfValueOfValueOfInterface()interface接口型变量反射类型对象reflect.Typereflect.Value

第三条不太好懂:如果需要操作一个反射变量,那么它必须是可设置的。反射变量可设置的本质是它存储了原变量本身,这样对反射变量的操作,就会反映到原变量本身;反之,如果反射变量不能代表原变量,那么操作了反射变量,不会对原变量产生任何影响,这会给使用者带来疑惑。所以第二种情况在语言层面是不被允许的。

举一个经典例子:

var x float64 = 3.4
v := reflect.ValueOf(x)
v.SetFloat(7.1) // Error: will panic.
vxreflect.ValueOf(x)vxv
ValueValue

就像在一般的函数里那样,当我们想改变传入的变量时,使用指针就可以解决了。

var x float64 = 3.4
p := reflect.ValueOf(&x)
fmt.Println("type of p:", p.Type())
fmt.Println("settability of p:", p.CanSet())

输出是这样的:

type of p: *float64
settability of p: false
pxp.Elem()xx
v := p.Elem()
v.SetFloat(7.1)
fmt.Println(v.Interface()) // 7.1
fmt.Println(x) // 7.1
Value