判断任意类型的实例是否可以设置为任意类型的值
Determine whether instance of arbitrary type can be set to arbitrarily typed value
是否可以确定(使用reflect
)任意类型的实例是否可以设置为任意值,即确定Value.Set()
是否会因类型不兼容而崩溃?
下面列出了一个 MCVE。我想知道的是 'can I write set()
without using the defer/recover construct?'
我想避免 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())
}
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 reports whether the value of v can be changed. A Value can be changed only if it is addressable and was not obtained by the use of unexported struct fields. If CanSet returns false, calling Set or any type-specific setter (e.g., SetBool, SetInt) will panic.
是否可以确定(使用reflect
)任意类型的实例是否可以设置为任意值,即确定Value.Set()
是否会因类型不兼容而崩溃?
下面列出了一个 MCVE。我想知道的是 'can I write set()
without using the defer/recover construct?'
我想避免 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())
}
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 reports whether the value of v can be changed. A Value can be changed only if it is addressable and was not obtained by the use of unexported struct fields. If CanSet returns false, calling Set or any type-specific setter (e.g., SetBool, SetInt) will panic.