go reflect 反射

news2024/11/14 18:00:13

目录

一、反射

1、reflect.Type 和 reflect.Value

2、rtype 和 rvalue

3、reflect.TypeOf 工作原理

4、reflect.ValueOf 工作原理

5、reflect.ValueOf 与 reflect.TypeOf 比较

6、性能优化建议

二、问题:

1、静态类型和动态类型

2、值类型与引用类型

(1)值类型(Value Types)

(2)非值类型(Reference Types)

3、对于需要反射的结构体,使用引用类型结构体是不是更好


一、反射

1、reflect.Typereflect.Value

Go语言的反射(reflection)是通过reflect包来实现的,它提供了一些工具用于在运行时检查类型和值。reflect 的核心功能依赖于 reflect.Typereflect.Value 两个结构。reflect.Typereflect.Value 都是封装了底层数据结构,用来访问动态类型和动态值。

  • reflect.Type:表示类型信息,用来获取静态类型的信息(如类型名、类型的大小、类型的字段等)。
  • reflect.Value:它是一个结构体,封装了一个指向实际数据的指针。通过它可以获取对象的值、修改值或调用方法

2、rtypervalue

每个 Go 类型都有一个称为 rtype 的结构体,它是类型信息的底层实现。rvalue 是指实际数据值的底层实现。Go 的运行时会通过反射把数据和类型信息组合在一起,从而允许我们在运行时动态地访问或修改数据。

(1)rtype:由 Go 编译器在编译时生成,包含类型的元数据。对于每个类型(包括结构体、数组、切片、基本类型等)都有一个对应的 rtype,它在程序运行时通过反射机制被加载到内存中,以供反射操作使用。

  • 对于静态类型(如 int、struct、slice等),Go 运行时在程序启动时就加载了类型信息。reflect.TypeOf 只是返回这些类型元数据的引用,因此在这些情况下,reflect.TypeOf 的开销相对较小。对于结构体类型,编译器会根据结构体定义生成一个 rtype,这个 rtype 包含了结构体的字段信息、字段顺序、字段类型、大小等信息。
  • 对于动态类型(接口类型interface),reflect.TypeOf 需要根据实际值的动态类型来查找类型信息,涉及到更多的运行时查找操作。

(2)rvalue:在运行时创建,当通过 reflect.ValueOf() 等反射操作获取一个值时,Go 会创建一个 rvalue。它封装了数据的实际值,并与 rtype 一起用于实现反射的功能。

  • 对于值类型(如 int、array、struct 等),rvalue 会直接封装该值。
  • 对于引用类型或指针rvalue 会封装指向实际内存中数据的指针。

3、reflect.TypeOf 工作原理

当调用 reflect.TypeOf 时,Go 会查询该类型的 rtype。每个 Go 类型都有一个称为 rtype 的结构体,rtype 会包含关于该类型的元数据信息,包括类型的大小、字段、方法等。

  • 查询类型元数据reflect.TypeOf 会访问传入对象的类型信息,通常这个信息是通过指向 rtype 结构体的指针来存储的。
  • 返回类型信息reflect.TypeOf 会根据传入的值返回一个 reflect.Type,这个 reflect.Type 本质上是对 rtype 的封装,包含了该类型的各种元数据信息。

4、reflect.ValueOf 工作原理

当调用 reflect.ValueOf 时,Go 会返回一个 reflect.Value 对象,该对象封装了传入对象的实际数据(指向底层数据的指针)以及该数据的动态类型。通过 reflect.Value,可以读取或修改对象的值。

  • 类型信息reflect.ValueOf 会通过 reflect.TypeOf 获取传入对象的类型信息,即 rtype,并将其存储在 reflect.Value 中。
  • 内存分配:对于非值类型的对象(如指针、切片、数组、interface、chan等),reflect.ValueOf 会创建一个新的 reflect.Value 实例,封装传入值的内存地址(指针)或直接复制值。
  • 复制操作:对于值类型的对象(如 int、array、struct等),reflect.ValueOf 可能会创建该值的副本。这意味着如果传入的是大对象或结构体,可能会涉及到较大的内存分配和复制操作。

5、reflect.ValueOf 与 reflect.TypeOf 比较

  • reflect.TypeOf:只返回类型信息,获取的是 reflect.Type。它仅仅是对静态类型信息的封装,因此开销相对较低,特别是当类型信息已经在程序启动时加载到内存中的时候。reflect.TypeOf 不会涉及值的拷贝或修改。

  • reflect.ValueOf:返回的是实际的值,它不仅封装了类型信息,还封装了该值的实际数据。对于值类型,可能会发生复制;对于引用类型,通常会封装指针。此外,它还要处理值的可变性、指针封装等因素,因此它的开销通常大于 reflect.TypeOf

6、性能优化建议

  • 减少不必要的反射调用:频繁调用 reflect.ValueOf 或者其他反射函数会带来额外的性能开销。尽量避免在性能关键路径中使用反射。
  • 避免对大对象进行深拷贝:对结构体或数组等较大对象进行深拷贝可能会导致性能瓶颈。如果反射需要频繁复制数据,考虑是否可以优化数据结构或减少不必要的复制。
  • 使用引用(指针)类型,避免反射时的内存复制。

