15 反射

变量的内在机制

  • Go 的变量分为两部分
    • 类型信息:预先定义好的元信息
    • 值信息:程序运行过程中可动态变化的

反射介绍

  • 程序运行时 对程序本身进行访问和修改的能力
    • 一般,程序在编译时,变量被转换成内存地址,变量名不会被编译器写道可执行部分
    • 在运行程序时,程序无法获取自身的信息
  • 但对于支持反射的语言,在程序编译时会将变量的反射信息(字段名、类型信息、结构体信息等)整合到可执行文件中,并给程序提供 接口 访问反射信息
  • Go 在运行期间 使用 reflect包 访问程序的反射信息

reflect 包

  • 在 Go 的反射机制中,任何接口值 都是由一个具体类型具体类型的值 两部分组成
  • 反射相关功能由 内置 reflect 包提供,
    • 任意接口值在反射中都可以理解为reflect.Typereflect.Value 两部分,
    • reflect 包提供了reflect.TypeOf reflect.ValueOf 获取任意对象的 Value 和 Type

TypeOf

  • 使用reflect.TypeOf() 可以获得任意值的类型对象(reflect.Type), 程序通过类型对象可以访问任意值的类型信息

    • ```go
      package main

      import (

      "fmt"
      "reflect"
      

      )

      func reflectType(x interface{}) {

      v := reflect.TypeOf(x)
      fmt.Printf("type:%v\n", v)
      

      }
      func main() {

      var a float32 = 3.14
      reflectType(a) // type:float32
      var b int64 = 100
      reflectType(b) // type:int64
      

      }

      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

      ### type name 和 type kind

      * 在反射中,关于类型还划分为两种:`类型Type` 和 `种类Kind`

      * `type` 关键字,使我们可以构造很多自定义类型

      * `kind` 指底层的类型

      * 在反射中,需要区分 指针、结构体等大品种的类型时,会用到`kind`

      * 示例:定义 指针类型和结构体类型:

      * ```go
      package main

      import (
      "fmt"
      "reflect"
      )

      type myInt int64

      func reflectType(x interface{}) {
      t := reflect.TypeOf(x)
      fmt.Printf("type:%v kind:%v\n", t.Name(), t.Kind())
      }

      func main() {
      var a *float32 // 指针
      var b myInt // 自定义类型
      var c rune // 类型别名
      reflectType(a) // type: kind:ptr
      reflectType(b) // type:myInt kind:int64
      reflectType(c) // type:int32 kind:int32

      type person struct {
      name string
      age int
      }
      type book struct{ title string }
      var d = person{
      name: "沙河小王子",
      age: 18,
      }
      var e = book{title: "《跟小王子学Go语言》"}
      reflectType(d) // type:person kind:struct
      reflectType(e) // type:book kind:struct
      }
    • Go 的反射中,像数组、切片、Map、指针等类型变量,他们的.Name()返回空

  • reflect 包中,定义的 Kind 类型:

    • ```go
      type Kind uint
      const (
      Invalid Kind = iota  // 非法类型
      Bool                 // 布尔型
      Int                  // 有符号整型
      Int8                 // 有符号8位整型
      Int16                // 有符号16位整型
      Int32                // 有符号32位整型
      Int64                // 有符号64位整型
      Uint                 // 无符号整型
      Uint8                // 无符号8位整型
      Uint16               // 无符号16位整型
      Uint32               // 无符号32位整型
      Uint64               // 无符号64位整型
      Uintptr              // 指针
      Float32              // 单精度浮点数
      Float64              // 双精度浮点数
      Complex64            // 64位复数类型
      Complex128           // 128位复数类型
      Array                // 数组
      Chan                 // 通道
      Func                 // 函数
      Interface            // 接口
      Map                  // 映射
      Ptr                  // 指针
      Slice                // 切片
      String               // 字符串
      Struct               // 结构体
      UnsafePointer        // 底层指针
      
      )
      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



      ## ValueOf

      * `reflect.ValueOf()` 返回的是 `reflect.Value` 类型,包含了**原始值**的值信息

      * `reflect.Value` 与**原始值**之间可以**相互转换**

      * `reflect.Value` 提供获取原始值的方法:

      * | 方法 | 说明 |
      | :--------------------------: | :----------------------------------------------------------: |
      | Interface() **interface {}** | 将值以 interface{} 类型返回,可以通过类型断言转换为指定类型 |
      | Int() int64 | 将值以 int 类型返回,所有有符号整型均可以此方式返回 |
      | Uint() uint64 | 将值以 uint 类型返回,所有无符号整型均可以此方式返回 |
      | Float() float64 | 将值以双精度(float64)类型返回,所有浮点数(float32、float64)均可以此方式返回 |
      | Bool() bool | 将值以 bool 类型返回 |
      | Bytes() []bytes | 将值以字节数组 []bytes 类型返回 |
      | String() string | 将值以字符串类型返回 |

      ### 通过反射获取值

      ```go
      func reflectValue(x interface{}) {
      v := reflect.ValueOf(x)
      k := v.Kind()
      switch k {
      case reflect.Int64:
      // v.Int()从反射中获取整型的原始值,然后通过int64()强制类型转换
      fmt.Printf("type is int64, value is %d\n", int64(v.Int()))
      case reflect.Float32:
      // v.Float()从反射中获取浮点型的原始值,然后通过float32()强制类型转换
      fmt.Printf("type is float32, value is %f\n", float32(v.Float()))
      case reflect.Float64:
      // v.Float()从反射中获取浮点型的原始值,然后通过float64()强制类型转换
      fmt.Printf("type is float64, value is %f\n", float64(v.Float()))
      }
      }
      func main() {
      var a float32 = 3.14
      var b int64 = 100
      reflectValue(a) // type is float32, value is 3.140000
      reflectValue(b) // type is int64, value is 100
      // 将int类型的原始值转换为reflect.Value类型
      c := reflect.ValueOf(10)
      fmt.Printf("type c :%T\n", c) // type c :reflect.Value
      }

