本文主要讲述reflect库实现的原理思路,reflect包实现具备两个基础unsafe操做内存对齐和runtime包的变量。
runtime变量是reflect的实现基础,基于unsafe包操做runtime变量实现reflect功能。golang
首先咱们按照go的规则先简单的定义一个变量类型Value,Value有两个类型成员属性typ和ptr,typ是类型表示这个变量是什么对象,ptr是一个地址指向这个变量的地址。c#
// 若是看reflect或runtime源码会发现二者相识,只不过被我删了很多属性。 type Value struct { typ Type ptr uintptr } type Type interface { Name() string // by all type Index(int) Value // by Slice Array MapIndex(value) Value // by Map Send(Value) // By Chan }
当咱们去操做一个变量时就按照Type类型来操做,而操做对象的数据就在内存的ptr位置。数组
变量类型Type定义的是一个接口,由于不一样类型有不一样的操做方法,例如Map的获取/设置值,Slice和Array的获取一个索引,Chan具备发送和接实一个对象,Struct能够得到一个结构体属性,属性具备tag,这样不一样的类型就具备不一样独特的操做方法,若是Map类型调用Index方法没法实现就会panic了。函数
理解变量本质就是一个数据地址和一个类型数据组成,而后基于者两个变量来操做就是reflect。ui
一个reflect简单的例子,reflect.TypeOf
和reflect.ValueOf
方法将一个runtime类型和变量转换成reflect类型和变量,依赖unsafe操做内存对齐来强制转换,reflect类型和变量和runtime中同样的,就能够实现自由操做了。this
最后reflect.Value
调用Interface()
方法将变量从reflect状态转换回来成runtime状态了。google
package main import ( "fmt" "reflect" ) type Student struct { Name string Age int } func main() { s := new(Student) fmt.Println(reflect.TypeOf(s)) fmt.Println(reflect.TypeOf(s).Elem()) fmt.Println(reflect.TypeOf(*s)) v := reflect.ValueOf(s).Elem() v.Field(0).SetString("66") fmt.Printf("%#v\n", v.Interface()) }
先从reflect/type.go简单的抄一点代码来。rtype对象就是Type接口的简化实现,kind就是这个类型的类型,而后其余组合类型(Ptr、Slice、Map等)就额外添加了一些属性和方法。指针
type rtype struct { size uintptr ptrdata uintptr kind uint8 ... }
ptrType是指针类型的定义,属性rtype就是指针的类型,elem就是指针指向的类型,那么一个Ptr Type调用Elem得到指针的类型就返回了elem值。code
// ptrType represents a pointer type. type ptrType struct { rtype elem *rtype // pointer element (pointed at) type }
structType是指针类型的定义,rtype是结构体类型的基础信息,pkgPath就是结构体的名称,当一个结构体调用Name方法时就返回了pkgPath,若是是结构体指针调用Name方法就没有返回数据,由于没有pkgPath须要先Elem一次转换成结构体类型,而结构体类型的Field、FieldByIndex、FieldByName、FieldByNameFunc方法就对象结构体类型fields信息进行变量操做了。对象
而在结构体属性structField中,name、typ分别记录这个属性的名称和类型,offsetEmbed是属性偏移位置。
// structType represents a struct type. type structType struct { rtype pkgPath name fields []structField // sorted by offset } // Struct field type structField struct { name name // name is always non-empty typ *rtype // type of field offsetEmbed uintptr // byte offset of field<<1 | isEmbedded }
chanType是chan类型的ing有,rtype是chan自己,elem是chan操做对象的类型和指针指向相识,dir就是chan的反向进、出、进出。
// chanType represents a channel type. type chanType struct { rtype elem *rtype // channel element type dir uintptr // channel direction (ChanDir) }
sliceType是切片类型定义,切片类型rtype是自己信息,elem就是切片操做的对象类型。
// sliceType represents a slice type. type sliceType struct { rtype elem *rtype // slice element type }
arrayType是数组类型,在切片上额外多了两个属性,slice是数组转换成切片的类型,预先静态定义好了,而len是数组长度。
// arrayType represents a fixed array type. type arrayType struct { rtype elem *rtype // array element type slice *rtype // slice type len uintptr }
上述example讲述了部分类型的定义,完整查看源码reflect.type.go。
method、interface、map暂未彻底看完,懂原理后不必看没有几行使用相关知识。
reflect.Kind是定义反射类型常量,是类型的标识。rtype的kind属性就是指reflect.Kind。
// A Kind represents the specific kind of type that a Type represents. // The zero Kind is not a valid kind. 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 )
Kind方法注释说明返回kind值就是rtype.kind,类型是reflect.Kind是go中类型的主要分类,是iota定义的类型常量。
// Kind returns the specific kind of this type. Kind() Kind
变量实现的方法定义在类型连续后面的一块内存中,能够unsafe读到一个类型的所有方法,就能够实现Implements方法判断是否实现了一个接口了。
// Implements reports whether the type implements the interface type u. Implements(u Type) bool
ChanDir方法很简单就返回chanType.dir,注释说若是不是Chan类型panic了,类型不chan就没有dir这个属性没法处理就panic了,在调用前通常都明确了Kind是Chan。
// ChanDir returns a channel type's direction. // It panics if the type's Kind is not Chan. ChanDir() ChanDir
Elem方法全称是element,就是指元素类型也能够叫指向类型,注释要求Kind必须是Array、Chan、Map、Ptr、Slice类型否在就panic,和Chan的ChanDir方法同样,只有这5个类型才有elem属性。
查看前面定义就能够知道Arry、Slice、Ptr、Chan的elem就是指向的对象的类型,map是值的类型,例如如下类型Elem后Kind都是Int。
[20]int []int *int chan int map[string]int
// Elem returns a type's element type. // It panics if the type's Kind is not Array, Chan, Map, Ptr, or Slice. Elem() Type
Field和NumField方法是得到结构体的指定索引的属性和结构体属性数量,注释同样有说明要求Kind是Struct类型否在panic,由于就结构体类型才有[]StructField能实现这些方法。
根据前面structType定义两个方法的实现思路就是typ.fields[i]转换一下和len(typ.fields).
// Field returns a struct type's i'th field. // It panics if the type's Kind is not Struct. // It panics if i is not in the range [0, NumField()). Field(i int) StructField // NumField returns a struct type's field count. // It panics if the type's Kind is not Struct. NumField() int
NumIn和In方法是Func Kind独有的方法,NumIn返回这个Func具备多个入参,对于返回参数就是NumOut;In方法是得到这个Func指定第i参数的类型。
// NumIn returns a function type's input parameter count. // It panics if the type's Kind is not Func. NumIn() int // In returns the type of a function type's i'th input parameter. // It panics if the type's Kind is not Func. // It panics if i is not in the range [0, NumIn()). In(i int) Type
Key方法是Map Kind独有方法,返回map键的类型。
// Key returns a map type's key type. // It panics if the type's Kind is not Map. Key() Type
Len方法是Array Kind独有方法,返回Array定义的长度。
// Len returns an array type's length. // It panics if the type's Kind is not Array. Len() int
上述说明reflect.Type的部分方法实现原理,剩余方法原理相似,就是操做rtype的属性,部分Kind类型是具备独有方法能够调用。
反射Value对象定义了三个属性 类型、数据位置、flag,数据内存位置就在ptr位置,操做方法就须要依靠typ类型来判断数据类型操做了。
Type是静态数据,而Value是动态数据,Value的不少方法具体值是和数据相关的。
type Value struct { typ *rtype ptr unsafe.Pointer flag }
通用方法是指全部类型具备的方法,仅说明根据Type和Value定义实现这个方法大概的思路,具体实现代码并不同,以源码为准。
Type方法返回这个值的类型,大体思路就是返回v.typ,具体实现还有一些额外处理。
func (v Value) Type() Type
Kind方法实现大体思路就是返回v.typ.kind。
// Kind returns v's Kind. If v is the zero Value (IsValid returns false), // Kind returns Invalid. func (v Value) Kind() Kind
Interface 法思路就是返回v.ptr值转换成一个interface{}变量,这样就从reflect.Value从新转换会变量了。
// Interface returns v's current value as an interface{}. // It is equivalent to: // var i interface{} = (v's underlying value) // It panics if the Value was obtained by accessing unexported struct fields. func (v Value) Interface() (i interface{})
Convert 方法思路就是v.ptr值转换成参数t的类型,实现规则是Conversions语法文档 镜像地址。
// Convert returns the value v converted to type t. If the usual Go conversion rules do not allow conversion of the value v to type t, Convert panics. func (v Value) Convert(t Type) Value
Set 方法实现就是设置v.ptr=x.ptr,要求v和x的类型是同样的。
同时要这个Value是CanSet,若是将一个int转换成reflect.Value,函数传递的是一个值的副本,那么再对int设置新的值就无效了,CanSet返回就是false,须要传递*int这样的指针类型才能有效设置
// Set assigns x to the value v. It panics if CanSet returns false. As in Go, x's value must be assignable to v's type. func (v Value) Set(x Value)
SetBool 方法是设置bool Kind的值,前置要求Kind是同样的,类型还有SetInt、SetString等方法。
// SetBool sets v's underlying value. It panics if v's Kind is not Bool or if CanSet() is false. func (v Value) SetBool(x bool)
Method 返回这个值的指定索引方法。
// Method returns a function value corresponding to v's i'th method. // The arguments to a Call on the returned function should not include // a receiver; the returned function will always use v as the receiver. // Method panics if i is out of range or if v is a nil interface value. func (v Value) Method(i int) Value
Len方法返回数据数据,注释说明要求是Array, Chan, Map, Slice, or String,前四个返回就是数据量,而String Kind返回字符串长度。
// It panics if v's Kind is not Array, Chan, Map, Slice, or String. func (v Value) Len() int
IsNil方法判断指针是不是空,在go的实现中chan、func、interface、map、pointer、slice底层才是指针类型,才能判断IsNil否在panic,判断这些指针类型的ptr是否为0,在go代码编写中也只有这几种类型能够i==nil
这样的比较。
在go1.13中新增了IsZero方法,判断是不是空值,里面这些指针类型会判断IsNil,其余类型就是判断数据值是否是零值那样。
// IsNil reports whether its argument v is nil. The argument must be // a chan, func, interface, map, pointer, or slice value; if it is // not, IsNil panics. Note that IsNil is not always equivalent to a // regular comparison with nil in Go. For example, if v was created // by calling ValueOf with an uninitialized interface variable i, // i==nil will be true but v.IsNil will panic as v will be the zero Value. func (v Value) IsNil() bool
Index方法获取指定类型的索引,就Array、Slice、String能够执行,否在panic,在ptr指向的位置进行一个计算获得的偏移位置得到到索引的值。
// Index returns v's i'th element. It panics if v's Kind is not Array, Slice, or String or i is out of range. func (v Value) Index(i int) Value
Field方法是返回结构体指定索引的值,要求Kind是Struct,经过指定索引的偏移来得到这个值的地址,而后类型里面得到到类型,最后返回索引的值。
// Field returns the i'th field of the struct v. It panics if v's Kind is not Struct or i is out of range. func (v Value) Field(i int) Value
Elem方法是返回Ptr和Interface Kind指向值,为了解除引用。
为何Value.Elem方法没有了Slice、Map等类型? 具体额外独立的操做方法Index、MapIndex等。
// 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
MapIndex和MapKeys是Map Kind独有的方法,获取到map的索引值和所有键,经过typ提供的类型和ptr地址进行复杂的map操做。
// MapIndex returns the value associated with key in the map v. // It panics if v's Kind is not Map. // It returns the zero Value if key is not found in the map or if v represents a nil map. // As in Go, the key's value must be assignable to the map's key type. func (v Value) MapIndex(key Value) Value // MapKeys returns a slice containing all the keys present in the map, // in unspecified order. // It panics if v's Kind is not Map. // It returns an empty slice if v represents a nil map. func (v Value) MapKeys() []Value
Send方法是Chan Kind独有方法,给chan放一个数据进去。
func (v Value) Send(x Value)
以上讲述了reflect库的原理就是操做runtime变量,而runtime变量就是一个类型加地址。
本文并无完整分析reflect库,经过这些原理就能够大概理解这些方法的做用和操做了,具体请参考源码。