导航菜单

反射(Reflection)是 Go 语言在运行时检查和操作类型信息的能力。它强大但伴随性能开销和类型安全风险,理解反射的原理和适用场景对于编写高级 Go 程序至关重要。

Type / Value / Kind 概念

反射三大核心概念

reflect.Type 表示类型信息(如 int*Userstruct { Name string });reflect.Value 表示值的包装,可读写;reflect.Kind 表示基础分类(如 IntStringStructPtrSlice)。

三者关系如下:

概念说明示例
Type变量的类型type User struct{...}int*int
Value变量的值42&User{Name: "Tom"}
Kind类别的枚举值reflect.Intreflect.Structreflect.Ptr
package main

import (
    "fmt"
    "reflect"
)

type User struct {
    Name string
    Age  int
}

func main() {
    x := 42
    t := reflect.TypeOf(x)   // int
    v := reflect.ValueOf(x)  // 42
    k := t.Kind()            // reflect.Int

    fmt.Println("Type:", t)   // int
    fmt.Println("Value:", v)  // 42
    fmt.Println("Kind:", k)   // int

    // Kind vs Type 的区别
    u := User{Name: "Tom", Age: 25}
    ut := reflect.TypeOf(u)
    fmt.Println("Type:", ut)              // main.User
    fmt.Println("Kind:", ut.Kind())       // struct

    p := &u
    pt := reflect.TypeOf(p)
    fmt.Println("Type:", pt)              // *main.User
    fmt.Println("Kind:", pt.Kind())       // ptr
}

TypeOf / ValueOf

reflect.TypeOf

获取变量的类型信息:

func main() {
    var s = "hello"
    t := reflect.TypeOf(s)

    fmt.Println(t.Name())       // string
    fmt.Println(t.PkgPath())    // 内置类型没有包路径
    fmt.Println(t.Size())       // 16 (64位系统)
    fmt.Println(t.Kind())       // string

    // 获取指针指向的类型
    p := &s
    pt := reflect.TypeOf(p)
    fmt.Println(pt.Kind())       // ptr
    fmt.Println(pt.Elem())       // string
    fmt.Println(pt.Elem().Kind()) // string
}

reflect.ValueOf

获取变量的值信息:

func main() {
    x := 3.14
    v := reflect.ValueOf(x)

    fmt.Println(v.Type())          // float64
    fmt.Println(v.Kind())          // float64
    fmt.Println(v.Float())         // 3.14
    fmt.Println(v.Interface())     // 3.14 (interface{})
    fmt.Println(v.CanSet())        // false — 不可修改!

    // 使用 Interface() 还原为原始类型
    asFloat := v.Interface().(float64)
    fmt.Printf("%.2f\n", asFloat)  // 3.14
}

修改变量值 — Elem().Set()

func main() {
    x := 42
    fmt.Println("before:", x) // 42

    // ❌ 错误:传入值的副本,CanSet() == false
    v := reflect.ValueOf(x)
    fmt.Println("CanSet:", v.CanSet()) // false

    // ✅ 正确:传入指针
    pv := reflect.ValueOf(&x)
    fmt.Println("CanSet:", pv.CanSet())  // false — 指针本身不可设
    fmt.Println("Elem CanSet:", pv.Elem().CanSet()) // true

    pv.Elem().SetInt(100)
    fmt.Println("after:", x) // 100
}
// 通用的 SetAny 函数
func SetAny(ptr interface{}, newVal interface{}) error {
    v := reflect.ValueOf(ptr)
    if v.Kind() != reflect.Ptr {
        return fmt.Errorf("must pass a pointer")
    }
    nv := reflect.ValueOf(newVal)
    if !nv.Type().AssignableTo(v.Elem().Type()) {
        return fmt.Errorf("type mismatch: cannot assign %v to %v",
            nv.Type(), v.Elem().Type())
    }
    v.Elem().Set(nv)
    return nil
}

// 使用
var num int = 10
SetAny(&num, 42)  // num = 42

结构体反射

NumField / Field / Tag.Get

结构体反射是反射最常见的应用场景之一,广泛用于 ORM、JSON 序列化等框架:

type User struct {
    Name string `json:"name" db:"user_name"`
    Age  int    `json:"age"  db:"user_age"`
    Email string `json:"email,omitempty" db:"email"`
}

func InspectStruct(v interface{}) {
    t := reflect.TypeOf(v)
    val := reflect.ValueOf(v)

    fmt.Printf("Type: %s, Kind: %s\n", t.Name(), t.Kind())
    fmt.Printf("Fields: %d\n", t.NumField())

    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        fieldValue := val.Field(i)

        fmt.Printf("  Field[%d]: Name=%s, Type=%s, Value=%v\n",
            i, field.Name, field.Type, fieldValue)

        // 获取 tag
        jsonTag := field.Tag.Get("json")
        dbTag := field.Tag.Get("db")
        fmt.Printf("    Tags: json=%q, db=%q\n", jsonTag, dbTag)
    }
}

// 输出:
// Type: User, Kind: struct
// Fields: 3
//   Field[0]: Name=Name, Type=string, Value=Tom
//     Tags: json="name", db="user_name"
//   Field[1]: Name=Age, Type=int, Value=25
//     Tags: json="age", db="user_age"
//   Field[2]: Name=Email, Type=string, Value=tom@example.com
//     Tags: json="email,omitempty", db="email"

通过反射修改结构体字段

func SetField(obj interface{}, fieldName string, newValue interface{}) error {
    v := reflect.ValueOf(obj)
    if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
        return fmt.Errorf("expected pointer to struct")
    }
    v = v.Elem()
    field := v.FieldByName(fieldName)
    if !field.IsValid() {
        return fmt.Errorf("no such field: %s", fieldName)
    }
    if !field.CanSet() {
        return fmt.Errorf("cannot set field: %s (unexported?)", fieldName)
    }
    nv := reflect.ValueOf(newValue)
    if !nv.Type().AssignableTo(field.Type()) {
        return fmt.Errorf("type mismatch for field %s", fieldName)
    }
    field.Set(nv)
    return nil
}

