时间:2022-12-06来源:www.pcxitongcheng.com作者:电脑系统城
前面的随笔Golang 利用反射对结构体优雅排序的操作方法分享了如何通过反射获取变量的类型和值,
也就是Golang反射三大定律中的前两个,即从interface{}
到反射对象和从反射对象到interface{}
。
这篇随笔主要分享通过反射修改各种类型变量值的方法。
reflect
提供func (v Value) CanSet() bool
判断对象值是否修改。
一般情况下,通过反射修改变量值,需要满足以下两个条件。
类似函数传参,如果需要在函数内修改入参数的内容,那么就需要传引用,而不是传值。
函数内修改入参指向的内容,才能将修改效果“带出”该函数的作用域。
同理,反射修改变量的值,应当是可以寻址的,修改的是反射对象指向的数据内容,
因此,通过反射函数func ValueOf(i any) Value
i
是引用时,i
指向的内容可寻址,因此返回参数Value
不可修改,Value.Elem
可修改。i
是地址时,返回参数Value
可修改。i
是引用地址时,返回参数Value
及Value.Elem
均可修改。上述三种情况如下图所示,经过寻址的内容才有可能是可修改的。
这个主要是针对结构体的成员,该成员的字段名的首字母需要是大写,即是“public”
的。
slice
是引用类型,slice
的数据结构如下图所示,通过反射可以修改slice
指向的内容。
修改指定下标的数据内容,并且数据类型需要和修改前一只,否则会panic
。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
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.go
valueS Kind:slice CanSet:false Index(0).CanSet:true
after edit:[10 20 3]
如果需要整体修改修改slice
,那么需要传入slice
的地址
1 2 3 4 5 6 7 8 9 10 11 12 |
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.go
valuePtrS kind:ptr CanSet:false
valueS kind:slice CanSet:true
replace edit:[4 5 6 7]
array
不是引用类型,因此func ValueOf(i any) Value
需要传入array
的地址。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
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.go
valuePtrS kind:ptr CanSet:false
valueS kind:array CanSet:true
after edit:[10 2 3]
replace edit:[4 5 6]
带修改的结构体的成员的字段名首字母需要大写。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
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.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}
反射通过func (v Value) SetMapIndex(key, elem Value)
修改map
指定key
的value
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
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.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
到此这篇关于Golang反射修改变量值的文章就介绍到这了
2024-07-16
如何使用 Go 依赖库管理器修复损坏的依赖项?2024-07-07
Java框架如何简化代码的调试过程2023-03-17
Python 使用tf-idf算法计算文档关键字权重并生成词云的方法有这么一段代码,可以先看一下有没有什么问题,作用是输入一段json字符串,反序列化成map,然后将另一个inputMap的内容,merge进这个map 1 2 3 4 5 6 7 8 9 10 11 12 13 14...
2023-03-15
由于数据库的类型为Data 类型,所以插入数据库的时候我先把前端传入的string类型的时间转为Time 再插入。 Go 提供了两种插入的方式,即time.Parse 和 time.ParseInLocation 。两种方式,他们的差异比较大。...
2023-03-09