go反射----2值

声明:文章内容取自雨痕老师《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
原文地址:https://www.cnblogs.com/zheng-chuang/p/6060223.html