目录
1. 前言2. 判断是否可修改2.1 该值是可寻址的2.2 该值是可导出的3. 修改slice4. 修改array5. 修改结构体6. 修改map1. 前言
前面的随笔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是引用地址时,返回参数Value及Value.Elem均可修改。
上述三种情况如下图所示,经过寻址的内容才有可能是可修改的。
2.2 该值是可导出的
这个主要是针对结构体的成员,该成员的字段名的首字母需要是大写,即是“public”的。
3. 修改slice
slice是引用类型,slice的数据结构如下图所示,通过反射可以修改slice指向的内容。
修改指定下标的数据内容,并且数据类型需要和修改前一只,否则会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.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:%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]
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.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:%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}
6. 修改map
反射通过func (v Value) SetMapIndex(key, elem Value)修改map指定key的value
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反射修改变量值的文章就介绍到这了,更多相关Golang反射修改变量值内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
X 关闭
X 关闭
- 15G资费不大降!三大运营商谁提供的5G网速最快?中国信通院给出答案
- 2联想拯救者Y70发布最新预告:售价2970元起 迄今最便宜的骁龙8+旗舰
- 3亚马逊开始大规模推广掌纹支付技术 顾客可使用“挥手付”结账
- 4现代和起亚上半年出口20万辆新能源汽车同比增长30.6%
- 5如何让居民5分钟使用到各种设施?沙特“线性城市”来了
- 6AMD实现连续8个季度的增长 季度营收首次突破60亿美元利润更是翻倍
- 7转转集团发布2022年二季度手机行情报告:二手市场“飘香”
- 8充电宝100Wh等于多少毫安?铁路旅客禁止、限制携带和托运物品目录
- 9好消息!京东与腾讯续签三年战略合作协议 加强技术创新与供应链服务
- 10名创优品拟通过香港IPO全球发售4100万股 全球发售所得款项有什么用处?

