声明:文章内容取自雨痕老师《Go语言学习笔记》
和Type获取类型信息不同,Value专注于对象实例数据读写。
在前面章节曾提到过,接口变量会复制对象,且是unaddressable的,所以要想修改目标对象,就必须使用指针。
package main
import (
"fmt"
"reflect"
)
func main() {
a := 100
va, vp := reflect.ValueOf(a), reflect.ValueOf(&a).Elem()
fmt.Println(va.CanAddr(), va.CanSet())
fmt.Println(vp.CanAddr(), vp.CanSet())
}
输出:
false false
true true
就算传入指针,一样需要通过Elem获取目标对象。因为被接口存储的指针本身是不能寻址和进行设置操作的。
注意,不能对非导出字段直接进行设置操作,无论是当前包还是外包。 ```golang package main
import (
"fmt"
"reflect"
"unsafe"
)
type User struct {
Name string
code int
}
func main() {
p := new(User)
v := reflect.ValueOf(p).Elem()
name := v.FieldByName("Name")
code := v.FieldByName("code")
fmt.Printf("name:canaddr = %v,canset = %v
", name.CanAddr(), name.CanSet())
fmt.Printf("code:canaddr = %v,canset = %v
", code.CanAddr(), code.CanSet())
if name.CanSet() {
name.SetString("Tom")
}
if code.CanAddr() {
(int)(unsafe.Pointer(code.UnsafeAddr())) = 100
}
fmt.Printf("%+v
", *p)
}
输出:
name:canaddr = true,canset = true
code:canaddr = true,canset = false
{Name:Tom code:100}
<hr>
Value.Pointer和Value.Int等方法类似,将Value.data存储的数据转换为指针,目标必须是指针类型。而UnsafeAddr返回任何CanAddr Value.data地址(相当于&取地址操作),比如Elem后的Value,以及字段成员地址。
以结构体里的指针类型字段为例,Pointer返回该字段所保存的地址,而UnsafeAddr返回该字段自身的地址(结构对象地址+偏移量)
<hr>
可通过Interface方法进行类型推断和转换。
```golang
package main
import (
"fmt"
"reflect"
)
type user struct {
Name string
Age int
}
func main() {
u := user{
"雨痕",
60, //如果最后一个字段不和}相连,则必须加上,
}
v := reflect.ValueOf(&u)
if !v.CanInterface() {
fmt.Println("CanInterfae:fail.")
return
}
p, ok := v.Interface().(*user)
if !ok {
fmt.Println("Interface:fail.")
return
}
p.Age++
fmt.Printf("%+v
", u)
}
输出:
{Name:雨痕 Age:61}
也可直接使用Value.Int、Bool等方法进行类型转换,但失败时会引发panic,且不支持ok-idiom。
复合类型对象设置示例: ```golang package main
import (
"fmt"
"reflect"
)
func main() {
c := make(chan int, 4)
v := reflect.ValueOf(c)
if v.TrySend(reflect.ValueOf(100)) {
fmt.Println(v.TryRecv())
}
}
输出:
100 true
接口有两种nil状态,这一直是个潜在麻烦。解决方法是用IsNil判断值是否为nil。
```golang
package main
import (
"fmt"
"reflect"
)
func main() {
var a interface{} = nil
var b interface{} = (*int)(nil)
fmt.Println(a == nil)
fmt.Println(b == nil, reflect.ValueOf(b).IsNil())
}
输出:
true
false true
也可用unsafe转换后直接判断iface.data是否为零值。
package main
import (
"fmt"
"unsafe"
)
func main() {
var b interface{} = (*int)(nil)
iface := (*[2]uintptr)(unsafe.Pointer(&b))
fmt.Println(iface, iface[1] == 0)
}
输出:
&[4785664 0] true
让人很无奈的是,Value里的某些方法并未实现ok-idom或返回error,所以得自行判断返回的是否为Zero Value。
package main
import (
"fmt"
"reflect"
)
func main() {
v := reflect.ValueOf(struct{ name string }{})
fmt.Println(v.FieldByName("name").IsValid())
fmt.Println(v.FieldByName("xxx").IsValid())
}
输出:
true
false