我目前正在学习go,我的一些代码如下所示:
a, err := doA() if err != nil { return nil, err } b, err := doB(a) if err != nil { return nil, err } c, err := doC(b) if err != nil { return nil, err } ... and so on ...
这在我看来有点不对劲,因为错误检查占用了大多数行。有没有更好的方法来进行错误处理?我是否可以通过一些重构来避免这种情况?
更新: 感谢您的所有答案。请注意,在我的示例中,doB取决于a,doC取决于b,依此类推。因此,大多数建议的重构在这种情况下不起作用。还有其他建议吗?
这是一个常见的投诉,有几个答案。
以下是一些常见的问题:
1-还不错
这是对这些抱怨的非常普遍的反应。实际上,您的代码中有几行额外的代码实际上并不是那么糟糕。这只是廉价的打字,在阅读方面非常容易处理。
2-这实际上是一件好事
这是基于这样的事实,即键入和读取这些额外的行可以很好地提醒您,实际上您的逻辑可能会在此时逃逸,并且您必须撤消放置在其前几行中的所有资源管理。通常将其与异常进行比较,这会以隐式方式破坏逻辑流,迫使开发人员始终牢记隐藏的错误路径。前一段时间,我在这里写了一篇更深入的评论。
3-使用紧急/恢复
在某些特定情况下,您可以通过使用panic已知类型来避免某些工作,然后recover在您的程序包代码问世之前立即使用,将其转换为适当的错误,然后返回该错误。这种技术最常用于展开递归逻辑,例如(un)元帅。
panic
recover
我个人尽量不要滥用过多,因为我与第1点和第2点的关联度更高。
4-稍微重新组织代码
在某些情况下,您可以稍微重新组织逻辑以避免重复。
举一个简单的例子:
err := doA() if err != nil { return err } err := doB() if err != nil { return err } return nil
也可以组织为:
err := doA() if err != nil { return err } return doB()
5-使用命名结果
某些人使用命名结果从return语句中删除err变量。但是,我建议您不要这样做,因为这样做节省的时间很少,降低了代码的清晰度,并且当在纾困返回语句之前定义一个或多个结果时,逻辑上容易产生细微问题。
6-在if条件之前使用语句
正如汤姆·王尔德(Tom Wilde)在下面的评论中很好地提醒的那样,ifGo中的语句在条件之前接受一个简单的语句。因此,您可以执行以下操作:
if
if err := doA(); err != nil { return err }
这是一个很好的Go习惯用法,经常使用。
在某些特定情况下,我宁愿避免以这种方式嵌入该语句,只是为了清晰起见而使其独立存在,但这是一个微妙的个人事情。