如何在Golang中判断任意类型的变量是否为零?

huangapple go评论76阅读模式
英文:

How to know if a variable of arbitrary type is Zero in Golang?

问题

因为不是所有类型都可以进行比较,例如切片。所以我们不能这样做:

var v ArbitraryType
v == reflect.Zero(reflect.TypeOf(v)).Interface()
英文:

Because not all types are comparable, e.g. a slice. So we can't do this

var v ArbitraryType
v == reflect.Zero(reflect.TypeOf(v)).Interface()

答案1

得分: 8

Go 1.13在reflect包中引入了Value.IsZero方法。使用它可以检查零值,示例如下:

if reflect.ValueOf(v).IsZero() {
	// v是零值,执行某些操作
}

除了基本类型外,它还适用于Chan、Func、Array、Interface、Map、Ptr、Slice、UnsafePointer和Struct类型。

英文:

Go 1.13 introduced Value.IsZero method in reflect package. This is how you can check for zero value using it:

if reflect.ValueOf(v).IsZero() {
	// v is zero, do something
}

Apart from basic types, it also works for Chan, Func, Array, Interface, Map, Ptr, Slice, UnsafePointer, and Struct.

答案2

得分: 3

正如Peter Noyes所指出的,你只需要确保不要比较不可比较的类型。幸运的是,使用reflect包非常简单:

func IsZero(v interface{}) (bool, error) {
    t := reflect.TypeOf(v)
    if !t.Comparable() {
        return false, fmt.Errorf("type is not comparable: %v", t)
    }
    return v == reflect.Zero(t).Interface(), nil
}

这里可以看到一个使用示例。

英文:

As Peter Noyes points out, you just need to make sure you're not comparing a type which isn't comparable. Luckily, this is very straightforward with the reflect package:

func IsZero(v interface{}) (bool, error) {
	t := reflect.TypeOf(v)
	if !t.Comparable() {
		return false, fmt.Errorf("type is not comparable: %v", t)
	}
	return v == reflect.Zero(t).Interface(), nil
}

See an example use here.

答案3

得分: 3

反射

这取决于当 v 是一个接口时你想要的行为(对于其他类型来说是一样的):

  • reflect.ValueOf(v).IsZero() 检查接口中封装的值是否为零
  • reflect.ValueOf(&v).Elem().IsZero() 检查接口变量本身是否为零

演示(playground):

var v interface{} = ""
fmt.Println(reflect.ValueOf(v).IsZero())           // true,空字符串""是零值
fmt.Println(reflect.ValueOf(&v).Elem().IsZero())   // false,接口本身不是零值

这种差异是由于 ValueOf 的参数是 interface{}。如果传递一个接口,它将解封装其中的动态值。ValueOf 的 Go 文档提醒:

> ValueOf 返回一个新的 Value,其初始化为接口 i 中存储的具体值

通过使用 ValueOf(&v) 而不是 "接口 i 中存储的具体值" 将是指向 v 的指针。然后你使用 Elem() 解引用以获取原始值,最后检查 IsZero()

大多数情况下,你可能想要使用 reflect.ValueOf(&v).Elem().IsZero(),但具体情况可能有所不同。

Go 1.18 和泛型

使用泛型,你可以通过在 var zero T*new(T) 上使用 == 运算符来检查变量是否为零值。类型参数必须是可比较的comparable 约束或可比较类型的类型集)。

func IsZeroVar[T ~int64 | ~string](v T) bool {
    var zero T
    return v == zero
}

func IsZeroNew[T ~int64 | ~string](v T) bool {
    return v == *new(T)
}

如果类型参数不可比较,你必须退回到反射,如上所示。

英文:

Reflect

It depends of what behavior you want when v is an interface (for other types it's the same):

  • reflect.ValueOf(v).IsZero() checks whether the value boxed in the interface is zero
  • reflect.ValueOf(&v).Elem().IsZero() checks whether the interface variable is zero

Demonstration (playground):

var v interface{} = ""
fmt.Println(reflect.ValueOf(v).IsZero())           // true, the empty string "" is zero
fmt.Println(reflect.ValueOf(&v).Elem().IsZero())   // false, the interface itself is not zero

The difference is due to the argument of ValueOf being interface{}. If you pass an interface, it will unbox the dynamic value boxed in it. The Go docs of ValueOf remind that:

> ValueOf returns a new Value initialized to the concrete value stored in the interface i

By using ValueOf(&v) instead "the concrete value stored in the interface i" will be a pointer to v. Then you dereference with Elem() to get the original value and finally check IsZero().

Most often than not, what you want is probably reflect.ValueOf(&v).Elem().IsZero(), though YMMV.

Go 1.18 and generics

With generics, you can check if a variable is zero value by using the == operator on a var zero T or *new(T). The type parameter must be comparable (comparable constraint or type set of comparable types).

func IsZeroVar[T ~int64 | ~string](v T) bool {
    var zero T
    return v == zero
}

func IsZeroNew[T ~int64 | ~string](v T) bool {
    return v == *new(T)
}

If the type param is not comparable, you must fall back to reflection, as shown above.

答案4

得分: 2

以下两个都给我合理的结果(可能是因为它们是相同的?)

reflect.ValueOf(v) == reflect.Zero(reflect.TypeOf(v)))

reflect.DeepEqual(reflect.ValueOf(v), reflect.Zero(reflect.TypeOf(v)))

例如,各种整数0的形式和未初始化的“struct”都是“zero”。

不幸的是,空字符串和数组不是。而nil会引发异常。
如果你愿意,你可以对这些进行特殊处理。

英文:

Both of the following give me reasonable results (probably because they're the same?)

reflect.ValueOf(v) == reflect.Zero(reflect.TypeOf(v)))

reflect.DeepEqual(reflect.ValueOf(v), reflect.Zero(reflect.TypeOf(v)))

e.g. various integer 0 flavours and uninitialized structs are "zero"

Sadly, empty strings and arrays are not. and nil gives an exception.
You could special case these if you wanted.

huangapple
  • 本文由 发表于 2015年10月14日 11:07:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/33115946.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定