reflect.New必须传结构体本身的reflect.Type,不能传指针类型;传*MyStruct会panic,正确做法是reflect.TypeOf(MyStruct{})或reflect.TypeOf(&MyStruct{}).Elem()。

reflect.New 传什么类型才不 panic
必须传结构体本身的 reflect.Type,不能传指针类型。传 *MyStruct 会直接 panic:「panic: reflect: New(nil)」或「cannot use nil as type *T」。
- ✅ 正确:
t := reflect.TypeOf(MyStruct{})→reflect.New(t) - ✅ 也正确:
t := reflect.TypeOf(&MyStruct{}).Elem()(先取指针再解引用) - ❌ 错误:
t := reflect.TypeOf(&MyStruct{})→reflect.New(t)(传的是*MyStruct,结果是**MyStruct) - ⚠️ 注意:如果从字符串查注册表(如
typeRegistry["User"]),确保注册时存的是reflect.TypeOf(User{}),不是reflect.TypeOf(&User{})
json.Unmarshal 为什么总报 “invalid argument”
因为 reflect.New 返回的是 reflect.Value,而 json.Unmarshal(或 json.Decoder.Decode)只接受 interface{} 类型的可寻址指针(即 *T)。
- ❌ 错误写法:
v := reflect.New(t); json.Unmarshal(data, v)——v是reflect.Value,不是*T - ✅ 正确写法:
v := reflect.New(t); json.Unmarshal(data, v.Interface())——.Interface()才能转成真正的*T - ⚠️ 补充:若后续要返回解码后的值(非指针),用
v.Elem().Interface(),但传给Unmarshal的必须是v.Interface()
字段赋值失败:CanSet() 返回 false 怎么办
90% 的原因是值不可寻址。反射无法修改不可寻址的副本,比如 reflect.ValueOf(MyStruct{}) 得到的值就是只读的。
- ✅ 唯一可靠路径:从
reflect.New(t)开始 →.Elem()获取可寻址的 struct 值 → 再操作字段 - ✅ 字段必须导出(首字母大写),
FieldByName("name")对小写字段返回零值,CanSet()恒为false - ❌ 不要这么做:
v := reflect.ValueOf(MyStruct{}); v.FieldByName("Name").SetString("x")—— 直接 panic - ⚠️ 小心嵌套:若字段是
*string,用field.SetString("x")前需先field.Set(reflect.ValueOf(new(string))),否则 panic
性能与适用边界:什么时候该用 reflect.New
reflect.New 比 &MyStruct{} 慢 10–20 倍,且无法内联、逃逸分析失效。它唯一不可替代的场景,是类型在编译期完全未知。
立即学习“go语言免费学习笔记(深入)”;
- ✅ 合理场景:ORM 映射(根据 SQL 结果动态构造结构体)、配置驱动插件系统(
"handler": "AuthHandler")、通用反序列化函数 - ❌ 过度使用:已知具体类型时硬套反射,比如
func CreateUser() interface{} { return reflect.New(reflect.TypeOf(User{})).Interface() } - ⚠️ 关键提醒:反射不调用任何用户定义构造逻辑(如 init 函数、自定义 NewXXX),它只做内存分配 + 零值填充;需要初始化逻辑的,必须手动补全



















