如何检查一个空结构体?

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

How to check for an empty struct?

问题

我定义了一个结构体...

type Session struct {
    playerId string
    beehive string
    timestamp time.Time
}

有时我会给它赋一个空的session(因为不能使用nil)

session = Session{};

然后我想检查它是否为空:

if session == Session{} {
     // 做一些操作...
}

显然这样是行不通的。我该如何编写它?

英文:

I define a struct ...

type Session struct {
    playerId string
    beehive string
    timestamp time.Time
}

Sometimes I assign an empty session to it (because nil is not possible)

session = Session{};

Then I want to check, if it is empty:

if session == Session{} {
     // do stuff...
}

Obviously this is not working. How do I write it?

答案1

得分: 314

你可以使用==运算符将Session与零值组合字面量进行比较,因为Session中的所有字段都是可比较的

if (Session{}) == session {
    fmt.Println("是零值")
}

playground示例

由于解析歧义,在if条件中需要使用括号将组合字面量括起来。

上述使用==的方法适用于所有字段都是可比较的的结构体。如果结构体包含一个非可比较的字段(切片、映射或函数),那么必须逐个比较字段与它们的零值。

与比较整个值相比,另一种方法是比较必须在有效会话中设置为非零值的字段。例如,如果在有效会话中玩家ID必须为""之外的值,可以使用以下代码:

if session.playerId == "" {
    fmt.Println("是零值")
}
英文:

You can use == to compare with a zero value composite literal because all fields in Session are comparable:

if (Session{}) == session  {
	fmt.Println("is zero value")
}

playground example

Because of a parsing ambiguity, parentheses are required around the composite literal in the if condition.

The use of == above applies to structs where all fields are comparable. If the struct contains a non-comparable field (slice, map or function), then the fields must be compared one by one to their zero values.

An alternative to comparing the entire value is to compare a field that must be set to a non-zero value in a valid session. For example, if the player id must be != "" in a valid session, use

if session.playerId == "" {
	fmt.Println("is zero value")
}

答案2

得分: 64

以下是3个更多的建议或技巧:

使用额外的字段

您可以添加一个额外的字段来判断结构体是否已经填充或为空。我故意将其命名为ready而不是empty,因为bool类型的零值是false,所以如果您创建一个新的结构体Session{},它的ready字段将自动为false,并且它会告诉您真相:该结构体尚未准备好(为空)。

type Session struct {
    ready bool

    playerId string
    beehive string
    timestamp time.Time
}

当您初始化结构体时,必须将ready设置为true。您不再需要isEmpty()方法(尽管如果您愿意,仍然可以创建一个),因为您可以直接测试ready字段本身。

var s Session

if !s.ready {
    // do stuff (populate s)
}

当结构体变得更大或包含不可比较的字段(例如切片、map和函数值)时,这个额外的bool字段的重要性会增加。

使用现有字段的零值

这与前面的建议类似,但它使用现有字段的零值,在结构体为空时被视为“无效”。这个方法的可用性取决于具体实现。

例如,如果在您的示例中,playerId不能是空字符串"",您可以使用它来测试结构体是否为空:

var s Session

if s.playerId == "" {
    // do stuff (populate s, give proper value to playerId)
}

在这种情况下,值得将此检查合并到一个isEmpty()方法中,因为这个检查是依赖于具体实现的:

func (s Session) isEmpty() bool {
    return s.playerId == ""
}

并使用它:

if s.isEmpty() {
    // do stuff (populate s, give proper value to playerId)
}

使用指向结构体的指针

第二个建议是使用指向结构体的指针:*Session。指针可以具有nil值,因此您可以测试它:

var s *Session

if s == nil {
    s = new(Session)
    // do stuff (populate s)
}
英文:

Here are 3 more suggestions or techniques:

With an Additional Field

