【golang长途旅行第32站】反射

反射

应用场景

  1. 序列化与反序列化
  2. ORM 框架
  3. 依赖注入(DI)框架
  4. 动态函数/方法调用

Go语言反射机制要点

Go语言的反射机制主要有以下特点:

  1. 动态类型检查 - 允许程序在运行时获取变量的类型(Type)、类别(Kind)和值(Value)信息

  2. 结构体分析 - 对于结构体类型,可以进一步获取其包含的字段和方法等详细信息

  3. 动态操作 - 支持在运行时修改变量的值以及调用与之关联的方法

  4. 使用方式 - 需要导入标准库中的"reflect"包来实现反射功能

这些特性使得Go程序能够在运行时动态地检查和操作各种类型的变量,为编写更灵活的代码提供了可能。

重要函数和机制

  1. 基础反射函数

    1. reflect.TypeOf(变量名)
      功能:获取变量的类型信息
      返回值:reflect.Type类型
      用途:用于分析变量的静态类型信息
    2. reflect.ValueOf(变量名)
      功能:获取变量的值信息
      返回值:reflect.Value类型(结构体类型)
      用途:通过reflect.Value可以获取变量的详细信息,如字段值、方法等
  2. 类型转换关系

    变量、interface{}和reflect.Value三者之间可以相互转换:

    变量 → interface{} → reflect.Value

    reflect.Value → interface{} → 变量

    这种转换在实际开发中经常使用,特别是在需要处理未知类型数据时

    代码示例:

    // 反射类型转换示例函数

    func reflectConvert(b interface{}) {

    // 第一步:interface{} → reflect.Value

    rVal := reflect.ValueOf(b)

    // 第二步:reflect.Value → interface{}

    iVal := rVal.Interface()

    // 第三步:interface{} → 原类型(使用类型断言)

    // 假设原类型为Stu结构体

    v := iVal.(Stu)

    }

为何要以interface{}类型传入呢,不直接以结构体传入?这种情况的出现自然有缘由。

如果指定某个结构体类型,那么这个函数就只适用于这一个结构体了,如果使用接口,那只需要传入的结构体实现了这个接口,就可以作为参数传入,使代码量减少,维护性增强

快速入门

  1. 对基本数据类型进行反射
    代码示例:
    func Change(b interface{}) {
    rType := reflect.TypeOf(b)
    rValue := reflect.ValueOf(b)
    fmt.Printf("%v,%v,%T,%v\n", rType, rValue, b, b)
    fmt.Println(2 + b.(int))
    fmt.Println(2 + rValue.Int())
    }
    func main() {
    a := 10
    Change(a)
    }

输出结果:

int,10,int,10

12

12
注意点:虽然rValue的输出是10,但是2+rValue是跑不通的,因为reflect包实现了运行时反射,但是在编译时,还是处于静态,rValue还是被当作成一个refelct.interface类型,不同类型之间不能相加

注意事项

  1. reflect.Value.Kind()的作用
    该函数用于获取一个变量的"类别(Kind)",其返回值是一个预定义的常量(例如 Int, String等)。使用者需要查阅官方手册来了解所有可能的常量值。
  2. "类型(Type)"与"类别(Kind)"的区别与联系
  • Type 指变量明确的类型。
  • Kind 指变量底层的基本类别。
  • 两者可能相同,也可能不同。
  • 举例来说:
    • 变量 num int:其 Type 是 int,Kind 也是 int(此时相同)。

    • 变量 stu Student(一个自定义结构体):其 Type 是 包名.Student,而 Kind 是 struct(此时不同)。

  1. 使用反射的方式来获取变量的值(并返回对应的类型),要求数据类型匹配,比如x是int类型,那么就应该使用reflect.Value(x).Int(),而不能使用其它的,否则报panic。如果是结构体,就用类型断言。
  2. 通过反射修改内容时,需要传入的是地址
    代码示例:
    func reflects(b interface{}) {
    rValue := reflect.ValueOf(b)
    fmt.Println(rValue.Elem())
    rValue.Elem().SetInt(21)
    fmt.Println(rValue.Elem())
    }
    func main() {
    num := 2
    reflects(&num)
    }

输出结果

2

21
其中,rValue.Elem()类似于rValue,但是由于rValue是reflect.Value类型,所以不能使用rValue

  1. Method()函数,用于获得某个方法,此时对于方法的排序是根据方法的字母ASCII由小到大排列
相关推荐
yw00yw2 分钟前
常见的设计模式
开发语言·javascript·设计模式
风飘百里10 分钟前
Go语言DDD架构的务实之路
后端·架构
郭庆汝11 分钟前
GraphRAG——v0.3.5版本
后端·python·flask
轻松Ai享生活19 分钟前
Linux Swap 详解 (2) - 配置与优化
后端
xiguolangzi21 分钟前
springBoot3 生成订单号
后端
我不是星海41 分钟前
RabbitMQ基础入门实战
java·开发语言
用户67570498850242 分钟前
从入门到实战:一文掌握微服务监控系统 Prometheus + Grafana
后端
ruokkk1 小时前
AI 编程真香!我用 Next.js + AI 助手,给孩子们做了个专属绘本网站
前端·后端·ai编程
乘风破浪酱524361 小时前
Bearer Token介绍
前端·后端
AAA修煤气灶刘哥1 小时前
定时任务从入门到防坑,cron 表达式看这篇就够
java·后端