如何检查一个空的结构?


110

我定义一个结构...

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

有时我给它分配一个空会话(因为nil不可能)

session = Session{};

然后我要检查是否为空:

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

显然这是行不通的。我该怎么写?


4
会话{}不是“空”会话;使用每个字段的零值进行初始化。
保罗·汉金

Answers:


177

您可以使用==与零值复合文字进行比较,因为所有字段都是可比较的

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

游乐场的例子

由于存在歧义,因此在if条件下,在组合文字周围需要括号。

==上面的用法适用于所有字段都是可比较的结构。如果结构包含不可比较的字段(切片,映射或函数),则必须将字段与它们的零值进行一一比较。

比较整个值的另一种方法是比较在有效会话中必须设置为非零值的字段。例如,如果在有效会话中玩家ID必须为!=“”,请使用

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

4
@kristen取消引用指针并进行比较。如果session为非零*Session,则使用if (Session{} == *session {
松饼顶

3
我收到一个错误,struct containing []byte cannot be compared因为,好,我的结构包含一个字节片。
再也没有

14
@Nevermore答案适用于具有可比较字段的结构。如果您的结构包含不可比的值,例如[] byte,那么您需要编写代码以测试所有字段或使用其他答案中概述的反射包。
松饼顶部

2
如@Nevermore所述,==与切片字段进行比较将失败。为了比较这些结构,请使用reflect.DeepEqual或考虑一些更专门的内容,例如此处讨论的内容:stackoverflow.com/questions/24534072/…–
asgaines,

“在[如果条件]中分析歧义”节省了我的时间,谢谢:) coz当我在fmt.Println(session == Session {})中尝试使用它时,它可以工作。
弗朗瓦

37

这里还有3条建议或技巧:

有一个额外的领域

您可以添加一个附加字段,以告知该结构是否已填充或为空。我故意命名它ready,并不是empty因为a的零值boolfalse,所以如果您创建一个新的结构,如Session{}它的ready字段将自动出现false,它将告诉您真相:该结构尚未准备好(它是空的)。

type Session struct {
    ready bool

    playerId string
    beehive string
    timestamp time.Time
}

初始化结构时,必须设置readytrueisEmpty()不再需要您的方法(尽管您可以根据需要创建一个方法),因为您可以只测试ready字段本身。

var s Session

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

bool随着结构的变大或它包含不可比较的字段(例如,切片map和函数值),此附加字段的意义也随之增加。

使用现有字段的零值

这与之前的建议类似,但是它使用现有字段的零值,当结构不为空时,该字段被视为无效。可用性取决于实现。

例如,如果在您的示例中您playerId不能为空string "",则可以使用它来测试您的结构是否为空,如下所示:

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)
}

使用指向您的结构的指针

第二个建议是对结构使用Pointer *Session。指针可以具有nil值,因此您可以对其进行测试:

var s *Session

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

好答案。谢谢,icza!
Evgeny Goldin

很棒的答案!我认为遵循最后的选择看起来很惯用。
DeivinsonTejeda

19

使用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")
  } 
}

2
使用reflect.DeepEqual是一个非常干净的解决方案,但我想知道是否需要更多的处理时间?我认为它正在比较每个字段,再加上您引入一个新的导入。
thurt

4

请记住,使用指向结构的指针时,您必须取消引用变量,而不能将其与指向空结构的指针进行比较:

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

检查这个操场

同样在这里您可以看到,拥有属性的结构是指针的一部分,无法以相同的方式进行比较...


0

作为其他答案的替代方法,可以通过类似于语句的语法(case而不是通过:)来实现此目的if

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

游乐场的例子


0

快速补充一下,因为我今天解决了同一问题:

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

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

我没有针对性能进行测试,但是我想这应该比通过进行比较要快reflect.DeepEqual()


-1

也许像这样

package main

import "fmt"
import "time"

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

func (s Session) Equal(o Session) bool {
   if(s.playerId != o.playerId) { return false }
   if(s.beehive != o.beehive) { return false }
   if(s.timestamp != o.timestamp) { return false }
   return true
}

func (s Session) IsEmpty() bool {
    return s.Equal(Session{})
}

func main() {
    x := Session{}
    if x.IsEmpty() {
       fmt.Print("is empty")
    } 
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.