当前位置:   article > 正文

go reflect struct 遍历,反射

go 遍历所有属性

reflect 打印属性

  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. type User struct {
  7. Id int `json:"id"`
  8. Name string `json:"name"`
  9. addr string `json:"_"`
  10. }
  11. func main(){
  12. u := User{Id:1001, Name:"aaa" , addr:"bbb"}
  13. t := reflect.TypeOf(u)
  14. v := reflect.ValueOf(u)
  15. for i := 0; i < v.NumField(); i++ {
  16. if v.Field(i).CanInterface() { //判断是否为可导出字段
  17. fmt.Printf("%s %s = %v -tag:%s \n",
  18. t.Field(i).Name,
  19. t.Field(i).Type,
  20. v.Field(i).Interface(),
  21. t.Field(i).Tag)
  22. }
  23. }
  24. }
  25. //结果
  26. Id int = 1001 -tag:json:"id"
  27. Name string = aaa -tag:json:"name"

注:当结构体中含有非导出字段时,v.Field(i).Interface()会panic,

所以使用v.Field(i).CanInterface()先判断一下

struct 嵌套结构遍历

  1. package main
  2. import (
  3. "reflect"
  4. "fmt"
  5. )
  6. type User struct {
  7. Id int
  8. Name string
  9. Address Address
  10. }
  11. type Address struct {
  12. Add string
  13. Res int
  14. }
  15. func main(){
  16. u := User{Id:1001, Name:"aaa" , Address:Address{Add:"ccccccccc", Res:12}}
  17. t := reflect.TypeOf(u)
  18. v := reflect.ValueOf(u)
  19. for i := 0; i < v.NumField(); i++ {
  20. if v.Field(i).CanInterface() { //判断是否为可导出字段
  21. //判断是否是嵌套结构
  22. if v.Field(i).Type().Kind() == reflect.Struct{
  23. structField := v.Field(i).Type()
  24. for j :=0 ; j< structField.NumField(); j++ {
  25. fmt.Printf("%s %s = %v -tag:%s \n",
  26. structField.Field(j).Name,
  27. structField.Field(j).Type,
  28. v.Field(i).Field(j).Interface(),
  29. structField.Field(j).Tag)
  30. }
  31. continue
  32. }
  33. fmt.Printf("%s %s = %v -tag:%s \n",
  34. t.Field(i).Name,
  35. t.Field(i).Type,
  36. v.Field(i).Interface(),
  37. t.Field(i).Tag)
  38. }
  39. }
  40. }
  41. //结果
  42. Id int = 1001 -tag:
  43. Name string = aaa -tag:
  44. Add string = ccccccccc -tag:
  45. Res int = 12 -tag:

根据名字获取 嵌套结构属性值

func Field(i int) StructField //使用索引来访问字段,索引从0开始,如果越界将panic

func FieldByName(name string) (StructField,bool) //使用名称来访问字段,如果未找到那么返回false

func FieldByNameFunc(match func(string) bool) (StructField,bool) //访问名称使得match函数返回true的字段,在同一个内嵌层次上,只能有一个字段使得match返回true。如果同一层次上多个字段使得match返回true,那么这些字段都认为是不符合要求的

func FieldByIndex(index []int) StructField //这个方法使得访问结构的内嵌字段成为可能。将访问各个层次的字段的索引排列起来,就形成了一个[]int,参数index不可越界,否则panic

  1. package main
  2. import (
  3. "reflect"
  4. "fmt"
  5. )
  6. type Test struct {
  7. Name string
  8. User User
  9. }
  10. type User struct {
  11. Age int
  12. Name string
  13. }
  14. var test Test = Test{
  15. Name:"aaa",
  16. User:User{
  17. Name:"bbb",
  18. Age:12,
  19. },
  20. }
  21. func main() {
  22. sysConfig := reflect.ValueOf(&test).Elem()
  23. fmt.Println(sysConfig.FieldByIndex([]int{1})) //打印User层属性
  24. //fmt.Println(sysConfig.FieldByName("Age")) //直接打印下一层不成功
  25. flag := sysConfig.FieldByName("Age") == reflect.Value{}
  26. if flag {
  27. fmt.Println(sysConfig.FieldByIndex([]int{1}).FieldByName("Age")) //12
  28. }
  29. }

当再加一层, FieldByIndex([]int{1, 2}), 1和2都要加上才可以,直接就写2不行。第0层要使用其他的field方法。

  1. package main
  2. import (
  3. "reflect"
  4. "fmt"
  5. )
  6. type Test struct {
  7. Name string
  8. User User
  9. }
  10. type User struct {
  11. Age int
  12. Name string
  13. Addr Addr
  14. }
  15. type Addr struct {
  16. Address string
  17. }
  18. var test Test = Test{
  19. Name:"aaa",
  20. User:User{
  21. Name:"bbb",
  22. Age:12,
  23. Addr:Addr{
  24. Address:"dddddd",
  25. },
  26. },
  27. }
  28. func main() {
  29. var val reflect.Value
  30. sysConfig := reflect.ValueOf(&test).Elem()
  31. fmt.Println(sysConfig.FieldByIndex([]int{1, 2})) //Addr
  32. //fmt.Println(sysConfig.FieldByName("Age"))
  33. flag := sysConfig.FieldByName("Address") == val
  34. if flag {
  35. fmt.Println(sysConfig.FieldByIndex([]int{1, 2}).FieldByName("Address")) //ddddd
  36. }
  37. }

反射使用情况总结

