golang 反射机制

在 go 语言中,实现反射能力的是 reflect包,能够让程序操作不同类型的对象。其中,在反射包中有两个非常重要的 类型和 函数,两个函数分别是:

  • reflect.TypeOf
  • reflect.ValueOf
    两个类型是 reflect.Type 和 reflect.Value,它们与函数是一一对应的关系:

使用场景:map和struct的相互转化,json序列化,ORM框架,rpc服务的注册和调用

1 Type 和 TypeOf

reflect.Type 类型是一个接口类型,内部指定了若干方法,通过这些方法我们可以获取到反射类型的各种信息,例如:字段、方法等

使用 reflect.TypeOf() 函数可以获取将任意值的类型对象 (reflect.Type),程序通过类型对象可以访问任意值的类型信息

复制代码
func main() {
    type MyInt int
    type cat struct {
        Name string
        Type int `json:"type" id:"100"`
    }
    inst := cat{Name: "mimi", Type: 1}
    typeOfCat := reflect.TypeOf(inst)
    // 显示反射类型对象的名称和种类
    fmt.Println(typeOfCat.Name(), typeOfCat.Kind())
    
    for i := 0; i < typeOfCat.NumField(); i++ {
        // 获取每个成员的结构体字段类型
        fieldType := typeOfCat.Field(i)
        // 输出成员名和tag
        fmt.Printf("name: %v  tag: '%v'\n", fieldType.Name, fieldType.Tag)
    }
    // 通过字段名, 找到字段类型信息
    if catType, ok := typeOfCat.FieldByName("Type"); ok {
        // 从tag中取出需要的tag
        fmt.Println(catType.Tag.Get("json"), catType.Tag.Get("id"))
    }
    
    var Zero MyInt
    // 获取Zero常量的反射类型对象
    typeOfA := reflect.TypeOf(Zero)
    // 显示反射类型对象的名称和种类
    fmt.Println(typeOfA.Name(), typeOfA.Kind())
}

2 Value 和 ValueOf

reflect.Value 类型是一个结构体,封装了反射对象的值,内部若干方法,可以通过这些方法来获取和修改对象的值,使用 reflect.ValueOf 函数可以返回 Value 类型,value 类型还可以生成原始类型对象

反射值对象(reflect.Value)提供对结构体访问的方法,通过这些方法可以完成对结构体任意值的访问,方法列表参考 Type 常用方法

修改成员的值 使用 reflect.Value 对包装的值进行修改时,需要遵循一些规则。如果该对象不可寻址或者成员是私有的,则无法修改对象值

复制代码
func main() {
    type dog struct {
        LegCount int
        age int
    }
    // 获取dog实例地址的反射值对象
    valueOfDog := reflect.ValueOf(&dog{})
    // 取出dog实例地址的元素
    valueOfDog = valueOfDog.Elem()
    // 获取legCount字段的值
    vLegCount := valueOfDog.FieldByName("LegCount")
    vAge := valueOfDog.FieldByName("age")
    // 尝试设置legCount的值
    vLegCount.SetInt(4)
    // 这里会报错
    vAge.SetInt(4)
    fmt.Println(vLegCount.Int())
}

3 通过反射调用函数

使用反射调用函数时,需要将参数使用反射值对象的切片 \[\]reflect.Value 构造后传入 Call() 方法中,调用完成时,函数的返回值通过 \[\]reflect.Value 返回

复制代码
package main
import (
    "fmt"
    "reflect"
)
// 普通函数
func add(a, b int) int {
    return a + b
}
func main() {
    // 将函数包装为反射值对象
    funcValue := reflect.ValueOf(add)
    // 构造函数参数, 传入两个整型值
    paramList := []reflect.Value{reflect.ValueOf(10), reflect.ValueOf(20)}
    // 反射调用函数
    retList := funcValue.Call(paramList)
    // 获取第一个返回值, 取整数值
    fmt.Println(retList[0].Int())
}

4 反射性能

通过反射生成对象和字段赋值都会影响性能,但是通过反射的确确确实实能简化代码,为业务逻辑提供统一的代码, 比如标准库中json的编解码、rpc服务的注册和调用, 一些ORM框架比如gorm等,都是通过反射处理数据的,这是为了能处理通用的类型。

相关推荐
进击的程序猿~2 小时前
Go Interface源码深度解析指南
开发语言·后端·golang
不爱洗脚的小滕6 小时前
【Golang】Go 语言实现高可用、用户态感知与多端广播的服务端 SSE 架构
开发语言·架构·golang
golang学习记19 小时前
Go 项目使用docker compose的正确方式
开发语言·docker·golang
2501_931803751 天前
深入理解 GORM:从模型定义到关联查询的核心原理
golang
似璟如你1 天前
Java 开发者的 Go 语法基础:从 0 开始快速上手 Go
java·开发语言·后端·golang·go·编程语言
microrain2 天前
从监控孤岛到视联一体:SagooIoT视频监控中心的设计实践
物联网·golang·开源·sagooiot
GoFly开发者2 天前
GoFly 社区 GMQT|国产自研 MQTT‑Broker,打造私有化物联网消息底座
物联网·mqtt·golang·mqtt broker·mqtt私有化部署
刘步权2 天前
KY-RTI分布仿真技术:第十四章 Go程序设计
golang·hla·rti·高层体系结构·多语言仿真
microrain3 天前
从数据烟囱到融合中心:SagooIoT统一数据处理平台的设计实践
物联网·golang·开源·sagooiot