英文:
Quick way to detect empty values via reflection in Go
问题
我在一个interface{}
中存储了一个int
/string
/bool
/等等的值,并且想要确定它是否未初始化,也就是说它的值是以下之一:
0
""
false
- 或者
nil
我该如何检查这个呢?
英文:
I have a int
/string
/bool
/etc.. value stored in an interface{}
and want to determine if it's uninitialized, meaning that it has a value of either
0
""
false
- or
nil
How do I check this?
答案1
得分: 77
从我理解的来看,你想要的是这样的:
func IsZeroOfUnderlyingType(x interface{}) bool {
return x == reflect.Zero(reflect.TypeOf(x)).Interface()
}
当谈到接口和nil
时,人们常常会混淆两个非常不同且无关的概念:
- 一个
nil
接口值,它是一个没有底层值的接口值。这是接口类型的零值。 - 一个非
nil
的接口值(即它有一个底层值),但它的底层值是其底层类型的零值。例如,底层值是nil
映射、nil
指针或0数字等。
我理解你在问第二个问题。
更新:由于上面的代码使用了==
,它对于不可比较的类型是不起作用的。我相信使用reflect.DeepEqual()
代替将使其适用于所有类型:
func IsZeroOfUnderlyingType(x interface{}) bool {
return reflect.DeepEqual(x, reflect.Zero(reflect.TypeOf(x)).Interface())
}
英文:
From what I understand, you want something like:
func IsZeroOfUnderlyingType(x interface{}) bool {
return x == reflect.Zero(reflect.TypeOf(x)).Interface()
}
When talking about interfaces and nil
, people always get confused with two very different and unrelated things:
- A
nil
interface value, which is an interface value that doesn't have an underlying value. This is the zero value of an interface type. - A non-
nil
interface value (i.e. it has an underlying value), but its underlying value is the zero value of its underlying type. e.g. the underlying value is anil
map,nil
pointer, or 0 number, etc.
It is my understanding that you are asking about the second thing.
Update: Due to the above code using ==
, it won't work for types that are not comparable. I believe that using reflect.DeepEqual()
instead will make it work for all types:
func IsZeroOfUnderlyingType(x interface{}) bool {
return reflect.DeepEqual(x, reflect.Zero(reflect.TypeOf(x)).Interface())
}
答案2
得分: 23
Go 1.13 (Q3 2019)应该简化了该检测过程:
新的
Value.IsZero()
方法报告一个Value是否是其类型的零值。
它是在src/reflect/value.go
中实现的,来自提交c40bffd和CL 171337,解决了issue 7501。
参见playground示例(一旦支持Go 1.13)。
var p *string
v := reflect.ValueOf(p)
fmt.Printf("1.13 v.IsZero()='%v' vs. IsZero(v)='%v'\n", v.IsZero(), IsZero(v))
// 输出:
// 1.13 v.IsZero()='true' vs. IsZero(v)='true'
然而:
v = v.Elem()
fmt.Printf("1.13 v.Elem().IsZero()='%v' vs. IsZero(v.Elem())='%v'\n", v.IsZero(), IsZero(v))
// 报错:
//
// panic: reflect: call of reflect.Value.IsZero on zero Value
如issue 46320中所解释的:
你描述的问题是由于Go中接口类型的性质以及reflect包是建立在接口类型之上的事实所导致的。
接口类型的值总是描述了其他值。
当你将一个接口类型的值传递给reflect.ValueOf
时,你得到的是描述那个其他值的反射对象,而不是接口类型的值。要使用
reflect
包处理interface
值,通常需要使用指针。这与
IsZero()
无关,这只是reflect
包的工作原理。package main import ( "fmt" "reflect" ) func main() { var c interface{} fmt.Println(reflect.ValueOf(&c).Elem().Kind()) fmt.Println(reflect.ValueOf(&c).Elem().IsZero()) }
英文:
Go 1.13 (Q3 2019) should simplify that detection process:
> The new Value.IsZero()
method reports whether a Value is the zero value for its type.
It is implemented in src/reflect/value.go
, from commit c40bffd and CL 171337, resolving issue 7501
See playground example (as soon as Go 1.13 is supported)
var p *string
v := reflect.ValueOf(p)
fmt.Printf("1.13 v.IsZero()='%v' vs. IsZero(v)='%v'\n", v.IsZero(), IsZero(v))
// Ouput:
// 1.13 v.IsZero()='true' vs. IsZero(v)='true'
However:
v = v.Elem()
fmt.Printf("1.13 v.Elem().IsZero()='%v' vs. IsZero(v.Elem())='%v'\n", v.IsZero(), IsZero(v))
// Panic:
//
// panic: reflect: call of reflect.Value.IsZero on zero Value
As explained in issue 46320:
> The problem you are describing is due to the nature of interface types in Go, and to the fact that the reflect package is built on interface types.
>
> A value of interface type always describes some other value.
When you pass a value of interface type to reflect.ValueOf
, you get a reflection object describing that other value, not the value of interface type.
>
> To work with interface
values with the reflect
package you generally need to use pointers.
>
> This doesn't have anything to do with IsZero()
, it's just how the reflect
package works.
>
> go
> package main
>
> import (
> "fmt"
> "reflect"
> )
>
> func main() {
> var c interface{}
>
> fmt.Println(reflect.ValueOf(&c).Elem().Kind())
> fmt.Println(reflect.ValueOf(&c).Elem().IsZero())
> }
>
>
> Basically, one needs to call IsValid()
before IsZero()
答案3
得分: 4
interface{}
类型的零值只能是nil
,不能是0
、""
或false
。
package main
import "fmt"
func main() {
var v interface{}
fmt.Println(v == nil, v == 0, v == "", v == false)
}
(也可以在 http://play.golang.org/p/z1KbX1fOgB 查看)
输出结果
true false false false
*: [Q]当为一个值分配内存时,无论是通过声明还是通过make或new的调用,如果没有提供显式的初始化,该内存将被赋予默认初始化。这个值的每个元素都被设置为其类型的零值:布尔类型为false,整数类型为0,浮点数类型为0.0,字符串类型为"",指针、函数、接口、切片、通道和映射类型为nil。[/Q]
英文:
The zero value* of type interface{}
is only nil
, not 0
or ""
or false
.
package main
import "fmt"
func main() {
var v interface{}
fmt.Println(v == nil, v == 0, v == "", v == false)
}
(Also http://play.golang.org/p/z1KbX1fOgB)
Output
true false false false
*: [Q]When memory is allocated to store a value, either through a declaration or a call of make or new, and no explicit initialization is provided, the memory is given a default initialization. Each element of such a value is set to the zero value for its type: false for booleans, 0 for integers, 0.0 for floats, "" for strings, and nil for pointers, functions, interfaces, slices, channels, and maps.[/Q]
答案4
得分: 0
@newacct的答案无法检测原始零值,调用reflect.Value.Interface()
会导致错误。可以使用reflect.Value.IsValid()
来检查。
更新方法:
func IsZero(v reflect.Value) bool {
return !v.IsValid() || reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface())
}
func TestIsZero(t *testing.T) {
var p *string
v := reflect.ValueOf(p)
assert.Equal(t, true, v.IsValid())
assert.True(t, IsZero(v))
assert.Equal(t, uintptr(0), v.Pointer())
v = v.Elem()
assert.Equal(t, false, v.IsValid())
assert.True(t, IsZero(v))
}
英文:
@newacct's answer can't detect raw zero value, calling reflect.Value.Interface()
on which will cause error. It can use reflect.Value.IsValid()
to check that.
// IsValid reports whether v represents a value.
// It returns false if v is the zero Value.
// If IsValid returns false, all other methods except String panic.
// Most functions and methods never return an invalid value.
// If one does, its documentation states the conditions explicitly.
func (v Value) IsValid() bool
Update the methods:
func IsZero(v reflect.Value) bool {
return !v.IsValid() || reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface())
}
func TestIsZero(t *testing.T) {
var p *string
v := reflect.ValueOf(p)
assert.Equal(t, true, v.IsValid())
assert.True(t, IsZero(v))
assert.Equal(t, uintptr(0), v.Pointer())
v = v.Elem()
assert.Equal(t, false, v.IsValid())
assert.True(t, IsZero(v))
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论