新聞中心
Go語(yǔ)言中類似 x、x.f[1] 和 *p 形式的表達(dá)式都可以表示變量,但是其它如 x + 1 和 f(2) 則不是變量。一個(gè)變量就是一個(gè)可尋址的內(nèi)存空間,里面存儲(chǔ)了一個(gè)值,并且存儲(chǔ)的值可以通過(guò)內(nèi)存地址來(lái)更新。

創(chuàng)新互聯(lián)服務(wù)項(xiàng)目包括從江網(wǎng)站建設(shè)、從江網(wǎng)站制作、從江網(wǎng)頁(yè)制作以及從江網(wǎng)絡(luò)營(yíng)銷策劃等。多年來(lái),我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢(shì)、行業(yè)經(jīng)驗(yàn)、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機(jī)構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,從江網(wǎng)站推廣取得了明顯的社會(huì)效益與經(jīng)濟(jì)效益。目前,我們服務(wù)的客戶以成都為中心已經(jīng)輻射到從江省份的部分城市,未來(lái)相信會(huì)繼續(xù)擴(kuò)大服務(wù)區(qū)域并繼續(xù)獲得客戶的支持與信任!
對(duì)于 reflect.Values 也有類似的區(qū)別。有一些 reflect.Values 是可取地址的;其它一些則不可以。考慮以下的聲明語(yǔ)句:
x := 2 // value type variable?
a := reflect.ValueOf(2) // 2 int no
b := reflect.ValueOf(x) // 2 int no
c := reflect.ValueOf(&x) // &x *int no
d := c.Elem() // 2 int yes (x)
其中 a 對(duì)應(yīng)的變量則不可取地址。因?yàn)?a 中的值僅僅是整數(shù) 2 的拷貝副本。b 中的值也同樣不可取地址。c 中的值還是不可取地址,它只是一個(gè)指針 &x 的拷貝。實(shí)際上,所有通過(guò) reflect.ValueOf(x) 返回的 reflect.Value 都是不可取地址的。但是對(duì)于 d,它是 c 的解引用方式生成的,指向另一個(gè)變量,因此是可取地址的。我們可以通過(guò)調(diào)用 reflect.ValueOf(&x).Elem(),來(lái)獲取任意變量x對(duì)應(yīng)的可取地址的 Value。
我們可以通過(guò)調(diào)用 reflect.Value 的 CanAddr 方法來(lái)判斷其是否可以被取地址:
fmt.Println(a.CanAddr()) // "false"
fmt.Println(b.CanAddr()) // "false"
fmt.Println(c.CanAddr()) // "false"
fmt.Println(d.CanAddr()) // "true"
每當(dāng)我們通過(guò)指針間接地獲取的 reflect.Value 都是可取地址的,即使開(kāi)始的是一個(gè)不可取地址的 Value。在反射機(jī)制中,所有關(guān)于是否支持取地址的規(guī)則都是類似的。例如,slice 的索引表達(dá)式 e[i]將隱式地包含一個(gè)指針,它就是可取地址的,即使開(kāi)始的e表達(dá)式不支持也沒(méi)有關(guān)系。
以此類推,reflect.ValueOf(e).Index(i) 對(duì)于的值也是可取地址的,即使原始的 reflect.ValueOf(e) 不支持也沒(méi)有關(guān)系。
使用 reflect.Value 對(duì)包裝的值進(jìn)行修改時(shí),需要遵循一些規(guī)則。如果沒(méi)有按照規(guī)則進(jìn)行代碼設(shè)計(jì)和編寫(xiě),輕則無(wú)法修改對(duì)象值,重則程序在運(yùn)行時(shí)會(huì)發(fā)生宕機(jī)。
判定及獲取元素的相關(guān)方法
使用 reflect.Value 取元素、取地址及修改值的屬性方法請(qǐng)參考下表。
| 方法名 | 備 注 |
|---|---|
| Elem() Value | 取值指向的元素值,類似于語(yǔ)言層*操作。當(dāng)值類型不是指針或接口時(shí)發(fā)生宕 機(jī),空指針時(shí)返回 nil 的 Value |
| Addr() Value | 對(duì)可尋址的值返回其地址,類似于語(yǔ)言層&操作。當(dāng)值不可尋址時(shí)發(fā)生宕機(jī) |
| CanAddr() bool | 表示值是否可尋址 |
| CanSet() bool | 返回值能否被修改。要求值可尋址且是導(dǎo)出的字段 |
值修改相關(guān)方法
使用 reflect.Value 修改值的相關(guān)方法如下表所示。
| Set(x Value) | 將值設(shè)置為傳入的反射值對(duì)象的值 |
|---|---|
| Setlnt(x int64) | 使用 int64 設(shè)置值。當(dāng)值的類型不是 int、int8、int16、 int32、int64 時(shí)會(huì)發(fā)生宕機(jī) |
| SetUint(x uint64) | 使用 uint64 設(shè)置值。當(dāng)值的類型不是 uint、uint8、uint16、uint32、uint64 時(shí)會(huì)發(fā)生宕機(jī) |
| SetFloat(x float64) | 使用 float64 設(shè)置值。當(dāng)值的類型不是 float32、float64 時(shí)會(huì)發(fā)生宕機(jī) |
| SetBool(x bool) | 使用 bool 設(shè)置值。當(dāng)值的類型不是 bod 時(shí)會(huì)發(fā)生宕機(jī) |
| SetBytes(x []byte) | 設(shè)置字節(jié)數(shù)組 []bytes值。當(dāng)值的類型不是 []byte 時(shí)會(huì)發(fā)生宕機(jī) |
| SetString(x string) | 設(shè)置字符串值。當(dāng)值的類型不是 string 時(shí)會(huì)發(fā)生宕機(jī) |
以上方法,在 reflect.Value 的 CanSet 返回 false 仍然修改值時(shí)會(huì)發(fā)生宕機(jī)。
在已知值的類型時(shí),應(yīng)盡量使用值對(duì)應(yīng)類型的反射設(shè)置值。
值可修改條件之一:可被尋址
通過(guò)反射修改變量值的前提條件之一:這個(gè)值必須可以被尋址。簡(jiǎn)單地說(shuō)就是這個(gè)變量必須能被修改。示例代碼如下:
package main
import (
"reflect"
)
func main() {
// 聲明整型變量a并賦初值
var a int = 1024
// 獲取變量a的反射值對(duì)象
valueOfA := reflect.ValueOf(a)
// 嘗試將a修改為1(此處會(huì)發(fā)生崩潰)
valueOfA.SetInt(1)
}程序運(yùn)行崩潰,打印錯(cuò)誤:
panic: reflect: reflect.Value.SetInt using unaddressable value
報(bào)錯(cuò)意思是:SetInt 正在使用一個(gè)不能被尋址的值。從 reflect.ValueOf 傳入的是 a 的值,而不是 a 的地址,這個(gè) reflect.Value 當(dāng)然是不能被尋址的。將代碼修改一下,重新運(yùn)行:
package main
import (
"fmt"
"reflect"
)
func main() {
// 聲明整型變量a并賦初值
var a int = 1024
// 獲取變量a的反射值對(duì)象(a的地址)
valueOfA := reflect.ValueOf(&a)
// 取出a地址的元素(a的值)
valueOfA = valueOfA.Elem()
// 修改a的值為1
valueOfA.SetInt(1)
// 打印a的值
fmt.Println(valueOfA.Int())
}代碼輸出如下:
1
下面是對(duì)代碼的分析:
- 第 14 行中,將變量 a 取值后傳給 reflect.ValueOf()。此時(shí) reflect.ValueOf() 返回的 valueOfA 持有變量 a 的地址。
- 第 17 行中,使用 reflect.Value 類型的 Elem() 方法獲取 a 地址的元素,也就是 a 的值。reflect.Value 的 Elem() 方法返回的值類型也是 reflect.Value。
- 第 20 行,此時(shí) valueOfA 表示的是 a 的值且可以尋址。使用 SetInt() 方法設(shè)置值時(shí)不再發(fā)生崩潰。
- 第 23 行,正確打印修改的值。
提示
當(dāng) reflect.Value 不可尋址時(shí),使用 Addr() 方法也是無(wú)法取到值的地址的,同時(shí)會(huì)發(fā)生宕機(jī)。雖然說(shuō) reflect.Value 的 Addr() 方法類似于語(yǔ)言層的&操作;Elem() 方法類似于語(yǔ)言層的*操作,但并不代表這些方法與語(yǔ)言層操作等效。
值可修改條件之一:被導(dǎo)出
結(jié)構(gòu)體成員中,如果字段沒(méi)有被導(dǎo)出,即便不使用反射也可以被訪問(wèn),但不能通過(guò)反射修改,代碼如下:
package main
import (
"reflect"
)
func main() {
type dog struct {
legCount int
}
// 獲取dog實(shí)例的反射值對(duì)象
valueOfDog := reflect.ValueOf(dog{})
// 獲取legCount字段的值
vLegCount := valueOfDog.FieldByName("legCount")
// 嘗試設(shè)置legCount的值(這里會(huì)發(fā)生崩潰)
vLegCount.SetInt(4)
}程序發(fā)生崩潰,報(bào)錯(cuò):
panic: reflect: reflect.Value.SetInt using value obtained using unexported field
報(bào)錯(cuò)的意思是:SetInt() 使用的值來(lái)自于一個(gè)未導(dǎo)出的字段。
為了能修改這個(gè)值,需要將該字段導(dǎo)出。將 dog 中的 legCount 的成員首字母大寫(xiě),導(dǎo)出 LegCount 讓反射可以訪問(wèn),修改后的代碼如下:
type dog struct {
LegCount int
}然后根據(jù)字段名獲取字段的值時(shí),將字符串的字段首字母大寫(xiě),修改后的代碼如下:
vLegCount := valueOfDog.FieldByName("LegCount")再次運(yùn)行程序,發(fā)現(xiàn)仍然報(bào)錯(cuò):
panic: reflect: reflect.Value.SetInt using unaddressable value
這個(gè)錯(cuò)誤表示第 13 行構(gòu)造的 valueOfDog 這個(gè)結(jié)構(gòu)體實(shí)例不能被尋址,因此其字段也不能被修改。修改代碼,取結(jié)構(gòu)體的指針,再通過(guò) reflect.Value 的 Elem() 方法取到值的反射值對(duì)象。修改后的完整代碼如下:
package main
import (
"reflect"
"fmt"
)
func main() {
type dog struct {
LegCount int
}
// 獲取dog實(shí)例地址的反射值對(duì)象
valueOfDog := reflect.ValueOf(&dog{})
// 取出dog實(shí)例地址的元素
valueOfDog = valueOfDog.Elem()
// 獲取legCount字段的值
vLegCount := valueOfDog.FieldByName("LegCount")
// 嘗試設(shè)置legCount的值(這里會(huì)發(fā)生崩潰)
vLegCount.SetInt(4)
fmt.Println(vLegCount.Int())
}代碼輸出如下:
4
代碼說(shuō)明如下:
- 第 11 行,將 LegCount 首字母大寫(xiě)導(dǎo)出該字段。
- 第 14 行,獲取 dog 實(shí)例指針的反射值對(duì)象。
- 第 17 行,取 dog 實(shí)例的指針元素,也就是 dog 的實(shí)例。
- 第 20 行,取 dog 結(jié)構(gòu)體中 LegCount 字段的成員值。
- 第 23 行,修改該成員值。
- 第 25 行,打印該成員值。
值的修改從表面意義上叫可尋址,換一種說(shuō)法就是值必須“可被設(shè)置”。那么,想修改變量值,一般的步驟是:
- 取這個(gè)變量的地址或者這個(gè)變量所在的結(jié)構(gòu)體已經(jīng)是指針類型。
- 使用 reflect.ValueOf 進(jìn)行值包裝。
- 通過(guò) Value.Elem() 獲得指針值指向的元素值對(duì)象(Value),因?yàn)橹祵?duì)象(Value)內(nèi)部對(duì)象為指針時(shí),使用 set 設(shè)置時(shí)會(huì)報(bào)出宕機(jī)錯(cuò)誤。
- 使用 Value.Set 設(shè)置值。
分享標(biāo)題:創(chuàng)新互聯(lián)GO教程:Go語(yǔ)言通過(guò)反射修改變量的值
瀏覽路徑:http://m.5511xx.com/article/dpiiogi.html


咨詢
建站咨詢
