reflect.Value.Set要求值可寻址,否则panic;必须传指针并Elem()解引用;结构体字段需导出;Kind()用于类型分支判断,Name()返回定义名;反射调用方法需receiver匹配且参数严格对应;避免热路径重复调用TypeOf/ValueOf。

reflect.Value.Set 要求值可寻址,否则 panic
直接对 reflect.ValueOf(x) 的结果调用 SetInt() 会触发 panic: reflect: cannot set —— 这是最常踩的坑。根本原因是:非指针传入的值在反射中是只读副本,无法写回原变量。
- 必须传入指针:用
reflect.ValueOf(&x)获取指针的Value,再用.Elem()解引用得到可设置的Value - 结构体字段同理:要修改
user.Name,得先确保传入的是&user,再通过.FieldByName("Name")获取字段Value,且该字段必须是导出字段(首字母大写) - 切片、map、channel 等引用类型本身可修改内容,但不能用
Set替换整个值;只有指针指向的底层值才支持Set
Type.Kind() 和 Type.Name() 不是一回事
比如 type MyInt int,reflect.TypeOf(MyInt(0)).Name() 返回 "MyInt",而 .Kind() 返回 int。很多泛型序列化逻辑依赖 Kind() 判断基础类别(Struct、Slice、Map),而非 Name()。
-
Kind()是运行时底层类型分类,稳定可靠,适合做分支判断 -
Name()是用户定义的类型名,可能为空(如匿名结构体)、或与底层类型不一致,不适合用于逻辑分发 - 嵌套类型如
*[]string,.Kind()是Ptr,需连续调用.Elem()才能到Slice;.Name()对指针/切片等返回空字符串
反射调用方法必须满足 receiver 可寻址 + 方法存在
reflect.Value.Call 不是万能的:如果目标方法是值接收者,传入的 Value 必须是可寻址的(即来自指针);如果是指针接收者,则必须传指针——否则 Call 会静默失败或 panic。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 检查方法是否存在:用
v.MethodByName("MethodName").IsValid(),而非直接Call - 参数必须是
[]reflect.Value,每个元素需与方法签名严格匹配;传int却给reflect.ValueOf(int64)会 panic - 方法返回值是
[]reflect.Value,需手动解包;错误处理必须显式检查len(results) > 0 && !results[0].IsNil()
性能敏感场景下,避免在热路径反复调用 reflect.TypeOf / reflect.ValueOf
这两个函数开销不小:每次调用都要做接口拆包、类型哈希查找、内存分配。实测在循环内调用,比直接类型断言慢 10–20 倍。
立即学习“go语言免费学习笔记(深入)”;
- 提前缓存
Type和Value:对固定类型结构体,可在初始化阶段调用一次reflect.TypeOf(T{})并复用 - 用
switch v.(type)替代反射做简单类型分发;反射只留给真正动态的场景(如 ORM 映射、通用 JSON 解析) - 注意
reflect.Value持有原始数据引用,长期持有可能阻止 GC;临时使用后尽快丢弃

















