反射机制 #

反射(Reflection)是Go语言的高级特性,允许程序在运行时检查类型信息、修改变量值、调用方法等。反射是很多框架和库的基础。

反射基础 #

reflect包 #

Go的反射功能由 reflect 包提供,核心类型是 reflect.Typereflect.Value

go
package main

import (
    "fmt"
    "reflect"
)

func main() {
    x := 42
    
    t := reflect.TypeOf(x)
    v := reflect.ValueOf(x)
    
    fmt.Println("类型:", t)
    fmt.Println("值:", v)
    fmt.Println("种类:", t.Kind())
}

Kind 种类 #

Kind 表示底层的具体类型:

go
package main

import (
    "fmt"
    "reflect"
)

func main() {
    var (
        b    bool       = true
        i    int        = 42
        f    float64    = 3.14
        s    string     = "hello"
        sl   []int      = []int{1, 2, 3}
        m    map[string]int = map[string]int{"a": 1}
        st   struct{ X int } = struct{ X int }{10}
    )
    
    checkType(b)
    checkType(i)
    checkType(f)
    checkType(s)
    checkType(sl)
    checkType(m)
    checkType(st)
}

func checkType(x interface{}) {
    t := reflect.TypeOf(x)
    fmt.Printf("类型: %-10s 种类: %v\n", t.Name(), t.Kind())
}

常用Kind值:

Kind 说明
Bool 布尔类型
Int, Int8, Int16, Int32, Int64 整数类型
Uint, Uint8, Uint16, Uint32, Uint64 无符号整数
Float32, Float64 浮点类型
String 字符串
Array 数组
Slice 切片
Map 映射
Struct 结构体
Func 函数
Interface 接口
Ptr 指针

获取和设置值 #

获取值 #

go
package main

import (
    "fmt"
    "reflect"
)

func main() {
    x := 42
    v := reflect.ValueOf(x)
    
    fmt.Println("值:", v.Int())
    fmt.Println("类型:", v.Type())
    fmt.Println("能否获取Int:", v.CanInt())
}

设置值 #

要修改值,必须传递指针:

go
package main

import (
    "fmt"
    "reflect"
)

func main() {
    x := 42
    
    v := reflect.ValueOf(&x).Elem()
    
    fmt.Println("修改前:", x)
    
    if v.CanSet() {
        v.SetInt(100)
    }
    
    fmt.Println("修改后:", x)
}

重要: reflect.ValueOf 传递的是值副本,要修改原值必须传递指针并使用 Elem() 获取指针指向的值。

结构体反射 #

获取字段信息 #

go
package main

import (
    "fmt"
    "reflect"
)

type Person struct {
    Name string `json:"name" validate:"required"`
    Age  int    `json:"age" validate:"min=0,max=150"`
    City string `json:"city"`
}

func main() {
    p := Person{
        Name: "张三",
        Age:  30,
        City: "北京",
    }
    
    t := reflect.TypeOf(p)
    v := reflect.ValueOf(p)
    
    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        value := v.Field(i)
        
        fmt.Printf("字段: %s\n", field.Name)
        fmt.Printf("  类型: %s\n", field.Type)
        fmt.Printf("  值: %v\n", value.Interface())
        fmt.Printf("  标签: %s\n", field.Tag.Get("json"))
        fmt.Printf("  验证: %s\n", field.Tag.Get("validate"))
        fmt.Println()
    }
}

修改结构体字段 #

go
package main

import (
    "fmt"
    "reflect"
)

type Config struct {
    Host string
    Port int
}

func main() {
    config := Config{
        Host: "localhost",
        Port: 8080,
    }
    
    fmt.Println("修改前:", config)
    
    v := reflect.ValueOf(&config).Elem()
    
    hostField := v.FieldByName("Host")
    if hostField.CanSet() {
        hostField.SetString("127.0.0.1")
    }
    
    portField := v.FieldByName("Port")
    if portField.CanSet() {
        portField.SetInt(3000)
    }
    
    fmt.Println("修改后:", config)
}

遍历嵌套结构体 #

go
package main

import (
    "fmt"
    "reflect"
)

type Address struct {
    City    string
    Country string
}

type User struct {
    Name    string
    Age     int
    Address Address
}

func printStruct(v reflect.Value, indent string) {
    t := v.Type()
    
    for i := 0; i < v.NumField(); i++ {
        field := t.Field(i)
        fieldValue := v.Field(i)
        
        fmt.Printf("%s%s: ", indent, field.Name)
        
        if fieldValue.Kind() == reflect.Struct {
            fmt.Println()
            printStruct(fieldValue, indent+"  ")
        } else {
            fmt.Println(fieldValue.Interface())
        }
    }
}

func main() {
    user := User{
        Name: "张三",
        Age:  30,
        Address: Address{
            City:    "北京",
            Country: "中国",
        },
    }
    
    printStruct(reflect.ValueOf(user), "")
}

方法反射 #

获取方法信息 #

go
package main

import (
    "fmt"
    "reflect"
)

type Calculator struct{}

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

func (c Calculator) Subtract(a, b int) int {
    return a - b
}

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

