一、reflect.Value
动态修改变量值
在前来两篇文章中讲述了如何通过 reflect.Value 的各种方法来获取变量为基本数据类型、Map 和结构体形式时的 value 值或者 Key 对应的值以及结构体字段的值。
如果变量时一个指针类型,我们还可以通过 Elem
方法来获取变量的值,这个方法就相当于指针类型的 *
。与之对应的是 Addr
函数,这个方法就相当于普通变量的获取地址的操作,既 &
,相当于但是不是等效的。
在对变量值进行动态修改之前还需要通过函数做一些判断:
- CanAddr() bool:判断地址是否可寻
- CanSet() bool:判断返回值能够被修改,要求可寻址且可导出的字段
判断之后,可以通过以下这些方法对变量值进行修改:
SetXxx(x value) | 方法说明 |
SetInx(x int64) | 将变量值设置为 int64 类型,当原值类型不是整数 int(包括 int8,in16, int32,int64)时会发生 panic |
SetUnx(x uint64) | 将变量值设置为 uint64 类型,当原值类型不是 uint 类型时 int(包括 uint8,uin16, uint32,uint64)时会发生 panic |
SetFloat(x float64) | 将变量值设置为 float64,当原值类型不是 float64 或者 float32 时会发生 panic |
SetBool(x bool) | 将变量设置为 bool 类型,当原值类型不是布尔类型时会引发 panic |
SetBytes(x []byte) | 将变量设置为 字节数组,当原值类型不是字节数组时会发生 panic |
SetString(x string) | 将变量设置为 string,当原值类型不是 string 时会引发 panic |
动态修改变量只能修改变量值,不能修改变量类型。
变量可被寻址则可以被动态修改
变量无法被动态修改的情况
package main import ( "fmt" "reflect" ) func main(){ var zulu = 12138 var yankee = 3.14 var xray = "stark" var tango = make(map[string]interface{}) tango["name"] = "stark" // 获取反射值对象 zuluValueOf := reflect.ValueOf(zulu) yankeeValueOf := reflect.ValueOf(yankee) xrayValueOf := reflect.ValueOf(xray) tangoValueOf := reflect.ValueOf(tango) // 是否可以被寻址 fmt.Println(zuluValueOf.CanAddr()) // false fmt.Println(yankeeValueOf.CanAddr()) // false fmt.Println(xrayValueOf.CanAddr()) // false fmt.Println(tangoValueOf.CanAddr()) // false // 是否可以被寻址 fmt.Println(zuluValueOf.CanSet()) // false fmt.Println(yankeeValueOf.CanSet()) // false fmt.Println(xrayValueOf.CanSet()) // false fmt.Println(tangoValueOf.CanSet()) // false } 复制代码
直接获取变量的反射值对象,该反射值对象时无法被寻址无法被修改的,要通过获取指针变量的反射值对象才可以将指针从一个值指向另一个值,实现动态修改变量值,因为指针是包含类型的,所以只能动态修改值不能动态修改该类型
动态修改变变量值
package main import ( "fmt" "reflect" ) func main(){ var zulu = 12138 // 获取 Int 指针类型的反射值对象 zuluPtrValueOf := reflect.ValueOf(&zulu).Elem() fmt.Printf("%T\n", zuluPtrValueOf) fmt.Printf("动态修改前的值为:%v\n", zulu) fmt.Printf("是否可以被动态修改:%v\n", zuluPtrValueOf.CanSet()) zuluPtrValueOf.SetInt(12) fmt.Printf("修改后的值为:%v\n", zulu) } 复制代码
执行上述代码,输出结果如下:
reflect.Value 动态修改前的值为:12138 是否可以被动态修改:true 修改后的值为:12 复制代码
如果原变量类型是 Int,动态修改为 Float,则会引发 panic
func main(){ var zulu = 12138 // 如果将原本 Int 类型改为 Float 类型会导致 panic zuluPtrValueOf := reflect.ValueOf(&zulu).Elem() fmt.Printf("%T\n", zuluPtrValueOf) fmt.Printf("动态修改前的值为:%v\n", zulu) fmt.Printf("是否可以被动态修改:%v\n", zuluPtrValueOf.CanSet()) zuluPtrValueOf.SetFloat(12.0) fmt.Printf("修改后的值为:%v\n", zulu) } 复制代码
执行上述代码,输出结果如下:
对于结构体动态的动态修改就是修改其字段的值,字段的值要可被动态修改除了要满足可被寻址的条件之外,还要满足可被导出的条件,可导出既可以被访问也就说字段首字母要大写否则是无法动态修改的。
package main import ( "fmt" "reflect" ) func main() { // 实例化一个 Teacher 结构体 teacher := Teacher{"Stark", 33} teacherValueOf := reflect.ValueOf(&teacher) teacherElem := teacherValueOf.Elem() nameValueOf := teacherElem.FieldByName("name") fmt.Printf("%v\n", nameValueOf.CanSet()) // 修改 nameValueOf.SetString("Tony Stark") fmt.Printf("修改后为:%v", teacher.name) } type Teacher struct { name string age int } 复制代码
将 Teacher 结构体和 main 函数中的 name
字段改为 Name
,再次执行 main 函数,输出结果如下:
true 修改后为:Tony Stark