reflect.New必须传入reflect.Type而非值,正确用法是reflect.New(reflect.TypeOf(MyStruct{}))或reflect.New(reflect.TypeOf(&MyStruct{}).Elem()),返回的reflect.Value需调用Interface()转为Go值。

reflect.New 传入的必须是类型,不是值
很多人第一次用 reflect.New 时直接传结构体变量,结果 panic:reflect.New(nil) 或 reflect: New of non-type。这是因为 reflect.New 要的是 reflect.Type,不是实例本身。
正确做法是先用 reflect.TypeOf 拿到类型,再传给 reflect.New;如果只有结构体字面量,得先声明类型或用指针取类型:
-
reflect.New(reflect.TypeOf(MyStruct{}).Type)❌ 错误:TypeOf返回的是reflect.Type,但Type字段不存在(它本身就是Type) -
reflect.New(reflect.TypeOf(MyStruct{}))✅ 正确:reflect.TypeOf返回的就是reflect.Type -
reflect.New(reflect.TypeOf(&MyStruct{}).Elem())✅ 也对:取指针的元素类型,等价于结构体本身类型
创建后要 .Interface() 才能转回 Go 值
reflect.New 返回的是 reflect.Value,代表一个指针;不调用 .Interface() 就没法当普通结构体用,也不能直接赋值给目标类型变量。
常见错误是拿到 reflect.Value 后直接类型断言,比如 v.(MyStruct) —— 这会 panic,因为 v 是指针类型的 reflect.Value,其底层是 *MyStruct,不是 MyStruct。
立即学习“go语言免费学习笔记(深入)”;
- 要得到可赋值的指针:用
v.Interface(),然后类型断言为*MyStruct - 要得到解引用后的值(非指针):用
v.Elem().Interface(),断言为MyStruct - 如果后续要设置字段,必须保证是可寻址的(
reflect.New创建的天然可寻址),否则.FieldByName("X").Set会 panic
字段名必须导出才能用反射设置
即使你用 reflect.New 成功创建了结构体实例,如果想通过反射修改字段,小写开头的字段(未导出)完全不可见、不可设值 —— .FieldByName 返回零值 reflect.Value,调用 .Set 会 panic:reflect: reflect.Value.Set using unaddressable value。
这不是 reflect.New 的问题,而是 Go 反射的访问规则限制:
- 只有首字母大写的字段才出现在
reflect.Type的字段列表中 -
struct{ x int }中的x在反射里根本“不存在” - 如果必须操作私有字段,只能用
unsafe(不推荐)或改结构体定义
性能敏感场景下避免高频调用 reflect.New
reflect.New 内部涉及类型检查、内存分配和反射对象构建,比直接写 &MyStruct{} 慢 10–100 倍(取决于结构体大小)。在循环、HTTP handler、高频事件中反复调用,容易成为瓶颈。
更稳妥的做法是缓存 reflect.Type,甚至提前构造好 reflect.Value 模板:
- 把
reflect.TypeOf(T{})提到包级变量或初始化函数里,避免每次重复计算 - 如果只是填充固定结构体,考虑用
sync.Pool复用已分配的实例,而不是每次都reflect.New - 真要动态类型 + 高性能,优先考虑代码生成(
go:generate+text/template),而非运行时反射
反射不是语法糖,它是绕过编译期类型检查的逃生通道 —— 用的时候得清楚自己在跳哪道墙,以及落地时有没有缓冲垫。










