必须传结构体指针并调用Elem()获得可寻址、可设置的reflect.Value,字段需导出才能用Field或FieldByName访问和修改,非导出字段仅可读;FieldByName依据Go源码名而非struct tag;StructTag需用tag.Get("key")解析。
怎么用
读取结构体字段值
必须先拿到结构体的
,且它得是可寻址的(比如传指针进去),否则调用
会 panic。常见错误是直接对普通变量调用
,这时返回的
是不可寻址的,
调用会失败。
正确做法是:
—— 先取地址,再用
解引用到原结构体,才能安全访问字段。
如果结构体字段是导出的(首字母大写),
可读可写;非导出字段只能读(且需通过
+
判断是否能安全转回原始类型)
返回的是
,要取真实值得调用
或对应类型方法(如
、
)
索引
从 0 开始,超出字段数会 panic,建议先用
校验
为什么
找不到字段
最常见原因是字段名拼写不匹配,或结构体字段没加导出标记。Go 反射只认「导出字段」(即首字母大写),即使你用
tag 或
tag 命名了小写字段,
也找不到
字段 —— 它根本不在反射字段列表里。
另一个容易忽略的点:字段名是 Go 源码中的名字,不是 struct tag 里的值。比如
,要用
,不是
。
立即学习
“
go语言免费学习笔记(深入)
”;
检查字段是否导出:非导出字段无法通过
访问(哪怕传指针也不行)
大小写必须完全一致,Go 反射不支持忽略大小写的查找
若不确定字段是否存在,用
替代
,避免 panic
如何通过反射修改结构体字段值
只有满足两个条件才能改:字段必须导出,且
必须可设置(
)。这意味着你几乎总是得传结构体指针进去,然后调用
。
典型错误是:写
—— 这里
不可设置,
会 panic。
正确链路:
目标字段类型必须和
方法匹配,比如对 int 字段不能用
,否则 panic
如果字段是 interface{} 类型,需先
,不能直接
用
解析 struct tag 时要注意什么
是字符串,得手动解析。别直接用
然后
,因为 tag 值可能含空格、双引号、逗号分隔多个 key:value 对,标准做法是调用
。
例如
,
返回
,
返回
。注意:如果 key 不存在,
返回空字符串,不会 panic。
只在结构体类型(
)上可用,
上没有
方法
获取 tag 要先拿到字段的
:用
或
如果 tag 值带双引号(如
),
返回的内容已自动去引号;但若写成
(无引号),则不合法,go tool 会报错
反射操作结构体字段本身不难,难在错误时机太多、panic 太突然。多数问题都出在「忘了传指针」「误把 tag 当字段名」「对非导出字段强行 set」这三类地方。写的时候多打一行
,能省掉大半调试时间。
reflect.Value.Fieldreflect.ValueFieldreflect.ValueOf(s).Field(i)ValueFieldreflect.ValueOf(&s).Elem().Field(i)Elem()Field(i)FieldByNameCanInterface()Field(i)reflect.ValueInterface()Int()String()iNumField()reflect.Value.FieldByNamejsongormFieldByName("name")nametype User struct { Name string `json:"user_name"` }FieldByName("Name")"user_name"FieldByNameFieldByNameOK("X")FieldByNamereflect.ValueCanSet() == trueElem()v := reflect.ValueOf(s); v.FieldByName("X").SetString("new")vSetStringreflect.ValueOf(&s).Elem().FieldByName("X").SetString("new")SetXXXSetStringSet(reflect.ValueOf(yourValue))SetStringreflect.StructTagStructTagstring(tag)strings.Splittag.Get("key")`json:"name,omitempty" db:"user_name"`tag.Get("json")"name,omitempty"tag.Get("db")"user_name"GetStructTagreflect.Typereflect.ValueTagreflect.StructFieldt.Field(i).Tagt.FieldByName("X").Tag`json:"id"`Get`json:id`fmt.Printf("canSet: %v, canInterface: %v\n", v.CanSet(), v.CanInterface())