全部学科
NodeJS全栈
nodejs
Python全栈
python
小程序首页
📅 2026-05-14 7 分钟 ✍️ juanwangdev

Go反射与代码生成

Go反射在运行时检查类型和值,实现通用代码逻辑。

reflect包核心

TypeOf和ValueOf

Go
import "reflect"

// 获取类型信息
t := reflect.TypeOf(42)
fmt.Println(t.Name())  // int
fmt.Println(t.Kind())  // int

// 获取值信息
v := reflect.ValueOf(42)
fmt.Println(v.Int())   // 42
fmt.Println(v.Type())  // int

Kind类型分类

Go
const (
    Invalid Kind = iota
    Bool
    Int
    Int8
    Int16
    Int32
    Int64
    Uint
    Uint8
    Uint16
    Uint32
    Uint64
    Uintptr
    Float32
    Float64
    Complex64
    Complex128
    Array
    Chan
    Func
    Interface
    Map
    Pointer
    Slice
    String
    Struct
    UnsafePointer
)

// Kind是底层类型
// Type是具体类型名

类型信息操作

获取类型详情

Go
type User struct {
    Name string
    Age  int
}

u := User{Name: "Tom", Age: 25}
t := reflect.TypeOf(u)

// 类型名称
fmt.Println(t.Name())  // User
fmt.Println(t.Kind())  // struct

// 结构体字段数量
fmt.Println(t.NumField())  // 2

// 遍历字段
for i := 0; i < t.NumField(); i++ {
    field := t.Field(i)
    fmt.Println(field.Name, field.Type)
}
// Name string
// Age int

值信息操作

获取和修改值

Go
x := 10
v := reflect.ValueOf(&x).Elem()  // 传递指针获取可修改值

// 检查是否可修改
if v.CanSet() {
    v.SetInt(20)
}
fmt.Println(x)  // 20

// 直接传值无法修改
v = reflect.ValueOf(x)
// v.SetInt(20)  // panic! 不可修改

传递指针并使用Elem()才能修改值。

读取结构体字段

Go
u := User{Name: "Tom", Age: 25}
v := reflect.ValueOf(u)

// 按字段名读取
name := v.FieldByName("Name").String()
age := v.FieldByName("Age").Int()
fmt.Println(name, age)  // Tom 25

// 按索引读取
for i := 0; i < v.NumField(); i++ {
    field := v.Field(i)
    fmt.Println(field)
}

常用反射函数

函数用途返回值
TypeOf获取类型reflect.Type
ValueOf获取值reflect.Value
Name类型名string
Kind底层类型reflect.Kind
NumField字段数量int
Field获取字段StructField
FieldByName按名获取字段StructField
CanSet是否可修改bool
Elem指针指向值reflect.Value

反射应用场景

通用序列化

Go
func marshal(obj interface{}) (string, error) {
    v := reflect.ValueOf(obj)
    t := reflect.TypeOf(obj)

    if t.Kind() != reflect.Struct {
        return "", errors.New("只支持结构体")
    }

    result := ""
    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        value := v.Field(i)
        result += fmt.Sprintf("%s=%v,", field.Name, value)
    }
    return result, nil
}

动态调用方法

Go
func callMethod(obj interface{}, name string, args ...interface{}) ([]interface{}, error) {
    v := reflect.ValueOf(obj)
    method := v.MethodByName(name)

    if !method.IsValid() {
        return nil, errors.New("方法不存在")
    }

    // 构造参数
    in := make([]reflect.Value, len(args))
    for i, arg := range args {
        in[i] = reflect.ValueOf(arg)
    }

    // 调用方法
    out := method.Call(in)
    result := make([]interface{}, len(out))
    for i, v := range out {
        result[i] = v.Interface()
    }
    return result, nil
}

反射限制

性能开销

Go
// 反射比直接调用慢约10-100倍
// 原因:
// 1. 运行时类型检查
// 2. 动态方法查找
// 3. 额外的内存分配

// 建议:热路径避免反射

类型安全

Go
// 反射绕过编译时类型检查
// 可能导致运行时panic

v := reflect.ValueOf(42)
v.String()  // panic! int不能转为string

// 必须检查Kind
if v.Kind() == reflect.String {
    s := v.String()
}

反射最佳实践

适用场景

Go
// ✓ 适合反射:
// 1. JSON/XML序列化框架
// 2. ORM数据库映射
// 3. 通用配置加载
// 4. 插件系统
// 5. 测试工具

// ✗ 不适合反射:
// 1. 热路径高频调用
// 2. 性能敏感代码
// 3. 简单已知类型操作

安全检查

Go
func safeGetInt(v reflect.Value) (int, error) {
    if v.Kind() != reflect.Int {
        return 0, fmt.Errorf("不是int类型: %v", v.Kind())
    }
    return int(v.Int()), nil
}

要点总结

  • reflect.TypeOf获取类型信息
  • reflect.ValueOf获取值信息
  • Kind是底层类型,Name是类型名
  • 传指针+Elem()才能修改值
  • CanSet()检查是否可修改
  • NumField获取结构体字段数量
  • FieldByName按名称获取字段
  • 反射有性能开销,热路径避免使用
  • 反射绕过类型检查,需谨慎使用
  • 适用:序列化、ORM、通用框架

📝 发现内容有误?点击此处直接编辑

← 上一篇 Go代码格式化与静态分析工具
下一篇 → Go文档生成
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库