go语言反射遍历结构体需传指针并调用.elem()获取可寻址值,用reflect.type获取字段元信息、reflect.value取值,须校验isvalid()和caninterface(),并按kind分支处理各类字段类型。

Go 语言没有运行时对象属性枚举能力,所谓“遍历结构体字段”本质是用 reflect 包在运行时查类型定义、读内存值——它不是面向对象的“对象遍历”,而是对静态结构体布局的逆向解析。
为什么传 struct{} 会 panic 而传 *struct{} 才能遍历
因为 reflect.ValueOf(s).NumField() 要求底层是可寻址的结构体值。直接传 struct{} 实例得到的是只读副本,NumField() 可能返回 0 或 panic;传 &s 后调用 .Elem() 才拿到可操作的结构体 reflect.Value。
- 错误写法:
v := reflect.ValueOf(myStruct); v.NumField()→ 返回 0 或 panic - 正确写法:
v := reflect.ValueOf(&myStruct).Elem(); v.NumField()→ 返回字段数 - 必须加
.Elem():否则v.Kind()是reflect.Ptr,不是reflect.Struct - 若已传指针但忘了
.Elem(),v.Field(i)会 panic:“cannot call Field on ptr”
如何安全读取字段名、类型、json 标签和值
reflect.Type 和 reflect.Value 必须配对使用:前者拿元信息(字段叫什么、有没有 json:"id"),后者取实际值。分开用就会“有名字没值”或“有值没标签”。
- 字段名和标签:用
t.Field(i).Name和t.Field(i).Tag.Get("json") - 字段值:用
v.Field(i).Interface(),但前提是该字段导出(首字母大写) - 私有字段(如
age int):反射无法读取,v.Field(i).CanInterface()为 false,强行调用会 panic - 嵌套结构体:先判断
v.Field(i).Kind() == reflect.Struct,再递归处理
遍历时遇到 slice、map、指针字段怎么不 panic
结构体字段类型千差万别,直接 .Interface() 容易崩溃。必须按 Kind() 分支处理,并提前校验有效性。
-
reflect.Ptr:先field.IsNil(),非 nil 再.Elem(),否则.Elem()panic -
reflect.Slice或reflect.Map:先field.Len(),再循环field.Index(j)或field.MapKeys() -
reflect.Interface:需field.Elem()解包一次才能继续,否则Kind()始终是interface - 所有字段访问前建议加
if field.IsValid() && field.CanInterface()防御
真正难的不是写通遍历逻辑,而是决定哪些字段该进、哪些该跳过——比如忽略 json:"-"、跳过 omitempty 且为空的字段、或根据自定义 tag(如 db:"ignore")过滤。这些规则没法靠反射自动推断,得你亲手编码判断。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











