Go REFLECT Library | 02 - 反射的类型 Type

1,177 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第29天,点击查看活动详情

本文紧接 Go REFLECT Library | 01 - 反射的类型 Type 继续讲解通过指针的 反射类型对象 获取了指针指向的对象之后的操作

三、反射获取结构体

在通过指针的 反射类型对象 获取了指针指向的对象之后,我们就可以对这个对象进行一些操作或者获取对象信息。

指针的 反射类型对象 获取指针指向的对象的类型如果是结构体,可以通过 反射类型对象reflect.Type 的一个方法来获取结构体成员信息,比如

方法方法说明
Field(i int) StructField通过索引获取结构体中对应的字段,当不是结构体或者索引越界会 panic
NumField() int获取结构体中包含的字段的数量,不是结构体会 panic
FieldByName(name string) (StrucField, bool)根据给定的字符串返回字符串对应的结构体字段的信息,没有找到时 bool 为 false, 当不是结构体或者索引越界会 panic
FieldByIndex(index []int) StructField针对嵌套结构体,多层访问时,根据 []int 提供的每个结构体的索引依次访问,返回字段信息,没有找到返回零值,当不是结构体或者索引越界会 panic
FieldByNameFunc(match func(string) bool) (StructField, bool)根据匹配函数匹配需要的字段,当不是结构体或者索引越界会 panic
func main(){

   zulu := Zulu{"stark", 33}

   zuluPtr := &zulu

   zuluType := reflect.TypeOf(zuluPtr)

   fmt.Printf("zuluType 的类型为:%v,类型名为:%v,种类为:%v\n", zuluType, zuluType.Name(), zuluType.Kind())

   // 使用反射类型对象(Type)获取指针指向的对象
   zuluStructByReflect := zuluType.Elem()

   fmt.Printf("zuluStructByReflect 的类型为:%v,类型名为:%v,种类为:%v\n", zuluStructByReflect, zuluStructByReflect.Name(), zuluStructByReflect.Kind())

   // 结构体字段的数量
   numField := zuluStructByReflect.NumField()
   fmt.Println("反射类型对象获取的指针指向的对象的字段数量有:", numField)

   // 获取第一个字段
   firstField := zuluStructByReflect.Field(0)
   fmt.Printf("第一个字段是:%v, 类型是: %v", firstField, (reflect.TypeOf(firstField)).Name())
}

type Zulu struct {
   Name string
   Age int
}

执行上述代码,输出结果如下:

zuluType 的类型为:*main.Zulu,类型名为:,种类为:ptr
zuluStructByReflect 的类型为:main.Zulu,类型名为:Zulu,种类为:struct
反射类型对象获取的指针指向的对象的字段数量有: 2
第一个字段是:{Name  string  0 [0] false}, 类型是: StructField

zuluPtr 结构体指针指向的结构体有两个字段,并且在调用 Field(0) 方法时返回一个 StructField 结构体,该结构体包含的字段如下:

image.png

其中:

  • Name:字段名称
  • PkgPath:字段在结构体中的路径
  • Type:字段本身的反射类型对象,类型为 reflect.Type 可以进一步获取字段的类型信息
  • Tag:结构体标签
  • Index:FieldByIndex 中的索引顺序
  • Anonymous:表示该字段是否为匿名字段
func main(){

   t := Teacher{"Stark", 33, "NYC"}

   s := Stu{"Peter", 18, "HighSchool","M", t}

   sPtr := &s

   sReflectType := reflect.TypeOf(sPtr)

   fmt.Printf("sReflectType 的类型为:%v,类型名为:%v,种类为:%v\n", sReflectType, sReflectType.Name(), sReflectType.Kind())

   // 使用反射类型对象(Type)获取指针指向的对象
   sStructByReflect := sReflectType.Elem()

   fmt.Printf("sStructByReflect 的类型为:%v,类型名为:%v,种类为:%v\n", sStructByReflect, sStructByReflect.Name(), sStructByReflect.Kind())

   // 结构体字段的数量
   numField := sStructByReflect.NumField()
   fmt.Println("反射类型对象获取的指针指向的对象的字段数量有:", numField)

   // 遍历所有的字段
   for i := 0; i < numField; i++ {
      field := sStructByReflect.Field(i)
      fmt.Printf("结构体的第 %v 个字段为:%v\n", (i+1), field.Name)
   }

   // 获取内嵌结构体 Teacher 的字段
   embedFieldByIndex := sStructByReflect.FieldByIndex([]int{4, 2})
   fmt.Println("结构体字段的名称为:", embedFieldByIndex.Name)
   fmt.Println("结构体字段的路径为:", embedFieldByIndex.PkgPath)
   fmt.Println("结构体字段的类型为:", embedFieldByIndex.Type)
   fmt.Println( "结构体字段的标签为:",embedFieldByIndex.Tag)
   fmt.Println("结构体字段的索引为:", embedFieldByIndex.Index)
   fmt.Println( "结构体字段的是否为匿名:",embedFieldByIndex.Anonymous)
}

type Stu struct {
   Name string `json:"name"`
   Age int `json:"age"`
   Grade string `json:"grade"`
   Gender string `json:"gender"`
   Teacher
}

type Teacher struct {
   Name string `json:"name"`
   Age int `json:"age"`
   Address string `json:"address"`
}

执行上述代码,输出结果如下:

sReflectType 的类型为:*main.Stu,类型名为:,种类为:ptr
sStructByReflect 的类型为:main.Stu,类型名为:Stu,种类为:struct
反射类型对象获取的指针指向的对象的字段数量有: 5
结构体的第 1 个字段为:Name
结构体的第 2 个字段为:Age
结构体的第 3 个字段为:Grade
结构体的第 4 个字段为:Gender
结构体的第 5 个字段为:Teacher
结构体字段的名称为: Address
结构体字段的路径为: 
结构体字段的类型为: string
结构体字段的标签为: json:"address"
结构体字段的索引为: [2]
结构体字段的是否为匿名: false

Go 编程 | 连载 17 - 结构体方法 中提到了使用 reflect 包获取结构体标签,其实就是使用了 StructField 结构体的 Tag 字段来获取的。

标签在序列化和反序列化以及对象关系映射时都会用到结构体标签,字段调用 Tag 表返回一个 StructTag 类型

image.png

StructTag 类型的 Get 方法可以获取指定标签的内容。