反射的定义

  • 反射是指一类的应用,他们能够 自描述 自控制
  • python反射 根据字符串执行函数、根据字符串导入包

go中反射的简介

  • go是静态语言。反射就是go提供一种机制,在编译时不知道类型的情况下,可以做如下的事情
    • 更新变量
    • 运行时查看值
    • 调用方法
    • 对他们的布局进行操作

为什么使用反射

两个经典场景

  1. 你编写的这个函数,还不知道传给你的类型具体是什么,可能是还没约定好,也可能是传入的类型很多
  2. 希望通过用户的输入来决定调用按个函数(根据字符串调用方法),动态执行函数
  • 举例使用 interface.type判断类型
  1. package main
  2. import "fmt"
  3. func main() {
  4. var s interface{} = "abc"
  5. switch s.(type) {
  6. case string:
  7. fmt.Println("s.type=string")
  8. case int:
  9. fmt.Println("s.type=int")
  10. case bool:
  11. fmt.Println("s.type=bool")
  12. default:
  13. fmt.Println("未知的类型")
  14. }
  15. }
  • 上述类型判断的问题
    • 类型判断会写很多,代码很长
    • 类型还会增删,不灵活

使用反射获取变量内部的信息

  • reflect包提供 valueOf和Typeof
  • reflect.ValueOf : 获取输入接口中数据的值,如果为空返回 0
  • reflect.Typeof : 获取输入接口中值的类型,如果为空返回 nil
  • Typeof传入所有类型,因为所有的类型都实现了空接口

举例1 内置类型的测试

  1. package main
  2. import (
  3. "log"
  4. "reflect"
  5. )
  6. func main() {
  7. var s interface{} = "abc"
  8. // TypeOf会返回模板的对象
  9. reflectType := reflect.TypeOf(s)
  10. reflectValue := reflect.ValueOf(s)
  11. log.Printf("[typeof:%v]", reflectType)
  12. log.Printf("[valueof:%v]", reflectValue)
  13. }

举例2 自定义struct的反射

  • 生成的举例,对未知类型的进行 遍历探测它的Field,抽象成一个函数
  • go语言里面struct成员变量小写,在反射的时候直接panic reflect.Value.Interface: cannot return value obtained from unexported field or method
  • 结构体方法名小写是不会panic,反射时也不会被查看到
  • 指针方法是不能被反射查看到的

对于成员变量

  1. 先获取intereface的reflect.Type,然后遍历NumField
  2. 再通过reflect.Type的Field获取字段
  3. 最后通过Field的interface获取对应的value

