Go反射指南

简介: 反射与接口息息相关

 概念:

官方对此有个非常简明的介绍,两句话耐人寻味:

  1. 反射提供一种让程序检查自身结构的能力
  2. 反射是困惑的源泉

第1条,再精确点的描述是“反射是一种检查interface变量的底层类型和值的机制”。 第2条,很有喜感的自嘲,不过往后看就笑不出来了,因为你很可能产生困惑。

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

  • reflect.TypeOf()   能获取类型信息;
  • reflect.ValueOf() 能获取数据的运行时表示;

只有这么简单吗?当然不是,请继续阅读。

引出:

其实了解反射的第一步,应从interface入手,因为反射与接口存在着千丝万缕的关系。

如下是一段interface的源码

type iface struct {
    tab  *itab
    data unsafe.Pointer
}
// layout of Itab known to compilers
// allocated in non-garbage-collected memory
// Needs to be in sync with
// ../cmd/compile/internal/gc/reflect.go:/^func.dumptypestructs.
type itab struct {
    inter  *interfacetype
    _type  *_type
    link   *itab
    bad    int32
    inhash int32      // has this itab been added to hash?
    fun    [1]uintptr // variable sized
}

image.gif

看不懂也没关系,我对其大致简化一番,从reflect角度再来看看,并思考从iface中看到的字段:

type I interface{
    // 方法集
}
type iface struct{
    typ reflect.Type   // 储存类型信息
    val reflect.Value  // 储存实际值
}

image.gif

之所以引出interface,是因为想说interface类型有个(value,type)对,而反射就是检查interface的这个(value, type)对的。具体一点说就是Go提供一组方法提取interface的value,提供另一组方法提取interface的type。

  • reflect.Type 提供一组接口处理interface的类型,即(value, type)中的type
  • reflect.Value 提供一组接口处理interface的值,即(value, type)中的value

下面会提到反射对象,所谓反射对象即反射包里提供的两种类型的对象。

  • reflect.Type 类型对象
  • reflect.Value 类型对象

三大法则:

第一法则:

interface{} 变量,可以反射出反射对象;

下面示例,看看是如何通过反射获取一个变量的值和类型的:

package main
import (
    "fmt"
    "reflect"
)
func main() {
    var x float64 = 3.4
    t := reflect.TypeOf(x)  //t is reflext.Type
    fmt.Println("type:", t)
    v := reflect.ValueOf(x) //v is reflext.Value
    fmt.Println("value:", v)
}
运行如下:
type: float64
value: 3.4

image.gif

是不是疑惑了,明明是上述是x->reflect类型,却依然说是 interface{} --变为--> reflect类型呢?这是因为,在TypeOf 与 ValueOf 内部,自动将 值类型,转化为了 接口类型。

第二法则:

从反射对象可以获取 interface{} 变量;

package main
import (
    "fmt"
    "reflect"
)
func main() {
    var x float64 = 3.4
    v := reflect.ValueOf(x) //v is reflext.Value
    var y float64 = v.Interface().(float64)
    fmt.Println("value:", y)
}

image.gif

1、用reflect.ValueOf(x) 获取,value值。

2、v.Interface() 转化成接口。

3、类型断言转化成,对应的基本类型

第三法则:

要修改反射对象,其值必须可设置。

通过反射可以将interface类型变量转换成反射对象,可以使用该反射对象设置其持有的值。在介绍何谓反射对象可修改前,先看一下失败的例子:

package main
import (
    "reflect"
)
func main() {
    var x float64 = 3.4
    v := reflect.ValueOf(x)
    v.SetFloat(7.1) // Error: will panic.
}
如下代码,通过反射对象v设置新值,会出现panic。报错如下:
panic: reflect: reflect.Value.SetFloat using unaddressable value

image.gif

错误原因即是v是不可修改的。

反射对象失败,取决于是否可以修改其储存的值。回想一下函数传参时,是传值还是传址,就不难理解上例中为何失败。

上例中,传入 reflect.ValueOf() 函数的其实是x的值,而非x本身。即通过v修改其值是无法影响x的,也即是无效的修改,所以 golang 会报错。

想到此处,即可明白,如果构建v时使用x的地址就可实现修改了,但此时v代表的是指针地址,我们要设置的是指针所指向的内容,也即我们想要修改的是*v。 那怎么通过v修改x的值呢?

reflect.Value 提供了 Elem() 方法,可以获得指针向指向的Value 。看如下代码:

package main
import (
"reflect"
    "fmt"
)
func main() {
    var x float64 = 3.4
    v := reflect.ValueOf(&x)
    v.Elem().SetFloat(7.1)
    fmt.Println("x :", v.Elem().Interface())
}

image.gif

1、调用reflect.ValueOf 获取变量指针。

2、调用 reflect.Value.Elem 获取指针指向的变量。

3、调用 reflect.Value.SetFloat() 更新变量。

总结:

