網站首頁 編程語言 正文
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
是引用地址時,返回參數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
原文鏈接:https://www.cnblogs.com/amos01/p/16930711.html
- 上一篇:C語言之如何求三次方根_C 語言
- 下一篇:C++日期和時間編程小結_C 語言
相關推薦
- 2022-06-04 Android自定義scrollview實現回彈效果_Android
- 2022-08-04 Python對FTP交互封裝的實現_python
- 2022-09-19 C++四種cast使用詳細介紹_C 語言
- 2022-11-25 Linux?apache實現https的配置方法_Linux
- 2022-11-07 一文教會你用python連接并簡單操作SQLserver數據庫_python
- 2022-05-29 ASP.NET?Core全局異常處理_實用技巧
- 2022-08-21 python數字圖像處理之基本形態學濾波_python
- 2022-12-14 React?Native設備信息查看調試詳解_React
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支