• 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())
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    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())
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    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())
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    4 反射性能

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

  • 相关阅读:
    redis五大常见数据结构的操作命令(string, hash, list, set和zset)
    Ansible Ad-hoc,命令执行模块
    openlayer注册4490坐标系,添加4490超图服务
    Kafka_2.12-2.1.0+Zookeeper-3.4.13集群部署详细教程
    losf命令详解
    分页列表缓存,你真的会吗
    Sentinel整合OpenFeign对远程调用限流并降级
    C嘎嘎 - 基础01
    ASPICE风险管理与合规性策略:确保汽车软件开发的稳健与高效
    等保测评三级等保—安全设计思路
  • 原文地址:https://blog.csdn.net/chengfangdong/article/details/133967175