Go反射是基于interface{}的受限运行时自省机制,仅支持读取和条件修改已知结构值,不可绕过类型系统;reflect.TypeOf/ValueOf须传变量而非字面量,CanSet()为false时不可修改,序列化/ORM中仅映射编译期固化信息。
Go 反射不是“动态类型语言那种随便改类型”的能力,而是一套在 interface{} 基础上、严格受限的运行时自省机制——它只允许你读取和(在满足条件时)修改已知结构的值,不能绕过类型系统造新类型或删字段。
nic这两个函数是反射入口,但它们只接受接口值;传入未包装的原始值(比如直接传 int 字面量)看似能编译,实则隐式转成 interface{} 后再反射,容易掩盖可寻址性问题。
reflect.ValueOf(&x) 而非 reflect.ValueOf(x+1)
reflect: call of reflect.Value.Interface on zero Value —— 说明你对空 reflect.Value(比如字段不存在、map 查不到 key)调用了 Interface()
reflect.TypeOf(nil) 返回 nil 的 reflect.Type,不是 “nil 类型”,而是无类型;此时不能调 .Name() 或 .Kind(),会 panic这是反射第三律的硬约束:只有指向变量地址的 reflect.Value 才可修改。值本身不可寻址(比如结构体字段直取、map value、函数返回值),CanSet() 就返回 false,Set* 系列方法会 panic。
reflect.ValueOf(s).Field(0).SetInt(42) → panic。必须用指针:reflect.ValueOf(&s).Elem().Field(0).SetInt(42)
reflect.ValueOf(x).CanAddr() 为 true 是 CanSet() 的前提,但不充分;还需确保该值不是从只读上下文(如 map value、channel receive)来的&slice)它不解析代码,也不生成新类型,而是把结构体标签(如 json:"name")、字段顺序、嵌套关系这些编译期就固化的信息,在运行时“翻出来”做映射。
reflect.ValueOf(&v).Elem() 得到可设置的结构体值,遍历每个字段,匹配 key 名 → 找到对应 reflect.Value → 调 Set() 写入db:"id" 标签提取字段名,再用 .Interface() 转回具体类型传给 SQL 驱动json.Unmarshal 可能触发上百次反射调用;热路径(如高并发 API)应避免用反射做核心逻辑,优先用代码生成(如 easyjson)或预编译结构体描述符真正难的不是调对 reflect.ValueOf,而是判断什么时候不该用反射——比如字段名拼写错误、标签漏写、嵌套指针没解引用,这些错误全在运行时暴露,且堆栈里看不到源码行号。写反射前,先问一句:这个逻辑能不能用泛型(Go 1.18+)或接口抽象掉?