082-反射(结构体字段遍历)
Posted --Allen--
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了082-反射(结构体字段遍历)相关的知识,希望对你有一定的参考价值。
如何知道一个未知结构体包含哪些字段呢?利用反射,可以很容易做到。
1. 遍历结构体的 field 和 method
还记得 reflect.Type 接口吧,这个接口还包含这 4 个方法:
type interface Type
...
NumField() int
Field(i int) StructField
NumMethod() int
Method(int) Method
...
也就是说,只要你能拿到 Type 类型的接口值,就可以知道结构体包含了几个字段,几个方法。通过 NumField 和 Method 方法,你就可以获取关于第 i 个 field 和 method 的具体信息。
!!!注意:只有 Kind 为 Struct 的 Type 才可以调用上面这 4 个方法,否则程序会 panic.
field 和 method 的信息是通过 StructField 和 Method 类型进行描述的:
type StructField struct
// Name is the field name.
Name string
// PkgPath is the package path that qualifies a lower case (unexported)
// field name. It is empty for upper case (exported) field names.
// See https://golang.org/ref/spec#Uniqueness_of_identifiers
PkgPath string
Type Type // field type
Tag StructTag // field tag string
Offset uintptr // offset within struct, in bytes
Index []int // index sequence for Type.FieldByIndex
Anonymous bool // is an embedded field
type Method struct
// Name is the method name.
// PkgPath is the package path that qualifies a lower case (unexported)
// method name. It is empty for upper case (exported) method names.
// The combination of PkgPath and Name uniquely identifies a method
// in a method set.
// See https://golang.org/ref/spec#Uniqueness_of_identifiers
Name string
PkgPath string
Type Type // method type
Func Value // func with receiver as first argument
Index int // index for Type.Method
2. 示例
package main
import (
"fmt"
"reflect"
)
type Data struct
weight uint32
height uint32
// 定义一个结构体
type Person struct
Name string `tips:this is name`
age int32 `how old are you?`
Data
// 为 *Person 定义方法
func (p *Person) GetName() string
return p.Name
func (p *Person) GetAge() int32
return p.age
func main()
p := Person
"allen",
19,
Data
50,
180,
,
// 1. 取到 type 接口值
t := reflect.TypeOf(p)
fmt.Println("字段枚举:")
fmt.Println("------------------------------")
for i := 0; i < t.NumField(); i++
f := t.Field(i)
fmt.Printf("index:%d\\nName:%s\\nPkgPath:%s\\nType:%v\\nTag:%s\\nOffset:%v\\nIndex:%v\\nAnonymous:%v\\n",
i, f.Name, f.PkgPath, f.Type, f.Tag, f.Offset, f.Index, f.Anonymous)
fmt.Println("------------------------------")
fmt.Println("方法枚举:")
fmt.Println("------------------------------")
for i := 0; i < t.NumMethod(); i++
m := t.Method(i)
fmt.Printf("index:%d\\nName:%s\\nPkgPath:%s\\nType:%v\\nFunc:%v\\nIndex:%v\\n",
i, m.Name, m.PkgPath, m.Type, m.Func, m.Index)
fmt.Println("------------------------------")
输出结果:
字段枚举:
------------------------------
index:0
Name:Name
PkgPath:
Type:string
Tag:tips:this is name
Offset:0
Index:[0]
Anonymous:false
------------------------------
index:1
Name:age
PkgPath:main
Type:int32
Tag:how old are you?
Offset:16
Index:[1]
Anonymous:false
------------------------------
index:2
Name:Data
PkgPath:
Type:main.Data
Tag:
Offset:20
Index:[2]
Anonymous:true
------------------------------
方法枚举:
------------------------------
有同学会很好奇,为什么方法一个都没有遍历出来?不知道你是否还记得“方法的接收器”相关的知识。在上面的例子里,方法的接收器是指针类型,这意味着那两个方法并没有定义在 Person 类型上,而是定义在 *Person 上。
你使用 TypeOf(p) 拿到的类型是关于 Person 的类型信息,而不是 *Person 类型信息。因此,如果你想枚举方法,就只能使用 TypeOf(&p) 拿到关于 *Person 的类型信息,才可以正确的枚举出方法。
3. 如何打印字段对应的值
字段名也有了,那么字段的值怎么获取呢?
要知道,Type 接口只能拿到关于类型的信息,无法拿到 Value 的信息。如果想获取字段值,就只能从 Value 这个结构体入手了。同样的,Value 也提供了 4 个方法:
type struct Value
...
func (v Value) NumField() int
func (v Value) Field(i int) Value
func (v Value) NumMethod() int
func (v Value) Method(i int) Value
不过,这 4 个方法和 Type 接口那 4 个方法返回值不一样,它返回的又是 Value 类型。好了,剩下的就是码代码了,这个实在是太简单,同学们自己完全可以写出来。
4. 总结
- 知道如何遍历结构体的字段和方法
- 知道 Type 接口关于遍历字段和方法的方法与 Value 的区别
以上是关于082-反射(结构体字段遍历)的主要内容,如果未能解决你的问题,请参考以下文章
go语言学习笔记 — 进阶 — 反射:反射的类型对象(reflect.Type)— 结构体标签(struct tag)是结构体字段的额外信息标签