对于方法

  1. 先获取intereface的reflect.Type,然后遍历NumMethod
  2. 再分别通过reflect.Type的 t.Method获取真实的方法
  3. 最后通过Name和Type获取方法的类型和值
  1. package main
  2. import (
  3. "log"
  4. "reflect"
  5. )
  6. type Person struct {
  7. Name string
  8. Age int
  9. }
  10. type Student struct {
  11. Person //匿名结构体嵌套
  12. StudentId int
  13. SchoolName string
  14. IsBaoSong bool //是考上来的吗
  15. Hobbies []string
  16. // panic: reflect.Value.Interface: cannot return value obtained from unexported field or method
  17. //hobbies []string
  18. Labels map[string]string
  19. }
  20. func (s *Student) GoHome() {
  21. log.Printf("[回家了][sid:%d]", s.StudentId)
  22. }
  23. func (s Student) GotoSchool() {
  24. log.Printf("[去上学了][sid:%d]", s.StudentId)
  25. }
  26. func (s Student) baosong() {
  27. log.Printf("[竞赛保送][sid:%d]", s.StudentId)
  28. }
  29. func main() {
  30. s := Student{
  31. Person: Person{Name: "xiaoyi", Age: 9900},
  32. StudentId: 123,
  33. SchoolName: "五道口皇家男子职业技术学院",
  34. IsBaoSong: true,
  35. Hobbies: []string{"唱", "跳", "Rap"},
  36. //hobbies: []string{"唱", "跳", "Rap"},
  37. Labels: map[string]string{"k1": "v1", "k2": "v2"},
  38. }
  39. // 获取目标对象
  40. t := reflect.TypeOf(s)
  41. log.Printf("[对象的类型名称:%s]", t.Name())
  42. // 获取目标对象的值类型
  43. v := reflect.ValueOf(s)
  44. // 遍历获取成员变量
  45. for i := 0; i < t.NumField(); i++ {
  46. // Field 代表对象的字段名
  47. key := t.Field(i)
  48. value := v.Field(i).Interface()
  49. //
  50. if key.Anonymous {
  51. log.Printf("[匿名字段][第:%d个字段][字段名:%s][字段的类型:%v][字段的值:%v]", i+1, key.Name, key.Type, value)
  52. } else {
  53. log.Printf("[命名字段][第:%d个字段][字段名:%s][字段的类型:%v][字段的值:%v]", i+1, key.Name, key.Type, value)
  54. }
  55. }
  56. // 打印方法
  57. for i := 0; i < t.NumMethod(); i++ {
  58. m := t.Method(i)
  59. log.Printf("[第:%d个方法][方法名称:%s][方法的类型:%v]", i+1, m.Name, m.Type)
  60. }
  61. /*
  62. 2021/07/10 15:21:07 [对象的类型名称:Student]
  63. 2021/07/10 15:21:07 [匿名字段][第:1个字段][字段名:Person][字段的类型:main.Person][字段的值:{xiaoyi 9900}]
  64. 2021/07/10 15:21:07 [命名字段][第:2个字段][字段名:StudentId][字段的类型:int][字段的值:123]
  65. 2021/07/10 15:21:07 [命名字段][第:3个字段][字段名:SchoolName][字段的类型:string][字段的值:五道口皇家男子职业技术学院]
  66. 2021/07/10 15:21:07 [命名字段][第:4个字段][字段名:IsBaoSong][字段的类型:bool][字段的值:true]
  67. 2021/07/10 15:21:07 [命名字段][第:5个字段][字段名:Hobbies][字段的类型:[]string][字段的值:[唱 跳 Rap]]
  68. 2021/07/10 15:21:07 [命名字段][第:6个字段][字段名:Labels][字段的类型:map[string]string][字段的值:map[k1:v1 k2:v2]]
  69. 2021/07/10 15:21:07 [第:1个方法][方法名称:GotoSchool][方法的类型:func(main.Student)]
  70. */
  71. }
  • 抽成一个函数
  1. package main
  2. import (
  3. "log"
  4. "reflect"
  5. )
  6. type Person struct {
  7. Name string
  8. Age int
  9. }
  10. type Student struct {
  11. Person //匿名结构体嵌套
  12. StudentId int
  13. SchoolName string
  14. IsBaoSong bool //是考上来的吗
  15. Hobbies []string
  16. // panic: reflect.Value.Interface: cannot return value obtained from unexported field or method
  17. //hobbies []string
  18. Labels map[string]string
  19. }
  20. //func (s *Student) GoHome() {
  21. // log.Printf("[回家了][sid:%d]", s.StudentId)
  22. //}
  23. func (s Student) GoHome() {
  24. log.Printf("[回家了][sid:%d]", s.StudentId)
  25. }
  26. func (s Student) GotoSchool() {
  27. log.Printf("[去上学了][sid:%d]", s.StudentId)
  28. }
  29. func (s Student) Baosong() {
  30. log.Printf("[竞赛保送][sid:%d]", s.StudentId)
  31. }
  32. func main() {
  33. s := Student{
  34. Person: Person{Name: "xiaoyi", Age: 9900},
  35. StudentId: 123,
  36. SchoolName: "五道口皇家男子职业技术学院",
  37. IsBaoSong: true,
  38. Hobbies: []string{"唱", "跳", "Rap"},
  39. //hobbies: []string{"唱", "跳", "Rap"},
  40. Labels: map[string]string{"k1": "v1", "k2": "v2"},
  41. }
  42. p := Person{
  43. Name: "李逵",
  44. Age: 124,
  45. }
  46. reflectProbeStruct(s)
  47. reflectProbeStruct(p)
  48. }
  49. func reflectProbeStruct(s interface{}) {
  50. // 获取目标对象
  51. t := reflect.TypeOf(s)
  52. log.Printf("[对象的类型名称:%s]", t.Name())
  53. // 获取目标对象的值类型
  54. v := reflect.ValueOf(s)
  55. // 遍历获取成员变量
  56. for i := 0; i < t.NumField(); i++ {
  57. // Field 代表对象的字段名
  58. key := t.Field(i)
  59. value := v.Field(i).Interface()
  60. //
  61. if key.Anonymous {
  62. log.Printf("[匿名字段][第:%d个字段][字段名:%s][字段的类型:%v][字段的值:%v]", i+1, key.Name, key.Type, value)
  63. } else {
  64. log.Printf("[命名字段][第:%d个字段][字段名:%s][字段的类型:%v][字段的值:%v]", i+1, key.Name, key.Type, value)
  65. }
  66. }
  67. // 打印方法
  68. for i := 0; i < t.NumMethod(); i++ {
  69. m := t.Method(i)
  70. log.Printf("[第:%d个方法][方法名称:%s][方法的类型:%v]", i+1, m.Name, m.Type)
  71. }
  72. /*
  73. 2021/07/10 15:21:07 [对象的类型名称:Student]
  74. 2021/07/10 15:21:07 [匿名字段][第:1个字段][字段名:Person][字段的类型:main.Person][字段的值:{xiaoyi 9900}]
  75. 2021/07/10 15:21:07 [命名字段][第:2个字段][字段名:StudentId][字段的类型:int][字段的值:123]
  76. 2021/07/10 15:21:07 [命名字段][第:3个字段][字段名:SchoolName][字段的类型:string][字段的值:五道口皇家男子职业技术学院]
  77. 2021/07/10 15:21:07 [命名字段][第:4个字段][字段名:IsBaoSong][字段的类型:bool][字段的值:true]
  78. 2021/07/10 15:21:07 [命名字段][第:5个字段][字段名:Hobbies][字段的类型:[]string][字段的值:[唱 跳 Rap]]
  79. 2021/07/10 15:21:07 [命名字段][第:6个字段][字段名:Labels][字段的类型:map[string]string][字段的值:map[k1:v1 k2:v2]]
  80. 2021/07/10 15:21:07 [第:1个方法][方法名称:GotoSchool][方法的类型:func(main.Student)]
  81. */
  82. }

