Go语言快速入门Go语言快速入门
首页
基础篇
进阶篇
高阶篇
实战篇
Go官方网站
编程指南
首页
基础篇
进阶篇
高阶篇
实战篇
Go官方网站
编程指南
  • 高阶篇

    • 🎯 高阶篇
    • Context
    • 反射
    • 泛型
    • 性能优化
    • 最佳实践

反射

反射是在运行时检查和操作类型的能力。强大但要谨慎使用!我刚开始学反射的时候,觉得特别神奇,什么都想用反射来实现。但后来在实际项目中才明白,反射是把双刃剑,性能开销大、代码可读性差,能不用就不用。通常只有在写框架、序列化这类通用工具时才会用到。

什么是反射?

反射让你可以在运行时:

  • 检查变量的类型
  • 读取和修改变量的值
  • 调用方法
  • 创建新的类型实例

反射的代价

  • 性能较差(比直接调用慢10-100倍)
  • 代码可读性降低
  • 编译时无法检查错误

原则:能不用就不用,必须用时才用。

reflect包基础

Type 和 Value

import "reflect"

var x float64 = 3.14

// 获取类型
t := reflect.TypeOf(x)
fmt.Println("Type:", t)           // float64
fmt.Println("Kind:", t.Kind())    // float64

// 获取值
v := reflect.ValueOf(x)
fmt.Println("Value:", v)          // 3.14
fmt.Println("Type:", v.Type())    // float64
fmt.Println("Kind:", v.Kind())    // float64
fmt.Println("Float:", v.Float())  // 3.14

Kind vs Type

type MyInt int

var x MyInt = 42

t := reflect.TypeOf(x)
fmt.Println("Type:", t)        // main.MyInt(自定义类型名)
fmt.Println("Kind:", t.Kind()) // int(底层类型)

Kind是有限的基础类型:

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

检查类型

检查结构体

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

p := Person{Name: "张三", Age: 25}
t := reflect.TypeOf(p)

fmt.Println("类型名:", t.Name())      // Person
fmt.Println("包路径:", t.PkgPath())   // main
fmt.Println("字段数:", t.NumField())  // 2

// 遍历字段
for i := 0; i < t.NumField(); i++ {
    field := t.Field(i)
    fmt.Printf("字段: %s, 类型: %s, Tag: %s\n",
        field.Name, field.Type, field.Tag)
}
// 输出:
// 字段: Name, 类型: string, Tag: json:"name" validate:"required"
// 字段: Age, 类型: int, Tag: json:"age" validate:"min=0,max=150"

读取标签

field, _ := t.FieldByName("Name")

jsonTag := field.Tag.Get("json")
validateTag := field.Tag.Get("validate")

fmt.Println("json tag:", jsonTag)       // name
fmt.Println("validate tag:", validateTag) // required

读取和修改值

读取值

type Person struct {
    Name string
    Age  int
}

p := Person{Name: "张三", Age: 25}
v := reflect.ValueOf(p)

// 读取字段值
name := v.FieldByName("Name").String()
age := v.FieldByName("Age").Int()

fmt.Println("Name:", name)  // 张三
fmt.Println("Age:", age)    // 25

修改值

要修改值,必须传入指针并使用 Elem() 获取指向的值:

p := Person{Name: "张三", Age: 25}
v := reflect.ValueOf(&p)  // 传入指针!
elem := v.Elem()          // 获取指向的值

// 修改字段
elem.FieldByName("Name").SetString("李四")
elem.FieldByName("Age").SetInt(30)

fmt.Println(p)  // {李四 30}

判断是否可修改

v := reflect.ValueOf(x)
fmt.Println("CanSet:", v.CanSet())  // false

v = reflect.ValueOf(&x).Elem()
fmt.Println("CanSet:", v.CanSet())  // true

调用方法

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 main() {
    calc := Calculator{}
    v := reflect.ValueOf(calc)
    
    // 获取方法
    addMethod := v.MethodByName("Add")
    
    // 准备参数
    args := []reflect.Value{
        reflect.ValueOf(3),
        reflect.ValueOf(4),
    }
    
    // 调用方法
    result := addMethod.Call(args)
    fmt.Println("3 + 4 =", result[0].Int())  // 7
}

创建新值

// 创建新的结构体
t := reflect.TypeOf(Person{})
v := reflect.New(t)  // 返回 *Person

