反射

先看一个问题

  1. 前面我们学习过结构体的序列化和反序列化,里面 tags 的底层采用的就是反射的原理。 ```go package main

import ( “encoding/json” “fmt” )

type Monster struct { Name string json:"name" Age int json:"age" Sal float64 json:"sal" Sex string json:"sex" }

func main() { monster := Monster{ Name: “玉兔精”, Age: 20, Sal: 888.99, Sex: “female”, }

  1. data, _ := json.Marshal(monster)
  2. fmt.Println("json result: ", string(data))

}

  1. 2. 应用领域
  2. ```go
  3. // 定义了两个匿名函数
  4. test1 := func(v1 int, v2 int) {
  5. t.Log(v1, v2)
  6. }
  7. test2 := func(v1 int, v2 int, s string) {
  8. t.Log(v1, v2, s)
  9. }
  10. // 定义一个适配器函数用作统一处理接口,其大致结构如下:
  11. bridge := func(call interface{}, args...interface{}) {
  12. //内容
  13. }
  14. //实现调用 test1 对应的函数
  15. bridge(test1, 1, 2)
  16. //实现调用 test2 对应的函数
  17. bridge(test2, 1, 2, "test2")
  18. // 要求使用反射机制完成

基本介绍

  1. 反射的基本介绍
    • 反射可以在运行时动态获取变量的各种信息,比如变量的类型,类别
    • 如果是结构体变量,还可以获取到结构体本身的信息(包括结构体的字段、方法)
    • 通过反射,可以修改变量的值,可以调用关联的方法。
    • 使用反射,需要 import(“reflect”)
  2. 反射重要的函数和概念
    • reflect.TypeOf(变量名), 获取变量的类型,返回 reflect.Type 类型
    • reflect.ValueOf(变量名),获取变量的值,返回 reflect.Value 类型 reflect.Value 是一个结构体类型。
    • 变量、interface{} 和 reflect.Value 是可以相互转换的。
  3. 类型转换

    • 变量 -> 手段:传递参数->interface{} -> 手段:reflect.Value() 函数 -> reflect.Value
    • reflect.Value -> 手段:Interface -> interface{} 手段:类型断言 -> 变量

      快速入门

  4. 编写一个案例。演示对基本数据类型、interface{}、reflect.Value进行反射操作。 ```go package main

import ( “fmt” “reflect” )

//专门演示反射 func reflectTest01(b interface{}) {

  1. //通过反射获取传入的变量的 type,kind,value
  2. //1. 先获取到 reflect.Type
  3. rtyp := reflect.TypeOf(b)
  4. fmt.Println("rtype =", rtyp)
  5. //2. 获取到 reflect.Value
  6. rVal := reflect.ValueOf(b)
  7. fmt.Println("rVal =", rVal) //不是普通的100
  8. fmt.Printf("rVal = %v rVal = %T \n", rVal, rVal)
  9. //将 reflect.Value 改为 int
  10. n2 := 2 + rVal.Int()
  11. fmt.Println(n2)
  12. // 将 reflect.Value 改为 interface{}
  13. iVal := rVal.Interface()
  14. //将 interface{} 通过断言转成需要的类型
  15. num2 := iVal.(int)
  16. fmt.Printf("num2 = %v num2 = %T \n", num2, num2)

}

func main() { / 案例一: 编写一个案例。演示对基本数据类型、interface{}、reflect.Value进行反射操作。 /

  1. //1. 先定义一个 int
  2. var num int = 100
  3. reflectTest01(num)

}

  1. 2. 编写一个案例,演示对(结构体类型、interface{}、reflect.Value)进行反射的基本操作。
  2. ```go
  3. package main
  4. import (
  5. "fmt"
  6. "reflect"
  7. )
  8. type Student struct {
  9. Name string
  10. Age int
  11. }
  12. //专门演示反射[对结构体的反射]
  13. func reflectTest02(b interface{}) {
  14. //通过反射获取传入的变量的 type,kind,value
  15. //1. 先获取到 reflect.Type
  16. rType := reflect.TypeOf(b)
  17. fmt.Println("rType =", rType)
  18. //2. 获取到 reflect.Value
  19. rVal := reflect.ValueOf(b)
  20. fmt.Println("rVal =", rVal)
  21. fmt.Printf("rVal = %v rVal = %T \n", rVal, rVal)
  22. // 将 reflect.Value 改为 interface{}
  23. iVal := rVal.Interface()
  24. fmt.Printf("iVal = %v iVal = %T \n", iVal, iVal)
  25. //将 interface{} 通过断言转成需要的类型
  26. //println(iVal.Name) 报错
  27. //这里,我们就简单使用了待检测的类型
  28. //可以使用 switch 的断言形式来做得更加灵活
  29. stu, ok := iVal.(Student)
  30. if ok {
  31. fmt.Printf("stu.Name = %v \n", stu.Name)
  32. }
  33. }
  34. func main() {
  35. //定义一个 Student 实例
  36. stu := Student{
  37. Name: "Tom",
  38. Age: 20,
  39. }
  40. reflectTest02(stu)
  41. }

