日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關咨詢
選擇下列產品馬上在線溝通
服務時間:8:30-17:00
你可能遇到了下面的問題
關閉右側工具欄

新聞中心

這里有您想知道的互聯(lián)網營銷解決方案
創(chuàng)新互聯(lián)GO教程:Go語言通過反射修改變量的值

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

創(chuàng)新互聯(lián)服務項目包括從江網站建設、從江網站制作、從江網頁制作以及從江網絡營銷策劃等。多年來,我們專注于互聯(lián)網行業(yè),利用自身積累的技術優(yōu)勢、行業(yè)經驗、深度合作伙伴關系等,向廣大中小型企業(yè)、政府機構等提供互聯(lián)網行業(yè)的解決方案,從江網站推廣取得了明顯的社會效益與經濟效益。目前,我們服務的客戶以成都為中心已經輻射到從江省份的部分城市,未來相信會繼續(xù)擴大服務區(qū)域并繼續(xù)獲得客戶的支持與信任!

對于 reflect.Values 也有類似的區(qū)別。有一些 reflect.Values 是可取地址的;其它一些則不可以??紤]以下的聲明語句:

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 對應的變量則不可取地址。因為 a 中的值僅僅是整數(shù) 2 的拷貝副本。b 中的值也同樣不可取地址。c 中的值還是不可取地址,它只是一個指針 &x 的拷貝。實際上,所有通過 reflect.ValueOf(x) 返回的 reflect.Value 都是不可取地址的。但是對于 d,它是 c 的解引用方式生成的,指向另一個變量,因此是可取地址的。我們可以通過調用 reflect.ValueOf(&x).Elem(),來獲取任意變量x對應的可取地址的 Value。

我們可以通過調用 reflect.Value 的 CanAddr 方法來判斷其是否可以被取地址:

fmt.Println(a.CanAddr()) // "false"
fmt.Println(b.CanAddr()) // "false"
fmt.Println(c.CanAddr()) // "false"
fmt.Println(d.CanAddr()) // "true"

每當我們通過指針間接地獲取的 reflect.Value 都是可取地址的,即使開始的是一個不可取地址的 Value。在反射機制中,所有關于是否支持取地址的規(guī)則都是類似的。例如,slice 的索引表達式 e[i]將隱式地包含一個指針,它就是可取地址的,即使開始的e表達式不支持也沒有關系。

以此類推,reflect.ValueOf(e).Index(i) 對于的值也是可取地址的,即使原始的 reflect.ValueOf(e) 不支持也沒有關系。

使用 reflect.Value 對包裝的值進行修改時,需要遵循一些規(guī)則。如果沒有按照規(guī)則進行代碼設計和編寫,輕則無法修改對象值,重則程序在運行時會發(fā)生宕機。

判定及獲取元素的相關方法

使用 reflect.Value 取元素、取地址及修改值的屬性方法請參考下表。

反射值對象的判定及獲取元素的方法

方法名 備  注
Elem() Value 取值指向的元素值,類似于語言層*操作。當值類型不是指針或接口時發(fā)生宕 機,空指針時返回 nil 的 Value
Addr() Value 對可尋址的值返回其地址,類似于語言層&操作。當值不可尋址時發(fā)生宕機
CanAddr() bool 表示值是否可尋址
CanSet() bool 返回值能否被修改。要求值可尋址且是導出的字段

值修改相關方法

使用 reflect.Value 修改值的相關方法如下表所示。

反射值對象修改值的方法

Set(x Value) 將值設置為傳入的反射值對象的值
Setlnt(x int64) 使用 int64 設置值。當值的類型不是 int、int8、int16、 int32、int64 時會發(fā)生宕機
SetUint(x uint64) 使用 uint64 設置值。當值的類型不是 uint、uint8、uint16、uint32、uint64 時會發(fā)生宕機
SetFloat(x float64) 使用 float64 設置值。當值的類型不是 float32、float64 時會發(fā)生宕機
SetBool(x bool) 使用 bool 設置值。當值的類型不是 bod 時會發(fā)生宕機
SetBytes(x []byte) 設置字節(jié)數(shù)組 []bytes值。當值的類型不是 []byte 時會發(fā)生宕機
SetString(x string) 設置字符串值。當值的類型不是 string 時會發(fā)生宕機

以上方法,在 reflect.Value 的 CanSet 返回 false 仍然修改值時會發(fā)生宕機。

在已知值的類型時,應盡量使用值對應類型的反射設置值。

值可修改條件之一:可被尋址

通過反射修改變量值的前提條件之一:這個值必須可以被尋址。簡單地說就是這個變量必須能被修改。示例代碼如下:

package main

import (
    "reflect"
)

func main() {

    // 聲明整型變量a并賦初值
    var a int = 1024

    // 獲取變量a的反射值對象
    valueOfA := reflect.ValueOf(a)

    // 嘗試將a修改為1(此處會發(fā)生崩潰)
    valueOfA.SetInt(1)
}

程序運行崩潰,打印錯誤:

