Go语言学习笔记之反射用法详解

2020-01-28 12:41:26王旭

        return
    }
    p.Age++
    fmt.Printf("%+vn", u)
}

输出:


{Name:q.yuhen Age:61}

也可以直接使用 Value.Int、Bool 等方法进行类型转换,但失败时会引发 pani,且不支持 ok-idiom。

复合类型对象设置示例:
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。
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 是否为零值。
func main()  {
    var b interface{} = (*int)(nil)
    iface := (*[2]uintptr)(unsafe.Pointer(&b))
    fmt.Println(iface, iface[1] == 0)
}
输出:


&[712160 0] true

让人很无奈的是,Value 里的某些方法并未实现 ok-idom 或返回 error,所以得自行判断返回的是否为 Zero Value。
func main()  {
    v := reflect.ValueOf(struct {name string}{})
    println(v.FieldByName("name").IsValid())
    println(v.FieldByName("xxx").IsValid())
}

输出:


true
false

三、方法

动态调用方法,谈不上有多麻烦。只须按 In 列表准备好所需参数即可。
type X struct {}
func (X) Test(x, y int) (int, error)  {
    return x + y, fmt.Errorf("err: %d", x + y)
}
func main()  {
    var a X
    v := reflect.ValueOf(&a)
    m := v.MethodByName("Test")
    in := []reflect.Value{
        reflect.ValueOf(1),
        reflect.ValueOf(2),
    }
    out := m.Call(in)
    for _, v := range out {
        fmt.Println(v)
    }
}

输出:


3
err: 3

对于变参来说,用 CallSlice() 要更方便一些。