Go REFLECT Library | 05 - reflect.Value 动态修变量值

简介: Go REFLECT Library | 05 - reflect.Value 动态修变量值

一、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)
}
复制代码

执行上述代码,输出结果如下:

image.png

对于结构体动态的动态修改就是修改其字段的值,字段的值要可被动态修改除了要满足可被寻址的条件之外,还要满足可被导出的条件,可导出既可以被访问也就说字段首字母要大写否则是无法动态修改的。

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
}
复制代码

image.png

将 Teacher 结构体和 main 函数中的 name 字段改为 Name,再次执行 main 函数,输出结果如下:

true
修改后为:Tony Stark


相关文章
初识go变量,使用var和:=来声明变量,声明变量的三种方式
这篇文章介绍了Go语言中使用`var`和`:=`声明变量的三种不同方式,包括声明单个或多个变量、通过值确定数据类型以及在函数体内使用`:=`声明局部变量。
初识go变量,使用var和:=来声明变量,声明变量的三种方式
|
15天前
|
存储 编译器 Go
go语言中的变量、常量、数据类型
【11月更文挑战第3天】
30 9
|
3月前
|
Go
Go1.22 新特性:for 循环不再共享循环变量,且支持整数范围
Go1.22 新特性:for 循环不再共享循环变量,且支持整数范围
|
3月前
|
安全 Go
|
3月前
|
自然语言处理 Go 开发者
深入理解Go语言中的变量作用域
【8月更文挑战第31天】
27 0
|
3月前
|
存储 人工智能 Java
深入理解 go reflect - 要不要传指针
深入理解 go reflect - 要不要传指针
20 0
|
3月前
|
存储 缓存 人工智能
深入理解 go reflect - 反射为什么慢
深入理解 go reflect - 反射为什么慢
38 0
|
3月前
|
存储 人工智能 JSON
深入理解 go reflect - 反射基本原理
深入理解 go reflect - 反射基本原理
54 0
|
5月前
|
存储 安全 Go
【Go语言精进之路】构建高效Go程序:掌握变量、常量声明法则与iota在枚举中的奥秘
【Go语言精进之路】构建高效Go程序:掌握变量、常量声明法则与iota在枚举中的奥秘
66 2
|
5月前
|
算法 编译器 Go
Go语言中,常量和变量的定义和使用
Go语言中,常量和变量的定义和使用