Golang反射修改變量值的操作代碼
1. 前言
前面的隨筆Golang 利用反射對(duì)結(jié)構(gòu)體優(yōu)雅排序的操作方法分享了如何通過(guò)反射獲取變量的類(lèi)型和值,
也就是Golang反射三大定律中的前兩個(gè),即從interface{}到反射對(duì)象和從反射對(duì)象到interface{}。
這篇隨筆主要分享通過(guò)反射修改各種類(lèi)型變量值的方法。
2. 判斷是否可修改
reflect提供func (v Value) CanSet() bool判斷對(duì)象值是否修改。
一般情況下,通過(guò)反射修改變量值,需要滿(mǎn)足以下兩個(gè)條件。
2.1 該值是可尋址的
類(lèi)似函數(shù)傳參,如果需要在函數(shù)內(nèi)修改入?yún)?shù)的內(nèi)容,那么就需要傳引用,而不是傳值。
函數(shù)內(nèi)修改入?yún)⒅赶虻膬?nèi)容,才能將修改效果“帶出”該函數(shù)的作用域。
同理,反射修改變量的值,應(yīng)當(dāng)是可以尋址的,修改的是反射對(duì)象指向的數(shù)據(jù)內(nèi)容,
因此,通過(guò)反射函數(shù)func ValueOf(i any) Value
- 入?yún)?code>i是引用時(shí),
i指向的內(nèi)容可尋址,因此返回參數(shù)Value不可修改,Value.Elem可修改。 - 入?yún)?code>i是地址時(shí),返回參數(shù)
Value可修改。 - 入?yún)?code>i是引用地址時(shí),返回參數(shù)
Value及Value.Elem均可修改。
上述三種情況如下圖所示,經(jīng)過(guò)尋址的內(nèi)容才有可能是可修改的。

2.2 該值是可導(dǎo)出的
這個(gè)主要是針對(duì)結(jié)構(gòu)體的成員,該成員的字段名的首字母需要是大寫(xiě),即是“public”的。
3. 修改slice
slice是引用類(lèi)型,slice的數(shù)據(jù)結(jié)構(gòu)如下圖所示,通過(guò)反射可以修改slice指向的內(nèi)容。

修改指定下標(biāo)的數(shù)據(jù)內(nèi)容,并且數(shù)據(jù)類(lèi)型需要和修改前一只,否則會(huì)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())
// 修改指定下標(biāo)的元素值
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())
// 獲取指針指向的內(nèi)容
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不是引用類(lèi)型,因此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())
// 獲取指針指向的內(nèi)容
valueS := valuePtrS.Elem()
fmt.Printf("valueS kind:%v CanSet:%v\n", valueS.Kind(), valueS.CanSet())
// 修改指定下標(biāo)數(shù)據(jù)
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. 修改結(jié)構(gòu)體
帶修改的結(jié)構(gòu)體的成員的字段名首字母需要大寫(xiě)。
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)
// 指針本身不可修改 可指向的內(nèi)容
fmt.Printf("Kind:%v CanSet:%v\n", valueS.Kind(), valueS.CanSet())
// 獲取指針指向的內(nèi)容
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)
// 替換整體內(nèi)容
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
反射通過(guò)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)
// 迭代器訪(fǎng)問(wèn)
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 ---")
// 通過(guò)key訪(fǎng)問(wèn)
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
到此這篇關(guān)于Golang反射修改變量值的文章就介紹到這了,更多相關(guān)Golang反射修改變量值內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決golang處理http response碰到的問(wèn)題和需要注意的點(diǎn)
這篇文章主要介紹了解決golang處理http response碰到的問(wèn)題和需要注意的點(diǎn),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12
golang結(jié)構(gòu)化日志slog的用法簡(jiǎn)介
日志是任何軟件的重要組成部分,Go?提供了一個(gè)內(nèi)置日志包(slog),在本文中,小編將簡(jiǎn)單介紹一下slog包的功能以及如何在?Go?應(yīng)用程序中使用它,感興趣的可以了解下2023-09-09
golang中select語(yǔ)句的簡(jiǎn)單實(shí)例
Go的select語(yǔ)句是一種僅能用于channl發(fā)送和接收消息的專(zhuān)用語(yǔ)句,此語(yǔ)句運(yùn)行期間是阻塞的,下面這篇文章主要給大家介紹了關(guān)于golang中select語(yǔ)句的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-06-06
go json數(shù)據(jù)轉(zhuǎn)發(fā)的實(shí)現(xiàn)代碼
這篇文章主要介紹了go json數(shù)據(jù)轉(zhuǎn)發(fā)的實(shí)現(xiàn)代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09
golang爬蟲(chóng)colly?發(fā)送post請(qǐng)求
本文主要介紹了golang爬蟲(chóng)colly?發(fā)送post請(qǐng)求實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07