以上为本篇博客精华内容,如有不妥,请及时私信联系我,斟酌之后必加以纠正。

待后续深入学习时,会转回继续修改。

参考内容:

1、《Go专家编程》

2、《Go语言设计与实践》

3、sql生成器-实战


目录
相关文章
|
5月前
|
算法 机器人 测试技术
动态规划入门详解
等待了好久好久,今天终于可以对动态规划动手了(☆▽☆)
247 3
|
5月前
|
存储 缓存 安全
一文带你读懂 Go 1.24 map 重构了什么?
本文聚焦 Go 1.24 map 底层重构,解释它如何从旧版 bucket + overflow 方案,演进为 Swiss Table + 局部 split 的新结构,以及它所带来的性能提升。
335 1
一文带你读懂 Go 1.24 map 重构了什么?
|
5月前
|
设计模式 Java Go
Go中的switch的8种使用场景:没有你想的那么简单
在 Go 中灵活使用 switch,可以使代码更清晰、更易维护。 switch 是 Go 中不可或缺的控制结构之一
966 0
|
5月前
|
存储 人工智能 关系型数据库
OpenClaw怎么可能没痛点?用RDS插件来释放OpenClaw全部潜力
OpenClaw插件是深度介入Agent生命周期的扩展机制,提供24个钩子,支持自动注入知识、持久化记忆等被动式干预。相比Skill/Tool,插件可主动在关键节点(如对话开始/结束)执行逻辑,适用于RAG增强、云化记忆等高级场景。
1290 56
OpenClaw怎么可能没痛点?用RDS插件来释放OpenClaw全部潜力
|
5月前
|
机器学习/深度学习 编解码 人工智能
102类农业害虫图像识别数据集分享(适用于YOLO系列深度学习分类检测任务)
102类农业害虫图像识别数据集分享(适用于YOLO系列深度学习分类检测任务) 数据集分享 在智慧农业与智能害虫监测的时代背景下,构建高质量的农业害虫识别数据集已成为实现自动化检测与分类的核心环节。
548 2
|
5月前
|
人工智能 Linux API
阿里云/本地部署OpenClaw多Bot群内协作指南:一键配置提示词+大模型API完整方案及避坑指南
OpenClaw真正强大的地方,在于支持多Bot在同一群组内自动协作,通过1个Boss Bot+多个执行Bot的分工模式,实现一句话下发需求、全流程自动执行、自动汇总结果。本文将复杂的多智能体配置完全整理为可直接使用的配置与提示词,同时提供2026年阿里云部署、MacOS/Linux/Windows11本地部署流程,以及阿里云千问大模型API、免费Coding Plan API配置方法,搭配全套代码命令与常见问题解答,让你无需理解复杂配置,即可拥有一支全自动AI协作团队。
1025 4
|
5月前
|
缓存 安全 测试技术
GO项目开发规范文档解读
本篇博客的目的,更多是为快速翻阅与回忆使用。
301 1
|
5月前
|
存储 缓存 编译器
依赖管理(go mod)
上部分,我们介绍了使用GOPATH的痛点:多个项目无法共享同一个GOPATH,虽然vendor仍然无法多个项目无法共享同一个GOPATH,但是它提供了一种机制,让项目的依赖隔离而不互相干扰。版本格式 v(major).(minorr).(patch)中的 major 指的是大版本,minor 指的是小版本,patch 指的是补丁版本。从GOPATH到vendor,再到Go Module,这是一个不断演进的过程,了解每代依赖管理的痛点,能够更好的明白下一代依赖管理的初衷。
246 1
|
4月前
|
Ubuntu 算法 关系型数据库
Debian/Ubuntu 环境 PolarDB-X 单机版 DEB 包安装综合指南
本文整合阿里云文档,详解Ubuntu 18.04与Debian 10下PolarDB-X单机版安装:因官方仅提供RPM包,需用alien转DEB,但二者压缩格式不同(Ubuntu用zstd,Debian 10不支持),必须在目标系统本地转换,不可复用。含依赖处理、配置初始化及启动验证全流程。
767 19
|
5月前
|
算法 自动驾驶 安全
【路径规划】在二维和三维空间中实现RRT_算法,根据障碍物位置和尺寸实现的避障功能附matlab代码
✅作者简介:热爱科研的Matlab仿真开发者,擅长毕业设计辅导、数学建模、数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。 🍎 往期回顾关注个人主页:Matlab科研工作室 👇 关注我领取海量matlab电子书和数学建模资料 🍊个人信条:格物致知,完整Matlab代码获取及仿真咨询内容私信。 🔥 内容介绍 一、路径规划与避障的重要性 在机器人运动控制、自动驾驶、无人机导航等众多领域,路径规划与避障是关键技术。例如,在工业机器人的操作场景中,机器人需要在复杂的工作空间内,从起始点运动到目标点,同时避开各种障碍物,如生产设备、固定支架等,以确保生产任务
274 0