interface
反射是基于interface底层的。先复习一下空interface的底层:
1 2 3 4
| type eface struct { _type *_type data unsafe.Pointer }
|
第一个字段是接口存放实体的类型详细信息,第二个字段放的是实体的地址
反射基本函数源码
反射包里面定义个一个接口和一个结构体,即 reflect.Type
和 reflect.Value
,它们提供很多函数来获取存储在接口里的类型信息。
reflect.Type
主要提供了关于类型相关的信息,所以它和_type
关联比较紧密,后者则结合_type
和data
两者,因此程序员可以获取甚至改变类型的值
可以使用两个基础的关于反射的函数来获取上述的接口和结构体:
1 2
| func TypeOf(i interface{}) Type func ValueOf(i interface{}) Value
|
关于源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| func TypeOf(i interface{}) Type { eface := *(*emptyInterface)(unsafe.Pointer(&i)) return toType(eface.typ) }
func toType(t *rtype) Type { if t == nil { return nil } return t }
type emptyInterface struct { typ *rtype word unsafe.Pointer }
|
这里的 emptyInterface
和上面提到的 eface
是一回事(字段名略有差异,字段是相同的),且在不同的源码包:前者在 reflect
包,后者在 runtime
包。 eface.typ
就是动态类型。
可以看到,TypeOf
的作用,简而言之就是从传入的接口变量i,转化为eface
,然后从eface
中取出保存实体类型信息的_type
作为入参,传入toType
函数之中。toType
以eface.typ
为实体,返回一个Type
接口,这就是我们所需要的。
而我们获取到的Type
接口则根据实体的_type
保存了与实体有关的各种信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
| type Type interface {
Align() int FieldAlign() int
Method(int) Method
MethodByName(string) (Method, bool)
NumMethod() int
Name() string
PkgPath() string
Size() uintptr
String() string
Kind() Kind
Implements(u Type) bool
AssignableTo(u Type) bool
ConvertibleTo(u Type) bool
Comparable() bool
Bits() int
ChanDir() ChanDir
IsVariadic() bool
Elem() Type
Field(i int) StructField
FieldByIndex(index []int) StructField
FieldByName(name string) (StructField, bool)
FieldByNameFunc(match func(string) bool) (StructField, bool)
In(i int) Type
Key() Type
Len() int
NumField() int
NumIn() int
NumOut() int
Out(i int) Type
common() *rtype uncommon() *uncommonType }
|
再看一下ValueOf
函数: 我们经常要在需要改变实体的情况下用到它,所以需要结合类型信息的值信息,所以Value
既要包含_type
还要包含data
国际惯例先上源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| func ValueOf(i interface{}) Value { if i == nil { return Value{} } return unpackEface(i) }
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} }
|
主要来看这个分解eface,其实就是把i转化为eface,然后取出它的_type和data,组装起来称为一个结构体Value。
Value 结构体定义了很多方法,通过这些方法可以直接操作 Value 字段 ptr 所指向的实际数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| func (v Value) SetLen(n int) func (v Value) SetCap(n int) func (v Value) SetMapIndex(key, val Value)
func (v Value) Index(i int) Value func (v Value) FieldByName(name string) Value
func (v Value) Int() int64
func (v Value) NumField() int
func (v Value) TrySend(x reflect.Value) bool
func (v Value) Call(in []Value) (r []Value)
func (v Value) CallSlice(in []Value) []Value
|
需要注意的一点是,可以通过函数让interface
、Type
、Value
三者转换,因为Value基本上存储了eface的所有信息。

反射三大定律
- Reflection goes from interface value to reflection object.
- Reflection goes from reflection object to interface value.
- To modify a reflection object, the value must be settable.
前两条是说接口型变量interface
和反射类型变量reflect.Type
和reflect.Value
可以相互转换。
第三条是说如果操作一个反射变量,那么它就必须是可以设置的。反射变量可设置的本质是它存储了原变量本身,这样对反射变量的操作,就会反映到原变量本身;反之,如果反射变量不能代表原变量,那么操作了反射变量,不会对原变量产生任何影响,这会给使用者带来疑惑。所以第二种情况在语言层面是不被允许的。
举一个例子:
1 2 3
| var x float64 = 3.4 v := reflect.ValueOf(x) v.SetFloat(7.1)
|
这里在调用v := reflect.ValueOf(x)
是使用的传值调用,也就是说对于float类型的变量,传进去的是它的一个拷贝,所以不可以。解决方法是传进去一个指针。
一个栗子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
| package main
import ( "reflect" "fmt" )
type Child struct { Name string Grade int Handsome bool }
type Adult struct { ID string `qson:"Name"` Occupation string Handsome bool }
func handsome(i interface{}) { v := reflect.ValueOf(i)
if v.Kind() != reflect.Slice { return }
if e := v.Type().Elem(); e.Kind() != reflect.Struct { return }
st := v.Type().Elem()
foundName := false for i := 0; i < st.NumField(); i++ { f := st.Field(i) tag := f.Tag.Get("qson")
if (tag == "Name" || f.Name == "Name") && f.Type.Kind() == reflect.String { foundName = true break } }
if !foundName { return }
if niceField, foundHandsome := st.FieldByName("Handsome"); foundHandsome == false || niceField.Type.Kind() != reflect.Bool { return }
for i := 0; i < v.Len(); i++ { e := v.Index(i) handsome := e.FieldByName("Handsome")
var name reflect.Value for j := 0; j < st.NumField(); j++ { f := st.Field(j) tag := f.Tag.Get("qson")
if tag == "Name" || f.Name == "Name" { name = v.Index(i).Field(j) } }
if name.String() == "qcrao" { handsome.SetBool(true) } } }
func main() { children := []Child{ {Name: "Ava", Grade: 3, Handsome: true}, {Name: "qcrao", Grade: 6, Handsome: false}, }
adults := []Adult{ {ID: "Steve", Occupation: "Clerk", Handsome: true}, {ID: "qcrao", Occupation: "Go Programmer", Handsome: false}, }
fmt.Printf("adults before handsome: %v\n", adults) handsome(adults) fmt.Printf("adults after handsome: %v\n", adults)
fmt.Println("-------------")
fmt.Printf("children before handsome: %v\n", children) handsome(children) fmt.Printf("children after handsome: %v\n", children) }
|
在弄懂了底层后,可以很清晰的看出这个例子中反射运行的机制。