panic: reflect: reflect.Value.SetInt using unaddressable value

報錯意思是:SetInt 正在使用一個不能被尋址的值。從 reflect.ValueOf 傳入的是 a 的值,而不是 a 的地址,這個 reflect.Value 當然是不能被尋址的。將代碼修改一下,重新運行:

package main

import (
    "fmt"
    "reflect"
)

func main() {

    // 聲明整型變量a并賦初值
    var a int = 1024

    // 獲取變量a的反射值對象(a的地址)
    valueOfA := reflect.ValueOf(&a)

    // 取出a地址的元素(a的值)
    valueOfA = valueOfA.Elem()

    // 修改a的值為1
    valueOfA.SetInt(1)

    // 打印a的值
    fmt.Println(valueOfA.Int())
}

代碼輸出如下:

1

下面是對代碼的分析:

  • 第 14 行中,將變量 a 取值后傳給 reflect.ValueOf()。此時 reflect.ValueOf() 返回的 valueOfA 持有變量 a 的地址。
  • 第 17 行中,使用 reflect.Value 類型的 Elem() 方法獲取 a 地址的元素,也就是 a 的值。reflect.Value 的 Elem() 方法返回的值類型也是 reflect.Value。
  • 第 20 行,此時 valueOfA 表示的是 a 的值且可以尋址。使用 SetInt() 方法設置值時不再發(fā)生崩潰。
  • 第 23 行,正確打印修改的值。

提示

當 reflect.Value 不可尋址時,使用 Addr() 方法也是無法取到值的地址的,同時會發(fā)生宕機。雖然說 reflect.Value 的 Addr() 方法類似于語言層的
&操作;Elem() 方法類似于語言層的
*操作,但并不代表這些方法與語言層操作等效。

值可修改條件之一:被導出

結構體成員中,如果字段沒有被導出,即便不使用反射也可以被訪問,但不能通過反射修改,代碼如下:

package main

import (
    "reflect"
)

func main() {

    type dog struct {
            legCount int
    }
    // 獲取dog實例的反射值對象
    valueOfDog := reflect.ValueOf(dog{})

    // 獲取legCount字段的值
    vLegCount := valueOfDog.FieldByName("legCount")

    // 嘗試設置legCount的值(這里會發(fā)生崩潰)
    vLegCount.SetInt(4)
}

程序發(fā)生崩潰,報錯:

panic: reflect: reflect.Value.SetInt using value obtained using unexported field

報錯的意思是:SetInt() 使用的值來自于一個未導出的字段。

為了能修改這個值,需要將該字段導出。將 dog 中的 legCount 的成員首字母大寫,導出 LegCount 讓反射可以訪問,修改后的代碼如下:

type dog struct {
    LegCount int
}

然后根據(jù)字段名獲取字段的值時,將字符串的字段首字母大寫,修改后的代碼如下:

vLegCount := valueOfDog.FieldByName("LegCount")

再次運行程序,發(fā)現(xiàn)仍然報錯:

panic: reflect: reflect.Value.SetInt using unaddressable value

這個錯誤表示第 13 行構造的 valueOfDog 這個結構體實例不能被尋址,因此其字段也不能被修改。修改代碼,取結構體的指針,再通過 reflect.Value 的 Elem() 方法取到值的反射值對象。修改后的完整代碼如下:

package main

import (
    "reflect"
    "fmt"
)

func main() {

    type dog struct {
            LegCount int
    }
    // 獲取dog實例地址的反射值對象
    valueOfDog := reflect.ValueOf(&dog{})

    // 取出dog實例地址的元素
    valueOfDog = valueOfDog.Elem()

    // 獲取legCount字段的值
    vLegCount := valueOfDog.FieldByName("LegCount")

    // 嘗試設置legCount的值(這里會發(fā)生崩潰)
    vLegCount.SetInt(4)

    fmt.Println(vLegCount.Int())
}

代碼輸出如下:

4

代碼說明如下:

  • 第 11 行,將 LegCount 首字母大寫導出該字段。
  • 第 14 行,獲取 dog 實例指針的反射值對象。
  • 第 17 行,取 dog 實例的指針元素,也就是 dog 的實例。
  • 第 20 行,取 dog 結構體中 LegCount 字段的成員值。
  • 第 23 行,修改該成員值。
  • 第 25 行,打印該成員值。

值的修改從表面意義上叫可尋址,換一種說法就是值必須“可被設置”。那么,想修改變量值,一般的步驟是:

  1. 取這個變量的地址或者這個變量所在的結構體已經是指針類型。
  2. 使用 reflect.ValueOf 進行值包裝。
  3. 通過 Value.Elem() 獲得指針值指向的元素值對象(Value),因為值對象(Value)內部對象為指針時,使用 set 設置時會報出宕機錯誤。
  4. 使用 Value.Set 設置值。

網站欄目:創(chuàng)新互聯(lián)GO教程:Go語言通過反射修改變量的值
文章地址:http://www.5511xx.com/article/dpiiogi.html