恐慌和恢复是针对特殊情况的。检查指针是否为nil 通常不是,所以你应该远离它。使用if 语句检查指针是否为nil 比使用recover() 引入延迟函数要干净得多。尤其是这个recover() 将停止所有其他类型的恐慌,即使是那些由于试图取消引用nil 指针之外的其他原因而导致的恐慌。 (使用defer 和recover() 也比使用if 进行简单的nil 检查要慢。)
如果始终要求数据是非nil 值,则应考虑不使用指针。如果数据总是需要非nil,但由于某种原因你需要使用指针,那么如果任何指针仍然是nil,这可能是让你的代码恐慌的有效情况。
如果它可能是nil,那么你必须检查nil 的情况并适当地处理它。你必须明确这一点,Go 不会帮你省略这个检查。
为了避免在每个地方都检查nil,实用函数或方法是合理的。请注意,即使接收者是nil,也可以调用方法,这在这种情况下可能很有用。
例如,您可以附加以下方法:
func (i *inner1) Get() (string, bool) {
if i == nil {
return "", false
}
return i.value, true
}
func (i *inner2) Get() (string, bool) {
if i == nil {
return "", false
}
return i.value.Get()
}
func (o *outter) Get() (string, bool) {
if o == nil {
return "", false
}
return o.value.Get()
}
请注意,每个Get() 方法都需要检查一个指针,无论数据结构有多复杂。
测试它:
o := &outter{
value: &inner2{
value: &inner1{
value: "I need this data",
},
},
}
fmt.Println(o.Get())
o.value.value = nil
fmt.Println(o.Get())
o.value = nil
fmt.Println(o.Get())
o = nil
fmt.Println(o.Get())
输出(在Go Playground上试试):
I need this data true
false
false
false
上述解决方案隐藏outter 的内部结构,这对于使用outter 的用户很有用(如果outter 的内部结构发生变化,则不需要更新客户端,只需outter.Get()方法)。
类似的方法是添加只返回接收者结构字段的方法:
func (i *inner1) Value() (string, bool) {
if i == nil {
return "", false
}
return i.value, true
}
func (i *inner2) Inner1() *inner1 {
if i == nil {
return nil
}
return i.value
}
func (o *outter) Inner2() *inner2 {
if o == nil {
return nil
}
return o.value
}
这种方法要求客户了解outter 的内部结构,但同样在使用时不需要任何nil 检查:
o := &outter{
value: &inner2{
value: &inner1{
value: "I need this data",
},
},
}
fmt.Println(o.Inner2().Inner1().Value())
o.value.value = nil
fmt.Println(o.Inner2().Inner1().Value())
o.value = nil
fmt.Println(o.Inner2().Inner1().Value())
o = nil
fmt.Println(o.Inner2().Inner1().Value())
输出是一样的。在Go Playground 上试试这个。