跳到主要内容

Reflection(反射)

反射让程序在运行时检查类型和值。reflect.Type 描述类型,reflect.Value 访问值;编码器、测试工具和通用框架会使用它。反射应集中在边界层,能用接口、泛型或显式分支解决时优先使用那些方案。

读取类型和值

package main

import (
"fmt"
"reflect"
)

func inspect(value any) {
t := reflect.TypeOf(value)
if t == nil {
fmt.Println("nil")
return
}
v := reflect.ValueOf(value)
fmt.Println(t, v.Kind(), v.IsValid())
}

func main() { inspect(map[string]int{"count": 1}) }

TypeOf(nil) 返回 nil;零 ValueIsValid 为 false。Kind 表示底层种类,不是定义类型名称;要识别业务定义类型,还应检查 Type.NamePkgPath 或显式协议。

修改值

只有可寻址且可设置的值才能通过 Set 修改,通常需要传入非 nil 指针并调用 Elem

func setInt(target any, value int) error {
v := reflect.ValueOf(target)
if !v.IsValid() || v.Kind() != reflect.Pointer || v.IsNil() {
return fmt.Errorf("target must be a non-nil *int")
}
element := v.Elem()
if element.Kind() != reflect.Int || !element.CanSet() {
return fmt.Errorf("target must point to a settable int")
}
element.SetInt(int64(value))
return nil
}

片段需要导入 fmt,并应由调用方传入 *int 进行验证,例如 value := 0; setInt(&value, 3)

反射访问未导出字段、对类型不匹配的值调用 Set,或在不可安全转换的值上调用 Interface,都可能 panic。CanInterfaceCanSetIsValid 是边界检查的一部分,而不是可选装饰。

常见错误

  • 忽略 nil、IsValidCanSetCanInterface 检查。
  • 假设 Kind 就是业务定义类型名称。
  • 在热路径频繁反射,未通过 benchmark 评估开销。
  • 用反射绕过未导出字段的可见性和类型设计。
  • 用反射实现本来可以由接口或泛型表达的固定协议。

TypeScript 的类型反射大多发生在编译器或装饰器工具中;Go 的 reflect 是运行时 API,代价和 panic 风险由程序承担。

小结

  • Type 看类型,Value 看运行时值。
  • Value 操作前必须验证有效性、可设置性和可导出边界。
  • 只在运行时结构未知且边界集中可控时使用反射。