func main() {
    c := Calculator{}
    t := reflect.TypeOf(c)
    
    fmt.Println("值接收者的方法:")
    for i := 0; i < t.NumMethod(); i++ {
        method := t.Method(i)
        fmt.Printf("  %s: %v\n", method.Name, method.Type)
    }
    
    pt := reflect.TypeOf(&c)
    fmt.Println("\n指针接收者的方法:")
    for i := 0; i < pt.NumMethod(); i++ {
        method := pt.Method(i)
        fmt.Printf("  %s: %v\n", method.Name, method.Type)
    }
}

动态调用方法 #

go
package main

import (
    "fmt"
    "reflect"
)

type Service struct {
    Name string
}

func (s *Service) Hello(name string) string {
    return fmt.Sprintf("Hello, %s! I'm %s.", name, s.Name)
}

func (s *Service) Add(a, b int) int {
    return a + b
}

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)
    }
    
    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() {
    service := &Service{Name: "MyService"}
    
    result, _ := callMethod(service, "Hello", "World")
    fmt.Println(result)
    
    result, _ = callMethod(service, "Add", 10, 20)
    fmt.Println("10 + 20 =", result)
}

创建实例 #

使用反射创建值 #

go
package main

import (
    "fmt"
    "reflect"
)

func createInstance(t reflect.Type) (interface{}, error) {
    if t.Kind() == reflect.Ptr {
        t = t.Elem()
    }
    
    if t.Kind() != reflect.Struct {
        return nil, fmt.Errorf("only struct types are supported")
    }
    
    v := reflect.New(t)
    return v.Interface(), nil
}

func main() {
    type Person struct {
        Name string
        Age  int
    }
    
    pType := reflect.TypeOf(Person{})
    
    instance, _ := createInstance(pType)
    
    person := instance.(*Person)
    person.Name = "张三"
    person.Age = 30
    
    fmt.Printf("%+v\n", person)
}

实用示例:简单的ORM映射 #

go
package main

import (
    "fmt"
    "reflect"
    "strings"
)

type Model interface {
    TableName() string
}

type User struct {
    ID   int    `db:"id" pk:"true"`
    Name string `db:"name"`
    Age  int    `db:"age"`
}

func (u User) TableName() string {
    return "users"
}

func getDBField(model interface{}) map[string]interface{} {
    result := make(map[string]interface{})
    
    v := reflect.ValueOf(model)
    if v.Kind() == reflect.Ptr {
        v = v.Elem()
    }
    
    t := v.Type()
    
    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        dbTag := field.Tag.Get("db")
        
        if dbTag != "" && dbTag != "-" {
            result[dbTag] = v.Field(i).Interface()
        }
    }
    
    return result
}

func buildInsert(model Model) string {
    fields := getDBField(model)
    
    var columns, placeholders []string
    for col := range fields {
        columns = append(columns, col)
        placeholders = append(placeholders, "?")
    }
    
    return fmt.Sprintf(
        "INSERT INTO %s (%s) VALUES (%s)",
        model.TableName(),
        strings.Join(columns, ", "),
        strings.Join(placeholders, ", "),
    )
}

func main() {
    user := User{
        ID:   1,
        Name: "张三",
        Age:  30,
    }
    
    fields := getDBField(user)
    fmt.Println("字段映射:", fields)
    
    sql := buildInsert(user)
    fmt.Println("SQL:", sql)
}

实用示例:结构体复制 #

go
package main

import (
    "fmt"
    "reflect"
)

func CopyStruct(src, dst interface{}) error {
    srcValue := reflect.ValueOf(src)
    dstValue := reflect.ValueOf(dst)
    
    if dstValue.Kind() != reflect.Ptr {
        return fmt.Errorf("dst must be a pointer")
    }
    
    if srcValue.Kind() == reflect.Ptr {
        srcValue = srcValue.Elem()
    }
    dstValue = dstValue.Elem()
    
    srcType := srcValue.Type()
    
    for i := 0; i < srcType.NumField(); i++ {
        srcField := srcType.Field(i)
        srcFieldValue := srcValue.Field(i)
        
        dstFieldValue := dstValue.FieldByName(srcField.Name)
        if dstFieldValue.IsValid() && dstFieldValue.CanSet() {
            if dstFieldValue.Type() == srcFieldValue.Type() {
                dstFieldValue.Set(srcFieldValue)
            }
        }
    }
    
    return nil
}

type Source struct {
    Name  string
    Age   int
    Email string
}

type Destination struct {
    Name string
    Age  int
    City string
}

func main() {
    src := Source{
        Name:  "张三",
        Age:   30,
        Email: "zhangsan@example.com",
    }
    
    var dst Destination
    
    CopyStruct(src, &dst)
    
    fmt.Printf("源: %+v\n", src)
    fmt.Printf("目标: %+v\n", dst)
}

小结 #

函数/方法 说明
reflect.TypeOf() 获取类型信息
reflect.ValueOf() 获取值信息
Kind() 获取底层类型种类
Elem() 获取指针指向的元素
NumField() 获取结构体字段数量
Field(i) 获取第i个字段
FieldByName() 按名称获取字段
NumMethod() 获取方法数量
Method(i) 获取第i个方法
Call() 调用方法

反射是强大的工具,但也有代价:性能较低、类型安全性弱、代码可读性差。应该谨慎使用,优先考虑接口和代码生成等替代方案。