必须传入结构体指针并调用Elem()才能获取可写Value;直接传值仅得只读Value,CanSet()为false;遍历前需检查v.Kind()==reflect.Struct以防panic。
reflect.ValueOf 获取结构体字段值直接传入结构体实例(非指针)会得到只读的 Value,无法修改字段;若要写入,必须传入指针并调用 Elem()。常见错误是忘记解引用,导致 CanSet() == false。
&myStruct 而不是 myStruct
reflect.ValueOf(ptr).Elem() 得到可写的结构体 Value
v.Kind() == reflect.Struct,避免 panictype User struct {
Name string
Age int
}
u := User{Name: "Alice", Age: 30}
v := reflect.ValueOf(&u).Elem() // 必须 Elem()
for i := 0; i < v.NumField(); i++ {
field := v.Field(i)
if field.CanInterface() {
fmt.Printf("%s: %v\n", v.Type().Field(i).Name, field.Interface())
}
}
只有首字母大写的导出字段才能被 reflect 访问;小写字段会被跳过,且不会报错——这是最常被忽略的“静默失败”点。
v.Type().Field(i) 获取 StructField,它包含 Tag 和 Name
field.Tag.Get("json") 提取特定 tag 值,注意返回空字符串表示未设置type Config struct {
Host string `json:"host" env:"HOST"`
Port int `json:"port"`
}
c := Config{Host: "localhost", Port: 8080}
v := reflect.ValueOf(c)
t := reflect.TypeOf(c)
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
jsonTag := field.Tag.Get("json")
if jsonTag == "-" { continue } // 显式忽略
if jsonTag == "" { jsonTag = field.Name }
val := v.Field(i).Interface()
fmt.Printf("%s → %s = %v\n", field.Name, jsonTag, val)
}
reflect.Value.FieldByName 返回零值却不报错字段名大小写敏感,且只匹配导出字段;传入小写字段名(如 
"name")会返回无效 Value,其 IsValid() == false,但不会 panic —— 容易误判为“字段不存在”而跳过处理。
field.IsValid() 检查结果有效性,而非只看 nil 或空t.FieldByNameFunc 实现忽略大小写的查找(需自己实现逻辑,reflect 不内置)FieldByName
v := reflect.ValueOf(u)
nameField := v.FieldByName("Name") // OK
nameField2 := v.FieldByName("name") // Invalid: !nameField2.IsValid()
if !nameField2.IsValid() {
fmt.Println("no such exported field: name")
}
reflect 对指针、接口、切片等类型需要手动解包;直接调用 Field() 在非结构体上会 panic。典型场景是字段类型为 *OtherStruct 或 []string,必须先判断 Kind() 再分支处理。
立即学习“go语言免费学习笔记(深入)”;
Elem(),再检查是否为结构体Elem(),再判断底层类型Len() 和 Index() 遍历,不能用 NumField()
type Order struct {
User *User
Tags []string
}
o := Order{User: &User{Name: "Bob"}}
v := reflect.ValueOf(o)
for i := 0; i < v.NumField(); i++ {
f := v.Field(i)
switch f.Kind() {
case reflect.Ptr:
if !f.IsNil() && f.Elem().Kind() == reflect.Struct {
// 递归处理 *User
}
case reflect.Slice:
for j := 0; j < f.Len(); j++ {
fmt.Println("tag:", f.Index(j).Interface())
}
}
}
字段名大小写、指针解引用、IsValid() 检查——这三个点在真实项目里最容易漏掉,一漏就是运行时静默异常或 panic。