反射修改结构体字段前必须确保字段可寻址且导出,传入指针并调用Elem(),检查CanSet()和Kind()匹配类型,嵌套字段需手动解引用并初始化nil指针。
Go 的 reflect.Value 只有在底层值可寻址(addressable)时,才能调用 Set* 类方法。传入非指针的结构体实例,reflect.ValueOf(s) 返回的是不可寻址的副本,此时调用 SetInt 等会 panic:reflect: reflect.Value.SetInt using unaddressable value。
reflect.ValueOf
type User struct{ Age int }
u := User{Age: 25}
v := reflect.ValueOf(u).FieldByName("Age")
v.SetInt(30) // panic!u := &User{Age: 25}
v := reflect.ValueOf(u).Elem().FieldByName("Age")
v.SetInt(30) // OKGo 反射无法修改未导出字段(小写开头),即使你传入了指针,FieldByName 也会返回零值 reflect.Value,后续 CanSet() 返回 false,Set* 直接 panic。
v.CanSet(),避免运行时 panicAge 改为 age,v := reflect.ValueOf(u).Elem().FieldByName("age") 得到的 v.IsValid() 为 true,但 v.CanSet() 为 false
SetInt、SetString、SetFloat 等方法只接受对应底层类型的值。传错类型会 panic:reflect: cannot SetInt into value of type xxx。
v.Kind() 或 v.Type(
) 做类型判断再调用对应 Set 方法int 和 int64 是不同类型;string 和 *string 更不能混用if v.Kind() == reflect.Int {
v.SetInt(42)
} else if v.Kind() == reflect.String {
v.SetString("hello")
}要修改 User.Profile.Name 这类嵌套字段,不能一步到位。必须手动 Elem() 或 Indirect() 处理中间的指针或接口,否则 FieldByName 找不到字段或返回不可寻址值。
nil 指针字段时,v.FieldByName("Profile").IsNil() 为 true,需先 Set(reflect.New(v.Type().Elem())) 初始化reflect.Indirect(v) 可自动解一层指针,但不递归;对多级嵌套仍需手动处理v.FieldByName("Profile").FieldByName("Name") 在 Profile 是 *Profile 且为 nil 时会 panic —— 必须先确保中间指针非 nil反射写入字段本身不难,真正复杂的是路径合法性校验和边界情况处理:字段是否存在、是否导出、是否可寻址、类型是否匹配、中间指针是否为空——这些都得自己兜底,标准库不会替你做。