二、问题:

1、静态类型和动态类型

  • 静态类型 是编译时确定的,它决定了变量所能持有的值的种类和支持的操作。
  • 动态类型 是运行时确定的,通常与接口类型相关,反映了变量当前存储的数据的类型。
  • 静态类型不能改变,一旦确定就不可改变;动态类型是可变的,尤其是在接口类型中。
  • 静态类型由编译器进行静态类型检查;动态类型在运行时通过反射或类型断言访问。
  • 通过反射或类型断言,可以在运行时获取动态类型的信息。

2、值类型与引用类型

(1)值类型(Value Types)

值类型是指存储数据本身的类型,变量直接包含其数据的副本。当我们将一个值类型变量赋值给另一个变量时,实际上是将数据的副本拷贝了一份。改变新变量的值不会影响原始变量。

常见的值类型

  • 基本类型:如 intfloatboolstring
  • 结构体类型struct
  • 数组类型array

值类型的特点

  • 直接存储数据:值类型的变量直接包含数据本身,而不是数据的引用。
  • 复制传递:当将一个值类型变量赋值给另一个变量时,数据会被复制。这意味着两个变量在内存中互不影响,它们各自拥有自己的数据副本。
  • 内存分配:值类型通常会在栈上分配内存,数据存储在变量的内存区域。
(2)非值类型(Reference Types)

非值类型是指存储数据的引用或地址的类型,变量保存的是数据的指针(引用),而不是数据本身。当我们将一个非值类型变量赋值给另一个变量时,实际上是将数据的引用(地址)传递给新变量。此时两个变量指向相同的内存位置,修改一个变量的值会影响到另一个变量。

常见的非值类型

  • 指针类型pointer
  • 切片类型slice
  • 映射类型map
  • 通道类型chan
  • 接口类型interface

非值类型的特点

  • 存储数据的引用:非值类型的变量存储的是数据的引用(指针),而不是数据本身。
  • 共享数据:当将一个非值类型变量赋值给另一个变量时,它们共享同一块内存区域。修改其中一个变量会影响到其他所有引用同一内存位置的变量。
  • 内存分配:非值类型通常会在堆上分配内存(但也有可能在栈上分配,具体取决于编译器的优化)。

3、对于需要反射的结构体,使用引用类型结构体是不是更好

        在 Go 中,对于需要反射的结构体,使用引用类型结构体(即结构体指针)通常比使用值类型结构体更优。

        减少内存开销。当传递一个结构体变量时,会发生值的复制。如果传递的是结构体指针,那么反射操作将直接作用于原始结构体,而不是副本,这避免了不必要的内存开销。

        提高灵活性。结构体指针可以直接修改原始结构体的字段,而值类型结构体的修改不会影响到原始数据。

        避免不必要的 Elem 调用。如果使用结构体的值类型(即传递结构体副本),在反射时需要使用 Elem() 来解引用结构体指针。如果使用结构体指针,则不需要。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2240324.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

gdb调试redis。sudo

1.先启动redis-server和一个redis-cli。 2.ps -aux|grep reids查看redis相关进程。 3.开始以管理员模式附加进程调试sudo gdb -p 2968.注意这里不能不加sudo,因为Redis 可能以 root 用户启动,普通用户无法附加到该进程。否则就会出现可能下列情形&#…

长连接配置以及断线重连

目录 长连接index 主要进行连接 import SockJS from "sockjs-client"; import Stomp from "stompjs"; import { notification } from "antd"; // 网络请求API import { nowApiAddressObj } from "../api/nowApiAddressObj";// 工具 i…

LeetCode【0054】螺旋矩阵

本文目录 1 中文题目2 求解方法:数学模拟2.1 方法思路2.2 Python代码2.3 复杂度分析 3 题目总结 1 中文题目 给定一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。 示例: 提示: 1 ≤ m …

万字长文解读深度学习——训练(DeepSpeed、Accelerate)、优化(蒸馏、剪枝、量化)、部署细节

🌺历史文章列表🌺 深度学习——优化算法、激活函数、归一化、正则化深度学习——权重初始化、评估指标、梯度消失和梯度爆炸深度学习——前向传播与反向传播、神经网络(前馈神经网络与反馈神经网络)、常见算法概要汇总万字长文解读…

C#版使用融合通信API发送手机短信息

目录 功能实现 范例运行环境 实现范例 类设计 类代码实现 调用范例 总结 功能实现 融合云通信服务平台,为企业提供全方位通信服务,发送手机短信是其一项核心功能,本文将讲述如何使用融合云服务API为终端手机用户发送短信信息&#xf…

第四十五章 Vue之Vuex模块化创建(module)

目录 一、引言 二、模块化拆分创建方式 三、模块化拆分完整代码 3.1. index.js 3.2. module1.js 3.3. module2.js 3.4. module3.js 3.5. main.js 3.6. App.vue 3.7. Son1.vue 3.8. Son2.vue 四、访问模块module的state ​五、访问模块中的getters ​六、mutati…

