learning_notes

学习笔记

View project on GitHub

反射

反射原理

是什么?

  • 在编译时并不知道这些变量的具体类型。
  • 在运行时(Run time)可以访问、检测和修改它本身状态或行为的一种能力。
  • 就是把类型元数据暴露给用户使用。

    应用

  • 对象序列化(encoding/json)
  • fmt 相关函数的实现
  • ORM
  • 依赖注入​

如何实现反射

Go语言反射是建立在Go类型系统和interface设计之上的。 在 reflect 包里定义了各种类型,实现了反射的各种函数,通过它们可以在运行时检测类型的信息、改变类型的值。

关键功能

核心功能是把interface变量转化为反射类型对象reflect.Type和reflect.Value,并通过反射类型对象提供的功能,访问真实对象的方法和属性。

type any = interface{}

// 获取反射对象reflect.Type
// TypeOf returns the reflection Type that represents the dynamic type of i. 
// If i is a nil interface value, TypeOf returns nil.
func TypeOf(i any) Type

// 获取反射对象reflect.Value
// ValueOf returns a new Value initialized to the concrete value stored in the interface i. 
// ValueOf(nil) returns the zero Value.
func ValueOf(i any) Value

// 反射对象转换回interface
func (v Value) Interface() (i any)

好处

  • 提升代码灵活性:不知道接口调用哪个函数.
  • 配置化
  • 插件化
  • 代码解耦
  • 依赖注入

坏处

  • 代码可读性差。
  • 静态语言,编译器能提前发现一些类型错误,但是对于反射代码是无能为力的。所以包含反射相关的代码,很可能会运行很久,才会出错,这时候经常是直接 panic,可能会造成严重的后果。
  • 反射对性能影响还是比较大的,比正常代码运行速度慢一到两个数量级。

DeepEqual 的实现只需要递归地调用 == 就可以比较两个变量是否是真的“深度”相等。

有一些异常情况:比如 func 类型是不可比较的类型,只有在两个 func 类型都是 nil 的情况下,才是“深度”相等;float 类型,由于精度的原因,也是不能使用 == 比较的;包含 func 类型或者 float 类型的 struct, interface, array 等。

1. 功能区别

函数 作用 返回值类型
reflect.TypeOf(obj) 获取 obj类型信息​(如结构体名称、方法、字段等) reflect.Type
reflect.ValueOf(obj) 获取 obj值信息​(如实际存储的数据),并支持动态操作值(修改、调用方法) reflect.Value

2. 返回对象的能力

reflect.Type(通过 TypeOf 获取)​

  • 提供类型元信息
    • 类型名称(Name()
    • 类型种类(Kind(),如 intstructslice
    • 方法列表(NumMethod()Method(i)
    • 结构体字段信息(NumField()Field(i)
    • 是否实现某接口(Implements(u Type)
  • 特性
    仅用于读取类型信息,不可修改数据

reflect.Value(通过 ValueOf 获取)​

  • 提供值操作能力
    • 获取实际值(Interface()
    • 修改值(Set()SetInt(),需满足可寻址性)
    • 调用方法(Call()
    • 判断是否零值(IsZero()
  • 可寻址性要求
    必须通过指针获取 Value 才能修改值(例如 Value.Elem())。

关键区别总结

| ​维度​ | reflect.Type | reflect.Value | |—————-|———————————————|———————————————-| | ​核心功能​ | 类型元数据分析 | 值操作与动态行为 | | ​数据修改​ | ❌ 不可修改 | ✅ 可修改(需可寻址) | | ​典型应用​ | 检查结构体字段、接口实现 | 动态调用方法、序列化/反序列化、依赖注入 |