【问题标题】:How to check for an empty struct?如何检查空结构?
【发布时间】:2015-04-11 09:47:03
【问题描述】:

我定义了一个结构...

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

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

session = Session{};

那我要查,是不是空的:

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

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

【问题讨论】:

  • Session{} 不是“空”会话;它被初始化为每个字段都是零值。
  • 我很好奇,你为什么不用一个接口来代替呢?如果我要编写一个会话处理程序,我不会期望一个带有空字段的会话。可能导致比其价值更多的问题。
  • 也许您想将此作为答案。我不记得为什么我在 2015 年的这个项目中这样做了 ;-)

标签: struct go


【解决方案1】:

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

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

playground example

由于parsing ambiguity,if 条件中的复合文字周围需要括号。

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

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

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

【讨论】:

  • @kristen 取消引用指针并进行比较。如果session 是非零*Session,则使用if (Session{} == *session {
  • 所以我得到一个错误,struct containing []byte cannot be compared,因为,好吧,我的结构包含一个字节片。
  • @Nevermore 答案适用于具有可比较字段的结构。如果您的结构包含不可比较的值,例如 []byte,那么您需要编写代码来测试所有字段或使用另一个答案中概述的反射包。
  • @Nevermore 提到,== 与切片字段的比较将失败。要比较这些结构,请使用 reflect.DeepEqual 或考虑更专业的内容,如此处讨论的:stackoverflow.com/questions/24534072/…
【解决方案2】:

这里还有 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)
}

这一额外的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)
}

使用指向你的结构的指针

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

var s *Session

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

【讨论】:

  • 很好的答案。谢谢你,icza!
  • 很棒的答案!我认为遵循最后一个选择看起来很地道。
【解决方案3】:

同时使用reflect.deepEqualworks,尤其是当你在结构体中有映射时

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

【讨论】:

  • 使用 reflect.DeepEqual 是一个非常干净的解决方案,但我想知道它是否需要更多的处理时间?我假设它正在比较每个字段,另外你还引入了一个新的导入。
【解决方案4】:

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

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

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

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

【讨论】:

    【解决方案5】:

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

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

    检查这个playground

    您还可以在这里看到,不能以相同的方式比较持有作为指针切片的属性的结构...

    【讨论】:

      【解决方案6】:

      作为其他答案的替代方案,如果您通过case 语句而不是if 执行此操作,则可以使用类似于您最初打算的方式的语法来执行此操作:

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

      playground example

      【讨论】:

        【解决方案7】:

        @cerise-limón 的 answer 是最好的。但是,如果您收到如下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")
            }
        }
        
        // Output: ./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 // <--- here
        }
        
        func main() {
            session := Session{}
            if (Session{}) == session  {
                fmt.Println("zero")
            }
        }
        
        // Output: zero
        

        【讨论】:

          【解决方案8】:

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

          此建议对于使用 JSON 以及 struct 没有特定名称的情况很有用。

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-04-05
            • 1970-01-01
            • 2017-05-28
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多