1.反射只能反射单层结构,如果struct又包含了一个struct那么,需要先遍历前面这个,获取后面struct的指针活元素才能遍历。

  1. type Test struct {
  2. Name string
  3. Test2 Test2
  4. }
  5. type Test2 struct {
  6. Add string
  7. }
  8. //遍历Test 获得Test2指针或者元素才能遍历Test2

reflect 打印方法

  • 打印方法最大的问题是方法有指针方法和struct方法之分, 也就是在写这个func (m *M) Pri() 加不加 *的问题.

  • 这个再调用的时候感觉是差不多的,但是反射的时候差别很大,在获取 NumMethod() 时获得的数量是不同的.(在下面"相似方法"进行详细描述)

  • 指针方法struct方法 类似于 Java中的 对象方法类方法 。指针方法类同与对象方法,只有是指针的时候才能调用。


详细分析

TypeOf 常用方法
  • Name() string : 返回类型名称
  • PkgPath() string : 返回类型所在包的路径。
  • Kind() Kind: 返回 Type 的类型,是下列值之一。
  1. const (
  2. Invalid Kind = iota
  3. Bool
  4. Int
  5. Int8
  6. Int16
  7. Int32
  8. Int64
  9. Uint
  10. Uint8
  11. Uint16
  12. Uint32
  13. Uint64
  14. Uintptr
  15. Float32
  16. Float64
  17. Complex64
  18. Complex128
  19. Array
  20. Chan
  21. Func
  22. Interface
  23. Map
  24. Ptr
  25. Slice
  26. String
  27. Struct
  28. UnsafePointer
  29. )
  • NumMethod() int:返回类型的函数个数。
  • Method(n int) Method:返回类型的第 n 个函数。
  • MethodByName(string) (Method, bool) : 根据名称返回 Type 的指定函数。
  • NumOut() int:返回函数类型的返回值的数量,如果不是函数类型,将会产生一个错误。
  • Out(i int) Type:返回函数类型的第 i 个返回值,如果不是函数类型,将产生一个错误。
  • NumIn() int:返回函数类型的输入参数数量。
  • In(i int) Type:返回函数类型的第 i 个输入参数,如果不是函数类型将产生一个错误。
  • Elem() Type:通常在我们反射的对像是指针类型时,使用该函数返回指针所指向的对像的类型。
  • NumField() int:返回结构体类型的字段数量。
  • Field(i int) StructField:返回结构体类型的第 i 个字段。
  • FieldByName(name string) (StructField, bool):按名称返回结构体的字段
相似方法辨析
Kind() : 一共27个反射类别

//底层都是 Kind(f & (1 << 5 -1))所以结果相同, 因为2进制31为 00011111 验证 27 个种类的位数正好是这么多.

** 但是不是很清楚28的情况怎么处理不越界 **, 懂了后补充.

  1. // TypeOf
  2. func (t *rtype) Kind() Kind { return Kind(t.kind & kindMask) }
  3. kindMask = (1 << 5) - 1
  4. // ValueOf
  5. const (
  6. flagKindWidth = 5 // there are 27 kinds
  7. flagKindMask flag = 1<<flagKindWidth - 1
  8. )
  9. func (f flag) kind() Kind {
  10. return Kind(f & flagKindMask)
  11. }
NumMethod()

获取方法数量,当传入的不是指针的时候, 只能获得struct方法,而不能获得指针方法.

  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. type MyStruct struct{
  7. name string
  8. }
  9. func (this *MyStruct)GetName() string {
  10. return this.name
  11. }
  12. func (this MyStruct) Pri() {
  13. fmt.Println(this)
  14. }
  15. func main() {
  16. s := MyStruct{name:"cc"}
  17. t := reflect.TypeOf(s)
  18. v := reflect.ValueOf(s)
  19. //当传入的是struct
  20. fmt.Println(t.NumMethod()) // 1
  21. fmt.Println(v.NumMethod()) // 1
  22. s1 := &MyStruct{name:"cc"} //这里等同于 new(MyStruct), 传入指针
  23. t1 := reflect.TypeOf(s1) //如果是 s 那么这里要传 &s
  24. v1 := reflect.ValueOf(s1)
  25. //传入的是指针, 所以指针方法和struct方法类似
  26. fmt.Println(t1.NumMethod()) // 2
  27. fmt.Println(v1.NumMethod()) // 2
  28. //typeOf 提供了将struct变成指针的方法
  29. t = reflect.PtrTo(t) //将t变为指向t的指针
  30. fmt.Println(t.NumMethod()) // 2
  31. fmt.Println(v.NumMethod()) // 1
  32. //这种是将指针变成struct的方法
  33. if t.Kind() == reflect.Ptr {
  34. t = t.Elem() //将t变成指向的对象
  35. fmt.Println("-----------")
  36. }
  37. fmt.Println(t.NumMethod()) // 1
  38. fmt.Println(v.NumMethod()) // 1
  39. //value 有将指针变成 struct的方法, 没有struct变成指针的方法
  40. if v1.Kind() == reflect.Ptr {
  41. v1 = v1.Elem() //将t变成指向的对象
  42. fmt.Println("-----------")
  43. }
  44. //t1 依然是指针, 但是v1已经变成struct了
  45. fmt.Println(t1.NumMethod()) // 2
  46. fmt.Println(v1.NumMethod()) // 1
  47. }

Go语言中反射包的实现原理(The Laws of Reflection)

The Laws of Reflection

http://studygolang.com/articles/1251

转载于:https://my.oschina.net/solate/blog/715681

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/煮酒与君饮/article/detail/907913
推荐阅读
相关标签
  

闽ICP备14008679号