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

  1. func main() {
  2. s := []int{1, 2, 3}
  3. valueS := reflect.ValueOf(s)
  4. // slice 是否可修改 不可整体修改
  5. fmt.Printf("valueS Kind:%v CanSet:%v Index(0).CanSet:%v\n", valueS.Kind(), valueS.CanSet(), valueS.Index(0).CanSet())
  6. // 修改指定下标的元素值
  7. valueS.Index(0).Set(reflect.ValueOf(10))
  8. valueS.Index(1).SetInt(20)
  9. fmt.Printf("after edit:%v\n", s)
  10. // panic: reflect: call of reflect.Value.SetFloat on int Value
  11. //valueS.Index(1).SetFloat(100)
  12. }

代码输出如下

  1. $ go run main.go
  2. valueS Kind:slice CanSet:false Index(0).CanSet:true
  3. after edit:[10 20 3]

如果需要整体修改修改slice,那么需要传入slice的地址

  1. func main() {
  2. s := []int{1, 2, 3}
  3. // slice的指针
  4. valuePtrS := reflect.ValueOf(&s)
  5. fmt.Printf("valuePtrS kind:%v CanSet:%v\n", valuePtrS.Kind(), valuePtrS.CanSet())
  6. // 获取指针指向的内容
  7. valueS := valuePtrS.Elem()
  8. fmt.Printf("valueS kind:%v CanSet:%v\n", valueS.Kind(), valueS.CanSet())
  9. // 整体修改slice
  10. valueS.Set(reflect.ValueOf([]int{4, 5, 6, 7}))
  11. fmt.Printf("replace edit:%v\n", s)
  12. }

代码输出如下

  1. $ go run main.go
  2. valuePtrS kind:ptr CanSet:false
  3. valueS kind:slice CanSet:true
  4. replace edit:[4 5 6 7]

4. 修改array

array不是引用类型,因此func ValueOf(i any) Value需要传入array的地址。

  1. func main() {
  2. s := [3]int{1, 2, 3}
  3. // array的指针
  4. valuePtrS := reflect.ValueOf(&s)
  5. fmt.Printf("valuePtrS kind:%v CanSet:%v\n", valuePtrS.Kind(), valuePtrS.CanSet())
  6. // 获取指针指向的内容
  7. valueS := valuePtrS.Elem()
  8. fmt.Printf("valueS kind:%v CanSet:%v\n", valueS.Kind(), valueS.CanSet())
  9. // 修改指定下标数据
  10. valueS.Index(0).SetInt(10)
  11. fmt.Printf("after edit:%v\n", s)
  12. // 整体修改slice
  13. valueS.Set(reflect.ValueOf([3]int{4, 5, 6}))
  14. fmt.Printf("replace edit:%v\n", s)
  15. //panic: reflect.Set: value of type [4]int is not assignable to type [3]int
  16. //valueS.Set(reflect.ValueOf([4]int{4, 5, 6}))
  17. }

代码输出如下

  1. $ go run main.go
  2. valuePtrS kind:ptr CanSet:false
  3. valueS kind:array CanSet:true
  4. after edit:[10 2 3]
  5. replace edit:[4 5 6]

5. 修改结构体

带修改的结构体的成员的字段名首字母需要大写。

  1. func main() {
  2. type myStruct struct {
  3. Num int `json:"num_json" orm:"column:num_orm"`
  4. Desc string `json:"desc_json" orm:"column:desc_orm"`
  5. }
  6. s := myStruct{
  7. Num: 1,
  8. Desc: "desc",
  9. }
  10. valueS := reflect.ValueOf(&s)
  11. // 指针本身不可修改 可指向的内容
  12. fmt.Printf("Kind:%v CanSet:%v\n", valueS.Kind(), valueS.CanSet())
  13. // 获取指针指向的内容
  14. valueS = valueS.Elem()
  15. fmt.Printf("Kind:%v CanSet:%v Field(0).CanSet:%v\n", valueS.Kind(), valueS.CanSet(), valueS.Field(0).CanSet())
  16. // 修改指定成员的值
  17. valueS.Field(0).SetInt(10)
  18. fmt.Printf("after edit:%+v\n", s)
  19. // 替换整体内容
  20. valueS.Set(reflect.ValueOf(myStruct{Num: 100, Desc: "new desc"}))
  21. fmt.Printf("after replace:%+v\n", s)
  22. }

代码输出如下,

  1. $ go run main.go
  2. Kind:ptr CanSet:false
  3. Kind:struct CanSet:true Field(0).CanSet:true
  4. after edit:{Num:10 Desc:desc}
  5. after replace:{Num:100 Desc:new desc}

6. 修改map

反射通过func (v Value) SetMapIndex(key, elem Value)修改map指定keyvalue

  1. func main() {
  2. m := map[int]string{
  3. 1: "1",
  4. 2: "2",
  5. 3: "3",
  6. }
  7. valueM := reflect.ValueOf(m)
  8. // 迭代器访问
  9. iter := valueM.MapRange()
  10. for iter.Next() {
  11. fmt.Printf("key:%v val:%v\n", iter.Key(), iter.Value())
  12. // 将所有value修改为"a"
  13. valueM.SetMapIndex(iter.Key(), reflect.ValueOf("a"))
  14. }
  15. fmt.Println("--- after edit ---")
  16. // 通过key访问
  17. keys := valueM.MapKeys()
  18. for i := 0; i < len(keys); i++ {
  19. fmt.Printf("key:%v val:%v\n", keys[i], valueM.MapIndex(keys[i]))
  20. }
  21. }

