经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » Go语言 » 查看文章
golang的reflection(转)
来源:cnblogs  作者:timliudream  时间:2018/11/15 10:27:39  对本文有异议
  1. 作者:BGbiao
  2. 链接:https://www.jianshu.com/p/42c19f88df6c
  3. 來源:简书

反射reflection

  • 可以大大提高程序的灵活性,使得interface{}有更大的发挥余地
  • 反射可以使用TypeOf和ValueOf函数从接口中获取目标对象信息
  • 反射会将匿名字段作为独立字段(匿名字段的本质)
  • 想要利用反射修改对象状态,前提是interface.data是settable,即pointer-interface
  • 通过反射可以“动态”调用方法

常用的类型、函数和方法

  1. //返回动态类型i的类型,如果i是一个空结构体类型,TypeOf将返回nil
  2. func TypeOf(i interface{}) Type
  3. //Type 接口类型
  4. type Type interface {
  5. Align() int
  6. FieldAlign() int
  7. //指定结构体中方法的下标,返回某个方法的对象,需要注意的是返回的Method是一个独立的结构体
  8. Method(int) Method
  9. /*
  10. type Method struct {
  11. Name string
  12. PkgPath string
  13. Type Type
  14. Func Value
  15. Index int
  16. }
  17. */
  18. MethodByName(string) (Method, bool)
  19. //返回该结构体类型的方法下标
  20. NumMethod() int
  21. //返回类型的名称,即动态类型i的名称
  22. Name() string
  23. PkgPath() string
  24. Size() uintptr
  25. String() string
  26. Kind() Kind
  27. Implements(u Type) bool
  28. AssignableTo(u Type) bool
  29. ConvertibleTo(u Type) bool
  30. Comparable() bool
  31. Bits() int
  32. ChanDir() ChanDir
  33. IsVariadic() bool
  34. Elem() Type
  35. //返回结构体类型第i个字段
  36. Field(i int) StructField
  37. //StructField结构体
  38. //type StructField struct {
  39. // Name string
  40. // PkgPath string
  41. // Type Type
  42. // Tag StructTag
  43. // Offset uintptr
  44. // Index []int
  45. // Anonymous bool
  46. //根据结构体字段索引获取嵌入字段的结构体信息
  47. FieldByIndex(index []int) StructField
  48. FieldByName(name string) (StructField, bool)
  49. FieldByNameFunc(match func(string) bool) (StructField, bool)
  50. In(i int) Type
  51. Key() Type
  52. Len() int
  53. //返回动态类型i(结构体字段)的字段总数
  54. NumField() int
  55. NumIn() int
  56. NumOut() int
  57. Out(i int) Type
  58. }
  59. //返回接口i的一个初始化的新值.ValueOf(nil)返回一个零值
  60. func ValueOf(i interface{}) Value
  61. // Value结构体
  62. type Value struct {
  63. }
  64. // Value结构体的一些方法
  65. // 返回结构体v中的第i个字段。如果v的类型不是结构体或者i超出了结构体的范围,则会出现panic
  66. func (v Value) Field(i int) Value
  67. //以接口类型返回v的当前值
  68. func (v Value) Interface() (i interface{})
  69. //等价于.
  70. var i interface{} = (v's underlying value)
  71.  
  72.  
  73. //通过反射方式修改结构体对象的一些方法
  74. //返回接口v包含或者指针v包含的值
  75. func (v Value) Elem() Value
  76. //判断该接口v是否可以被set修改
  77. func (v Value) CanSet() bool
  78.  
  79. //使用另外一个反射接口去修改反射值
  80. func (v Value) Set(x Value)
  81. //其他不同类型的Set
  82. func (v Value) SetBool(x bool)
  83. func (v Value) SetBytes(x []byte)
  84. func (v Value) SetFloat(x float64)
  85. func (v Value) SetInt(x int64)
  86. //设置结构体对象v的长度为n
  87. func (v Value) SetLen(n int)
  88. func (v Value) SetString(x string)
  89. //一些辅助方法
  90. //返回反射结构体的Value的类型.如果v为零值,IsValid将返回false
  91. func (v Value) Kind() Kind
  92. //判断value是否为有效值,通常用在判断某个字段是否在反射体的Value中
  93. func (v Value) IsValid() bool
  94.  
  95. //Kind常量
  96. type Kind uint
  97. const (
  98. Invalid Kind = iota
  99. Bool
  100. Int
  101. Int8
  102. Int16
  103. Int32
  104. Int64
  105. Uint
  106. Uint8
  107. Uint16
  108. Uint32
  109. Uint64
  110. Uintptr
  111. Float32
  112. Float64
  113. Complex64
  114. Complex128
  115. Array
  116. Chan
  117. Func
  118. Interface
  119. Map
  120. Ptr
  121. Slice
  122. String
  123. Struct
  124. UnsafePointer
  125. )

 

反射的基本操作

通过反射来获取结构体字段的名称以及其他相关信息。

  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. //定义结构体
  7. type User struct {
  8. Id int
  9. Name string
  10. Age int
  11. }
  12. //定义结构体方法
  13. func (u User) Hello() {
  14. fmt.Println("Hello xuxuebiao")
  15. }
  16. func main() {
  17. u := User{1, "bgops", 25}
  18. Info(u)
  19. u.Hello()
  20. }
  21. //定义一个反射函数,参数为任意类型
  22. func Info(o interface{}) {
  23. //使用反射类型获取o的Type,一个包含多个方法的interface
  24. t := reflect.TypeOf(o)
  25. //打印类型o的名称
  26. fmt.Println("type:", t.Name())
  27. //使用反射类型获取o的Value,一个空的结构体
  28. v := reflect.ValueOf(o)
  29. fmt.Println("Fields:")
  30. //t.NumField()打印结构体o的字段个数(Id,Name,Age共三个)
  31. for i := 0; i < t.NumField(); i++ {
  32. //根据结构体的下标i来获取结构体某个字段,并返回一个新的结构体
  33. /**
  34. type StructField struct {
  35. Name string
  36. PkgPath string
  37. Type Type
  38. Tag StructTag
  39. Offset uintptr
  40. Index []int
  41. Anonymous bool
  42. }
  43. **/
  44. f := t.Field(i)
  45. //使用结构体方法v.Field(i)根据下标i获取字段Value(Id,Name,Age)
  46. //在根据Value的Interface()方法获取当前的value的值(interface类型)
  47. val := v.Field(i).Interface()
  48. fmt.Printf("%6s:%v = %v\n", f.Name, f.Type, val)
  49. }
  50. //使用t.NumMethod()获取所有结构体类型的方法个数(只有Hello()一个方法)
  51. //接口Type的方法NumMethod() int
  52. for i := 0; i < t.NumMethod(); i++ {
  53. //使用t.Method(i)指定方法下标获取方法对象。返回一个Method结构体
  54. //Method(int) Method
  55. m := t.Method(i)
  56. //打印Method结构体的相关属性
  57. /*
  58. type Method struct {
  59. Name string
  60. PkgPath string
  61. Type Type
  62. Func Value
  63. Index int
  64. }
  65. */
  66. fmt.Printf("%6s:%v\n", m.Name, m.Type)
  67. }
  68. }

 

输出

  1. type: User
  2. Fields:
  3. Id:int = 1
  4. Name:string = bgops
  5. Age:int = 25
  6. Hello:func(main.User)
  7. Hello xuxuebiao

 

注意:我们上面的示例是使用值类型进行进行反射构造的。如果是指针类型的话,我们需要使用reflect.Struct字段进行判断接口类型的Kind()方法

  1. if k := t.Kind();k != reflect.Struct {
  2. fmt.Println("非值类型的反射")
  3. return
  4. }

 

匿名字段的反射以及嵌入字段

注意:反射会将匿名字段当做独立的字段去处理,需要通过类型索引方式使用FieldByIndex方法去逐个判断

  1. //根据指定索引返回对应的嵌套字段
  2. FieldByIndex(index []int) StructField
  3. type StructField struct {
  4. Name string
  5. PkgPath string
  6. Type Type
  7. Tag StructTag
  8. Offset uintptr
  9. Index []int
  10. Anonymous bool //是否为匿名字段
  11. }
  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. type User struct {
  7. Id int
  8. Name string
  9. Age int
  10. }
  11. type Manager struct {
  12. User
  13. title string
  14. }
  15. func main() {
  16. //注意匿名字段的初始化操作
  17. m := Manager{User: User{1, "biaoge", 24}, title: "hello biao"}
  18. t := reflect.TypeOf(m)
  19. fmt.Printf("%#v\n", t.FieldByIndex([]int{0}))
  20. fmt.Printf("%#v\n", t.FieldByIndex([]int{1}))
  21. fmt.Printf("%#v\n", t.FieldByIndex([]int{0, 0}))
  22. fmt.Printf("%#v\n", t.FieldByIndex([]int{0, 1}))
  23. }

输出:

  1. reflect.StructField{Name:"User", PkgPath:"", Type:(*reflect.rtype)(0x4ac660), Tag:"", Offset:0x0, Index:[]int{0}, Anonymous:true}
  2. reflect.StructField{Name:"title", PkgPath:"main", Type:(*reflect.rtype)(0x49d820), Tag:"", Offset:0x20, Index:[]int{1}, Anonymous:false}
  3. reflect.StructField{Name:"Id", PkgPath:"", Type:(*reflect.rtype)(0x49d1a0), Tag:"", Offset:0x0, Index:[]int{0}, Anonymous:false}
  4. reflect.StructField{Name:"Name", PkgPath:"", Type:(*reflect.rtype)(0x49d820), Tag:"", Offset:0x8, Index:[]int{1}, Anonymous:false}

 

通过反射修改目标对象

通过反射的方式去修改对象的某个值。需要注意的亮点是,首先,需要找到对象相关的名称,其次需要找到合适的方法去修改相应的值。

  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. func main() {
  7. x := 123
  8. v := reflect.ValueOf(&x)
  9. v.Elem().SetInt(5256)
  10. fmt.Println(x)
  11. }

 

输出:

  1. 5256
  1. 修改I的时候需要找到对象字段的名称;并且判断类型,使用相对正确的类型修改值
  1. v.FieldByName("Name");f.Kind() == reflect.String {
  2. f.SetString("test string")
  3. }

判断是否找到正确的字段名称:

  1. f := v.FieldByName("Name1")
  2. //判断反射对象Value中是否找到Name1字段
  3. if !f.IsValid() {
  4. fmt.Println("bad field")
  5. return
  6. }
  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. type User struct {
  7. Id int
  8. Name string
  9. Age int
  10. }
  11. //使用反射方式对结构体对象的修改有两个条件
  12. //1.通过指针
  13. //2.必须是可set的方法
  14. func main() {
  15. num := 123
  16. numv := reflect.ValueOf(&num)
  17. //通过Value的Elem()和SetX()方法可直接对相关的对象进行修改
  18. numv.Elem().SetInt(666)
  19. fmt.Println(num)
  20. u := User{1, "biao", 24}
  21. uu := reflect.ValueOf(&u)
  22. //Set()后面的必须是值类型
  23. //func (v Value) Set(x Value)
  24. test := User{2, "bgops", 2}
  25. testv := reflect.ValueOf(test)
  26. uu.Elem().Set(testv)
  27. fmt.Println("Change the test to u with Set(x Value)", uu)
  28. //此时的U已经被上面那个uu通过指针的方式修改了
  29. Set(&u)
  30. fmt.Println(u)
  31. }
  32. func Set(o interface{}) {
  33. v := reflect.ValueOf(o)
  34. //判断反射体值v是否是Ptr类型并且不能进行Set操作
  35. if v.Kind() == reflect.Ptr && ! v.Elem().CanSet() {
  36. fmt.Println("xxx")
  37. return
  38. //初始化对象修改后的返回值(可接受v或v的指针)
  39. } else {
  40. v = v.Elem()
  41. }
  42. //按照结构体对象的名称进行查找filed,并判断类型是否为string,然后进行Set
  43. if f := v.FieldByName("Name"); f.Kind() == reflect.String {
  44. f.SetString("BYBY")
  45. }
  46. }

输出:

  1. 666
  2. Change the test to u with Set(x Value) &{2 bgops 2}
  3. {2 BYBY 2}

通过反射进行动态方法的调用

使用反射的相关知识进行方法的动态调用

  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. type User struct {
  7. Id int
  8. Name string
  9. Age int
  10. }
  11. func (u User) Hello(name string, id int) {
  12. fmt.Printf("Hello %s,my name is %s and my id is %d\n", name, u.Name, id)
  13. }
  14. func main() {
  15. u := User{1, "biaoge", 24}
  16. fmt.Println("方法调用:")
  17. u.Hello("xuxuebiao", 121)
  18. //获取结构体类型u的Value
  19. v := reflect.ValueOf(u)
  20. //根据方法名称获取Value中的方法对象
  21. mv := v.MethodByName("Hello")
  22. //构造一个[]Value类型的变量,使用Value的Call(in []Value)方法进行动态调用method
  23. //这里其实相当于有一个Value类型的Slice,仅一个字段
  24. args := []reflect.Value{reflect.ValueOf("xuxuebiao"), reflect.ValueOf(5256)}
  25. fmt.Println("通过反射动态调用方法:")
  26. //使用Value的Call(in []Value)方法进行方法的动态调用
  27. //func (v Value) Call(in []Value) []Value
  28. //需要注意的是当v的类型不是Func的化,将会panic;同时每个输入的参数args都必须对应到Hello()方法中的每一个形参上
  29. mv.Call(args)
  30. }
  1. 方法调用:
  2. Hello xuxuebiao,my name is biaoge and my id is 121
  3. 通过反射动态调用方法:
  4. Hello xuxuebiao,my name is biaoge and my id is 5256

 

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号