Go语言通过反射修改变量的值
来源:云海天教程
时间:2023-01-07 12:09:59 352浏览 收藏
本篇文章给大家分享《Go语言通过反射修改变量的值》,覆盖了Golang的常见基础知识,其实一个语言的全部知识点一篇文章是不可能说完的,但希望通过这些问题,让读者对自己的掌握程度有一定的认识(B 数),从而弥补自己的不足,更好的掌握它。
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)
我们可以通过调用 reflect.Value 的 CanAddr 方法来判断其是否可以被取地址:
fmt.Println(a.CanAddr()) // "false"
fmt.Println(b.CanAddr()) // "false"
fmt.Println(c.CanAddr()) // "false"
fmt.Println(d.CanAddr()) // "true"
以此类推,reflect.ValueOf(e).Index(i) 对于的值也是可取地址的,即使原始的 reflect.ValueOf(e) 不支持也没有关系。
使用 reflect.Value 对包装的值进行修改时,需要遵循一些规则。如果没有按照规则进行代码设计和编写,轻则无法修改对象值,重则程序在运行时会发生宕机。
判定及获取元素的相关方法
使用 reflect.Value 取元素、取地址及修改值的属性方法请参考下表。*
操作。当值类型不是指针或接口时发生宕 机,空指针时返回 nil 的 ValueAddr() Value对可寻址的值返回其地址,类似于语言层&
操作。当值不可寻址时发生宕机CanAddr() bool表示值是否可寻址CanSet()bool返回值能否被修改。要求值可寻址且是导出的字段值修改相关方法
使用 reflect.Value 修改值的相关方法如下表所示。以上方法,在 reflect.Value 的 CanSet 返回 false 仍然修改值时会发生宕机。
在已知值的类型时,应尽量使用值对应类型的反射设置值。
值可修改条件之一:可被寻址
通过反射修改变量值的前提条件之一:这个值必须可以被寻址。简单地说就是这个变量必须能被修改。示例代码如下:package mainimport ( "reflect")func main() { // 声明整型变量a并赋初值 var a int = 1024 // 获取变量a的反射值对象 valueOfA := reflect.ValueOf(a) // 尝试将a修改为1(此处会发生崩溃) valueOfA.SetInt(1)}程序运行崩溃,打印错误:
panic: reflect: reflect.Value.SetInt using unaddressable value
报错意思是:SetInt 正在使用一个不能被寻址的值。从 reflect.ValueOf 传入的是 a 的值,而不是 a 的地址,这个 reflect.Value 当然是不能被寻址的。将代码修改一下,重新运行:package mainimport ( "fmt" "reflect")func main() { // 声明整型变量a并赋初值 var a int = 1024 // 获取变量a的反射值对象(a的地址) valueOfA := reflect.ValueOf(&a) // 取出a地址的元素(a的值) valueOfA = valueOfA.Elem() // 修改a的值为1 valueOfA.SetInt(1) // 打印a的值 fmt.Println(valueOfA.Int())}代码输出如下:
1
下面是对代码的分析:第 14 行中,将变量 a 取值后传给 reflect.ValueOf()。此时 reflect.ValueOf() 返回的 valueOfA 持有变量 a 的地址。第 17 行中,使用 reflect.Value 类型的 Elem() 方法获取 a 地址的元素,也就是 a 的值。reflect.Value 的 Elem() 方法返回的值类型也是 reflect.Value。第 20 行,此时 valueOfA 表示的是 a 的值且可以寻址。使用 SetInt() 方法设置值时不再发生崩溃。第 23 行,正确打印修改的值。
提示
当 reflect.Value 不可寻址时,使用 Addr() 方法也是无法取到值的地址的,同时会发生宕机。虽然说 reflect.Value 的 Addr() 方法类似于语言层的&
操作;Elem() 方法类似于语言层的*
操作,但并不代表这些方法与语言层操作等效。值可修改条件之一:被导出
结构体成员中,如果字段没有被导出,即便不使用反射也可以被访问,但不能通过反射修改,代码如下:package mainimport ( "reflect")func main() { type dog struct { legCount int } // 获取dog实例的反射值对象 valueOfDog := reflect.ValueOf(dog{}) // 获取legCount字段的值 vLegCount := valueOfDog.FieldByName("legCount") // 尝试设置legCount的值(这里会发生崩溃) vLegCount.SetInt(4)}程序发生崩溃,报错:
panic: reflect: reflect.Value.SetInt using value obtained using unexportedfield
报错的意思是:SetInt() 使用的值来自于一个未导出的字段。为了能修改这个值,需要将该字段导出。将 dog 中的 legCount 的成员首字母大写,导出 LegCount 让反射可以访问,修改后的代码如下:
type dog struct { LegCount int}然后根据字段名获取字段的值时,将字符串的字段首字母大写,修改后的代码如下:
vLegCount := valueOfDog.FieldByName("LegCount")再次运行程序,发现仍然报错:
panic: reflect: reflect.Value.SetInt using unaddressable value
这个错误表示第 13 行构造的 valueOfDog 这个结构体实例不能被寻址,因此其字段也不能被修改。修改代码,取结构体的指针,再通过 reflect.Value 的 Elem() 方法取到值的反射值对象。修改后的完整代码如下:package mainimport ( "reflect" "fmt")func main() { type dog struct { LegCount int } // 获取dog实例地址的反射值对象 valueOfDog := reflect.ValueOf(&dog{}) // 取出dog实例地址的元素 valueOfDog = valueOfDog.Elem() // 获取legCount字段的值 vLegCount := valueOfDog.FieldByName("LegCount") // 尝试设置legCount的值(这里会发生崩溃) vLegCount.SetInt(4) fmt.Println(vLegCount.Int())}代码输出如下:
4
代码说明如下:第 11 行,将 LegCount 首字母大写导出该字段。第 14 行,获取 dog 实例指针的反射值对象。第 17 行,取 dog 实例的指针元素,也就是 dog 的实例。第 20 行,取 dog 结构体中 LegCount 字段的成员值。第 23 行,修改该成员值。第 25 行,打印该成员值。
值的修改从表面意义上叫可寻址,换一种说法就是值必须“可被设置”。那么,想修改变量值,一般的步骤是:取这个变量的地址或者这个变量所在的结构体已经是指针类型。使用 reflect.ValueOf 进行值包装。通过 Value.Elem() 获得指针值指向的元素值对象(Value),因为值对象(Value)内部对象为指针时,使用 set 设置时会报出宕机错误。使用 Value.Set 设置值。
终于介绍完啦!小伙伴们,这篇关于《Go语言通过反射修改变量的值》的介绍应该让你收获多多了吧!欢迎大家收藏或分享给更多需要学习的朋友吧~golang学习网公众号也会发布Golang相关知识,快来关注吧!
-
174 收藏
-
246 收藏
-
353 收藏
-
126 收藏
-
395 收藏
-
450 收藏
-
330 收藏
-
193 收藏
-
271 收藏
-
414 收藏
-
179 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 507次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习
-
- 无奈的钥匙
- 这篇博文真是及时雨啊,很详细,感谢大佬分享,已加入收藏夹了,关注老哥了!希望老哥能多写Golang相关的文章。
- 2023-04-15 09:16:59
-
- 甜蜜的羊
- 太详细了,已加入收藏夹了,感谢师傅的这篇技术贴,我会继续支持!
- 2023-02-19 08:55:23
-
- 妩媚的砖头
- 这篇博文太及时了,细节满满,写的不错,已加入收藏夹了,关注楼主了!希望楼主能多写Golang相关的文章。
- 2023-02-15 14:16:08
-
- 舒服的天空
- 很棒,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,帮助很大,总算是懂了,感谢博主分享技术文章!
- 2023-02-02 17:26:48
-
- 合适的钢笔
- 这篇技术贴真是及时雨啊,作者加油!
- 2023-01-18 16:51:16
-
- 明亮的滑板
- 写的不错,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,帮助很大,总算是懂了,感谢作者分享博文!
- 2023-01-16 22:22:48
-
- 温婉的红酒
- 这篇文章真是及时雨啊,太细致了,很棒,码住,关注大佬了!希望大佬能多写Golang相关的文章。
- 2023-01-16 04:58:23
-
- 结实的芝麻
- 很详细,码住,感谢大佬的这篇文章内容,我会继续支持!
- 2023-01-14 18:41:36