首页 > 其他分享 >Golang反射修改变量值

Golang反射修改变量值

时间:2022-12-04 22:34:41浏览次数:64  
标签:反射 CanSet 变量值 reflect Golang 修改 valueS Printf fmt

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是引用地址时,返回参数ValueValue.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指定keyvalue

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

 

 

 

标签:反射,CanSet,变量值,reflect,Golang,修改,valueS,Printf,fmt
From: https://www.cnblogs.com/amos01/p/16930711.html

相关文章

  • JAVA反射举例
    1、什么是反射?我们在学Java的过程中一定会遇到Java反射,不管是我们常用的框架,还是我们解决一些特殊问题。多多少少都会牵扯到。那么什么才是反射?很抽象的一个概念!!没有之......
  • Golang抢占式调度
    Golang抢占式调度在1.12版本之前,go的调度器不支持抢占式调度,程序只能依靠Goroutine主动让出CPU资源才能触发调度,会引发一些问题,如某些Goroutine可以长时间占用线程,造成......
  • Golang 协程调度器原理及GPM模型
    目录进程和线程内核级线程用户级线程协程协程与线程的关系N:11:1M:Ngoroutine旧版本goroutine调度器调度器的实现Goroutine调度器的GMP模型设计思想GPM结构组成GPM运行模型......
  • golang的希尔排序
    1、什么是希尔排序:插入排序的一种又称“缩小增量排序”(DiminishingIncrementSort),是直接插入排序算法​的一种更高效的改进版本。希尔排序是非稳定排序算法。该方法因D.L.......
  • 反射
    当我们不知道类是否加了set/get方法,但又希望能够得到它里面的一些属性或方法的时候,需要通过反射来获取,下面我创建了一个student类,但是我没有加set和get方法:publicclassS......
  • Golang文件服务器
    主调函数,设置路由表packagemainimport( "fmt" "net/http" "store/handler")funcmain(){ http.HandleFunc("/file/upload",handler.UploadHandler) http.Ha......
  • Golang反射获得变量类型和值
    1.什么是反射反射是程序在运行期间获取变量的类型和值、或者执行变量的方法的能力。Golang反射包中有两对非常重要的函数和类型,两个函数分别是:reflect.TypeOf能获取类......
  • C# 反射编程 接口如何寻找父接口
    最近用反射,想找到一个接口的父接口,发现baseType是null。但是dotpeek反射里面显示是有baseType的。搜了一下,意识到你只能implement一个接口,而不能inherit一个接口。所以......
  • golang rang 字符串
    golang遍历字符串,有多种方式:``点击查看代码 //字符串,把字符串起来 s:="中国人,zgr" forpos,char:=ranges{ //range是按照字符来遍历,返回字符出现的位置......
  • 老司机带带你,教你学会Java中又骚又暴力的“反射”技术
    在Java中有这么一个很骚的技术,几乎贯穿了所有主流的框架,在所有主流框架的底层中你都可以看见它的身影,这个技术就是反射。关于反射,有很多小白会觉得很难,搞不清楚到底是怎么回......