Go中遍历结构体字段需用reflect.ValueOf和reflect.TypeOf获取反射对象,推荐传指针以支持读写;通过NumField()和Field(i)遍历,注意未导出字段不可Interface();需用CanInterface()校验安全性,并按Kind()分支处理类型。

在 Go 中,reflect 包是实现运行时类型检查和结构体字段动态访问的核心工具。要遍历结构体字段并动态获取对象信息,关键在于正确使用 reflect.ValueOf 和 reflect.TypeOf,并注意导出(首字母大写)字段的可见性限制。
获取结构体类型与值的反射对象
必须先将目标变量转为 reflect.Type 和 reflect.Value。注意:传入的必须是具体值或指针;若传非指针值,无法修改字段,且对嵌套结构体的深层访问可能受限。
- 用
reflect.TypeOf(v).Kind() == reflect.Struct确认类型是结构体 - 推荐传指针(如
&obj),便于后续读写字段;若只读,传值也可行 -
reflect.ValueOf(v).Elem()只在 v 是指针时才需调用,用于获取被指向的值
遍历字段并提取基本信息
通过 NumField() 获取字段数量,再用 Field(i) 和 FieldByName(name) 访问单个字段。每个字段对应一个 reflect.StructField(类型信息)和 reflect.Value(值信息)。
-
reflect.Type.Field(i)返回字段名、类型、tag(如json:"user_id")等元数据 -
reflect.Value.Field(i)返回该字段的可寻址值(需确保原始值可寻址,否则 panic) - 字段名首字母小写(未导出)时,
Field(i)可读元数据,但Field(i).Interface()会 panic —— 运行时不可访问
安全读取字段值并处理常见类型
调用 .Interface() 前,建议先用 .CanInterface() 或 .CanAddr() 判断是否可安全转换。对基础类型、切片、map、指针等,需按类型分支处理。
立即学习“go语言免费学习笔记(深入)”;
- 用
v.Kind()判断底层类型(如reflect.String,reflect.Int) - 对 slice/map,可用
v.Len()和v.Index(i)或v.MapKeys()迭代 - 对嵌套结构体,递归调用相同逻辑即可(注意循环引用保护)
- 读取 struct tag 示例:
sf.Tag.Get("json"),其中sf是StructField
实用示例:打印结构体所有导出字段名与值
以下是一个简洁可靠的遍历函数:
func PrintStructFields(v interface{}) {
val := reflect.ValueOf(v)
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
if val.Kind() != reflect.Struct {
fmt.Println("not a struct")
return
}
typ := val.Type()
for i := 0; i < val.NumField(); i++ {
field := typ.Field(i)
value := val.Field(i)
if !value.CanInterface() {
continue // 跳过不可导出字段的值访问
}
fmt.Printf("Field: %s, Type: %v, Value: %v, JSON tag: %s\n",
field.Name,
field.Type,
value.Interface(),
field.Tag.Get("json"))
}
}










