084-反射(通过 reflect.Value 修改值)
Posted --Allen--
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了084-反射(通过 reflect.Value 修改值)相关的知识,希望对你有一定的参考价值。
前面我们学习了一些关于反射的用法,比如:
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 的用法
以上是关于084-反射(通过 reflect.Value 修改值)的主要内容,如果未能解决你的问题,请参考以下文章