举例3 反射修改值

  • 必须是指针类型
  • pointer.Elem().Setxxx()
  1. package main
  2. import (
  3. "log"
  4. "reflect"
  5. )
  6. func main() {
  7. var num float64 = 3.14
  8. log.Printf("[num原始值:%f]",num)
  9. // 通过reflect.ValueOf获取num中的value
  10. // 必须是指针才可以修改值
  11. pointer:=reflect.ValueOf(&num)
  12. newValue:=pointer.Elem()
  13. // 赋值
  14. newValue.SetFloat(5.6)
  15. log.Printf("[num新值:%f]",num)
  16. pointer = reflect.ValueOf(num)
  17. // reflect: call of reflect.Value.Elem on float64 Value
  18. newValue = pointer.Elem()
  19. }

举例4 反射调用方法

  • 过程说明
    1. 首先reflect.ValueOf(p1)获取 得到反射类型对象
    2. reflect.ValueOf.MethodByName ,需要传入准确的方法名称,MethodByName代表注册
      • 名字错了 会panic: reflect: call of reflect.Value.Call on zero Value
    3. []reflect.Value ,这是最终需要调用方法的参数,无参数传空切片
  1. package main
  2. import (
  3. "log"
  4. "reflect"
  5. )
  6. type Person struct {
  7. Name string
  8. Age int
  9. Gender string
  10. }
  11. func (p Person) ReflectCallFuncWithArgs(name string, age int) {
  12. log.Printf("[调用的是带参数的方法][args.name:%s][args.age:%d][[p.name:%s][p.age:%d]",
  13. name,
  14. age,
  15. p.Name,
  16. p.Age,
  17. )
  18. }
  19. func (p Person) ReflectCallFuncWithNoArgs() {
  20. log.Printf("[调用的是不带参数的方法]")
  21. }
  22. func main() {
  23. p1 := Person{
  24. Name: "小乙",
  25. Age: 18,
  26. Gender: "男",
  27. }
  28. // 1. 首先通过 reflect.ValueOf(p1)获取 得到反射值类型
  29. getValue := reflect.ValueOf(p1)
  30. // 2. 带参数的方法调用
  31. methodValue := getValue.MethodByName("ReflectCallFuncWithArgs")
  32. // 参数是reflect.Value的切片
  33. args := []reflect.Value{reflect.ValueOf("李逵"), reflect.ValueOf(30)}
  34. methodValue.Call(args)
  35. // 3. 不带参数的方法调用
  36. methodValue = getValue.MethodByName("ReflectCallFuncWithNoArgs")
  37. // 参数是reflect.Value的切片
  38. args = make([]reflect.Value, 0)
  39. methodValue.Call(args)
  40. }

