Golang反射修改变量值

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均可修改。

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

Golang反射修改变量值

 

2.2 该值是可导出的

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

 

3. 修改slice

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

Golang反射修改变量值

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

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

代码输出如下

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

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

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

代码输出如下

$ go run main.go valuePtrS kind:ptr CanSet:false valueS kind:slice CanSet:true replace 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:%vn", valuePtrS.Kind(), valuePtrS.CanSet()) 	// 获取指针指向的内容 	valueS := valuePtrS.Elem() 	fmt.Printf("valueS kind:%v CanSet:%vn", valueS.Kind(), valueS.CanSet()) 	// 修改指定下标数据 	valueS.Index(0).SetInt(10) 	fmt.Printf("after edit:%vn", s) 	// 整体修改slice 	valueS.Set(reflect.ValueOf([3]int{4, 5, 6})) 	fmt.Printf("replace edit:%vn", 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.go valuePtrS kind:ptr CanSet:false valueS kind:array CanSet:true after 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:%vn", valueS.Kind(), valueS.CanSet()) 	// 获取指针指向的内容 	valueS = valueS.Elem() 	fmt.Printf("Kind:%v CanSet:%v Field(0).CanSet:%vn", valueS.Kind(), valueS.CanSet(), valueS.Field(0).CanSet()) 	// 修改指定成员的值 	valueS.Field(0).SetInt(10) 	fmt.Printf("after edit:%+vn", s) 	// 替换整体内容 	valueS.Set(reflect.ValueOf(myStruct{Num: 100, Desc: "new desc"})) 	fmt.Printf("after replace:%+vn", s) }

代码输出如下,

$ go run main.go Kind:ptr CanSet:false Kind:struct CanSet:true Field(0).CanSet:true after 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:%vn", 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:%vn", keys[i], valueM.MapIndex(keys[i])) 	} }

代码输出如下

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

 

 

 

发表评论

评论已关闭。

相关文章