当前位置:网站首页>Go反射法则
Go反射法则
2022-04-23 04:14:00 【沉淅尘】
Go反射法则
反射是 Go 语言比较重要的一个特性之一,虽然在大多数的应用和服务中并不常见,但是很多框架都依赖 Go 语言的反射机制实现一些动态的功能。作为一门静态语言,Golang 在设计上都非常简洁,所以在语法上其实并没有较强的表达能力,但是 Go 语言为我们提供的 reflect 包提供的动态特性却能够弥补它在语法上的一些劣势。
reflect 实现了运行时的反射能力,能够让 Golang 的程序操作不同类型的对象,我们可以使用包中的函数 TypeOf 从静态类型 interface{} 中获取动态类型信息并通过 ValueOf 获取数据的运行时表示,通过这两个函数和包中的其他工具我们就可以得到更强大的表达能力。
概述
在 go 语言中,实现反射能力的是 reflect包,能够让程序操作不同类型的对象。其中,在反射包中有两个非常重要的 类型和 函数,两个函数分别是:
reflect.TypeOf- 能获取对象的类型的信息reflect.ValueOf- 能获取对象的数据
两个类型是 reflect.Type 和 reflect.Value,它们与函数是一一对应的关系:

反射法则
运行时反射是程序在运行期间检查其自身结构的一种方式,它是 元编程 的一种,但是它带来的灵活性也是一把双刃剑,过量的使用反射会使我们的程序逻辑变得难以理解并且运行缓慢,我们在这一节中就会介绍 Go 语言反射的三大法则,这能够帮助我们更好地理解反射的作用。
- 从接口值可反射出反射对象;
- 从反射对象可反射出接口值;
- 要修改反射对象,其值必须可设置;
第一法则
反射的第一条法则就是,我们能够将 Go 语言中的接口类型变量转换成反射对象,上面提到的reflect.TypeOf 和 reflect.ValueOf 就是完成这个转换的两个最重要方法,如果我们认为 Go 语言中的类型和反射类型是两个不同『世界』的话,那么这两个方法就是连接这两个世界的桥梁。

我们通过以下例子简单介绍这两个方法的作用,其中 TypeOf 获取了变量 author 的类型也就是 string 而 ValueOf 获取了变量的值 draven,如果我们知道了一个变量的类型和值,那么也就意味着我们知道了关于这个变量的全部信息。
package main
import (
"fmt"
"reflect"
)
func main() {
author := "draven"
fmt.Println("TypeOf author:", reflect.TypeOf(author))
fmt.Println("ValueOf author:", reflect.ValueOf(author))
}
$ go run main.go
TypeOf author: string
ValueOf author: draven
从变量的类型上我们可以获当前类型能够执行的方法 Method 以及当前类型实现的接口等信息;
- 对于结构体,可以获取字段的数量并通过下标和字段名获取字段
StructField; - 对于哈希表,可以获取哈希表的
Key类型; - 对于函数或方法,可以获得入参和返回值的类型;
- …
总而言之,使用 TypeOf 和 ValueOf 能够将 Go 语言中的变量转换成反射对象,在这时我们能够获得几乎一切跟当前类型相关数据和操作,然后就可以用这些运行时获取的结构动态的执行一些方法。
很多读者可能都会对这个副标题产生困惑,为什么是从接口到反射对象,如果直接调用
reflect.ValueOf(1),看起来是从基本类型int到反射类型,但是TypeOf和ValueOf两个方法的入参其实是interface{}类型。我们在之前已经在 函数调用 一节中介绍过,Go 语言的函数调用都是值传递的,变量会在方法调用前进行类型转换,也就是
int类型的基本变量会被转换成interface{}类型,这也就是第一条法则介绍的是从接口到反射对象。
第二法则
我们既然能够将接口类型的变量转换成反射对象类型,那么也需要一些其他方法将反射对象还原成成接口类型的变量,reflect 中的 Interface 方法就能完成这项工作:

然而调用 Interface 方法我们也只能获得 interface{} 类型的接口变量,如果想要将其还原成原本的类型还需要经过一次强制的类型转换,如下所示:
v := reflect.ValueOf(1)
v.Interface{
}.(int)
从反射对象到接口值的过程是从接口值到反射对象的镜面过程,两个过程都需要经历两次转换:
- 从接口值到反射对象:
- 从基本类型到接口类型的类型转换;
- 从接口类型到反射对象的转换;
- 从反射对象到接口值:
- 反射对象转换成接口类型;
- 通过显式类型转换变成原始类型;

