经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » Go语言 » 查看文章
Golang反射修改变量值
来源:cnblogs  作者:Amos01  时间:2022/12/5 8:49:51  对本文有异议

1. 前言

前面的随笔Golang反射获取变量类型和值分享了如何通过反射获取变量的类型和值,

也就是Golang反射三大定律中的前两个,即从interface{}到反射对象和从反射对象到interface{}

这篇随笔主要分享通过反射修改各种类型变量值的方法。

 

2. 判断是否可修改

reflect提供func (v Value) CanSet() bool判断对象值是否修改。

一般情况下,通过反射修改变量值,需要满足以下两个条件。

 

2.1 该值是可寻址的

类似函数传参,如果需要在函数内修改入参数的内容,那么就需要传引用,而不是传值。

函数内修改入参指向的内容,才能将修改效果“带出”该函数的作用域。

同理,反射修改变量的值,应当是可以寻址的,修改的是反射对象指向的数据内容,

因此,通过反射函数func ValueOf(i any) Value

  • 入参i是引用时,i指向的内容可寻址,因此返回参数Value不可修改,Value.Elem可修改。
  • 入参i是地址时,返回参数Value可修改。
  • 入参i是引用地址时,返回参数ValueValue.Elem均可修改。

上述三种情况如下图所示,经过寻址的内容才有可能是可修改的。

 

2.2 该值是可导出的

 这个主要是针对结构体的成员,该成员的字段名的首字母需要是大写,即是“public”的。

 

3. 修改slice

slice是引用类型,slice的数据结构如下图所示,通过反射可以修改slice指向的内容。

修改指定下标的数据内容,并且数据类型需要和修改前一只,否则会panic

  1. func main() {
  2. s := []int{1, 2, 3}
  3. valueS := reflect.ValueOf(s)
  4. // slice 是否可修改 不可整体修改
  5. fmt.Printf("valueS Kind:%v CanSet:%v Index(0).CanSet:%v\n", valueS.Kind(), valueS.CanSet(), valueS.Index(0).CanSet())
  6. // 修改指定下标的元素值
  7. valueS.Index(0).Set(reflect.ValueOf(10))
  8. valueS.Index(1).SetInt(20)
  9. fmt.Printf("after edit:%v\n", s)
  10. // panic: reflect: call of reflect.Value.SetFloat on int Value
  11. //valueS.Index(1).SetFloat(100)
  12. }

代码输出如下

  1. $ go run main.go
  2. valueS Kind:slice CanSet:false Index(0).CanSet:true
  3. after edit:[10 20 3]

如果需要整体修改修改slice,那么需要传入slice的地址

  1. func main() {
  2. s := []int{1, 2, 3}
  3. // slice的指针
  4. valuePtrS := reflect.ValueOf(&s)
  5. fmt.Printf("valuePtrS kind:%v CanSet:%v\n", valuePtrS.Kind(), valuePtrS.CanSet())
  6. // 获取指针指向的内容
  7. valueS := valuePtrS.Elem()
  8. fmt.Printf("valueS kind:%v CanSet:%v\n", valueS.Kind(), valueS.CanSet())
  9. // 整体修改slice
  10. valueS.Set(reflect.ValueOf([]int{4, 5, 6, 7}))
  11. fmt.Printf("replace edit:%v\n", s)
  12. }

代码输出如下

  1. $ go run main.go
  2. valuePtrS kind:ptr CanSet:false
  3. valueS kind:slice CanSet:true
  4. replace edit:[4 5 6 7]

 

4. 修改array

array不是引用类型,因此func ValueOf(i any) Value需要传入array的地址。

  1. func main() {
  2. s := [3]int{1, 2, 3}
  3. // array的指针
  4. valuePtrS := reflect.ValueOf(&s)
  5. fmt.Printf("valuePtrS kind:%v CanSet:%v\n", valuePtrS.Kind(), valuePtrS.CanSet())
  6. // 获取指针指向的内容
  7. valueS := valuePtrS.Elem()
  8. fmt.Printf("valueS kind:%v CanSet:%v\n", valueS.Kind(), valueS.CanSet())
  9. // 修改指定下标数据
  10. valueS.Index(0).SetInt(10)
  11. fmt.Printf("after edit:%v\n", s)
  12. // 整体修改slice
  13. valueS.Set(reflect.ValueOf([3]int{4, 5, 6}))
  14. fmt.Printf("replace edit:%v\n", s)
  15. //panic: reflect.Set: value of type [4]int is not assignable to type [3]int
  16. //valueS.Set(reflect.ValueOf([4]int{4, 5, 6}))
  17. }

代码输出如下

  1. $ go run main.go
  2. valuePtrS kind:ptr CanSet:false
  3. valueS kind:array CanSet:true
  4. after edit:[10 2 3]
  5. replace edit:[4 5 6]

 

5. 修改结构体

带修改的结构体的成员的字段名首字母需要大写。

  1. func main() {
  2. type myStruct struct {
  3. Num int `json:"num_json" orm:"column:num_orm"`
  4. Desc string `json:"desc_json" orm:"column:desc_orm"`
  5. }
  6. s := myStruct{
  7. Num: 1,
  8. Desc: "desc",
  9. }
  10. valueS := reflect.ValueOf(&s)
  11. // 指针本身不可修改 可指向的内容
  12. fmt.Printf("Kind:%v CanSet:%v\n", valueS.Kind(), valueS.CanSet())
  13. // 获取指针指向的内容
  14. valueS = valueS.Elem()
  15. fmt.Printf("Kind:%v CanSet:%v Field(0).CanSet:%v\n", valueS.Kind(), valueS.CanSet(), valueS.Field(0).CanSet())
  16. // 修改指定成员的值
  17. valueS.Field(0).SetInt(10)
  18. fmt.Printf("after edit:%+v\n", s)
  19. // 替换整体内容
  20. valueS.Set(reflect.ValueOf(myStruct{Num: 100, Desc: "new desc"}))
  21. fmt.Printf("after replace:%+v\n", s)
  22. }

代码输出如下,

  1. $ go run main.go
  2. Kind:ptr CanSet:false
  3. Kind:struct CanSet:true Field(0).CanSet:true
  4. after edit:{Num:10 Desc:desc}
  5. after replace:{Num:100 Desc:new desc}

 

6. 修改map

反射通过func (v Value) SetMapIndex(key, elem Value)修改map指定keyvalue

  1. func main() {
  2. m := map[int]string{
  3. 1: "1",
  4. 2: "2",
  5. 3: "3",
  6. }
  7. valueM := reflect.ValueOf(m)
  8. // 迭代器访问
  9. iter := valueM.MapRange()
  10. for iter.Next() {
  11. fmt.Printf("key:%v val:%v\n", iter.Key(), iter.Value())
  12. // 将所有value修改为"a"
  13. valueM.SetMapIndex(iter.Key(), reflect.ValueOf("a"))
  14. }
  15. fmt.Println("--- after edit ---")
  16. // 通过key访问
  17. keys := valueM.MapKeys()
  18. for i := 0; i < len(keys); i++ {
  19. fmt.Printf("key:%v val:%v\n", keys[i], valueM.MapIndex(keys[i]))
  20. }
  21. }

代码输出如下

  1. $ go run main.go
  2. key:1 val:1
  3. key:2 val:2
  4. key:3 val:3
  5. --- after edit ---
  6. key:1 val:a
  7. key:2 val:a
  8. key:3 val:a

 

 

 

原文链接:https://www.cnblogs.com/amos01/p/16930711.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号