go反射修改变量值必须传入指针,因不可寻址变量调用set会panic;结构体字段需可导出且实例可寻址;set要求类型完全匹配,方法调用需接收者类型一致。

反射修改变量值前必须传入指针
Go 的反射无法直接修改不可寻址的变量值,reflect.ValueOf() 对普通变量(如 int、string 字面量或栈上副本)返回的是不可寻址的 Value,调用 Set() 会 panic:reflect: reflect.Value.Set using unaddressable value。
正确做法是:传入变量的地址,再用 reflect.Value.Elem() 获取其指向的可寻址值。
- 错误写法:
v := reflect.ValueOf(x); v.Set(reflect.ValueOf(42))→ panic - 正确写法:
v := reflect.ValueOf(&x).Elem(); v.Set(reflect.ValueOf(42)) - 若原变量是接口类型(如
interface{}),需先确认它底层是否持有一个可寻址值,否则仍会失败
Set() 要求类型完全匹配,不能自动转换
reflect.Value.Set() 不做隐式类型转换。即使两个类型底层相同(如 type MyInt int 和 int),只要不是同一类型,就会 panic:reflect: cannot set … of type … to … of type …。
- 必须确保源
Value的类型与目标Value类型一致:v.Type() == src.Type() - 常见绕法:用
Convert()转换(仅限可转换类型,且需类型在同一个包中定义或满足 Go 类型转换规则) - 例如:
v.Set(src.Convert(v.Type())),但src必须能合法转为v.Type(),否则 panic - 对结构体字段赋值时,字段本身也必须是可导出的(首字母大写),否则
Field(i)返回不可寻址的Value
修改结构体字段要同时满足可寻址 + 可导出
想通过反射修改结构体某个字段,该字段不仅得是导出字段(否则 FieldByName() 返回零值 Value),整个结构体实例还必须是可寻址的(即传指针进去)。
立即学习“go语言免费学习笔记(深入)”;
type User struct {
Name string // ✅ 导出,可修改
age int // ❌ 非导出,FieldByName("age") 返回无效 Value
}
u := &User{Name: "Alice"}
v := reflect.ValueOf(u).Elem()
nameField := v.FieldByName("Name")
if nameField.CanSet() { // true
nameField.SetString("Bob")
}
-
CanSet()是安全检查的必调方法,返回false时不要强行Set - 嵌套结构体字段需逐层
Elem(),比如v.Field(0).Elem().FieldByName("X") - 切片/映射/通道等引用类型字段,修改其内容(如
SetMapIndex)不依赖CanSet,但修改字段本身(如把整个 map 换成另一个 map)仍需要CanSet
反射调用方法需注意接收者类型匹配
用 reflect.Value.Call() 调用方法时,方法接收者类型必须与反射值类型一致:值方法要求传值(或指针解引用后是值),指针方法要求传指针。传错会导致 panic 或静默失败(如方法没执行)。
- 若方法定义为
func (u *User) Save() {},则必须用reflect.ValueOf(&u).MethodByName("Save").Call(nil) - 若误用
reflect.ValueOf(u).MethodByName("Save"),会报reflect: Call using nil *User as type *User(因为值拷贝后指针为 nil) - 方法参数需包装为
[]reflect.Value,每个元素类型也必须严格匹配,否则 panic - 返回值是
[]reflect.Value,需手动取[0].Interface()转回原类型










