测试嵌套结构中的 nil 值
Test for nil values in nested stucts
我在 go 中有一个深度嵌套的结构。这些是由 json 解组器构建的。
但是这个结构中有相当多的字段 'omitifempty' 所以我用一个可以在不同地方有 nills 的结构结束 op。
示例(真正的嵌套更深,更大:400 行结构):
package main
import "fmt"
type Foo struct {
Foo string
Bar *Bar
}
type Bar struct {
Bar string
Baz *Baz
}
type Baz struct {
Baz string
}
func main() {
f1 := Foo{Foo: "f1"}
f2 := Foo{Foo: "f2", Bar: &Bar{Bar: "br2"}}
f3 := Foo{Foo: "f3", Bar: &Bar{Bar: "br3", Baz: &Baz{Baz: "bz3"}}}
fmt.Println(f3.Bar.Baz.Baz) //-> bz3
fmt.Println(f2.Bar.Baz.Baz) //-> panic: runtime error: invalid memory address or nil pointer dereference
fmt.Println(f1.Bar.Baz.Baz) //-> panic: runtime error: invalid memory address or nil pointer dereference
//so far so good, but
//is there a more generic way to do this kind of testing?
if f2.Bar != nil && f2.Bar.Baz != nil {
fmt.Println(f2.Bar.Baz.Baz)
} else {
fmt.Println("something nil")
}
}
问题是是否有更通用的方法来测试引用树中的某个节点是否为零?我需要得到很多不同的项目,写所有这些 if 语句会很痛苦。
哦,速度是个问题。
如果你想避免'reflect
' (reflection, as a "generic" way to test fields of any struct
, a bit as in "Get pointer to value using reflection" or in this gist:它更慢),最可靠的方法是在Foo
上实现方法以便return正确的值
func (foo *Foo) BarBaz() string {
if f2.Bar != nil && f2.Bar.Baz != nil {
return f2.Bar.Baz.Baz
} else {
fmt.Println("something nil")
return "" // for example
}
}
如果要编写很多这样的函数,也许go 1.4 go generate
命令可以帮助生成其中的大部分。
一种优雅的处理方式(在我看来)是将 getter 添加到用作指针的结构中。 "technique" 也被 protobuf 生成的 Go 代码使用,它允许方法调用的自然链接,而不必担心由于 nil
指针导致的运行时恐慌。
在你的例子中,Bar
和 Baz
结构被用作指针,所以用吸气剂武装它们。重点是添加带有指针接收者的方法,首先必须检查接收者是否为nil
。如果是,return 结果类型的零值。如果不是,则继续 return 结构的字段:
func (b *Bar) GetBaz() *Baz {
if b == nil {
return nil
}
return b.Baz
}
func (b *Baz) GetBaz() string {
if b == nil {
return ""
}
return b.Baz
}
带有指针接收器的方法的好处是您可以使用 nil
接收器调用它们。它不会导致运行时恐慌,直到您尝试引用它们的字段,而我们不会这样做,这就是为什么我们首先检查接收者是否为 nil
(最终,接收者充当正常参数——这绝不是错误将 nil
作为指针参数传递)。
有了上面的 getter,使用就简化到这样了,在这些例子中都没有发生运行时恐慌:
fmt.Println(f3.Bar.GetBaz().GetBaz()) // naturally no panic
fmt.Println(f2.Bar.GetBaz().GetBaz()) // No panic
fmt.Println(f1.Bar.GetBaz().GetBaz()) // No panic
if baz := f2.Bar.GetBaz(); baz != nil {
fmt.Println(baz.GetBaz())
} else {
fmt.Println("something nil")
}
在 Go Playground 上试用。
我在 go 中有一个深度嵌套的结构。这些是由 json 解组器构建的。
但是这个结构中有相当多的字段 'omitifempty' 所以我用一个可以在不同地方有 nills 的结构结束 op。
示例(真正的嵌套更深,更大:400 行结构):
package main
import "fmt"
type Foo struct {
Foo string
Bar *Bar
}
type Bar struct {
Bar string
Baz *Baz
}
type Baz struct {
Baz string
}
func main() {
f1 := Foo{Foo: "f1"}
f2 := Foo{Foo: "f2", Bar: &Bar{Bar: "br2"}}
f3 := Foo{Foo: "f3", Bar: &Bar{Bar: "br3", Baz: &Baz{Baz: "bz3"}}}
fmt.Println(f3.Bar.Baz.Baz) //-> bz3
fmt.Println(f2.Bar.Baz.Baz) //-> panic: runtime error: invalid memory address or nil pointer dereference
fmt.Println(f1.Bar.Baz.Baz) //-> panic: runtime error: invalid memory address or nil pointer dereference
//so far so good, but
//is there a more generic way to do this kind of testing?
if f2.Bar != nil && f2.Bar.Baz != nil {
fmt.Println(f2.Bar.Baz.Baz)
} else {
fmt.Println("something nil")
}
}
问题是是否有更通用的方法来测试引用树中的某个节点是否为零?我需要得到很多不同的项目,写所有这些 if 语句会很痛苦。 哦,速度是个问题。
如果你想避免'reflect
' (reflection, as a "generic" way to test fields of any struct
, a bit as in "Get pointer to value using reflection" or in this gist:它更慢),最可靠的方法是在Foo
上实现方法以便return正确的值
func (foo *Foo) BarBaz() string {
if f2.Bar != nil && f2.Bar.Baz != nil {
return f2.Bar.Baz.Baz
} else {
fmt.Println("something nil")
return "" // for example
}
}
如果要编写很多这样的函数,也许go 1.4 go generate
命令可以帮助生成其中的大部分。
一种优雅的处理方式(在我看来)是将 getter 添加到用作指针的结构中。 "technique" 也被 protobuf 生成的 Go 代码使用,它允许方法调用的自然链接,而不必担心由于 nil
指针导致的运行时恐慌。
在你的例子中,Bar
和 Baz
结构被用作指针,所以用吸气剂武装它们。重点是添加带有指针接收者的方法,首先必须检查接收者是否为nil
。如果是,return 结果类型的零值。如果不是,则继续 return 结构的字段:
func (b *Bar) GetBaz() *Baz {
if b == nil {
return nil
}
return b.Baz
}
func (b *Baz) GetBaz() string {
if b == nil {
return ""
}
return b.Baz
}
带有指针接收器的方法的好处是您可以使用 nil
接收器调用它们。它不会导致运行时恐慌,直到您尝试引用它们的字段,而我们不会这样做,这就是为什么我们首先检查接收者是否为 nil
(最终,接收者充当正常参数——这绝不是错误将 nil
作为指针参数传递)。
有了上面的 getter,使用就简化到这样了,在这些例子中都没有发生运行时恐慌:
fmt.Println(f3.Bar.GetBaz().GetBaz()) // naturally no panic
fmt.Println(f2.Bar.GetBaz().GetBaz()) // No panic
fmt.Println(f1.Bar.GetBaz().GetBaz()) // No panic
if baz := f2.Bar.GetBaz(); baz != nil {
fmt.Println(baz.GetBaz())
} else {
fmt.Println("something nil")
}
在 Go Playground 上试用。