如何检查嵌套指针中的值

How to check value in nested pointer

如何轻松检查嵌套指针?

type inner1 struct {
    value string
}

type inner2 struct {
    value *inner1
}

type outter struct {
    value *inner2
}

我有这样的数据:

o := &outter{
    value: &inner2{
        value: &inner1{
            value: "I need this data",
        },
    },
} 

如果我想从中获取数据,我需要检查 nil 指针。

func printValue(o *outter) (string, bool) {
    if o.value != nil {
        v1 := o.value
        if v1.value != nil {
            v2 := v1.value
            return v2.value, true
        }
    }
    return "", false
}

是的,我可以做到。但在我的真实场景中,这个嵌套指针部分要长得多。我会更喜欢另一种方式。

我已经检查了这个答案,。但我需要替代方案。

我怎样才能做到这一点。有什么更有效和更好的选择吗?

我可以使用紧急恢复方法。这将解决我的问题。但这对我来说似乎很老套。

func printValue(o *outter) (string, bool) {
    defer func() (string, bool) {
        if r := recover(); r != nil {
            return "", false
        }
        return "", false
    }()
    return o.value.value.value, true
}

Panic and recover 用于特殊情况。检查指针是否为 nil 通常不是,所以你应该远离它。使用 if 语句检查指针是否为 nil 比使用 recover() 引入延迟函数要简洁得多。特别是因为这个 recover() 将阻止所有其他类型的恐慌,即使是那些由其他原因而不是试图取消引用 nil 指针引起的恐慌。 (使用 deferrecover() 也比使用 if 的简单 nil 检查慢。)

如果数据总是需要非nil值,你应该考虑不为它使用指针。如果数据总是需要非 nil 但由于某种原因你需要使用指针,那么如果任何指针仍然是 [=15,那么这可能是让你的代码恐慌的有效案例=].

如果可能是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 的内部结构不需要更新客户端=33=]改变,只是outter.Get()方法)。

类似的方法是添加仅 return 接收者结构字段的方法:

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.

上试试这个

没有简单的方法。您可以恢复方式,但这不是惯用的 IMO,您应该检查您是否没有发现其他不相关的错误。

我更喜欢一个 if 而不是多个。我不认为下面的代码丑陋或冗长。

func printValue(o *outter) (string, bool) {
    if o.value != nil and o.value.value != nil and o.value.value.value != nil {
        return *o.value.value.value, true
    }
    return "", false
}

使用反射

func getFieldByName(v interface{}, fields string, sep string) interface{} {
    r := reflect.ValueOf(v)
    s := strings.Split(fields, sep)
    for _, field := range s {
        r = reflect.Indirect(r)
        if r.IsValid() {
            r = reflect.Indirect(r).FieldByName(field)
        } else {
            return nil
        }
    }
    if r.IsValid() {
        return r.Interface()
    }
    return nil
}

使用紧急恢复

type safeGetFn func() interface{}

func safeGet(f safeGetFn, d interface{}) (ret interface{}) {
    defer func() interface{} {
        if r := recover(); r != nil {
            ret = d
        }
        return ret
    }()
    return f()
}

示例反射:https://play.golang.org/p/m0_zQqJm7MY

示例紧急恢复:https://play.golang.org/p/PNPPBXCvHxJ