// 设置字段
elem := v.Elem()
elem.FieldByName("Name").SetString("张三")
elem.FieldByName("Age").SetInt(25)

// 转换为具体类型
p := v.Interface().(*Person)
fmt.Println(*p)  // {张三 25}

// 创建切片
sliceType := reflect.SliceOf(reflect.TypeOf(0))
slice := reflect.MakeSlice(sliceType, 0, 10)
slice = reflect.Append(slice, reflect.ValueOf(1))
slice = reflect.Append(slice, reflect.ValueOf(2))
fmt.Println(slice.Interface())  // [1 2]

// 创建Map
mapType := reflect.MapOf(reflect.TypeOf(""), reflect.TypeOf(0))
m := reflect.MakeMap(mapType)
m.SetMapIndex(reflect.ValueOf("one"), reflect.ValueOf(1))
fmt.Println(m.Interface())  // map[one:1]

实战案例:简易JSON序列化

package main

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

func toJSON(v interface{}) string {
    val := reflect.ValueOf(v)
    typ := val.Type()
    
    if val.Kind() != reflect.Struct {
        return fmt.Sprintf("%v", v)
    }
    
    var parts []string
    
    for i := 0; i < val.NumField(); i++ {
        field := typ.Field(i)
        fieldVal := val.Field(i)
        
        // 跳过未导出的字段
        if !fieldVal.CanInterface() {
            continue
        }
        
        // 获取json标签
        name := field.Tag.Get("json")
        if name == "" {
            name = field.Name
        }
        if name == "-" {
            continue
        }
        
        // 处理omitempty
        if strings.Contains(name, ",omitempty") {
            name = strings.Split(name, ",")[0]
            if isZero(fieldVal) {
                continue
            }
        }
        
        // 格式化值
        var valStr string
        switch fieldVal.Kind() {
        case reflect.String:
            valStr = fmt.Sprintf(`"%s"`, fieldVal.String())
        case reflect.Int, reflect.Int64:
            valStr = fmt.Sprintf("%d", fieldVal.Int())
        case reflect.Float64:
            valStr = fmt.Sprintf("%f", fieldVal.Float())
        case reflect.Bool:
            valStr = fmt.Sprintf("%t", fieldVal.Bool())
        default:
            valStr = fmt.Sprintf("%v", fieldVal.Interface())
        }
        
        parts = append(parts, fmt.Sprintf(`"%s":%s`, name, valStr))
    }
    
    return "{" + strings.Join(parts, ",") + "}"
}

func isZero(v reflect.Value) bool {
    return v.IsZero()
}

type User struct {
    ID       int    `json:"id"`
    Username string `json:"username"`
    Password string `json:"-"`
    Email    string `json:"email,omitempty"`
}

func main() {
    user := User{
        ID:       1,
        Username: "zhangsan",
        Password: "secret",
        Email:    "",
    }
    
    json := toJSON(user)
    fmt.Println(json)
    // {"id":1,"username":"zhangsan"}
}

实战案例:表单验证器

package main

import (
    "fmt"
    "reflect"
    "regexp"
    "strconv"
    "strings"
)

type ValidationError struct {
    Field   string
    Message string
}

func (e ValidationError) Error() string {
    return fmt.Sprintf("%s: %s", e.Field, e.Message)
}

func Validate(v interface{}) []ValidationError {
    var errors []ValidationError
    
    val := reflect.ValueOf(v)
    typ := val.Type()
    
    // 处理指针
    if val.Kind() == reflect.Ptr {
        val = val.Elem()
        typ = val.Type()
    }
    
    for i := 0; i < val.NumField(); i++ {
        field := typ.Field(i)
        fieldVal := val.Field(i)
        
        tag := field.Tag.Get("validate")
        if tag == "" {
            continue
        }
        
        rules := strings.Split(tag, ",")
        for _, rule := range rules {
            if err := validateRule(field.Name, fieldVal, rule); err != nil {
                errors = append(errors, *err)
            }
        }
    }
    
    return errors
}

