Rob对golang reflect对一篇讲解贴:
The Laws of Reflection– By Rob Pike
golang作为一个静态语言,reflect给其提供了更灵活的runtime操作方式(元编程), 虽然在golang中,这也会带来性能问题,比如官方json包在序列化/反序列化的性能,让很多人改用了其他避开reflect的方式去处理问题,但是,也还是要承认,reflect给程序带来的极大灵活性无法让人割舍。
这里先从interface{}说起,因为这是一个golang中最为灵活的东西,Rob原话:
Some people say that Go’s interfaces are dynamically typed, but that is misleading. They are statically typed: a variable of interface type always has the same static type, and even though at run time the value stored in the interface variable may change type, that value will always satisfy the interface.
然后他又引用Russ Cox的blog对这句话做了详细的解释
A variable of interface type stores a pair: the concrete value assigned to the variable, and that value’s type descriptor. To be more precise, the value is the underlying concrete data item that implements the interface and the type describes the full type of that item.
1 | var r io.Reader |
For instance, after r contains, schematically, the (value, type) pair, (tty, os.File). Notice that the type os.File implements methods other than Read; even though the interface value provides access only to the Read method, the value inside carries all the type information about that value. That’s why we can do things like this:
1
2 var w io.Writer
w = r.(io.Writer)
and our empty interface value empty will again contain that same pair, (tty, *os.File). That’s handy: an empty interface can hold any value and contains all the information we could ever need about that value.
个人理解就是,在对某个interface类型变量赋值时,r内部是构造保存了一个变量初始化时时被赋予的(value, type) schema,当进行类型assert时,比如w = r.(io.Writer),w变量的内部,还是保存着原来的那个schema,只是r和w变了对这个schema的访问权限不一样而已。
接下来回到reflect本身:
在reflect包中,有两种类型reflect.Type, reflect.Value, reflect就是提供了一个机制, 去检测获取一个接口变量内部的(type,value)schema对,来看他说的几条法则
1. Reflection goes from interface value to reflection object
At the basic level, reflection is just a mechanism to examine the type and value pair stored inside an interface variable
1
2
3var x float64 = 3.4
// 这里返回reflect.Type类型
fmt.Println("type:",reflect.TypeOf(x))这里说对是reflect.TypeOf(i),reflect.ValueOf(i)函数,分别返回接口执行对(type,value)shema对的type,value,并分别以reflect.Type, reflect.Value类型存储.
reflect.Value有个重要的方法reflect.Value.Type()会返回value的类型,以reflect.Type类型返回。
reflect.Type, reflect.Value类型都有一个Kind方法,用于返回保存内容对类型。
1
2
3
4
5
6
7
8
9var x float64 = 3.4
v := reflect.ValueOf(x)
fmt.Println("type:", v.Type())
fmt.Println("kind is float64:", v.Kind() == reflect.Float64)
fmt.Println("value:", v.Float())
//type: float64
//kind is float64: true
//value: 3.42. Reflection goes from reflection object to interface value
1
2// Interface returns v's value as an interface{}.
func (v Value) Interface() interface{}reflect.Value类型有个Interface()方法,返回一个interface类型的值
3. To modify a reflection object, the value must be settable
reflect.Value有个CanSet()函数来判断变量是否可写:
1
2
3
4var x float64 = 3.4
v := reflect.ValueOf(x)
v.SetFloat(7.1) // Error: will panic.
// 这行代码会报错, 时因为这里的v是不可写的,这就类似对函数形参赋值不会修改函数外部的变量,是无意义的,所以golang把这个设为非法了通过传变量地址的方式来修改value:
1
2
3
4
5
6
7var x float64 = 3.4
p := reflect.ValueOf(&x)
v := p.Elem() //获取reflect.Value
fmt.Println("settability of v:", v.CanSet()) // 打印true
v.SetFloat(7.1)
fmt.Println(v.Interface())
fmt.Println(x)// 这里显示修改成功
附录(reflect的其他使用示例):
通过reflect获取interface变量的真实类型和值: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// 通过接口来获取任意参数,然后一一揭晓
func DoFiledAndMethod(input interface{}) {
getType := reflect.TypeOf(input)
fmt.Println("get Type is :", getType.Name())
getValue := reflect.ValueOf(input)
fmt.Println("get all Fields is:", getValue)
// 获取方法字段
// 1. 先获取interface的reflect.Type,然后通过NumField进行遍历
// 2. 再通过reflect.Type的Field获取其Field
// 3. 最后通过Field的Interface()得到对应的value
for i := 0; i < getType.NumField(); i++ {
field := getType.Field(i)
value := getValue.Field(i).Interface()
fmt.Printf("%s: %v = %v\n", field.Name, field.Type, value)
}
// 获取方法
// 1. 先获取interface的reflect.Type,然后通过.NumMethod进行遍历
for i := 0; i < getType.NumMethod(); i++ {
m := getType.Method(i)
fmt.Printf("%s: %v\n", m.Name, m.Type)
}
}