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


相关文章
|
1月前
|
存储 安全 编译器
掌握Go语言:探索Go语言中的变量,灵活性与可读性的完美结合(4)
掌握Go语言:探索Go语言中的变量,灵活性与可读性的完美结合(4)
|
5月前
|
Go
Go入门速成-定义变量
Go入门速成-定义变量
|
5月前
|
JSON JavaScript Go
Go 语言学习指南:变量、循环、函数、数据类型、Web 框架等全面解析
掌握 Go 语言的常见概念,如变量、循环、条件语句、函数、数据类型等等。深入了解 Go 基础知识的好起点是查阅 Go 官方文档
486 2
|
2天前
|
JSON 监控 安全
Golang深入浅出之-Go语言中的反射(reflect):原理与实战应用
【5月更文挑战第1天】Go语言的反射允许运行时检查和修改结构,主要通过`reflect`包的`Type`和`Value`实现。然而,滥用反射可能导致代码复杂和性能下降。要安全使用,应注意避免过度使用,始终进行类型检查,并尊重封装。反射的应用包括动态接口实现、JSON序列化和元编程。理解反射原理并谨慎使用是关键,应尽量保持代码静态类型。
14 2
|
3天前
|
Go
go 反射Reflect
go 反射Reflect
|
3天前
|
Go
【Go语言专栏】Go语言的并发编程进阶:互斥锁与条件变量
【4月更文挑战第30天】本文探讨了Go语言中的互斥锁(Mutex)和条件变量(Condition Variable)在并发编程中的应用。互斥锁用于保护共享资源,防止多goroutine同时访问,通过Lock和Unlock进行控制,需注意避免死锁。条件变量则允许goroutine在条件满足时被唤醒,常与互斥锁结合使用以提高效率。了解和掌握这些同步原语能提升Go并发程序的性能和稳定性。进一步学习可参考Go官方文档和并发模式示例。
|
3天前
|
存储 Go 开发者
【Go语言专栏】Go语言中的变量、常量和数据类型
【4月更文挑战第30天】Go语言以其简洁、高效和并发性备受开发者喜爱。本文聚焦变量、常量和数据类型。变量声明可使用`var`或类型推断,如`var a int = 10`和`b := "hello"`。常量用`const`声明,不可变,如`const pi float64 = 3.1415926`。数据类型包括基本类型(数值、布尔、字符串)和复合类型(数组、切片、结构体、接口)。理解这些基础将助力开发者深入学习Go语言的高级特性。
|
7天前
|
数据可视化
R语言多元(多变量)GARCH :GO-GARCH、BEKK、DCC-GARCH和CCC-GARCH模型和可视化
R语言多元(多变量)GARCH :GO-GARCH、BEKK、DCC-GARCH和CCC-GARCH模型和可视化
17 3
|
15天前
R语言多变量广义正交GARCH(GO-GARCH)模型对股市高维波动率时间序列拟合预测
R语言多变量广义正交GARCH(GO-GARCH)模型对股市高维波动率时间序列拟合预测
16 1
|
3月前
|
Go
Go语言必知必会100问题-01 小心变量遮蔽
变量的作用域是指一个变量可以被引用的地方/范围。换句话说,就是应用程序的一个区域,在该区域内变量是有效的,超出该区域便无效。在Go语言中,在代码块中声明的变量可以在内部代码块中重新声明,这种使用方法称为变量遮蔽/隐藏,注意在使用时要非常小心,否则很容易出现常见的错误。