理解golang的反射reflection

微信: lijiaocn 创建: 2017/11/06 15:34:13

说明

不同语言的反射模式是不同的。

在golang的官网上有一篇文章很详细的介绍了go语言的反射机制: The Laws of Reflection。

反射实现的前提

反射是建立在类型之上:

reflection builds on the type system

golang的变量类型是静态的,在创建变量的时候就已经确定,反射主要与golang的interface类型相关。

在golang的实现中,每个interface变量都有一个对应pair,pair中记录了实际变量的值和类型:

  1. (value, type)

value是实际变量值,type是实际变量的类型
例如,创建类型为*os.File的变量,然后将其赋给一个接口变量r:

  1. tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
  2. var r io.Reader
  3. r = tty

接口变量r的pair中将记录如下信息:

  1. (tty, *os.File)

这个pair在接口变量的连续赋值过程中是不变的,将接口变量r赋给另一个接口变量w:

  1. var w io.Writer
  2. w = r.(io.Writer)

接口变量w的pair与r的pair相同,都是:

  1. (tty, *os.File)

即使w是空接口类型,pair也是不变的。

pair的存在,是golang中实现反射的前提,理解了pair,就更容易理解反射。

从接口变量中获取value和type信息

reflect.TypeOf()是获取pair中的type,reflect.ValueOf()获取pair中的value:

  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. func main() {
  7. var x float64 = 3.4
  8. fmt.Println("type: ", reflect.TypeOf(x))
  9. fmt.Println("type: ", reflect.ValueOf(x))
  10. }

运行时输出的结果是:

  1. type: float64
  2. type: 3.4

pair中的value和type用类型reflect.Valuereflect.Type描述。

package: reflect中有很详细的信息,例如Kind()返回的类型:

  1. const (
  2. Invalid Kind = iota
  3. Bool
  4. Int
  5. Int8
  6. Int16
  7. Int32
  8. Int64
  9. Uint
  10. Uint8
  11. Uint16
  12. Uint32
  13. Uint64
  14. Uintptr
  15. Float32
  16. Float64
  17. Complex64
  18. Complex128
  19. Array
  20. Chan
  21. Func
  22. Interface
  23. Map
  24. Ptr
  25. Slice
  26. String
  27. Struct
  28. UnsafePointer
  29. )

从Value中获取接口信息

类型为”relfect.Value”变量,通过下面的方法可以获得接口变量:

  1. func (v Value) Interface() interface{}

当收到一个类型为reflect.Value类型的变量时,用下面方式将它转换对应的接口变量,然后进行类型判断:

  1. y := v.Interface().(float64)

之后就可以使用y的成员和方法。

通过reflect.Value设置实际变量的值

reflect.Value是通过reflect.ValueOf(X)获得的,只有当X是指针的时候,才可以通过reflec.Value修改实际变量X的值。

例如:

  1. var x float64 = 3.4
  2. p := reflect.ValueOf(&x) // Note: take the address of x.
  3. v := p.Elem()
  4. fmt.Println("type of p:", v.Type())
  5. fmt.Println("settability of p:", v.CanSet())
  6. v.SetFloat(77)

传入的是* float64,需要用p.Elem()获取所指向的Value。v.CantSet()输出的是true,因此可以用v.SetFloat()修改x的值。

收到reflect.Value变量后

如果得到了一个类型为reflect.Value的变量,可以通过下面的方式,获得变量的信息。

如果知道v的真实类型,直接转化成对应的类型:

  1. r := v.Interface().(已知的类型)

如果不知道v的真实类型,获取它的Type,然后遍历Type的Field,和v的Field:

  1. t := v.Type()
  2. for i := 0; i < v.NumField(); i++ {
  3. f := v.Field(i)
  4. fmt.Printf("%d: %s %s = %v\n", i, t.Field(i).Name, f.Type(), f.Interface())
  5. }

参考

  1. The Laws of Reflection
  2. package: reflect
ft_authoradmin  ft_create_time2017-11-07 13:10
 ft_update_time2017-11-07 13:14