用reflect.ValueOf获取结构体值后需调用.Elem()解引用指针,再通过.NumField()和.Field(i)遍历字段值,字段名须用.Type().Field(i).Name获取。

Go反射怎么拿到结构体字段名和值
用 reflect.ValueOf 获取结构体的 reflect.Value,再调用 .NumField() 和 .Field(i) 遍历字段;字段名必须通过 .Type().Field(i).Name 获取,不能从 Value 直接读——因为 Value 不带类型元信息。
常见错误:对指针结构体直接传给 reflect.ValueOf,结果得到的是指针的 Value,.NumField() 会 panic。得先 .Elem() 解引用:
type User struct {
Name string
Age int
}
u := &User{"Alice", 30}
v := reflect.ValueOf(u).Elem() // 必须 Elem()
for i := 0; i < v.NumField(); i++ {
field := v.Field(i)
name := v.Type().Field(i).Name
fmt.Printf("%s: %v\n", name, field.Interface())
}
为什么有些字段反射读不到(空值或 panic)
字段必须是导出的(首字母大写),否则 reflect.Value.Field(i) 会 panic:“cannot set unexported field”。小写字母开头的字段在反射中不可见,哪怕你只是想读取。
另外注意 nil 指针解引用也会 panic:reflect.ValueOf(nil).Elem() 直接崩溃。安全做法是加判断:
- 检查
v.Kind() == reflect.Ptr且v.IsNil() == false再.Elem() - 检查
v.Kind() == reflect.Struct才调.NumField() - 用
v.CanInterface()判断是否允许转回原始类型(避免 “call of reflect.Value.Interface on zero Value”)
struct tag 怎么通过反射读取
结构体字段的 tag(比如 `json:"name"`)不在 Value 上,而属于类型定义,必须用 reflect.Type.Field(i).Tag 获取。常用方法是 tag.Get("json") 或 tag.Lookup("yaml")。
注意:tag 字符串需用反引号包裹,且 key 区分大小写;tag.Get("JSON") 返回空字符串,不是 "name"。
type Config struct {
Host string `json:"host" yaml:"server"`
Port int `json:"port"`
}
t := reflect.TypeOf(Config{})
field := t.Field(0)
fmt.Println(field.Tag.Get("json")) // "host"
fmt.Println(field.Tag.Get("yaml")) // "server"
反射读结构体性能差在哪,什么情况下该避免
反射涉及运行时类型查找、内存布局解析、接口转换,比直接字段访问慢 10–100 倍。尤其在高频路径(如 HTTP 中间件、序列化循环)中,反复调用 reflect.Value.Field(i).Interface() 会显著拖慢吞吐。
真正需要反射的典型场景只有:通用序列化(json/xml 编码)、ORM 字段映射、配置绑定;其余多数情况可改用接口 + 方法,或代码生成(如 stringer、easyjson)绕过反射。
一个容易被忽略的点:reflect.Value 是非类型安全的,一旦字段类型变更(比如 int 改成 int64),编译器不报错,但运行时 .Int() 或 .String() 调用会 panic —— 这类问题很难在单元测试里覆盖全。










