使用守卫条件还是捕获异常更好?

9

用保护条款预防异常好还是用捕获异常的方法好?是否有最佳实践?这两种方法的优缺点是什么?

例如,下面的写法是否更好:


在方法开头使用保护条款来检查参数,以避免出现不必要的异常。或者是在代码块中捕获可能发生的异常,并采取相应措施来处理异常。两种方法都有自己的优缺点,选择哪种方法取决于情况和需求。
try
{ 
    param=myArray[3];
}
catch (IndexOutOfRangeException e)
{
    do something...
}

或者这样:
if(myArray.Length < 4)
{
    do something...
}
else
{
    param=myArray[3];
}

感谢大家的回答 :)

4
如果你能避免使用异常,那就更好了,因为异常会增加额外的开销。 - user195488
2
如果你期望在索引3处有某个东西,但实际上没有,那么这是一个错误。最好的做法是关闭你的应用程序并记录该错误,而不是捕获异常或尝试解决它。 - Anirudha
@Anirudh:如果您正在解析一个应该格式正确的文件,并且基于文件中的内容将项目放入和取出集合,那么如果文件无效,唯一需要进行的恢复操作是使 ParseFile 程序例程抛出指示文件无法加载的异常。尝试各种操作并捕获发生的任何异常似乎比在每个地方都使用保护条款更清晰,后者的唯一目的是在出现问题时抛出异常。 - supercat
4个回答

19

使用守卫条款预防异常还是捕获异常更好?

对于像索引越界这样的“愚蠢”异常,始终使用前者。

对于“外因”的异常,始终使用后者。

这两种方法的利弊如何?

对于“愚蠢”异常来说,后者只有缺点而没有优点。它们包括:

  • 与测试相比,异常处理的成本非常高。
  • 异常处理旨在模拟极为罕见的控制流情况;如果潜在地访问超出范围的索引是正常的,则不要编写异常处理程序。
  • 即使异常已被处理,异常也被报告为“第一次机会”异常发送给监听器。许多系统(例如ASP)会监听第一次机会异常,记录所有异常,并将产生大量异常的组件视为有缺陷的,因为它们确实存在缺陷。(我曾经在ASP的常用代码路径中故意引入了一个第一次机会异常,一天后我听到了很多关于此问题的反馈。有缺陷的子系统测试变得疯狂。)
  • 有些异常我称之为“愚蠢”的异常——空指针引用、索引超出范围等——由于它们非常容易避免并且明显危险,应始终将其视为致命错误和不可处理的错误(除非“处理程序”在关闭进程之前记录它们)。不要处理错误,而是消除错误。

最后,您应该阅读我的关于这个主题的文章。

http://ericlippert.com/2008/09/10/vexing-exceptions/


我在编码时使用你的帖子作为指南。我使用代码合约抛出“骨头头”的异常,使用TryX方法而不是抛出“恼人的”异常,并处理所有“外生的”异常。 - Daniel A.A. Pelsmaeker
守卫子句在处理外部异常时可能非常有用,特别是在存在一种廉价的方法可以合理地预测操作成功或失败的情况下。例如,如果要将一吉字节的数据写入一个或多个文件,则在开始之前检查空间是否存在可能会有所帮助。即使事先检查了空间,仍必须准备好后续可能不存在的可能性,但如果能更早地报告问题,就应该尽量这样做。 - supercat

18

使用守卫语句 - 捕获异常会带来高昂的运行时成本,并且一般情况下,为了提高可读性,您应该仅在错误条件下使用异常,而不是正常控制流程。


那是一个错误的情况。如果他期望在索引3处有某些内容,但实际上没有,那就是一个错误。最好关闭应用程序并记录该错误。 - Anirudha
@Anirudh 这取决于情况; 两者都可以是正确的。这取决于数组可能没有那么多值的预期,或者应用程序是否完全依赖于数组至少具有该大小。这决定了您是否应该检查它还是只需让它抛出异常。 - Servy
@anirudha,我很好奇为什么你总是建议在这种类型的异常发生时关闭应用程序?操作可能是可重试的,或者不严重到足以使应用程序关闭。为什么不让顶级异常处理程序记录错误,以便您可以返回并消除错误?有更加用户友好的方法来让开发人员意识到错误,而不是杀死程序。 - Howiecamp

5

警卫条款。您永远不应该使用try/catch来控制流程。捕获异常是昂贵的,应尽量避免。


2
更准确地说,最好避免在正常控制流程中使用try/catch。 - Andy Thomas

4

如果能避免异常,就一定要避免。这是在任何情况下都应该做的。

捕获和处理异常代价昂贵,不应该用于普通控制流程。使用防卫语句更加便捷和廉价。


@AshBurlaczenko - 很难找到一种情况,其中守卫比异常处理更昂贵。 - Justin Niessner
1
我不同意“总是”的说法。通常情况下是最好的,但并非总是如此。例如,如果您需要执行网络请求以确定后续的网络请求是否成功,那么这可能比仅执行第二个网络请求并在无效时捕获异常要昂贵得多。(即,如果您想从FTP服务器中拉取文件,则可能不想检查它是否存在;如果不存在,只需让您的获取请求失败即可。) - Servy
3
但是,你无法执行任何网络请求来告诉你下一个请求是否成功。在第一个请求后,有人可以拔掉网络电缆。网络异常是一种“外生性”异常;它完全不在你的控制范围之内。这与“愚蠢”的异常(例如索引超出范围)完全不同,对于后者,你可以100%准确地知道是否会抛出异常。 - Eric Lippert
1
@Servy:我同意你的观点。在某些情况下,测试异常条件是否可能发生可能比尝试最终失败的操作更便宜。在其他情况下,最好只是尝试操作并处理失败(特别是如果失败的尝试不会产生副作用,并且不会比先前的测试更昂贵)。可以说,后一种情况中的异常可能是“令人烦恼”的异常[即API可能应该提供一个“TryDoSomething”方法,以允许处理预期条件而不抛出异常]。 - supercat
在某些情况下,"始终如一"表示如果其他情况也包括在内,那么通常会是"大多数情况下"吧?也就是说,除非声明中排除了某些情况,否则总是如此。我同意这种说法。 - Ken Kin
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接