• 《Go语言四十二章经》第二十七章 反射(reflect)
    • 27.1 反射(reflect)
    • 27.2 反射结构体

    《Go语言四十二章经》第二十七章 反射(reflect)

    作者:李骁

    27.1 反射(reflect)

    反射是用程序检查其所拥有的结构,尤其是类型的一种能力;这是元编程的一种形式。

    反射可以在运行时检查类型和变量,例如它的大小、方法和 动态 的调用这些方法。这对于没有源代码的包尤其有用。

    这是一个强大的工具,除非真得有必要,否则应当避免使用或小心使用。

    变量的最基本信息就是类型和值。反射包的 Type 用来表示一个 Go 类型,反射包的 Value 为 Go 值提供了反射接口。

    两个简单的函数,reflect.TypeOf 和 reflect.ValueOf,返回被检查对象的类型和值。

    例如,x 被定义为:var x float64 = 3.4,那么 reflect.TypeOf(x) 返回 float64,reflect.ValueOf(x) 返回

    实际上,反射是通过检查一个接口的值,变量首先被转换成空接口。这从下面两个函数签名能够很明显的看出来:

    1. func TypeOf(i interface{}) Type
    2. func ValueOf(i interface{}) Value

    接口的值包含一个 type 和 value。

    反射可以从接口值反射到对象,也可以从对象反射回接口值。

    reflect.Type 和 reflect.Value 都有许多方法用于检查和操作它们。一个重要的例子是 Value 有一个 Type 方法返回 reflect.Value 的 Type。另一个是 Type 和 Value 都有 Kind 方法返回一个常量来表示类型:Uint、Float64、Slice 等等。同样 Value 有叫做 Int 和 Float 的方法可以获取存储在内部的值(跟 int64 和 float64 一样)

    问题的原因是 v 不是可设置的(这里并不是说值不可寻址)。是否可设置是 Value 的一个属性,并且不是所有的反射值都有这个属性:可以使用 CanSet() 方法测试是否可设置。反射中有些内容是需要用地址去改变它的状态的。
    当 v := reflect.ValueOf(x) 函数通过传递一个 x 拷贝创建了 v,那么 v 的改变并不能更改原始的 x。要想 v 的更改能作用到 x,那就必须传递 x 的地址 v = reflect.ValueOf(&x)。

    通过 Type() 我们看到 v 现在的类型是 *float64 并且仍然是不可设置的。
    要想让其可设置我们需要使用 Elem() 函数,这间接的使用指针:v = v.Elem()
    现在 v.CanSet() 返回 true 并且 v.SetFloat(3.1415) 设置成功了!

    27.2 反射结构体

    有些时候需要反射一个结构类型。下面例子较为完整反射了一个结构体的字段和方法:

    1. package main
    2. import (
    3. "fmt"
    4. "reflect"
    5. )
    6. // 结构体
    7. type ss struct {
    8. int
    9. string
    10. bool
    11. float64
    12. }
    13. func (s ss) Method1(i int) string { return "结构体方法1" }
    14. func (s *ss) Method2(i int) string { return "结构体方法2" }
    15. var (
    16. structValue = ss{ // 结构体
    17. 20,
    18. "结构体",
    19. false,
    20. 64.0,
    21. }
    22. )
    23. // 复杂类型
    24. var complexTypes = []interface{}{
    25. structValue, &structValue, // 结构体
    26. structValue.Method1, structValue.Method2, // 方法
    27. }
    28. func main() {
    29. // 测试复杂类型
    30. for i := 0; i < len(complexTypes); i++ {
    31. PrintInfo(complexTypes[i])
    32. }
    33. }
    34. func PrintInfo(i interface{}) {
    35. if i == nil {
    36. fmt.Println("--------------------")
    37. fmt.Printf("无效接口值:%v\n", i)
    38. fmt.Println("--------------------")
    39. return
    40. }
    41. v := reflect.ValueOf(i)
    42. PrintValue(v)
    43. }
    44. func PrintValue(v reflect.Value) {
    45. fmt.Println("--------------------")
    46. // ----- 通用方法 -----
    47. fmt.Println("String :", v.String()) // 反射值的字符串形式
    48. fmt.Println("Type :", v.Type()) // 反射值的类型
    49. fmt.Println("Kind :", v.Kind()) // 反射值的类别
    50. fmt.Println("CanAddr :", v.CanAddr()) // 是否可以获取地址
    51. fmt.Println("CanSet :", v.CanSet()) // 是否可以修改
    52. if v.CanAddr() {
    53. fmt.Println("Addr :", v.Addr()) // 获取地址
    54. fmt.Println("UnsafeAddr :", v.UnsafeAddr()) // 获取自由地址
    55. }
    56. // 获取方法数量
    57. fmt.Println("NumMethod :", v.NumMethod())
    58. if v.NumMethod() > 0 {
    59. // 遍历方法
    60. i := 0
    61. for ; i < v.NumMethod()-1; i++ {
    62. fmt.Printf(" ┣ %v\n", v.Method(i).String())
    63. // if i >= 4 { // 只列举 5 个
    64. // fmt.Println(" ┗ ...")
    65. // break
    66. // }
    67. }
    68. fmt.Printf(" ┗ %v\n", v.Method(i).String())
    69. // 通过名称获取方法
    70. fmt.Println("MethodByName :", v.MethodByName("String").String())
    71. }
    72. switch v.Kind() {
    73. // 结构体:
    74. case reflect.Struct:
    75. fmt.Println("=== 结构体 ===")
    76. // 获取字段个数
    77. fmt.Println("NumField :", v.NumField())
    78. if v.NumField() > 0 {
    79. var i int
    80. // 遍历结构体字段
    81. for i = 0; i < v.NumField()-1; i++ {
    82. field := v.Field(i) // 获取结构体字段
    83. fmt.Printf(" ├ %-8v %v\n", field.Type(), field.String())
    84. }
    85. field := v.Field(i) // 获取结构体字段
    86. fmt.Printf(" └ %-8v %v\n", field.Type(), field.String())
    87. // 通过名称查找字段
    88. if v := v.FieldByName("ptr"); v.IsValid() {
    89. fmt.Println("FieldByName(ptr) :", v.Type().Name())
    90. }
    91. // 通过函数查找字段
    92. v := v.FieldByNameFunc(func(s string) bool { return len(s) > 3 })
    93. if v.IsValid() {
    94. fmt.Println("FieldByNameFunc :", v.Type().Name())
    95. }
    96. }
    97. }
    98. }