如何解决不能将开发板连接到虚拟机的问题(连接显示灰色,不能选中)

-- 如果连接上rk3588单片机,虚拟机无法来连接,如何更改 -- 先将虚拟机关机 -- 将虚拟机的配置文件以文本文件的形式打开 -- 再将所有的FALSE改为TRUE即可 -- 然后再次打开虚拟机即可

什么是白盒测试

一、什么是白盒测试 白盒测试又称结构测试、逻辑驱动测试或基于代码的测试。 白盒测试是一种测试用例设计方法,盒子指的是被测试的软件,白盒指的是盒子是可视的,即清楚盒子内部的东西以及里面是如何运作的。 "白盒"法需要测试者…

图形 2.6 伽马校正

伽马校正 B站视频:图形 2.6 伽马校正 文章目录 伽马校正颜色空间传递函数 Gamma校正校正过程为什么需要校正?CRT与转换函数 为什么sRGB在Gamma 0.45空间? 人对亮度的敏感韦伯定律中灰值 线性工作流不在线性空间下进行渲染的问题统一到线性空…

Android setContentView执行流程(一)-生成DecorView

Android setContentView执行流程(一)-生成DecorView Android setContentView执行流程(二)-将布局添加到mContentParent setContentView的流程主要就是讲在Activity的onCreate方法中调用setContentView方法之后,我们自定义的xml文件加载的过程,学习它可以…

【计算机网络】【网络层】【习题】

计算机网络-网络层-习题 文章目录 13. 图 4-69 给出了距离-向量协议工作过程,表(a)是路由表 R1 初始的路由表,表(b)是相邻路由器 R2 传送来的路由表。请写出 R1 更新后的路由表(c)。…

图像处理实验四(Adaptive Filter)

一、Adaptive Filter简介 自适应滤波器(Adaptive Filter)是一种能够根据输入信号的统计特性自动调整自身参数以达到最佳滤波效果的滤波器。它广泛应用于信号处理领域,如信道均衡、系统识别、声学回波抵消、生物医学、雷达、波束形成等模块。 …

typedef 与 extern 的结合:一场误解的澄清

typedef 与 extern 的结合:一场误解的澄清 一、typedef 的基本用法二、extern 的基本用法三、typedef 与 extern 的结合:一场误解的澄清示例二:使用 extern 声明外部变量示例三:错误的用法:尝试在 typedef 中使用 extern四、总结在C语言编程的世界里,typedef和extern是两…

Qt_day5_常用类

常用类 目录 1. QString 字符串类(掌握) 2. 容器类(掌握) 2.1 顺序容器QList 2.2 关联容器QMap 3. 几种Qt数据类型(熟悉) 3.1 跨平台数据类型 3.2 QVariant 统一数据类型 3.3 QStringList 字符串列表 4. QD…

HashMap的put流程知道吗

HashMap 的 put 方法算是 HashMap 中比较核心的功能了,复杂程度高但是算法巧妙,同时在上一版本的基础之上优化了存储结构,从链表逐步进化成了红黑树,以满足存取性能上的需要。本文逐行分析了 put 方法的执行流程,重点放…

鸿蒙UI开发——实现环形文字

1、背 景 有朋友提问:您好关于鸿蒙UI想咨询一个问题 如果我想实现展示环形文字是需要通过在Text组件中设置transition来实现么,还是需要通过其他方式来实现。 针对这位粉丝朋友的提问,我们做一下解答。 2、实现环形文字效果 ❓ 什么是环形…

保存pytest的执行日志;在日志中显示当前是第几次执行

1、在本地保存执行日志: 在终端中执行时因为指定了-s参数,所以会打印相关信息,可以帮助我们后续定位问题: 但是显示在终端时后面无法查看,所以需要把执行日志保存在本地,使用tee 或 重定向符号>&#x…

2024年8个最佳在线websocket调试工具选择

精选了 8 款功能强大且易于使用的 WebSocket 测试工具: 工具名称支持的系统是否免费ApifoxWindows, Mac, Linux是WebSocket KingWindows, Mac, Linux是PostmanWindows, Mac, Linux是Socket.IO Test ClientWindows, Mac, Linux是InsomniaWindows, Mac, Linux是Wires…

H5流媒体播放器EasyPlayer.js播放器wasm编译打包之后报uncaught referenceErro的原因排查

EasyPlayer.js H5播放器,是一款能够同时支持HTTP、HTTP-FLV、HLS(m3u8)、WS、WEBRTC、FMP4视频直播与视频点播等多种协议,支持H.264、H.265、AAC、G711A、Mp3等多种音视频编码格式,支持MSE、WASM、WebCodec等多种解码方…

初识算法 · 位运算(2)

目录 前言: 判定字符是否唯一 丢失的数字 比特位计数 只出现一次的数字III 前言: ​本文的主题是位运算,通过四道题目讲解,一道是判断字符是否唯一,一道是只出现一次的数字III,一道是比特位计数&…