当然不是所有的变量都需要类型转换这一过程,如果本身就是 interface{} 类型的,那么它其实并不需要经过类型转换,对于大多数的变量来说,类型转换这一过程很多时候都是隐式发生的,只有在我们需要将反射对象转换回基本类型时才需要做显示的转换操作。
第三法则
Go 语言反射的最后一条法则是与值是否可以被更改相关的,如果我们想要更新一个 reflect.Value,那么它持有的值一定是可以被更新的,假设我们有以下代码:
func main() {
i := 1
v := reflect.ValueOf(i)
v.SetInt(10)
fmt.Println(i)
}
$ go run reflect.go
panic: reflect: reflect.flag.mustBeAssignable using unaddressable value
goroutine 1 [running]:
reflect.flag.mustBeAssignableSlow(0x82, 0x1014c0)
/usr/local/go/src/reflect/value.go:247 +0x180
reflect.flag.mustBeAssignable(...)
/usr/local/go/src/reflect/value.go:234
reflect.Value.SetInt(0x100dc0, 0x414020, 0x82, 0x1840, 0xa, 0x0)
/usr/local/go/src/reflect/value.go:1606 +0x40
main.main()
/tmp/sandbox590309925/prog.go:11 +0xe0
运行上述代码时会导致程序 panic 并报出 reflect: reflect.flag.mustBeAssignable using unaddressable value 错误,仔细想一下其实能够发现出错的原因,Go 语言的 函数调用 都是传值的,所以我们得到的反射对象其实跟最开始的变量没有任何关系,没有任何变量持有复制出来的值,所以直接对它修改会导致崩溃。
想要修改原有的变量我们只能通过如下所示的方法,首先通过 reflect.ValueOf 获取变量指针,然后通过 Elem 方法获取指针指向的变量并调用 SetInt 方法更新变量的值:
func main() {
i := 1
v := reflect.ValueOf(&i)
v.Elem().SetInt(10)
fmt.Println(i)
}
$ go run reflect.go
10
这种获取指针对应的 reflect.Value 并通过 Elem 方法迂回的方式就能够获取到可以被设置的变量,这一复杂的过程主要也是因为 Go 语言的函数调用都是值传递的,我们可以将上述代码理解成:
func main() {
i := 1
v := &i
*v = 10
}
如果不能直接操作 i 变量修改其持有的值,我们就只能获取 i 变量所在地址并使用 *v 修改所在地址中存储的整树。
版权声明
本文为[沉淅尘]所创,转载请带上原文链接,感谢
https://blog.csdn.net/weixin_41335923/article/details/121222673
边栏推荐
- Qt程序集成EasyPlayer-RTSP流媒体播放器出现画面闪烁是什么原因?
- [latex] formula group
- VHDL语言实现32位二进制数转BCD码
- AI CC 2019 installation tutorial under win10 (super detailed - small white version)
- QT program integration easyplayer RTSP streaming media player screen flicker what is the reason?
- 单极性非归零NRZ码、双极性非归零NRZ码、2ASK、2FSK、2PSK、2DPSK及MATLAB仿真
- 【ICCV 2019】MAP-VAE:Multi-Angle Point Cloud-VAE: Unsupervised Feature Learning for 3D Point Clouds..
- 秒杀所有区间相关问题
- io.Platform.packageRoot; // ignore: deprecated_member_use
- 創下國產手機在海外市場銷量最高紀錄的小米,重新關注國內市場
猜你喜欢

无线充电全国产化电子元件推荐方案

Set classic topics
![[AI vision · quick review of NLP natural language processing papers today, No. 32] wed, 20 APR 2022](/img/b2/269ae2e9be269c2bff73eb1da5b55d.png)
[AI vision · quick review of NLP natural language processing papers today, No. 32] wed, 20 APR 2022
![[AI vision · quick review of today's sound acoustic papers, issue 3] wed, 20 APR 2022](/img/48/0e95841743bada4faf3edfee31cb6a.png)
[AI vision · quick review of today's sound acoustic papers, issue 3] wed, 20 APR 2022

Machine translation baseline

MYSQL查询至少连续n天登录的用户

知乎有问题,谁来解答?

Introduction to Cortex-M3 register set, assembly language and C language interface

Win10 boot VMware virtual machine boot seconds blue screen problem perfect solution

Cortex-M3寄存器组、汇编语言与C语言的接口介绍
随机推荐
Stm32f4 MCU ADC sampling and FFT of ARM-DSP Library
无线键盘全国产化电子元件推荐方案
As a code farmer, what kind of experience is it that a girlfriend can code better than herself?
[AI vision · quick review of today's sound acoustic papers issue 1] Thu, 14 APR 2022
【BIM+GIS】ArcGIS Pro2.8如何打开Revit模型,BIM和GIS融合?
【论文阅读】【3d目标检测】point transformer
Qtspim manual - Chinese Translation
STM32 upper μ C / shell transplantation and Application
A function second kill 2sum 3sum 4sum problem
[latex] formula group
[echart] démarrer avec echart
What if win10 doesn't have a local group policy?
[AI vision · quick review of robot papers today, issue 28] wed, 1 Dec 2021
Set classic topics
Zotero6. Version 0 quicklook cannot be used / Chinese garbled code will not be displayed
CRF based medical entity recognition baseline
MYSQL50道基础练习题
Mysql---数据读写分离、多实例
[mapping program design] coordinate inverse artifact v1 0 (with C / C / VB source program)
Nel ASA:挪威Herøya设施正式启用