反射注意事项和细节说明

  1. reflect.Value.Kind,获取变量的类别,返回的是一个常量。
  2. Type 是类型,kind 是类型,Type 和 kind 可能是相同的,也可能是不同的。
    • 比如:var num int = 10 num 的 Type 是 int,Kind 也是 int。
    • 比如:var stu Student stu 的 Type 是包名.student,Kind 是 struct
  3. 通过反射可以让变量在 interface{} 和 reflect.Value 之间相互转换。
  4. 使用反射的方式来去变量的值(并返回对应的类型),要求数据类型匹配,比如 x 是 int,那么就应该使用 reflect.Value(x).Int(),而不能使用其他的,否则报 panic。
  5. 通过反射的来修改变量,注意当使用 SetXxx 方法来设置需要通过对应的指针类型来完成,这样才能改变传入的变量的值,同时需要使用到 reflect.Value.Elem() 方法。 ```go package main

import ( “fmt” “reflect” )

//通过反射,修改 num int 的值;修改 Student 的值

func reflect01(b interface{}) { //获取到 reflect.Value rVal := reflect.ValueOf(b) // 看看 rVal 的 kind fmt.Printf(“rVal Kind = %v \n”, rVal.Kind())

  1. // rVal.SetInt(20) error
  2. rVal.Elem().SetInt(20)

}

func main() { var num int = 10 reflect01(&num) fmt.Println(num) }

  1. <a name="314422fe"></a>
  2. ## 反射的最佳实践
  3. 1. 使用反射来遍历结构体的字段,调用结构体的方法,并获取结构体标签的值。
  4. ```go
  5. package main
  6. import (
  7. "fmt"
  8. "reflect"
  9. )
  10. // 定义了一个 Monster 结构体
  11. type Monster struct {
  12. Name string `json:"name"`
  13. Age int `json:"age"`
  14. Score float32
  15. Sex string
  16. }
  17. //方法,显示 s 的值
  18. func(s Monster) Print() {
  19. fmt.Println("---start---")
  20. fmt.Println(s)
  21. fmt.Println("---end---")
  22. }
  23. //方法, 返回两个数的和
  24. func (s Monster) GetSum(n1 int, n2 int) int {
  25. return n1 + n2
  26. }
  27. //方法,接受四个值,给 s 赋值
  28. func (s Monster) Set(name string, age int, score float32, sex string) {
  29. s.Name = name
  30. s.Age = age
  31. s.Score = score
  32. s.Sex = sex
  33. }
  34. func TestStruct(a interface{}) {
  35. // 获取 reflect.Type 类型
  36. typ := reflect.TypeOf(a)
  37. // 获取 reflect.Value
  38. val := reflect.ValueOf(a)
  39. // 获取到 a 对应的类别
  40. kd := val.Kind()
  41. // 如果传入的不是 struct ,就退出函数
  42. if kd != reflect.Struct {
  43. fmt.Println("expect struct...")
  44. return
  45. }
  46. // 获取该结构体有几个字段
  47. num := val.NumField()
  48. fmt.Printf("struct has %d fields\n", num)
  49. //遍历结构体的所有字段
  50. for i := 0; i < num; i++ {
  51. fmt.Printf("Field%d: %v \n", i, val.Field(i))
  52. // 获取到 struct 标签,注意需要通过 reflect.type 来获取 tag 标签的值
  53. tagVal := typ.Field(i).Tag.Get("json")
  54. // 如果该字段有 tag 标签就显示,否则不显示
  55. if tagVal != "" {
  56. fmt.Printf("Field%d: tag = %v \n", i, tagVal)
  57. }
  58. }
  59. // 获取到当前结构体有多少个方法
  60. numOfMethod := val.NumMethod()
  61. fmt.Printf("struct has %d methods \n", numOfMethod)
  62. // var params []reflect.Value
  63. // 方法的排序默认是按照 函数名的排序(ASCII码)
  64. val.Method(1).Call(nil) // 获取到第二个方法,调用它
  65. // 调用结构体的第1个方法 Method(1)
  66. var params []reflect.Value // 声明 []reflect.Value 切片
  67. params = append(params, reflect.ValueOf(10))
  68. params = append(params, reflect.ValueOf(40))
  69. res := val.Method(0).Call(params) // 传入的参数是 []reflect.Value , 返回[]reflect.Value
  70. fmt.Println("res =", res[0].Int())
  71. }
  72. func main() {
  73. //创建了一个 Monster 实例
  74. monster := Monster{
  75. Name: "黄鼠狼精",
  76. Age: 400,
  77. Score: 30.8,
  78. }
  79. // 将 Monster 实例传递给 TestStruct 函数
  80. TestStruct(monster)
  81. }
  1. 使用反射的方式来获取结构体的 tag 标签,遍历字段的值,修改字段值,调用结构体方法。 ```go package main

import ( “encoding/json” “fmt” “reflect” )

type Monster struct { Name string json:"name" Age int Score float32 Sex string }

func (s Monster) Print() { fmt.Println(“—-start—-“) fmt.Println(s) fmt.Println(“—-end—-“) }

func TestStruct(a interface{}) { tye := reflect.TypeOf(a) val := reflect.ValueOf(a) kd := val.Kind() if kd != reflect.Ptr && val.Elem().Kind() == reflect.Struct { fmt.Println(“expect struct”) return }

  1. num := val.Elem().NumField()
  2. val.Elem().Field(0).SetString("白象精")
  3. for i := 0; i < num; i++ {
  4. fmt.Printf("%d %v \n", i, val.Elem().Field(i).Kind())
  5. }
  6. fmt.Printf("struct has %d fields \n", num)
  7. tag := tye.Elem().Field(0).Tag.Get("json")
  8. fmt.Printf("tag = %s \n", tag)
  9. numOfMethod := val.Elem().NumMethod()
  10. fmt.Printf("struct has %d methods \n", numOfMethod)
  11. val.Elem().Method(0).Call(nil)

}

func main() { monster := Monster{ Name: “黄狮子”, Age: 408, Score: 92.8, }

  1. //先说明一下, Marshal 就是通过反射获取到 struct 的 tag 值
  2. result, _ := json.Marshal(monster)
  3. fmt.Println("json result:", string(result))
  4. TestStruct(&monster)
  5. fmt.Println(monster)

}

  1. 3. 定义了两个函数 test1 test2,定义一个适配器函数用作统一处理接口。
  2. - 定义了两个函数
  3. - 定义一个适配器函数用作统一处理接口
  4. - 要求使用反射机制完成
  5. ```go
  6. package test
  7. import (
  8. "reflect"
  9. "testing"
  10. )
  11. func TestReflectFunction(t *testing.T) {
  12. call1 := func(v1 int, v2 int) {
  13. t.Log(v1, v2)
  14. }
  15. call2 := func(v1 int, v2 int, s string) {
  16. t.Log(v1, v2, s)
  17. }
  18. var (
  19. function reflect.Value
  20. inValue []reflect.Value
  21. n int
  22. )
  23. bridge := func(call interface{}, args...interface{}) {
  24. n = len(args)
  25. inValue = make([]reflect.Value, n)
  26. for i :=0 ;i < n; i++ {
  27. inValue[i] = reflect.ValueOf(args[i])
  28. }
  29. function = reflect.ValueOf(call)
  30. function.Call(inValue)
  31. }
  32. bridge(call1, 1, 2)
  33. bridge(call2 ,1, 2, "test2")
  34. }
  1. 使用反射操作任意结构体类型 ```go package test

import ( “reflect” “testing” )

type user struct { UserId string Name string }

func TestReflectStruct(t testing.T) { var ( model user sv reflect.Value )

  1. model = &user{}
  2. sv = reflect.ValueOf(model)
  3. t.Log("reflect.Value.Elem", sv.Kind().String())
  4. t.Log("sv.Elem().Kind()", sv.Elem().Kind())
  5. sv = sv.Elem()
  6. sv.FieldByName("UserId").SetString("12345678")
  7. sv.FieldByName("Name").SetString("nickname")
  8. t.Log("model", model)

}

  1. 5. 使用反射创建并操作结构体
  2. ```go
  3. package test
  4. import (
  5. "reflect"
  6. "testing"
  7. )
  8. type user struct {
  9. UserId string
  10. Name string
  11. }
  12. func TestReflectStructPtr(t *testing.T) {
  13. var (
  14. model *user
  15. st reflect.Type
  16. elem reflect.Value
  17. )
  18. st = reflect.TypeOf(model) //获取类型 *user
  19. t.Log("reflect.TypeOf", st.Kind().String()) //ptr
  20. st = st.Elem() // st 指向的类型
  21. t.Log("reflect.TypeOf.elem", st.Kind().String()) //struct
  22. elem = reflect.New(st) // New 返回一个 value 类型值,该值持有一个指针
  23. t.Log("reflect.New", elem.Kind().String()) // ptr
  24. t.Log("reflect.New.Elem", elem.Elem().Kind().String()) //struct
  25. // model 就是创建的 user 结构体变量(实例)
  26. model = elem.Interface().(*user) // model 是 *user 它的指向和 elem 是一样的
  27. t.Log("model model.Name", model, model.Name)
  28. t.Log("elem elem.Elem", elem, elem.Elem())
  29. elem = elem.Elem() //取得 elem 指向的值
  30. elem.FieldByName("UserId").SetString("12345678") //赋值
  31. elem.FieldByName("Name").SetString("nickname")
  32. t.Log("model model.Name", model, model.Name)
  33. t.Log("*model", *model)
  34. }
  1. 课堂练习 ```go package main

import ( “fmt” “reflect” )

/ 课堂练习: (1) 编写一个 Cal 结构体,有两个字段 Num1,Num2 (2) 方法 GetSub(name string) (3) 使用反射机制完成对 GetSub 的调用,输出形式为 “tom 完成了 减法运行,8 - 3 = 5” /

type Cal struct { Num1 int Num2 int }

func (cal Cal) GetSub(name string) { fmt.Printf(“%v 完成了减法运行,%v - %v = %v \n”, name, cal.Num1, cal.Num2, cal.Num1 - cal.Num2) }

func TestStruct(a interface{}) { //typ := reflect.TypeOf(a) val := reflect.ValueOf(a) kd := val.Kind() // fmt.Println(val.Elem().Kind()) // struct if kd != reflect.Ptr && val.Elem().Kind() == reflect.Struct { fmt.Println(“expect struct…”) return }

  1. num := val.Elem().NumField()
  2. fmt.Printf("struct has %d fields \n", num)
  3. for i := 0; i < num; i++ {
  4. fmt.Printf("field(%d) %v \n", i, val.Elem().Field(i))
  5. }
  6. numOfMethod := val.Elem().NumMethod()
  7. fmt.Printf("struct has %d methods \n", numOfMethod)
  8. // 调用结构体的第1个方法 Method(1)
  9. var params []reflect.Value // 声明 []reflect.Value 切片
  10. params = append(params, reflect.ValueOf("tom"))
  11. val.Elem().Method(0).Call(params)

}

func main() { cal := Cal{ Num1: 8, Num2: 3, }

  1. cal.GetSub("tom")
  2. TestStruct(&cal)

} ```


课程来源