You can add an additional field to tell if the struct has been populated or it is empty. I intentionally named it ready and not empty because the zero value of a bool is false, so if you create a new struct like Session{} its ready field will be automatically false and it will tell you the truth: that the struct is not-yet ready (it's empty).

type Session struct {
    ready bool

    playerId string
    beehive string
    timestamp time.Time
}

When you initialize the struct, you have to set ready to true. Your isEmpty() method isn't needed anymore (although you can create one if you want to) because you can just test the ready field itself.

var s Session

if !s.ready {
    // do stuff (populate s)
}

Significance of this one additional bool field increases as the struct grows bigger or if it contains fields which are not comparable (e.g. slice, map and function values).

Using the Zero Value of an Existing Field

This is similar to the previous suggestion, but it uses the zero value of an existing field which is considered invalid when the struct is not empty. Usability of this is implementation dependant.

For example if in your example your playerId cannot be the empty string "", you can use it to test if your struct is empty like this:

var s Session

if s.playerId == "" {
    // do stuff (populate s, give proper value to playerId)
}

In this case it's worth incorporating this check into an isEmpty() method because this check is implementation dependant:

func (s Session) isEmpty() bool {
    return s.playerId == ""
}

And using it:

if s.isEmpty() {
    // do stuff (populate s, give proper value to playerId)
}

Use Pointer to your struct

The second suggestion is to use a Pointer to your struct: *Session. Pointers can have nil values, so you can test for it:

var s *Session

if s == nil {
    s = new(Session)
    // do stuff (populate s)
}

答案3

得分: 33

只是一个快速的补充,因为我今天遇到了同样的问题:

在 Go 1.13 中,可以使用新的 isZero() 方法:

if reflect.ValueOf(session).IsZero() {
     // 做一些操作...
}

我没有测试过这个方法的性能,但我猜想这应该比使用 reflect.DeepEqual() 进行比较更快。

英文:

Just a quick addition, because I tackled the same issue today:

With Go 1.13 it is possible to use the new isZero() method:

if reflect.ValueOf(session).IsZero() {
     // do stuff...
}

I didn't test this regarding performance, but I guess that this should be faster, than comparing via reflect.DeepEqual().

答案4

得分: 30

使用reflect.DeepEqual也可以工作,特别是当结构体内部有映射时。

package main

import "fmt"
import "time"
import "reflect"

type Session struct {
    playerId  string
    beehive   string
    timestamp time.Time
}

func (s Session) IsEmpty() bool {
    return reflect.DeepEqual(s, Session{})
}

func main() {
    x := Session{}
    if x.IsEmpty() {
        fmt.Print("is empty")
    }
}
英文:

Using reflect.deepEqual also works, especially when you have map inside the struct

package main

import "fmt"
import "time"
import "reflect"

type Session struct {
    playerId string
    beehive string
    timestamp time.Time
}

func (s Session) IsEmpty() bool {
  return reflect.DeepEqual(s,Session{})
}

func main() {
  x := Session{}
  if x.IsEmpty() {
    fmt.Print("is empty")
  } 
}

答案5

得分: 8

请注意,使用指向结构体的指针时,您需要对变量进行解引用,而不是将其与指向空结构体的指针进行比较:

session := &Session{}
if (Session{}) == *session {
	fmt.Println("session is empty")
}

请查看此playground

此外,您还可以看到,包含指针切片属性的结构体不能以相同的方式进行比较...

英文:

Keep in mind that with pointers to struct you'd have to dereference the variable and not compare it with a pointer to empty struct:

session := &Session{}
if (Session{}) == *session {
	fmt.Println("session is empty")
}

Check this playground.

Also here you can see that a struct holding a property which is a slice of pointers cannot be compared the same way...

答案6

得分: 2

作为对其他答案的替代方案,可以使用与您最初打算的方式类似的语法,通过case语句而不是if语句来实现:

session := Session{}
switch {
case Session{} == session:
    fmt.Println("zero")
default:
    fmt.Println("not zero")
}

playground示例

英文:

As an alternative to the other answers, it's possible to do this with a syntax similar to the way you originally intended if you do it via a case statement rather than an if:

session := Session{}
switch {
case Session{} == session:
	fmt.Println("zero")
default:
	fmt.Println("not zero")
}

playground example

1: https://play.golang.org/p/kgIqmRnA2M "playground example"

答案7

得分: 0

@cerise-limón的答案是最好的。不过,如果你遇到像下面这样的struct containing ... cannot be compared错误。

type Friends struct {
	nice []string
	mute []string
}

type Session struct {
	playerId  string
	beehive   string
	timestamp time.Time
	friends   Friends
}

func main() {
	session := Session{}
	if (Session{}) == session  {
		fmt.Println("zero")
	}
}

// 输出: ./prog.go:23:17: invalid operation: Session{} == session (struct containing Friends cannot be compared)

上述代码中,Friends对象导致了错误。

简单的解决方法是在字段中将其定义为指针。

type Friends struct {
	nice []string
	mute []string
	
}

type Session struct {
	playerId  string
	beehive   string
	timestamp time.Time
	friends   *Friends // <--- 在这里
}

func main() {
	session := Session{}
	if (Session{}) == session  {
		fmt.Println("zero")
	}
}

// 输出: zero
英文:

@cerise-limón's answer was the best. Though, if you get struct containing ... cannot be compared error like below.

type Friends struct {
	nice []string
	mute []string
}

type Session struct {
	playerId  string
	beehive   string
	timestamp time.Time
	friends   Friends
}

func main() {
	session := Session{}
	if (Session{}) == session  {
		fmt.Println(&quot;zero&quot;)
	}
}

// Output: ./prog.go:23:17: invalid operation: Session{} == session (struct containing Friends cannot be compared)

The above, Friends' object is causing an error.

The easy way to fix it is to define as a pointer in the field.

type Friends struct {
	nice []string
	mute []string
	
}

type Session struct {
	playerId  string
	beehive   string
	timestamp time.Time
	friends   *Friends // &lt;--- here
}

func main() {
	session := Session{}
	if (Session{}) == session  {
		fmt.Println(&quot;zero&quot;)
	}
}

// Output: zero

答案8

得分: 0

使用fmt.Sprintf可以帮助检测一个struct是否为空。当它为空时,输出结果为{ }

这个建议对于处理JSON和struct没有特定名称的情况非常有用。

if fmt.Sprintf("%v", session) == "{ }" {
	// session为空
}
英文:

Using fmt.Sprintf can help to detect is a struct empty or not. When it is empty the output is { }.

This suggestion is useful for working with JSONs and where the struct doesn't have a specific name.

if fmt.Sprintf(&quot;%v&quot;, session) == &quot;{ }&quot; {
	// session is empty
}

huangapple
  • 本文由 发表于 2015年2月11日 13:37:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/28447297.html
匿名

发表评论

匿名网友

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

确定