fix typo and optimize.

Change-Id: I7b6938936231fd722814984678ffa30402539fd9
This commit is contained in:
fuyc
2016-08-11 17:08:38 +08:00
parent ed57986ea7
commit 8fda418f3a
33 changed files with 128 additions and 126 deletions

View File

@@ -1,6 +1,6 @@
## 12.5. 通过reflect.Value修改值
到目前为止,反射还只是程序中变量的另一种访问方式。然而,在本节中我们将重点讨论如通过反射机制来修改变量。
到目前为止,反射还只是程序中变量的另一种读取方式。然而,在本节中我们将重点讨论如通过反射机制来修改变量。
回想一下Go语言中类似x、x.f[1]和*p形式的表达式都可以表示变量但是其它如x + 1和f(2)则不是变量。一个变量就是一个可寻址的内存空间,里面存储了一个值,并且存储的值可以通过内存地址来更新。
@@ -14,7 +14,7 @@ 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方法来判断其是否可以被取地址
@@ -27,7 +27,7 @@ fmt.Println(d.CanAddr()) // "true"
每当我们通过指针间接地获取的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
@@ -44,13 +44,13 @@ 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
@@ -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