084-反射(通过 reflect.Value 修改值)

本文深入探讨了Go语言中反射的可设置性和Elem方法的使用,讲解了如何通过指针和Set方法修改变量值,以及如何判断一个Value对象是否可设置和取地址。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前面我们学习了一些关于反射的用法,比如:

x := 2
// 拿到 x 的 Value 对象
a := reflect.ValueOf(x)

我们可以通过 a 来得知 x 的具体值是多少,那有没有办法通过 a 来修改 x 的值呢?在上面这个例子中,是不行的。

1. 可修改性

或者说叫可设置性。

Value 有一个方法,CanSet,它可以告知你是否可以设置值。还有一个方法就一并讲了,叫 CanAddr,表示能否取变量的地址。

比如:

x := 2
a := reflect.ValueOf(x)
fmt.Println(a.CanSet())  // Output: false
fmt.Println(a.CanAddr()) // Output: false

结果输出 false,这说明 a 不具有可设置性。原因在于 reflect.ValueOf 返回的变量 a 是通过 x 的副本生成的,因此,无论如何也你无法通过 a 来修改 x.

那换成 x 的地址怎么样?如下:

x := 2
a := reflect.ValueOf(&x)
fmt.Println(a.CanSet())  // Output: false
fmt.Println(a.CanAddr()) // Output: false

结果你发现,还是无法设置,也无法取地址。这是为什么呢?因为你拿到的 a 是变量 x 的地址的副本啊,这个副本同样无法设置,也无法取地址。

2. Elem 方法

这时候该轮到 Elem 上场了。其实在上一篇 json 序列化里你已经遇到过它了,它简单的可以理解成解引用。看下面的代码:

x := 2
a := reflect.ValueOf(&x)
// a 是指针的化身,通过 a 可以解出“真正的” x
b := a.Elem()
fmt.Println(b.CanSet())  // Output: true
fmt.Println(b.CanAddr()) // Output: true

这时候你会发现,b 已经可以设置,也可以提取地址了。

3. 设置值

3.1 通过指针直接设置

  • 方法一
x := 2
fmt.Printf("x = %d\n", x) // Output: x = 2
a := reflect.ValueOf(&x)
p := a.Interface().(*int)
*p = 10
fmt.Printf("x = %d\n", x) // Output: x = 10
  • 方法二
x := 2
fmt.Printf("x = %d\n", x) // Output: x = 2
a := reflect.ValueOf(&x)
b := a.Elem()
p := b.Addr().Interface().(*int)
*p = 10
fmt.Printf("x = %d\n", x) // Output: x = 10

上面两种方法其实差不多,只是第二种通过 a.Elem() 也可以拿到 x 的地址。

3.2 通过 Set 方法设置值

x := 2
fmt.Printf("x = %d\n", x) // Output: x = 2
a := reflect.ValueOf(&x)
b := a.Elem()
// 通过 SetInt 方法设置值
b.SetInt(10)
fmt.Printf("x = %d\n", x) // Output: x = 10

当然,如果变量的底层类型不是有符号类的整数,调用 SetInt 会引起 Panic.

除了 SetInt 方法,reflect.Value 还有 SetUint, SetFloat, SetBool, SetString 等类似的方法。

4. 总结

  • 掌握可取地址性
  • 掌握可设置性
  • 掌握 Elem 的用法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值