type of p: *float64
settability of p: false
反射对象 p 是不可写的,但是我们也不像修改 p,事实上我们要修改的是 *p。为了得到 p 指向的数据,可以调用 Value 类型的 Elem 方法。Elem 方法能够对指针进行“解引用”,然后将结果存储到反射 Value类型对象 v中:
v := p.Elem()
fmt.Println("settability of v:", v.CanSet())
在上面这段代码中,变量 v 是一个可写的反射对象,代码输出也验证了这一点:
settability of v: true
由于变量 v 代表 x, 因此我们可以使用 v.SetFloat 修改 x 的值:
v.SetFloat(7.1)
fmt.Println(v.Interface())
fmt.Println(x)
上面代码的输出如下:
7.1
7.1
反射不太容易理解,reflect.Type 和 reflect.Value 会混淆正在执行的程序,但是它做的事情正是编程语言做的事情。你只需要记住:只要反射对象要修改它们表示的对象,就必须获取它们表示的对象的地址。
结构体(struct)
在前面的例子中,变量 v 本身并不是指针,它只是从指针衍生而来。把反射应用到结构体时,常用的方式是 使用反射修改一个结构体的某些字段。只要拥有结构体的地址,我们就可以修改它的字段。
下面通过一个简单的例子对结构体类型变量 t 进行分析。
首先,我们创建了反射类型对象,它包含一个结构体的指针,因为后续会修改。
然后,我们设置 typeOfT 为它的类型,并遍历所有的字段。
注意:我们从 struct 类型提取出每个字段的名字,但是每个字段本身也是常规的 reflect.Value 对象。
type T struct {
A int
B string
}
t := T{23, "skidoo"}
s := reflect.ValueOf(&t).Elem()
typeOfT := s.Type()
for i := 0; i < s.NumField(); i++ {
f := s.Field(i)
fmt.Printf("%d: %s %s = %vn", i,
typeOfT.Field(i).Name, f.Type(), f.Interface())
}
上面这段代码的输出如下:
0: A int = 23
1: B string = skidoo
这里还有一点需要指出:变量 T 的字段都是首字母大写的(暴露到外部),因为struct中只有暴露到外部的字段才是“可写的”。
由于变量 s 包含一个“可写的”反射对象,我们可以修改结构体的字段:
f.Interface())s.Field(0).SetInt(77)
s.Field(1).SetString("Sunset Strip")
fmt.Println("t is now", t)
上面代码的输出如下:
t is now {77 Sunset Strip}
如果变量 s 是通过 t ,而不是 &t 创建的,调用 SetInt 和 SetString 将会失败,因为 t 的字段不是“可写的”。
结论









