• 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
    
  • 相关阅读:
    C#中upd分包与发送,已经实现全部代码
    Jmeter字体大小、背景色
    Linux查看日志 tail -f , grep
    xshell如何选中即复制,右键即粘贴
    Jmeter如何做接口测试
    Jmeter 线程数、ramp-up period (in seconds)、循环次数
    java反射机制入门01
    java实现文件夹(包括其中的子文件夹、子文件)的复制——递归
    ViewPager实现广告自动轮播核心代码(Handler+Thread)
    ViewPager实现启动引导页面(个人认为很详细)
  • 原文地址:https://www.cnblogs.com/zheng-chuang/p/6060223.html
Copyright © 2020-2023  润新知