回到简体

This commit is contained in:
chai2010
2016-02-15 11:06:34 +08:00
parent 9e878f9944
commit 2b37b23285
177 changed files with 2354 additions and 2354 deletions

View File

@@ -1,10 +1,10 @@
## 12.5. 通reflect.Value改值
## 12.5. 通reflect.Value改值
到目前止,反射還隻是程序中量的另一種訪問方式。然而,在本中我們將重點討論如果通反射機製來脩改變量。
到目前止,反射还只是程序中量的另一种访问方式。然而,在本中我们将重点讨论如果通反射机制来修改变量。
想一下Go言中似x、x.f[1]和*p形式的表式都可以表示但是其它如x + 1和f(2)不是量。一個變量就是一個可尋址的存空,里面存了一值,且存的值可以通過內存地址更新。
想一下Go言中似x、x.f[1]和*p形式的表式都可以表示但是其它如x + 1和f(2)不是量。一个变量就是一个可寻址的存空,里面存了一值,且存的值可以通过内存地址更新。
對於reflect.Values也有似的别。有一些reflect.Values是可取地址的其它一些不可以。考以下的聲明語句:
对于reflect.Values也有似的别。有一些reflect.Values是可取地址的其它一些不可以。考以下的声明语句:
```Go
x := 2 // value type variable?
@@ -14,9 +14,9 @@ c := reflect.ValueOf(&x) // &x *int no
d := c.Elem() // 2 int yes (x)
```
其中a對應的變量則不可取地址。因a中的值僅僅是整2的拷副本。b中的值也同不可取地址。c中的值是不可取地址,它是一個指針`&x`的拷貝。實際上,所有通reflect.ValueOf(x)返的reflect.Value都是不可取地址的。但是對於d它是c的解引用方式生成的指向另一個變量,因此是可取地址的。我可以通過調用reflect.ValueOf(&x).Elem()來獲取任意量x對應的可取地址的Value。
其中a对应的变量则不可取地址。因a中的值仅仅是整2的拷副本。b中的值也同不可取地址。c中的值是不可取地址,它是一个指针`&x`的拷贝。实际上,所有通reflect.ValueOf(x)返的reflect.Value都是不可取地址的。但是对于d它是c的解引用方式生成的指向另一个变量,因此是可取地址的。我可以通过调用reflect.ValueOf(&x).Elem()来获取任意量x对应的可取地址的Value。
可以通過調用reflect.Value的CanAddr方法來判斷其是否可以被取地址:
可以通过调用reflect.Value的CanAddr方法来判断其是否可以被取地址:
```Go
fmt.Println(a.CanAddr()) // "false"
@@ -25,9 +25,9 @@ fmt.Println(c.CanAddr()) // "false"
fmt.Println(d.CanAddr()) // "true"
```
當我們通過指針間接地取的reflect.Value都是可取地址的卽使開始的是一不可取地址的Value。在反射機製中,所有關於是否支持取地址的規則都是似的。例如slice的索引表式e[i]將隱式地包含一個指針,它就是可取地址的,卽使開始的e表式不支持也沒有關繫。以此reflect.ValueOf(e).Index(i)對於的值也是可取地址的,使原始的reflect.ValueOf(e)不支持也沒有關繫
当我们通过指针间接地取的reflect.Value都是可取地址的即使开始的是一不可取地址的Value。在反射机制中,所有关于是否支持取地址的规则都是似的。例如slice的索引表式e[i]将隐式地包含一个指针,它就是可取地址的,即使开始的e表式不支持也没有关系。以此reflect.ValueOf(e).Index(i)对于的值也是可取地址的,使原始的reflect.ValueOf(e)不支持也没有关系
從變量對應的可取地址的reflect.Value來訪問變量需要三個步驟。第一步是調用Addr()方法,它返迴一個Value里面保存了指向量的指。然是在Value上調用Interface()方法,也就是返迴一個interface{},里面通用包含指向量的指。最,如果我知道量的型,我可以使用型的斷言機製將得到的interface{}型的接口強製環爲普通的型指針。這樣我們就可以通過這個普通指針來更新量了:
从变量对应的可取地址的reflect.Value来访问变量需要三个步骤。第一步是用Addr()方法,它返回一个Value里面保存了指向量的指。然是在Value上用Interface()方法,也就是返回一个interface{},里面通用包含指向量的指。最,如果我知道量的型,我可以使用型的断言机制将得到的interface{}型的接口强制环为普通的型指针。这样我们就可以通过这个普通指针来更新量了:
```Go
x := 2
@@ -37,20 +37,20 @@ px := d.Addr().Interface().(*int) // px := &x
fmt.Println(x) // "3"
```
或者,不使用指,而是通過調用可取地址的reflect.Value的reflect.Value.Set方法更新對於的值:
或者,不使用指,而是通过调用可取地址的reflect.Value的reflect.Value.Set方法更新对于的值:
```Go
d.Set(reflect.ValueOf(4))
fmt.Println(x) // "4"
```
Set方法將在運行時執行和編譯時類似的可值性束的檢査。以上代碼,變量和值都是int型,但是如果量是int64型,那程序將拋出一panic常,所以關鍵問題是要保改型的量可以接受對應的值:
Set方法将在运行时执行和编译时类似的可值性束的检查。以上代码,变量和值都是int型,但是如果量是int64型,那程序将抛出一panic常,所以关键问题是要保改型的量可以接受对应的值:
```Go
d.Set(reflect.ValueOf(int64(5))) // panic: int64 is not assignable to int
```
通用對一個不可取地址的reflect.Value調用Set方法也會導致panic常:
通用对一个不可取地址的reflect.Value用Set方法也会导致panic常:
```Go
x := 2
@@ -58,7 +58,7 @@ b := reflect.ValueOf(x)
b.Set(reflect.ValueOf(3)) // panic: Set using unaddressable value
```
里有很多用基本數據類型的Set方法SetInt、SetUint、SetString和SetFloat等。
里有很多用基本数据类型的Set方法SetInt、SetUint、SetString和SetFloat等。
```Go
d := reflect.ValueOf(&x).Elem()
@@ -66,7 +66,7 @@ d.SetInt(3)
fmt.Println(x) // "3"
```
從某種程度上説,這些Set方法總是盡可能地完成任。以SetInt例,隻要變量是某種類型的有符號整數就可以工作,使是一些命名的型,要底層數據類型是有符號整數就可以,而且如果對於變量類型值太大的話會被自動截斷。但需要慎的是:對於一個引用interface{}型的reflect.Value調用SetInt會導致panic常,使那interface{}變量對於整數類型也不行。
从某种程度上说,这些Set方法总是尽可能地完成任。以SetInt例,只要变量是某种类型的有符号整数就可以工作,使是一些命名的型,要底层数据类型是有符号整数就可以,而且如果对于变量类型值太大的话会被自动截断。但需要慎的是:对于一个引用interface{}型的reflect.Value用SetInt会导致panic常,使那interface{}变量对于整数类型也不行。
```Go
x := 1
@@ -84,7 +84,7 @@ ry.SetString("hello") // panic: SetString called on interface Value
ry.Set(reflect.ValueOf("hello")) // OK, y = "hello"
```
當我們用Display示os.Stdout結構時,我們發現反射可以越Go言的導出規則的限製讀取結構體中未出的成,比如在Unix繫統上os.File結構體中的fd int成。然而,利用反射機製併不能脩改這些未出的成
当我们用Display示os.Stdout结构时,我们发现反射可以越Go言的导出规则的限制读取结构体中未出的成,比如在Unix系统上os.File结构体中的fd int成。然而,利用反射机制并不能修改这些未出的成
```Go
stdout := reflect.ValueOf(os.Stdout).Elem() // *os.Stdout, an os.File var
@@ -94,7 +94,7 @@ fmt.Println(fd.Int()) // "1"
fd.SetInt(2) // panic: unexported field
```
可取地址的reflect.Value會記録一個結構體成員是否是未出成,如果是的話則拒絶脩改操作。因此CanAddr方法不能正反映一個變量是否是可以被改的。另一個相關的方法CanSet是用於檢査對應的reflect.Value是否是可取地址可被改的:
可取地址的reflect.Value会记录一个结构体成员是否是未出成,如果是的话则拒绝修改操作。因此CanAddr方法不能正反映一个变量是否是可以被改的。另一个相关的方法CanSet是用于检查对应的reflect.Value是否是可取地址可被改的:
```Go
fmt.Println(fd.CanAddr(), fd.CanSet()) // "true false"