Go通过reflect.Value修改值
到目前为止,反射还只是程序中变量的另一种读取方式。然而,在本节中我们将重点讨论如何通过反射机制来修改变量。
回想一下,Go语言中类似x、x.f[1]和*p形式的表达式都可以表示变量,但是其它如x + 1和f(2)则不是变量。一个变量就是一个可寻址的内存空间,里面存储了一个值,并且存储的值可以通过内存地址来更新。
对于reflect.Values也有类似的区别。有一些reflect.Values是可取地址的;其它一些则不可以。考虑以下的声明语句:
x := 2 // value type variable? a := reflect.ValueOf(2) // 2 int no b := reflect.ValueOf(x) // 2 int no c := reflect.ValueOf(&x) // &x *int no d := c.Elem() // 2 int yes (x)
其中a对应的变量不可取地址。因为a中的值仅仅是整数2的拷贝副本。b中的值也同样不可取地址。c中的值还是不可取地址,它只是一个指针&x的拷贝。实际上,所有通过reflect.ValueOf(x)返回的reflect.Value都是不可取地址的。但是对于d,它是c的解引用方式生成的,指向另一个变量,因此是可取地址的。我们可以通过调用reflect.ValueOf(&x).Elem(),来获取任意变量x对应的可取地址的Value。
我们可以通过调用reflect.Value的CanAddr方法来判断其是否可以被取地址:
fmt.Println(a.CanAddr()) // "false" fmt.Println(b.CanAddr()) // "false" fmt.Println(c.CanAddr()) // "false" fmt.Println(d.CanAddr()) // "true"
每当我们通过指针间接地获取的reflect.Value都是可取地址的,即使开始的是一个不可取地址的Value。在反射机制中,所有关于是否支持取地址的规则都是类似的。例如,slice的索引表达式e[i]将隐式地包含一个指针,它就是可取地址的,即使开始的e表达式不支持也没有关系。以此类推,reflect.ValueOf(e).Index(i)对应的值也是可取地址的,即使原始的reflect.ValueOf(e)不支持也没有关系。
要从变量对应的可取地址的reflect.Value来访问变量需要三个步骤。第一步是调用Addr()方法,它返回一个Value,里面保存了指向变量的指针。然后是在Value上调用Interface()方法,也就是返回一个interface{},里面包含指向变量的指针。最后,如果我们知道变量的类型,我们可以使用类型的断言机制将得到的interface{}类型的接口强制转为普通的类型指针。这样我们就可以通过这个普通指针来更新变量了:
x := 2 d := reflect.ValueOf(&x).Elem() // d refers to the variable x px := d.Addr().Interface().(*int) // px := &x *px = 3 // x = 3 fmt.Println(x) // "3"
或者,不使用指针,而是通过调用可取地址的reflect.Value的reflect.Value.Set方法来更新对应的值:
d.Set(reflect.ValueOf(4)) fmt.Println(x) // "4"
Set方法将在运行时执行和编译时进行类似的可赋值性约束的检查。以上代码,变量和值都是int类型,但是如果变量是int64类型,那么程序将抛出一个panic异常,所以关键问题是要确保改类型的变量可以接受对应的值:
d.Set(reflect.ValueOf(int64(5))) // panic: int64 is not assignable to int
同样,对一个不可取地址的reflect.Value调用Set方法也会导致panic异常:
x := 2 b := reflect.ValueOf(x) b.Set(reflect.ValueOf(3)) // panic: Set using unaddressable value
这里有很多用于基本数据类型的Set方法:SetInt、SetUint、SetString和SetFloat等。
d := reflect.ValueOf(&x).Elem() d.SetInt(3) fmt.Println(x) // "3"
从某种程度上说,这些Set方法总是尽可能地完成任务。以SetInt为例,只要变量是某种类型的有符号整数就可以工作,即使是一些命名的类型、甚至只要底层数据类型是有符号整数就可以,而且如果对于变量类型值太大的话会被自动截断。但需要谨慎的是:对于一个引用interface{}类型的reflect.Value调用SetInt会导致panic异常,即使那个interface{}变量对于整数类型也不行。
x := 1 rx := reflect.ValueOf(&x).Elem() rx.SetInt(2) // OK, x = 2 rx.Set(reflect.ValueOf(3)) // OK, x = 3 rx.SetString("hello") // panic: string is not assignable to int rx.Set(reflect.ValueOf("hello")) // panic: string is not assignable to int var y interface{} ry := reflect.ValueOf(&y).Elem() ry.SetInt(2) // panic: SetInt called on interface Value ry.Set(reflect.ValueOf(3)) // OK, y = int(3) ry.SetString("hello") // panic: SetString called on interface Value ry.Set(reflect.ValueOf("hello")) // OK, y = "hello"
当我们用Display显示os.Stdout结构时,我们发现反射可以越过Go语言的导出规则的限制读取结构体中未导出的成员,比如在类Unix系统上os.File结构体中的fd int成员。然而,利用反射机制并不能修改这些未导出的成员:
stdout := reflect.ValueOf(os.Stdout).Elem() // *os.Stdout, an os.File var fmt.Println(stdout.Type()) // "os.File" fd := stdout.FieldByName("fd") fmt.Println(fd.Int()) // "1" fd.SetInt(2) // panic: unexported field
一个可取地址的reflect.Value会记录一个结构体成员是否是未导出成员,如果是的话则拒绝修改操作。因此,CanAddr方法并不能正确反映一个变量是否是可以被修改的。另一个相关的方法CanSet是用于检查对应的reflect.Value是否是可取地址并可被修改的:
fmt.Println(fd.CanAddr(), fd.CanSet()) // "true false"
相关文章:
Go通过reflect.Value修改值
到目前为止,反射还只是程序中变量的另一种读取方式。然而,在本节中我们将重点讨论如何通过反射机制来修改变量。 回想一下,Go语言中类似x、x.f[1]和*p形式的表达式都可以表示变量,但是其它如x 1和f(2)则不是变量。一个变量就是一…...
【MySql】4- 实践篇(二)
文章目录 1. SQL 语句为什么变“慢”了1.1 什么情况会引发数据库的 flush 过程呢?1.2 四种情况性能分析1.3 InnoDB 刷脏页的控制策略 2. 数据库表的空间回收2.1 innodb_file_per_table参数2.2 数据删除流程2.3 重建表2.4 Online 和 inplace 3. count(*) 语句怎样实现…...
获取多个接口的数据并进行处理,使用Promise.all来等待所有接口请求完成
Promise.all (等待机制) 方法 它调用了多个函数,这些函数返回了Promise对象,每个Promise对象代表了一个异步操作。 然后,使用Promise.all将这多个Promise对象包装成一个新的Promise对象,它会等待所有的Promise都完成(或…...
利用C++开发一个迷你的英文单词录入和测试小程序-升级版本
我们现在有了一个本地sqlite3的迷你英文单词小测试工具,需求就跟工作当中一样是不断变更的。这里虚构两个场景,并且一步一步的完成最终升级后的小demo。 场景:数据不依赖本地sqlite3,需要支持远程访问,用目前的restfu…...
用c动态数组(实现权重矩阵可视化)实现手撸神经网络230902
变量即内存、指针使用的架构原理: 1、用结构struct记录 网络架构,如 float*** ws 为权重矩阵的指针(指针地址); 2、用 = (float*)malloc (Num * sizeof(float)) 给 具体变量分配内存; 3、用 = (float**)malloc( Num* sizeof(float*) ) 给 指向 具体变量(一维数组)的…...
Android.mk和Android.bp
公司承接Android、iOS等APP开发、前后端网站开发、小程序开发、安全服务等项目! 公司官网:www.bincodesec.com 项目案例 一、编译不同类型的模块 1.编译成Java库 Android.mk include $(BUILD_JAVA_LIBRARY)Android.bp java_library {} 2.编译成Java静态库 And…...
CSS 常用样式-文本属性
一、水平对齐 text-align CSS中的text-align属性用于水平对齐文本。它可以应用于块级元素和表格单元格。 常见的属性值包括: left:左对齐,文本在容器的左侧。right:右对齐,文本在容器的右侧。center:居中…...
BootstrapBlazor企业级组件库:前端开发的革新之路
作为一名Web开发人员,开发前端我们一般都是使用JavaScript,而Blazor就是微软推出的基于.Net平台交互式客户Web UI 框架,可以使用C#替代JavaScript,减少我们的技术栈、降低学习前端的成本。 而采用Blazor开发,少不了需…...
力扣 -- 1745. 分割回文串 IV
解题步骤: 参考代码: class Solution { public:bool checkPartitioning(string s) {int ns.size();vector<vector<bool>> dp(n,vector<bool>(n));for(int in-1;i>0;i--){for(int ji;j<n;j){if(s[i]s[j]){dp[i][j]i1<j?dp[i…...
C# 给某个方法设定执行超时时间
C# 给某个方法设定执行超时时间在某些情况下(例如通过网络访问数据),常常不希望程序卡住而占用太多时间以至于造成界面假死。 在这时、我们可以通过Thread、Thread Invoke(UI)或者是 delegate.BeginInvoke 来避免界面假死, 但是…...
安装NodeJS并使用yarn下载前端依赖
文章目录 1、安装NodeJS1.1 下载NodeJS安装包1.2 解压并配置NodeJS1.3 验证是否安装成功2、使用yarn下载前端依赖2.1 安装yarn2.2 使用yarn下载前端依赖参考目标:在Windows下安装新版NodeJS,并使用yarn下载前端依赖,实现运行前端项目。 1、安装NodeJS 1.1 下载NodeJS安装包…...
(Java高级教程)第三章Java网络编程-第八节:博客系统搭建(前后端分离)
文章目录 一:前端页面回顾二:博客功能展示三:数据库表设计(1)表设计(2)封装DataSource 四:实体类和数据访问对象(1)实体类(2)数据访问…...
901. 股票价格跨度
设计一个算法收集某些股票的每日报价,并返回该股票当日价格的 跨度 。 当日股票价格的 跨度 被定义为股票价格小于或等于今天价格的最大连续日数(从今天开始往回数,包括今天)。 例如,如果未来 7 天股票的价格是 [100,…...
JavaScript中的模块化编程,包括CommonJS和ES6模块的区别。
聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 模块化编程概述⭐ CommonJS 模块⭐ ES6 模块⭐ 区别⭐ 写在最后 ⭐ 专栏简介 前端入门之旅:探索Web开发的奇妙世界 欢迎来到前端入门之旅!感兴趣的可以订阅本专栏哦!这个专栏是为那些对Web开发感兴趣、…...
从零开始 Spring Cloud 13:分布式事务
从零开始 Spring Cloud 13:分布式事务 1.分布式事务问题 用一个示例项目演示在分布式系统中使用事务会产生的问题。 示例项目的 SQL:seata_demo.sql 示例项目代码:seata-demo.zip 这个示例项目中的微服务的互相调用依赖于 Nacos…...
2023Node.js零基础教程(小白友好型),nodejs新手到高手,(二)NodeJS入门——buffer模块、计算机基础、fs模块、path模块
就算步子乱了又如何,接着跳下去就好了。——《闻香识女人》 开始 011_Buffer_介绍与创建 hello,大家好,我们来学习一下buffer。首先来看看 buffer 是一个什么东东。buffer,中文译为缓冲区,是一个类似于数组的对象&am…...
lua如何调用C/C++
1 lua vs C/C lua是脚本语言,优点是门槛低,可以热更新,缺点当然就是性能。C/C是编译型语言,有点是性能高,但是相对的,门槛高,技术不好的人写的代码可能还没有lua的性能高,容易出现c…...
简单聊一聊公平锁和非公平锁,parallel并行流
目录 一、降低锁的粒度,将synchronized关键字不放在方法上了,改为synchronized代码块。二、先区分一下公平锁和非公平锁1、公平锁2、非公平锁3、公平锁的优缺点:4、非公平锁的优缺点: 三、是否对症下药四、IntStream.rangeClosed是…...
【SpringCloud】微服务技术栈入门4 - RabbitMQ初探
目录 RabbitMQ安装 rabbitmqSpringAMQP 基础队列WorkQueue路由发布订阅 FanoutExchangeDirectExchangeTopicExchange RabbitMQ 安装 rabbitmq 首先确保自己已经安装好了 docker 是 docker 拉取镜像文件:docker pull rabbitmq:3-management 拉取完毕,打…...
cefsharp(117.2.20)cef117.2.2最新体验版
一、下载nupkg https://www.nuget.org/packages/CefSharp.WinForms/ https://www.nuget.org/packages/CefSharp.Common/ https://www.nuget.org/packages/cef.redist.x64/ https://www.nuget.org/packages/cef.redist.x86/ 此版本暂时不支持H264。上一版本支持H264 cefsharp…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...
线程同步:确保多线程程序的安全与高效!
全文目录: 开篇语前序前言第一部分:线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分:synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分ÿ…...
从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)
设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile,新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...
【配置 YOLOX 用于按目录分类的图片数据集】
现在的图标点选越来越多,如何一步解决,采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集(每个目录代表一个类别,目录下是该类别的所有图片),你需要进行以下配置步骤&#x…...
QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...
华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建
华为云FlexusDeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色,华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型,能助力我们轻松驾驭 DeepSeek-V3/R1,本文中将分享如何…...
在WSL2的Ubuntu镜像中安装Docker
Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包: for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...
Swagger和OpenApi的前世今生
Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...
视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...
Java编程之桥接模式
定义 桥接模式(Bridge Pattern)属于结构型设计模式,它的核心意图是将抽象部分与实现部分分离,使它们可以独立地变化。这种模式通过组合关系来替代继承关系,从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...
