← 返回首页

反射(Reflection)

什么是反射?

反射是程序在运行时检查变量类型和值的能力。Go 的反射通过 `reflect` 包提供,允许程序在运行时动态地获取类型信息、调用方法和修改值。

反射的基本概念

  • Type:表示 Go 的类型,如 int、string、struct 等
  • Value:表示 Go 的值,包含类型和实际值
  • Kind:表示类型的种类,如 Int、String、Struct 等

reflect.Type 和 reflect.Value

package main

import (
    "fmt"
    "reflect"
)

func main() {
    var x int = 42

    // 获取类型和值
    t := reflect.TypeOf(x)
    v := reflect.ValueOf(x)

    fmt.Println("类型:", t)
    fmt.Println("值:", v)
    fmt.Println("类型种类:", t.Kind())
    fmt.Println("值的种类:", v.Kind())
}

基本类型反射

package main

import (
    "fmt"
    "reflect"
)

func inspect(value interface{}) {
    v := reflect.ValueOf(value)
    t := v.Type()

    fmt.Printf("类型: %s, 种类: %s, 值: %v\n", t, t.Kind(), v)

    switch v.Kind() {
    case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
        fmt.Printf("  整数值: %d\n", v.Int())
    case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
        fmt.Printf("  无符号整数值: %d\n", v.Uint())
    case reflect.Float32, reflect.Float64:
        fmt.Printf("  浮点数值: %f\n", v.Float())
    case reflect.String:
        fmt.Printf("  字符串值: %s, 长度: %d\n", v.String(), v.Len())
    case reflect.Bool:
        fmt.Printf("  布尔值: %v\n", v.Bool())
    }
}

func main() {
    inspect(42)
    inspect(3.14)
    inspect("Hello, Go!")
    inspect(true)
    inspect(uint(100))
}

结构体反射

package main

import (
    "fmt"
    "reflect"
)

type Person struct {
    Name    string `json:"name" db:"person_name"`
    Age     int    `json:"age" db:"person_age"`
    Email   string `json:"email" db:"person_email"`
    Address string `json:"address,omitempty"`
}

func printStructInfo(s interface{}) {
    v := reflect.ValueOf(s)
    t := v.Type()

    fmt.Println("结构体类型:", t.Name())
    fmt.Println("字段数量:", t.NumField())

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

        fmt.Printf("  字段 %d:\n", i)
        fmt.Printf("    名称: %s\n", field.Name)
        fmt.Printf("    类型: %s\n", field.Type)
        fmt.Printf("    值: %v\n", fieldValue.Interface())
        fmt.Printf("    标签: %s\n", field.Tag)
    }
}

func main() {
    person := Person{
        Name:    "张三",
        Age:     25,
        Email:   "zhangsan@example.com",
        Address: "北京市",
    }

    printStructInfo(person)
}

修改值

package main

import (
    "fmt"
    "reflect"
)

func modifyValue(value interface{}) {
    v := reflect.ValueOf(value)

    // 检查是否可设置
    if v.CanSet() {
        switch v.Kind() {
        case reflect.Int:
            v.SetInt(100)
        case reflect.String:
            v.SetString("Modified")
        case reflect.Bool:
            v.SetBool(true)
        }
    }
}

func main() {
    // 使用指针才能修改值
    x := 42
    modifyValue(&x)
    fmt.Println("修改后:", x)

    s := "Hello"
    modifyValue(&s)
    fmt.Println("修改后:", s)
}

通过指针修改结构体字段

package main

import (
    "fmt"
    "reflect"
)

type User struct {
    Name  string
    Age   int
    Email string
}

func setField(obj interface{}, fieldName string, newValue interface{}) {
    v := reflect.ValueOf(obj)

    // 解引用指针
    if v.Kind() == reflect.Ptr {
        v = v.Elem()
    }

    // 获取字段
    field := v.FieldByName(fieldName)

    if field.IsValid() && field.CanSet() {
        newVal := reflect.ValueOf(newValue)
        if newVal.Type().AssignableTo(field.Type()) {
            field.Set(newVal)
            fmt.Printf("成功设置 %s = %v\n", fieldName, newValue)
        } else {
            fmt.Printf("类型不匹配: 无法将 %v 设置为 %s\n", newVal.Type(), field.Type())
        }
    } else {
        fmt.Printf("字段 %s 不存在或不可设置\n", fieldName)
    }
}

func main() {
    user := User{
        Name:  "张三",
        Age:   25,
        Email: "zhangsan@example.com",
    }

    fmt.Println("修改前:", user)

    setField(&user, "Name", "李四")
    setField(&user, "Age", 30)
    setField(&user, "Email", "lisi@example.com")

    fmt.Println("修改后:", user)
}

调用方法

package main

import (
    "fmt"
    "reflect"
)

type Calculator struct {
    result float64
}

func (c *Calculator) Add(a, b float64) float64 {
    c.result = a + b
    return c.result
}

func (c *Calculator) Subtract(a, b float64) float64 {
    c.result = a - b
    return c.result
}

func (c *Calculator) GetResult() float64 {
    return c.result
}