func validateRule(name string, val reflect.Value, rule string) *ValidationError {
    parts := strings.SplitN(rule, "=", 2)
    ruleName := parts[0]
    
    switch ruleName {
    case "required":
        if val.IsZero() {
            return &ValidationError{name, "不能为空"}
        }
        
    case "min":
        minVal, _ := strconv.Atoi(parts[1])
        switch val.Kind() {
        case reflect.String:
            if len(val.String()) < minVal {
                return &ValidationError{name, fmt.Sprintf("长度不能小于%d", minVal)}
            }
        case reflect.Int, reflect.Int64:
            if val.Int() < int64(minVal) {
                return &ValidationError{name, fmt.Sprintf("值不能小于%d", minVal)}
            }
        }
        
    case "max":
        maxVal, _ := strconv.Atoi(parts[1])
        switch val.Kind() {
        case reflect.String:
            if len(val.String()) > maxVal {
                return &ValidationError{name, fmt.Sprintf("长度不能大于%d", maxVal)}
            }
        case reflect.Int, reflect.Int64:
            if val.Int() > int64(maxVal) {
                return &ValidationError{name, fmt.Sprintf("值不能大于%d", maxVal)}
            }
        }
        
    case "email":
        if val.Kind() == reflect.String {
            pattern := `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`
            if !regexp.MustCompile(pattern).MatchString(val.String()) {
                return &ValidationError{name, "邮箱格式不正确"}
            }
        }
    }
    
    return nil
}

type RegisterForm struct {
    Username string `validate:"required,min=3,max=20"`
    Password string `validate:"required,min=6"`
    Email    string `validate:"required,email"`
    Age      int    `validate:"min=0,max=150"`
}

func main() {
    form := RegisterForm{
        Username: "ab",
        Password: "123",
        Email:    "invalid-email",
        Age:      -1,
    }
    
    errors := Validate(form)
    if len(errors) > 0 {
        fmt.Println("验证失败:")
        for _, err := range errors {
            fmt.Printf("  - %s\n", err)
        }
    }
}

实战案例:简易ORM

package main

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

// 生成建表SQL
func CreateTableSQL(v interface{}) string {
    t := reflect.TypeOf(v)
    
    if t.Kind() == reflect.Ptr {
        t = t.Elem()
    }
    
    tableName := toSnakeCase(t.Name())
    
    var columns []string
    
    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        
        colName := field.Tag.Get("db")
        if colName == "" {
            colName = toSnakeCase(field.Name)
        }
        
        colType := goTypeToSQL(field.Type)
        
        // 检查是否主键
        if field.Tag.Get("pk") == "true" {
            colType += " PRIMARY KEY"
        }
        
        columns = append(columns, fmt.Sprintf("  %s %s", colName, colType))
    }
    
    return fmt.Sprintf("CREATE TABLE %s (\n%s\n);",
        tableName,
        strings.Join(columns, ",\n"))
}

func goTypeToSQL(t reflect.Type) string {
    switch t.Kind() {
    case reflect.String:
        return "VARCHAR(255)"
    case reflect.Int, reflect.Int64:
        return "INTEGER"
    case reflect.Float64:
        return "REAL"
    case reflect.Bool:
        return "BOOLEAN"
    default:
        return "TEXT"
    }
}

func toSnakeCase(s string) string {
    var result strings.Builder
    for i, r := range s {
        if i > 0 && r >= 'A' && r <= 'Z' {
            result.WriteByte('_')
        }
        result.WriteRune(r)
    }
    return strings.ToLower(result.String())
}

// 生成插入SQL
func InsertSQL(v interface{}) (string, []interface{}) {
    val := reflect.ValueOf(v)
    typ := val.Type()
    
    if val.Kind() == reflect.Ptr {
        val = val.Elem()
        typ = val.Type()
    }
    
    tableName := toSnakeCase(typ.Name())
    
    var columns []string
    var placeholders []string
    var values []interface{}
    
    for i := 0; i < val.NumField(); i++ {
        field := typ.Field(i)
        fieldVal := val.Field(i)
        
        // 跳过主键
        if field.Tag.Get("pk") == "true" {
            continue
        }
        
        colName := field.Tag.Get("db")
        if colName == "" {
            colName = toSnakeCase(field.Name)
        }
        
        columns = append(columns, colName)
        placeholders = append(placeholders, "?")
        values = append(values, fieldVal.Interface())
    }
    
    sql := fmt.Sprintf("INSERT INTO %s (%s) VALUES (%s)",
        tableName,
        strings.Join(columns, ", "),
        strings.Join(placeholders, ", "))
    
    return sql, values
}

