什么是反射
维基百科上反射的定义:
在计算机科学中,反射是指计算机程序在运行时(Run time)可以访问、检测和修改它本身状态或行为的一种能力。用比喻来说,反射就是程序在运行的时候能够“观察”并且修改自己的行为。
我们知道一个变量在定义的时候是知道类型的,但是在运行中,可能会被随时转型,不管是显式还是隐式。
其本质就是程序在运行期探知对象的类型信息和内存结构。如果是汇编语言,我们完全没有这种烦恼。
其实什么是类型?只是确定如何解析这几段空间的0和1。
不同语言的反射模型不尽相同,有些语言还不支持反射。《Go 语言圣经》中是这样定义反射的:
Go 语言提供了一种机制在运行时更新变量和检查它们的值、调用它们的方法,但是在编译时并不知道这些变量的具体类型,这称为反射机制。
反射的使用场景
其实Go比较尴尬的点在于没有泛型。所以用了interface这种来定义“不定参数”。而反射大多就是用来解析interface变量的。
但是通常反射的代码往往是有坑的:
- 不好阅读。
- 一旦有没判断好的地方,就容易panic。
- 无法在编译的时候发现问题。
- 运行速度较慢。
在这里还是期待Go的泛型早日出来。
Go是怎么实现反射的
想想如果我们,要随时知道这个变量的类型,我们会怎么做?
记下来呗。
Go也是一样,一个记录了taye,一个记录了值,当然这个值是一个指针,在需要获取值的时候,根据type执行对应的函数。
对外暴露了两个方法:
func TypeOf(i interface{}) Type
func ValueOf(i interface{}) Value
func TypeOf(i interface{}) Type {
eface := *(*emptyInterface)(unsafe.Pointer(&i))
return toType(eface.type)
}
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
转换成 *emptyInterface
类型, 再将它的 typ
字段和 word
字段以及一个标志位字段组装成一个 Value
结构体,而这就是 ValueOf
函数的返回值,它包含类型结构体指针、真实数据的地址、标志位。
回到思路原点,其实就是返回了一个类型,一个地址,表达了如果去解析这段地址。
当然,这只是冰山一角,具体细节还望自行看源码。
反射的三大定律
- Reflection goes from interface value to reflection object.
- Reflection goes from reflection object to interface value.
前两条是说可以通过反射对象得到interface变量,也可以将interface变量转化为反射对象。
- To modify a reflection object, the value must be settable.
这是说如果你要改变一个反射对象,它的值必须是可修改的。
举个栗子:
var x float64 = 3.4
v := reflect.ValueOf(x)
v.SetFloat(7.1) // Error: will panic.
我们看valueOf的代码知道,它只是将传进去的变量的地址转为了emptyInterface
,返回了一种解析方式,告诉你这个变量是什么类型,你应该如果解析它。
如果需要改变量,我们需要先的到这个变量的具体位置。
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()
})(v.ptr))
}
x := unpackEface(eface)
if x.flag != 0 {
x.flag |= v.flag.ro()
}
return x
case Ptr:
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
fl := v.flag&flagRO | flagIndir | flagAddr
fl |= flag(typ.Kind())
return Value{typ, ptr, fl}
}
panic(&ValueError{"reflect.Value.Elem", v.kind()})
}
这里看到,要么interface,要么指针,其他的都会panic。
所以:
var x float64 = 3.14
v := reflect.ValueOf(x)
fmt.Println("settability of v:", v.CanSet())
v2 := reflect.ValueOf(&x)
p := v2.Elem()
fmt.Println("settability of p:", p.CanSet())
输出:
settability of v: false
settability of p: true
接语
看很多遍不如自己看一遍源码,其实很少的。
如果有不懂的还是欢迎评论,我都会回答的。