2

Golang反射修改变量值 - Amos01

 1 year ago
source link: https://www.cnblogs.com/amos01/p/16930711.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

前面的随笔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均可修改。

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

2309259-20221204220047552-1505622153.png

2.2 该值是可导出的

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

3. 修改slice

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

2309259-20221204220929432-1061847000.png

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

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

代码输出如下

$ go run main.govalueS Kind:slice CanSet:false Index(0).CanSet:trueafter edit:[10 20 3]

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

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

代码输出如下

$ go run main.govaluePtrS kind:ptr CanSet:falsevalueS kind:slice CanSet:truereplace edit:[4 5 6 7]

4. 修改array

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

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

代码输出如下

$ go run main.govaluePtrS kind:ptr CanSet:falsevalueS kind:array CanSet:trueafter edit:[10 2 3]replace edit:[4 5 6]

5. 修改结构体

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

func main() { type myStruct struct { Num int `json:"num_json" orm:"column:num_orm"` Desc string `json:"desc_json" orm:"column:desc_orm"` } s := myStruct{ Num: 1, Desc: "desc", } valueS := reflect.ValueOf(&s) // 指针本身不可修改 可指向的内容 fmt.Printf("Kind:%v CanSet:%v\n", valueS.Kind(), valueS.CanSet()) // 获取指针指向的内容 valueS = valueS.Elem() fmt.Printf("Kind:%v CanSet:%v Field(0).CanSet:%v\n", valueS.Kind(), valueS.CanSet(), valueS.Field(0).CanSet()) // 修改指定成员的值 valueS.Field(0).SetInt(10) fmt.Printf("after edit:%+v\n", s) // 替换整体内容 valueS.Set(reflect.ValueOf(myStruct{Num: 100, Desc: "new desc"})) fmt.Printf("after replace:%+v\n", s)}

代码输出如下,

$ go run main.goKind:ptr CanSet:falseKind:struct CanSet:true Field(0).CanSet:trueafter edit:{Num:10 Desc:desc}after replace:{Num:100 Desc:new desc}

6. 修改map

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

func main() { m := map[int]string{ 1: "1", 2: "2", 3: "3", } valueM := reflect.ValueOf(m) // 迭代器访问 iter := valueM.MapRange() for iter.Next() { fmt.Printf("key:%v val:%v\n", iter.Key(), iter.Value()) // 将所有value修改为"a" valueM.SetMapIndex(iter.Key(), reflect.ValueOf("a")) } fmt.Println("--- after edit ---") // 通过key访问 keys := valueM.MapKeys() for i := 0; i < len(keys); i++ { fmt.Printf("key:%v val:%v\n", keys[i], valueM.MapIndex(keys[i])) }}

代码输出如下

$ go run main.gokey:1 val:1key:2 val:2key:3 val:3--- after edit ---key:1 val:akey:2 val:akey:3 val:a

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK