用 reflect.ValueOf 获取结构体字段值最直接:先确保是 struct 类型,再调用 NumField 和 Field 遍历导出字段,非导出字段不可访问,修改需传指针。

用 reflect.ValueOf 获取结构体字段值最直接
Go 没有内置的「for range struct」语法,必须靠反射。核心是先转成 reflect.Value,再调用 NumField 和 Field 遍历——但注意:只能对 struct 类型的导出字段(首字母大写)取值,非导出字段会 panic 或返回零值。
- 必须传入指针或值本身,但若想修改字段,得传指针;只读可传值
-
reflect.ValueOf(x).Kind() == reflect.Struct是安全前提,否则NumField会 panic - 字段顺序严格按源码定义顺序,不是按字母或标签排序
- 示例:
type User struct { Name string; age int }<br>v := reflect.ValueOf(User{Name: "a", age: 12})<br>for i := 0; i < v.NumField(); i++ {<br> fmt.Println(v.Field(i).Interface()) // 输出 "a",然后 panic:无法访问 age
用 reflect.TypeOf 拿字段名、类型、tag 更可靠
如果要打印字段名、读取 json: 标签、判断是否为指针类型,就得用 reflect.Type。它和 Value 是平行的两个入口,不能混用——比如 Type.Field(i) 返回的是 StructField,含 Name、Type、Tag,但不带值。
-
reflect.TypeOf(x).Field(i).Name返回字段名(如"Name"),而reflect.ValueOf(x).Field(i).String()是值的字符串表示 - tag 解析必须用
reflect.StructTag.Get("json"),不是直接读Tag字段 - 嵌套结构体的字段不会自动展开,
Field(0)是整个内嵌 struct 的reflect.Value,需递归处理 - 常见错误:
v.Field(i).Type().Name()对匿名字段(如time.Time)返回空字符串,要用v.Field(i).Type().String()
遍历时容易 panic 的三个典型场景
反射代码跑着跑着就 crash,基本都掉在这三类坑里:
- 传了 nil 指针:
reflect.ValueOf(nil).Elem()直接 panic,检查前加if v.Kind() == reflect.Ptr && v.IsNil() - 试图读非导出字段:
v.Field(i).Interface()对小写字段 panic,改用v.Field(i).CanInterface()先判断 - 对非 struct 类型调
NumField:reflect.ValueOf(42).NumField()panic,务必先v.Kind() == reflect.Struct
性能差、别在热路径用反射遍历结构体
每次 reflect.ValueOf 都有分配开销,Field(i) 是接口转换 + 检查,比直接点字段慢 10–100 倍。真实项目中,除非是 ORM、序列化、调试工具这类“一次写、多次用”的通用逻辑,否则别在请求处理主流程里遍历字段。
立即学习“go语言免费学习笔记(深入)”;
- 如果只是想序列化,优先用
encoding/json内置逻辑,它底层也用反射,但做了缓存和优化 - 如果字段固定且不多,手写 switch 或 map 映射比反射更稳更快
- 反射获取的
reflect.Value不能直接传给fmt.Printf("%v"),可能无限递归;用fmt.Sprintf("%#v")更安全










