reflect.TypeOf 返回类型,reflect.ValueOf 返回值;前者对 nil 接口安全,后者会 panic;调用方法需确保 receiver 可寻址且方法导出;字段读写须导出且传指针;所有操作须前置校验。

怎么用 reflect.TypeOf 和 reflect.ValueOf 获取类型与值
这两个是反射的入口函数,必须一起理解:前者只管“是什么类型”,后者才管“值是多少”,且对 nil 接口行为不同。reflect.TypeOf 安全,哪怕传入 nil 接口也能返回对应类型(比如 *int);而 reflect.ValueOf 遇到 nil 接口会 panic,所以务必确保参数非空。
- 基本变量直接传:
reflect.ValueOf(x)得到的是值拷贝,不能修改原始变量 - 要修改字段或调用指针接收者方法,必须传地址:
reflect.ValueOf(&x),再用.Elem()解引用 -
.Kind()返回底层种类(如int、struct、ptr),.Type()返回完整类型(如*main.User),别混淆
为什么调用方法总失败?重点看 receiver 可寻址性
反射调用方法失败,90% 是因为 receiver 不可寻址——即没传指针,或方法定义在指针接收者上却用了值实例。
- 值接收者方法(
func (u User) Foo())可用reflect.ValueOf(u)调用,但改不了原结构体字段 - 指针接收者方法(
func (u *User) Bar())必须用reflect.ValueOf(&u),否则.MethodByName返回无效值(.IsValid() == false),不 panic 也不报错,极难排查 - 方法名必须首字母大写(导出),小写方法反射完全不可见,连
.MethodByName都找不到
怎么安全调用函数或方法而不 panic?
反射调用不是“丢进去就跑”,它对参数数量、类型、可导出性都极其严格。盲目 .Call 很容易 runtime panic。
- 先校验函数类型:
fn.Kind() == reflect.Func - 检查参数个数:
fn.Type().NumIn() == len(args) - 逐个比对参数类型兼容性:
fn.Type().In(i).AssignableTo(args[i].Type()),注意type MyInt int和int不兼容 - 避免用
recover()拦 panic 做主逻辑,应前置校验;错误处理建议封装成带error返回的工具函数
读写结构体字段时,CanSet() 为什么总是 false?
这是最常被忽略的访问控制细节:Go 反射严格遵循语言可见性规则——未导出字段(小写开头)无法被反射写入,哪怕你拿到 reflect.Value 也无权修改。
立即学习“go语言免费学习笔记(深入)”;
- 字段名必须首字母大写(如
Name),否则.FieldByName("name")返回零值Value - 必须传结构体指针:
reflect.ValueOf(&u).Elem(),否则即使字段导出,.CanSet()仍为false - 调用
.CanSet()前,先确认.IsValid(),避免对零值操作 - 设字符串用
.SetString(),设整数用.SetInt(),类型错配直接 panic
反射不是语法糖,它是运行时绕过编译检查的“特许通道”。所有限制(导出性、可寻址性、类型严格匹配)都是设计使然,不是 bug。踩坑往往不是 API 不熟,而是忘了 Go 的访问控制和内存模型本身就在那儿挡着。