// 使用
u := &User{Name: "Tom", Age: 25}
SetField(u, "Name", "Jerry") // u.Name = "Jerry"
SetField(u, "Age", 30)      // u.Age = 30

方法反射调用 — MethodByName().Call()

反射可以动态查找并调用对象的方法:

type Calculator struct{}

func (c Calculator) Add(a, b int) int {
    return a + b
}

func (c Calculator) Multiply(a, b int) int {
    return a * b
}

func (c Calculator) Greet(name string) string {
    return "Hello, " + name + "!"
}

func CallMethod(obj interface{}, methodName string, args ...interface{}) (interface{}, error) {
    v := reflect.ValueOf(obj)
    method := v.MethodByName(methodName)
    if !method.IsValid() {
        return nil, fmt.Errorf("method %s not found", methodName)
    }

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

    // 调用方法
    results := method.Call(in)
    if len(results) == 0 {
        return nil, nil
    }
    return results[0].Interface(), nil
}

func main() {
    calc := Calculator{}

    result, _ := CallMethod(calc, "Add", 3, 5)
    fmt.Println(result) // 8

    result, _ = CallMethod(calc, "Multiply", 4, 6)
    fmt.Println(result) // 24

    result, _ = CallMethod(calc, "Greet", "World")
    fmt.Println(result) // Hello, World!
}

性能影响

反射是 Go 中最昂贵的操作之一。以下基准测试展示了反射 vs 直接调用的性能差距:

// 直接调用 vs 反射调用的性能对比
func BenchmarkDirect(b *testing.B) {
    calc := Calculator{}
    for i := 0; i < b.N; i++ {
        calc.Add(i, i+1)
    }
}

func BenchmarkReflect(b *testing.B) {
    calc := Calculator{}
    method := reflect.ValueOf(calc).MethodByName("Add")
    for i := 0; i < b.N; i++ {
        arg1 := reflect.ValueOf(i)
        arg2 := reflect.ValueOf(i + 1)
        method.Call([]reflect.Value{arg1, arg2})
    }
}
BenchmarkDirect-8    2000000000   0.25 ns/op   0 B/op   0 allocs/op
BenchmarkReflect-8     15000000  82.5  ns/op  48 B/op   2 allocs/op
操作直接触发反射差距
函数调用0.25 ns~80 ns~320x 慢
字段访问~0.5 ns~50 ns~100x 慢
类型断言~1 ns~20 ns~20x 慢

应用场景 — JSON 序列化

反射最典型的应用场景是 encoding/json 序列化/反序列化:

// encoding/json 内部工作原理(简化)
func Marshal(v interface{}) ([]byte, error) {
    rv := reflect.ValueOf(v)
    rt := rv.Type()

    switch rt.Kind() {
    case reflect.Struct:
        return marshalStruct(rv)
    case reflect.Map:
        return marshalMap(rv)
    case reflect.Slice:
        return marshalSlice(rv)
    default:
        return marshalPrimitive(rv)
    }
}

func marshalStruct(v reflect.Value) ([]byte, error) {
    t := v.Type()
    var buf bytes.Buffer
    buf.WriteByte('{')

    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        if i > 0 {
            buf.WriteByte(',')
        }
        // 使用 json tag 作为 key
        jsonKey := field.Tag.Get("json")
        if jsonKey == "" {
            jsonKey = field.Name
        }
        // 写入 key: value
        buf.WriteByte('"')
        buf.WriteString(jsonKey)
        buf.WriteByte('"')
        buf.WriteByte(':')
        fieldVal := v.Field(i)
        fieldBytes, _ := Marshal(fieldVal.Interface())
        buf.Write(fieldBytes)
    }

    buf.WriteByte('}')
    return buf.Bytes(), nil
}
// 实际使用
type Config struct {
    Host    string `json:"host"`
    Port    int    `json:"port"`
    Debug   bool   `json:"debug"`
    Tags    []string `json:"tags,omitempty"`
}

cfg := Config{Host: "localhost", Port: 8080, Debug: true}
data, _ := json.MarshalIndent(cfg, "", "  ")
fmt.Println(string(data))
// {
//   "host": "localhost",
//   "port": 8080,
//   "debug": true
// }

替代方案:代码生成和泛型

代码生成替代反射

// stringer 工具:为常量自动生成 String() 方法
//go:generate stringer -type=Status
type Status int

const (
    StatusUnknown Status = iota
    StatusActive
    StatusInactive
    StatusBanned
)
// 自动生成:func (s Status) String() string { ... }

泛型替代反射

// ❌ 反射方案
func Contains(list interface{}, item interface{}) bool {
    listVal := reflect.ValueOf(list)
    itemVal := reflect.ValueOf(item)
    for i := 0; i < listVal.Len(); i++ {
        if reflect.DeepEqual(listVal.Index(i).Interface(), itemVal.Interface()) {
            return true
        }
    }
    return false
}

// ✅ 泛型方案 — 类型安全 + 零运行时开销
func Contains[T comparable](list []T, item T) bool {
    for _, v := range list {
        if v == item {
            return true
        }
    }
    return false
}

替代方案对比

方案类型安全性能复杂度适用场景
直接编码✅ 编译期⚡ 最快类型固定
泛型✅ 编译期⚡ 快类型多样但逻辑相同
代码生成✅ 编译期⚡ 快需要类型特化(如每种类型不同实现)
反射❌ 运行时🐢 慢运行时动态决策(如 JSON 序列化)

搜索