天天看點

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      

4. 總結

  • 掌握可取位址性
  • 掌握可設定性
  • 掌握 Elem 的用法