代码输出如下

  1. $ go run main.go
  2. key:1 val:1
  3. key:2 val:2
  4. key:3 val:3
  5. --- after edit ---
  6. key:1 val:a
  7. key:2 val:a
  8. key:3 val:a

Golang反射修改变量值的更多相关文章

  1. 通过反射将变量值转为变量名本身ZZ

      这是.NET反射的一个有趣小例子:  通过反射将变量值转为变量名本身. 当然要先添加命名空间:using System.Reflection; 示例代码如下: class Program { st ...

  2. IDEA在debug时修改变量值

    IDEA在debug调试时修改变量值 例如以下代码: int y1 = 0; anchor.setDy1(y1); 在代码中,这个y1永远是0,但是y1本身是个变量 debug的时候获取到这个属性,并 ...

  3. sap 调试工具,修改变量值

    1: 点击修改,输入变量值,按enter键.

  4. JS变量写到HTML页面中并修改变量值(前台处理数据序号问题)

    有时候我们在前台需要对序号进行处理,我们需要将JS变量写到页面中进行显示. 第一种方式:后台处理 第二种方式:JS中定义全局变量,然后进行显示,可以书写一个JS函数对不同的需要进行不同的替换,也就可以 ...

  5. Java反射-修改private final成员变量值,你知道多少?

    大家都知道使用java反射可以在运行时动态改变对象的行为,甚至是private final的成员变量,但并不是所有情况下,都可以修改成员变量.今天就举几个小例子说明.  基本数据类型 String类型 ...

  6. Java 反射修改类的常量值、静态变量值、属性值

    前言 有的时候,我们需要修改一个变量的值,但变量也许存在于 Jar 包中或其他位置,导致我们不能从代码层面进行修改,于是我们就用到了下面的场景,通过反射来进行修改变量的值. 定义一个实体类 class ...

  7. 动态修改 NodeJS 程序中的变量值

    如果一个 NodeJS 进程正在运行,有办法修改程序中的变量值么?答案是:通过 V8 的 Debugger 接口可以!本文将详细介绍实现步骤. 启动一个 HTTP Server 用简单的 Hello ...

  8. saltstack通过jinja模板,将变量值增加到配置文件中?通过引用变量值修改配置文件?

    需求描述: 在使用saltstack的时候,有的时候,需要根据不同的变量来增加配置,比如,bind,监听端口,这些都可以通过变量写入,并且在配置的时候引用,下面是一个例子,用来演示,如何使用jinja ...

  9. jetty debug修改 java static 静态变量值不会生效

    在jetty debug模式下修改static静态变量值不会重新Load 因为jetty是嵌入式web容器,static静态变量是全局的,如果想生效,就必须重启jetty 在热部署的时候tomcat会 ...

  10. Java反射之修改常量值

    1. 通过反射修改常量的值 package com.blueStarWei.invoke; import java.lang.reflect.Field; public class ModifyFin ...

随机推荐

  1. Linux宝塔后台管理地址使用SSL,并部署非443端口的https

    上传你的key和pem,然后点设置 点击配置文件 插入代码 1 ssl on; 2 ssl_certificate /xxx/yyy/zzz.pem; 3 ssl_certificate_key /x ...

  2. 发布日志 - kratos v2.1.0 版本发布

    github https://github.com/go-kratos/kratos/releases/tag/v2.1.0 新的功能 新增客户端负载均衡器(load balancing)和路由选择器 ...

  3. Kubernetes 调度 - 污点和容忍度详解

    当我们使用节点亲和力(Pod 的一个属性)时,它会将Pod吸引到一组节点(作为偏好或硬性要求).污点的行为完全相反,它们允许一个节点排斥一组 Pod. 在 Kubernetes 中,您可以标记(污染) ...

  4. kubernetes给容器生命周期设置操作事件

    Kubernetes支持预启动和预结束事件. Kubernetes在容器启动的时候发送预启动事件,在容器结束的时候发送预结束事件. 定义预启动和预结束事件操作 下面是Pod的配置文件: # cat l ...

  5. .Net 7内容汇总(3)--反射优化

    反射这玩意,一直以来都是慢的代名词.一说XXX系统大量的反射,好多人第一印象就是会慢. 但是呢,我们又不得不使用反射来做一些事情,毕竟这玩意可以说啥都能干了对吧. It's immensely pow ...

  6. PAT (Basic Level) Practice 1021 个位数统计 分数 15

    给定一个 k 位整数 N=dk−1​10k−1+⋯+d1​101+d0​ (0≤di​≤9, i=0,⋯,k−1, dk−1​>0),请编写程序统计每种不同的个位数字出现的次数.例如:给定 N= ...

  7. 邻接矩阵bfs

    #include<bits/stdc++.h> using namespace std; int a[11][11]; bool visited[11]; void store_graph ...

  8. 基于纯前端类Excel表格控件实现在线损益表应用

    财务报表也称对外会计报表,是会计主体对外提供的反映企业或预算单位一定时期资金.利润状况的会计报表,由资产负债表.损益表.现金流量表或财务状况变动表.附表和附注构成.财务报表是财务报告的主要部分,不包括 ...

  9. linux 安装/卸载go环境

    linux 安装/卸载go环境(基于centos8) 安装 下载go的安装包 Golang官网下载地址:https://golang.org/dl/ 将安装包解压放到到usr/local中,并解压 c ...

  10. vue+element-ui后台管理系统模板

    vue+element-ui后台管理系统模板 前端:基于vue2.0+或3.0+加上element-ui组件框架 后端:springboot+mybatis-plus写接口 通过Axios调用接口完成 ...