func callMethod(obj interface{}, methodName string, args ...interface{}) {
    v := reflect.ValueOf(obj)

    // 解引用指针
    if v.Kind() == reflect.Ptr {
        v = v.Elem()
    }

    method := v.MethodByName(methodName)

    if method.IsValid() {
        // 准备参数
        in := make([]reflect.Value, len(args))
        for i, arg := range args {
            in[i] = reflect.ValueOf(arg)
        }

        // 调用方法
        results := method.Call(in)

        // 输出结果
        for _, result := range results {
            fmt.Printf("结果: %v\n", result.Interface())
        }
    } else {
        fmt.Printf("方法 %s 不存在\n", methodName)
    }
}

func main() {
    calc := &Calculator{}

    callMethod(calc, "Add", 10.0, 5.0)
    callMethod(calc, "Subtract", 20.0, 8.0)
    callMethod(calc, "GetResult")
}

切片和映射反射

package main

import (
    "fmt"
    "reflect"
)

func inspectSlice(s interface{}) {
    v := reflect.ValueOf(s)

    if v.Kind() == reflect.Slice {
        fmt.Printf("切片长度: %d, 容量: %d\n", v.Len(), v.Cap())
        fmt.Println("元素:")

        for i := 0; i < v.Len(); i++ {
            fmt.Printf("  [%d]: %v\n", i, v.Index(i).Interface())
        }
    }
}

func inspectMap(m interface{}) {
    v := reflect.ValueOf(m)

    if v.Kind() == reflect.Map {
        fmt.Printf("Map 长度: %d\n", v.Len())
        fmt.Println("键值对:")

        for _, key := range v.MapKeys() {
            value := v.MapIndex(key)
            fmt.Printf("  %v: %v\n", key.Interface(), value.Interface())
        }
    }
}

func main() {
    // 切片
    numbers := []int{1, 2, 3, 4, 5}
    inspectSlice(numbers)

    // 映射
    colors := map[string]string{
        "red":   "#FF0000",
        "green": "#00FF00",
        "blue":  "#0000FF",
    }
    inspectMap(colors)
}

动态创建实例

package main

import (
    "fmt"
    "reflect"
)

type Product struct {
    Name  string
    Price float64
}

func newInstance(typ reflect.Type) interface{
    // 创建新实例
    value := reflect.New(typ).Elem()
    return value.Interface()
}

func main() {
    // 通过类型创建实例
    productType := reflect.TypeOf(Product{})
    newProduct := newInstance(productType)

    fmt.Printf("新实例类型: %T\n", newProduct)
    fmt.Printf("新实例值: %+v\n", newProduct)
}

反射的实际应用:JSON 序列化

package main

import (
    "encoding/json"
    "fmt"
    "reflect"
    "strings"
)

type Student struct {
    ID     int    `json:"id"`
    Name   string `json:"name"`
    Age    int    `json:"age"`
    Active bool   `json:"active"`
}

func toJSON(obj interface{}) string {
    data, err := json.Marshal(obj)
    if err != nil {
        return ""
    }
    return string(data)
}

func fromJSON(jsonStr string, obj interface{}) {
    err := json.Unmarshal([]byte(jsonStr), obj)
    if err != nil {
        fmt.Println("解析错误:", err)
    }
}

func main() {
    student := Student{
        ID:     1,
        Name:   "张三",
        Age:    20,
        Active: true,
    }

    // 序列化为 JSON
    jsonStr := toJSON(student)
    fmt.Println("JSON:", jsonStr)

    // 从 JSON 反序列化
    var newStudent Student
    fromJSON(jsonStr, &newStudent)
    fmt.Printf("反序列化: %+v\n", newStudent)
}

反射的性能考虑

反射的性能开销

  • 反射比直接调用慢很多
  • 反射操作会绕过编译器的类型检查
  • 反射代码更难调试和维护

最佳实践

  • 只在必要时使用反射(如序列化、ORM、依赖注入等)
  • 缓存反射结果以提高性能
  • 考虑使用代码生成替代反射
  • 在性能关键路径上避免使用反射

反射与接口

package main

import (
    "fmt"
    "reflect"
)

type Writer interface {
    Write(data string) error
}

type File struct {
    name string
}

func (f *File) Write(data string) error {
    fmt.Printf("写入文件 %s: %s\n", f.name, data)
    return nil
}

func checkInterface(obj interface{}) {
    v := reflect.ValueOf(obj)
    t := v.Type()

    // 检查是否实现了接口
    writerType := reflect.TypeOf((*Writer)(nil)).Elem()
    if t.Implements(writerType) {
        fmt.Println("实现了 Writer 接口")

        // 调用接口方法
        method := v.MethodByName("Write")
        results := method.Call([]reflect.Value{reflect.ValueOf("Hello, Reflection!")})
        fmt.Println("方法返回:", results[0].Interface())
    } else {
        fmt.Println("未实现 Writer 接口")
    }
}

func main() {
    file := &File{name: "test.txt"}
    checkInterface(file)

    var x int = 42
    checkInterface(x)
}