【问题标题】:Determine whether instance of arbitrary type can be set to arbitrarily typed value判断任意类型的实例是否可以设置为任意类型的值
【发布时间】:2021-04-10 05:12:06
【问题描述】:

是否可以确定(使用reflect)是否可以将任意类型的实例设置为任意值,即确定Value.Set() 是否会由于类型不兼容而出现恐慌?

下面列出了一个 MCVE。我想知道的是“我可以在不使用延迟/恢复结构的情况下编写set() 吗?”

我想避免 defer 不仅因为它看起来很丑,而且因为 Value.Set() 可能会因为其他原因而恐慌。

请注意,这不只是比较类型是否相等的问题,如下面的o2 示例所示。

package main

import (
    "fmt"
    "reflect"
)

// set a value V to interface i, returning true if this can be done, else false
//
// CAN WE WRITE THIS WITHOUT HAVING TO USE DEFER / RECOVER?
//
func set(v reflect.Value, i interface{}) bool {
    success := true
    defer func() {
        if r := recover(); r != nil {
            success = false
        }
    }()
    v.Set(reflect.ValueOf(i))
    return success
}

// get the type of a typed nil
func getType(typedNil interface{}) reflect.Type {
    return reflect.TypeOf(typedNil).Elem()
}

func main() {
    t1 := getType((*int)(nil))
    o1 := reflect.New(t1)

    t2 := getType((*interface{})(nil))
    o2 := reflect.New(t2)

    var ok bool
    var aInt = 456
    var aString string = "hello"
    var aUint uint = 123

    // Set o1 to various types
    
    ok = set(o1.Elem(), aInt) // Should return true
    fmt.Printf("After o1 set to aInt returned %v: obj is type %T content '%v'\n", ok, o1.Elem().Interface(), o1.Elem().Interface())

    ok = set(o1.Elem(), aString) // Should return false
    fmt.Printf("After o1 set to aString returned %v: obj is type %T content '%v'\n", ok, o1.Elem().Interface(), o1.Elem().Interface())

    ok = set(o1.Elem(), aUint) // Should return false
    fmt.Printf("After o1 set to aUint returned %v: obj is type %T content '%v'\n", ok, o1.Elem().Interface(), o1.Elem().Interface())

    fmt.Println("")

    // Set o2 to various types
    ok = set(o2.Elem(), aInt)  // Should return true
    fmt.Printf("After o2 set to aInt returned %v: obj is type %T content '%v'\n", ok, o2.Elem().Interface(), o2.Elem().Interface())

    ok = set(o2.Elem(), aString) // Should return true
    fmt.Printf("After o2 set to aString returned %v: obj is type %T content '%v'\n", ok, o2.Elem().Interface(), o2.Elem().Interface())
    
    ok = set(o2.Elem(), aUint) // Should return true
    fmt.Printf("After o2 set to aUint returned %v: obj is type %T content '%v'\n", ok, o2.Elem().Interface(), o2.Elem().Interface())
}

【问题讨论】:

    标签: go reflection


    【解决方案1】:

    reflect.Type 有一个Type.AssignableTo() 方法:

    // AssignableTo reports whether a value of the type is assignable to type u.
    AssignableTo(u Type) bool
    

    所以你可以用它来简化你的set()函数:

    func set(v reflect.Value, i interface{}) bool {
        if !reflect.TypeOf(i).AssignableTo(v.Type()) {
            return false
        }
        v.Set(reflect.ValueOf(i))
        return true
    }
    

    输出将是相同的(在Go Playground 上尝试):

    After o1 set to aInt returned true: obj is type int content '456'
    After o1 set to aString returned false: obj is type int content '456'
    After o1 set to aUint returned false: obj is type int content '456'
    
    After o2 set to aInt returned true: obj is type int content '456'
    After o2 set to aString returned true: obj is type string content 'hello'
    After o2 set to aUint returned true: obj is type uint content '123'
    

    您还可以(另外)致电Value.CanSet() 来检测某些“无法设置”的场景:

    CanSet 报告 v 的值是否可以更改。只有当它是可寻址的并且不是通过使用未导出的结构字段获得时才能更改值。如果 CanSet 返回 false,则调用 Set 或任何特定类型的 setter(例如,SetBool、SetInt)将发生恐慌。

    【讨论】:

    • 完美。谢谢。我确信这应该存在,但它在golang.org/pkg/reflect 的索引中神秘地消失了——尽管它在下面。
    • @abligh 因为reflect.Type是接口,不是具体实现,索引中没有列出接口的方法,只列出具体类型的方法,所以索引中没有。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-28
    • 1970-01-01
    • 2014-07-14
    相关资源
    最近更新 更多