结构体标签和反射

  • json的标签解析json
  • yaml的标签解析yaml
  • xorm gorm的标签 标识db字段
  • 自定义标签
  • 原理是t.Field.Tag.Lookup(“标签名”)
  • 混合的例子如下
  1. package main
  2. import (
  3. "encoding/json"
  4. "gopkg.in/yaml.v2"
  5. "io/ioutil"
  6. "log"
  7. "reflect"
  8. )
  9. type Person struct {
  10. Name string `json:"name" yaml:"yaml_name" mage:"name"`
  11. Age int `json:"age" yaml:"yaml_age" mage:"age"`
  12. City string `json:"-" yaml:"yaml_city" mage:"-"`
  13. }
  14. //json解析
  15. func jsonWork() {
  16. // 对象marshal成字符串
  17. p := Person{
  18. Name: "xiaoyi",
  19. Age: 18,
  20. City: "北京",
  21. }
  22. data, err := json.Marshal(p)
  23. if err != nil {
  24. log.Printf("[json.marshal.err][err:%v]", err)
  25. return
  26. }
  27. log.Printf("[person.marshal.res][res:%v]", string(data))
  28. // 从字符串解析成结构体
  29. p2Str := `
  30. {
  31. "name":"李逵",
  32. "age":28,
  33. "city":"山东"
  34. }
  35. `
  36. var p2 Person
  37. err = json.Unmarshal([]byte(p2Str), &p2)
  38. if err != nil {
  39. log.Printf("[json.unmarshal.err][err:%v]", err)
  40. return
  41. }
  42. log.Printf("[person.unmarshal.res][res:%v]", p2)
  43. }
  44. // yaml读取文件
  45. func yamlWork() {
  46. filename := "a.yaml"
  47. content, err := ioutil.ReadFile(filename)
  48. if err != nil {
  49. log.Printf("[ioutil.ReadFile.err][err:%v]", err)
  50. return
  51. }
  52. p := &Person{}
  53. //err = yaml.Unmarshal(content, p)
  54. err = yaml.UnmarshalStrict(content, p)
  55. if err != nil {
  56. log.Printf("[yaml.UnmarshalStrict.err][err:%v]", err)
  57. return
  58. }
  59. log.Printf("[yaml.UnmarshalStrict.res][res:%v]", p)
  60. }
  61. func jiexizidingyibiaoqian(s interface{}) {
  62. // typeOf type类型
  63. r := reflect.TypeOf(s)
  64. value := reflect.ValueOf(s)
  65. for i := 0; i < r.NumField(); i++ {
  66. field := r.Field(i)
  67. key := field.Name
  68. if tag, ok := field.Tag.Lookup("mage"); ok {
  69. if tag == "-" {
  70. continue
  71. }
  72. log.Printf("[找到了mage标签][key:%v][value:%v][标签:mage=%s]",
  73. key,
  74. value.Field(i),
  75. tag,
  76. )
  77. }
  78. }
  79. }
  80. func main() {
  81. //jsonWork()
  82. //yamlWork()
  83. p := Person{
  84. Name: "xiaoyi",
  85. Age: 18,
  86. City: "北京",
  87. }
  88. jiexizidingyibiaoqian(p)
  89. }

反射的副作用

1.代码可读性变差

2.隐藏的错误躲过编译检查

  • go静态语言,编译器能发现类型的错误
  • 但是对于反射代码是无能为力的,可能运行很久才会panic
  • 反射调用方法的副作用,将string参数传成 int
  1. panic: reflect: Call using float64 as type int
  2. goroutine 1 [running]:
  3. reflect.Value.call(0x31c260, 0xc0000783c0, 0x293, 0x328479, 0x4, 0xc00011df48, 0
  4. x2, 0x2, 0x353a00, 0xc0000200c0, ...)
  5. C:/Program Files/Go/src/reflect/value.go:406 +0x1337
  6. reflect.Value.Call(0x31c260, 0xc0000783c0, 0x293, 0xc00011df48, 0x2, 0x2, 0xc000
  7. 0783c0, 0x293, 0xc000047f30)
  8. C:/Program Files/Go/src/reflect/value.go:337 +0xc5
  9. main.main()
  10. D:/nyy_work/go_path/src/maday06/reader.go:41 +0x316

3. go反射性能问题

  1. type := reflect.value(obj)
  2. fieldValue:=type_.FieldByName("xx")
  • 每次取出的fieldValue类型是reflect.value
  • 它是一个具体的值,不是一个可复用的对象
  • 每次反射都要malloc这个reflect.Value结构退,还有GC
  • 反射比正常的代码要慢1-2个数据量级,如果是追求性能的关键模块应减少反射