通过反射设置变量的值

  • 在函数中通过反射修改变量的值,需要传递变量地址才能修改变量值

  • 反射中,使用专有的Elem()方法可以获取指针对应的值

    • ```go
      package mainimport (
      "fmt"
      "reflect"
      
      )func reflectSetValue1(x interface{}) {
      v := reflect.ValueOf(x)
      if v.Kind() == reflect.Int64 {
          v.SetInt(200) //修改的是副本,reflect包会引发panic
      }
      
      }
      func reflectSetValue2(x interface{}) {
      v := reflect.ValueOf(x)
      // 反射中使用 Elem()方法获取指针对应的值
      if v.Elem().Kind() == reflect.Int64 {
          v.Elem().SetInt(200)
      }
      
      }
      func main() {
      var a int64 = 100
      // reflectSetValue1(a) //panic: reflect: reflect.Value.SetInt using unaddressable value
      reflectSetValue2(&a)
      fmt.Println(a)
      
      }
      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

      ### IsNil() 和 IsValid

      #### IsNil()

      * 函数原型:`func (v Value) IsNil() bool`

      * 判断 v 持有的值是否为`Nil`
      * v 持有的值的分类必须是通道、函数、接口、映射、指针、切片之一,
      * 否则`IsNil`函数会导致 panic



      #### IsValid

      * 函数原型:`func (v Value) IsValid() bool`

      * 判断 v 是否持有一个值,如果 v 是**Value零值**,返回假,此时v除了IsValid , String, Kind 之外的方法都会导致 panic



      #### 示例

      * `IsNil` 常用于判断指针是否为空
      * `IsValid` 常用于判断返回值是否有效

      ```go
      func main() {
      // *int类型空指针
      var a *int
      fmt.Println("var a *int IsNil:", reflect.ValueOf(a).IsNil())
      // nil值
      fmt.Println("nil IsValid:", reflect.ValueOf(nil).IsValid())
      // 实例化一个匿名结构体
      b := struct{}{}
      // 尝试从结构体中查找"abc"字段
      fmt.Println("不存在的结构体成员:", reflect.ValueOf(b).FieldByName("abc").IsValid())
      // 尝试从结构体中查找"abc"方法
      fmt.Println("不存在的结构体方法:", reflect.ValueOf(b).MethodByName("abc").IsValid())
      // map
      c := map[string]int{}
      // 尝试从map中查找一个不存在的键
      fmt.Println("map中不存在的键:", reflect.ValueOf(c).MapIndex(reflect.ValueOf("娜扎")).IsValid())
      }

结构体反射

与结构体相关的方法

结构体反射示例

反射是把双刃剑

  • 反射式一个强大并富有表现力的工具,能使得我们写出灵活的代码
    • 基于反射的代码十分脆弱,反射中类型错误会在真正运行的时候才会引发 panic ,那很可能是在代码写完的很长时间之后
    • 大量使用反射的代码通常难以理解
    • 反射的性能低下