type User struct {
    ID       int    `db:"id" pk:"true"`
    Username string `db:"username"`
    Email    string `db:"email"`
    Age      int    `db:"age"`
}

func main() {
    // 生成建表SQL
    createSQL := CreateTableSQL(User{})
    fmt.Println("建表SQL:")
    fmt.Println(createSQL)
    
    // 生成插入SQL
    user := User{
        Username: "zhangsan",
        Email:    "zhangsan@example.com",
        Age:      25,
    }
    
    insertSQL, values := InsertSQL(user)
    fmt.Println("\n插入SQL:")
    fmt.Println(insertSQL)
    fmt.Println("参数:", values)
}

反射性能对比

func BenchmarkDirectCall(b *testing.B) {
    calc := Calculator{}
    for i := 0; i < b.N; i++ {
        calc.Add(1, 2)
    }
}

func BenchmarkReflectCall(b *testing.B) {
    calc := Calculator{}
    v := reflect.ValueOf(calc)
    method := v.MethodByName("Add")
    args := []reflect.Value{reflect.ValueOf(1), reflect.ValueOf(2)}
    
    for i := 0; i < b.N; i++ {
        method.Call(args)
    }
}

// 结果:
// BenchmarkDirectCall-8     1000000000    0.3 ns/op
// BenchmarkReflectCall-8       5000000  300.0 ns/op
// 反射慢约1000倍!

何时使用反射

场景是否使用反射原因
JSON/XML序列化✅需要动态处理结构体
ORM框架✅数据库映射
依赖注入✅动态创建和注入
表单验证✅通用验证逻辑
日常业务代码❌直接调用更好
性能关键代码❌反射太慢

练习

  1. 使用反射遍历结构体所有字段和值
  2. 实现一个通用的深拷贝函数
  3. 实现一个结构体转Map的函数
参考答案
// 1. 遍历结构体
func PrintStruct(v interface{}) {
    val := reflect.ValueOf(v)
    typ := val.Type()
    
    if val.Kind() == reflect.Ptr {
        val = val.Elem()
        typ = val.Type()
    }
    
    fmt.Printf("类型: %s\n", typ.Name())
    for i := 0; i < val.NumField(); i++ {
        field := typ.Field(i)
        value := val.Field(i)
        fmt.Printf("  %s: %v (%s)\n", field.Name, value.Interface(), field.Type)
    }
}

// 2. 深拷贝
func DeepCopy(src interface{}) interface{} {
    srcVal := reflect.ValueOf(src)
    
    if srcVal.Kind() == reflect.Ptr {
        srcVal = srcVal.Elem()
    }
    
    dst := reflect.New(srcVal.Type()).Elem()
    copyValue(dst, srcVal)
    
    return dst.Interface()
}

func copyValue(dst, src reflect.Value) {
    switch src.Kind() {
    case reflect.Struct:
        for i := 0; i < src.NumField(); i++ {
            if dst.Field(i).CanSet() {
                copyValue(dst.Field(i), src.Field(i))
            }
        }
    case reflect.Slice:
        dst.Set(reflect.MakeSlice(src.Type(), src.Len(), src.Cap()))
        for i := 0; i < src.Len(); i++ {
            copyValue(dst.Index(i), src.Index(i))
        }
    case reflect.Map:
        dst.Set(reflect.MakeMap(src.Type()))
        for _, key := range src.MapKeys() {
            dst.SetMapIndex(key, src.MapIndex(key))
        }
    default:
        dst.Set(src)
    }
}

// 3. 结构体转Map
func StructToMap(v interface{}) map[string]interface{} {
    result := make(map[string]interface{})
    
    val := reflect.ValueOf(v)
    typ := val.Type()
    
    if val.Kind() == reflect.Ptr {
        val = val.Elem()
        typ = val.Type()
    }
    
    for i := 0; i < val.NumField(); i++ {
        field := typ.Field(i)
        value := val.Field(i)
        
        if !value.CanInterface() {
            continue
        }
        
        name := field.Tag.Get("json")
        if name == "" || name == "-" {
            name = field.Name
        }
        
        result[name] = value.Interface()
    }
    
    return result
}

反射很强大,但记住:能不用就不用。下一节学习泛型!

最近更新: 2025/12/27 13:26
Contributors: 王长安
Prev
Context
Next
泛型