如何检查空结构?

我定义了一个结构。

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

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

session = Session{};

然后我想检查,如果它是空的:

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

显然这样不行,我该怎么写?

247933 次浏览

可以使用 = = 与零值复合文字进行比较,因为 Session中的所有字段都是 差不多:

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

游乐场的例子

由于 解析模糊性,在 if 条件下,复合文字周围需要括号。

上面使用的 ==适用于所有字段都是 差不多的结构体。如果结构包含一个不可比的字段(片、映射或函数) ,那么必须将这些字段逐个比较到它们的零值。

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

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

也可以使用 深度平等工程,特别是当结构中有 map 时

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

以下是另外三个建议或技巧:

带有附加字段

您可以添加一个额外的字段来判断 struct 是否已被填充或为空。我故意把它命名为 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)
}

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

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

看看这个 游乐场

在这里您还可以看到,保存指针片属性的结构不能以同样的方式进行比较..。

作为其他答案的替代方案,如果使用 case语句而不是 if语句,那么可以使用类似于您最初打算使用的语法来完成此操作:

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

游乐场的例子

简单补充一下,因为我今天解决了同样的问题:

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

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

我没有测试这个性能,但我想这应该比通过 reflect.DeepEqual()比较更快。

@ cerise-limón 的 回答是最好的。

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

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

这个建议对于使用 JSONs 以及 struct没有特定名称的情况非常有用。

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