有效的try / catch块用法?


21

应该使用catch块来编写逻辑,即处理流控制等吗?还是仅仅为了抛出异常?它会影响代码的效率或可维护性吗?

在catch块中写逻辑有什么副作用(如果有)?

编辑:

我见过一个Java SDK类,其中他们在catch块内编写了逻辑。例如(摘录自java.lang.Integer课堂):

        try {
            result = Integer.valueOf(nm.substring(index), radix);
            result = negative ? new Integer(-result.intValue()) : result;
        } catch (NumberFormatException e) {
            String constant = negative ? new String("-" + nm.substring(index))
                                       : nm.substring(index);
            result = Integer.valueOf(constant, radix);
        }

编辑2

我正在阅读一个教程,他们将其视为在异常内编写例外情况逻辑的优势:

异常使您能够编写代码的主要流程,并处理其他地方的特殊情况。

有什么具体准则,何时在catch块中编写逻辑,何时不编写逻辑?


12
@Coder我认为您过于概括了。尤其是由于使用许多现有的API,您将不可避免。
CodesInChaos

8
@Coder:在Java中,异常通常用作发出信号的机制,这些信号是合法代码流的一部分。例如,如果您尝试打开一个文件而失败,则Java库会通过引发异常来告诉您,因为导致打开文件的API没有其他返回错误的机制。
JeremyP 2012年

4
@Coder取决于。我认为在执行IO或解析复杂数据(几乎总是正确格式化)时,抛出异常来表示错误会使代码更简洁。
CodesInChaos 2012年

1
@Coded是的,该方法的执行可以告诉您它无法执行所要求的操作。但是异常不应该用于流控制,这就是为什么C#还具有不会抛出int.TryParse方法的原因。
安迪

1
@Coder:那完全是垃圾。在一个代码级别上异常的事情在另一个代码上可能并非异常,或者您可能希望例如在继续之前显示或添加错误或调试信息。
DeadMG

Answers:


46

您引用的示例是由于API设计不佳(没有一种干净的方法可以检查String是否为有效整数,除非尝试对其进行解析并捕获异常)。

在技​​术层面上,throw和try / catch是控制流构造,使您可以跳上调用堆栈,仅此而已。向上调用堆栈隐式地连接源中未紧密结合的代码,这不利于可维护性。因此,仅在需要这样做时才使用它,而替代方法甚至更糟。在被广泛接受的情况下选择更糟是错误处理(特殊返回代码是需要检查和手工传递到调用堆栈的每一层)。

如果您遇到其他选择更糟的情况(并且您确实仔细考虑了所有选择),那么我想说使用throw and try / catch进行控制流程就可以了。教条不是判断的好替代品。


1
+1,好点。我认为某些处理在捕获中可能是有效的,例如准备错误消息和错误日志记录。可以释放诸如db连接和(.NET)COM引用之类的昂贵资源,将其添加到Final模块中。
NoChance 2012年

@EmmadKareem我曾考虑过释放资源,但是通常即使没有错误,您仍然必须在某些时候释放那些资源。因此,您可能会重复自己。当然准备日志消息是可以接受的。
Scarridge

@MichaelBorgwardt我认为API的设计并不是那么糟糕(尽管可能会更好)。尝试解析无效的字符串显然是一种错误情况,因此正是您提到的“被广泛接受的情况”。同意,一种确定字符串在语法上是否正确的方法很方便(这可能会更好)。但是,不可能强迫所有人在实际解析之前调用此方法,并且我们需要处理错误。混合实际结果和错误代码是不舒服的,因此您仍然会抛出异常。但也许是运行时异常。
围巾岭

3
最后一句话+1。
FrustratedWithFormsDesigner 2012年

2
@scarfridge:我完全同意你的观点:)我没有用布尔值返回isInteger(String)方法来表示“可怜的API设计”
Michael Borgwardt 2012年

9

这往往取决于语言和范式。

我的大部分工作都是用Java(有时是C ++)完成的。趋势是仅将例外用于特殊情况。关于Stack Overflow,一些关于Java异常的性能开销的问题,正如您所看到的,这并不是可以忽略的。还有其他一些问题,例如代码的可读性和可维护性。如果使用得当,异常可以将错误处理委派给适当的组件。

但是,在Python中,要求宽恕比允许权限容易的想法成为主流。在Python社区中,这称为EAFP,它与C风格语言中的“跨越式外观”(LBYL)方法形成对比。


2
+1用于提及开销(有例外)。我使用.NET并且(至少在我的机器上)甚至在某种情况下甚至与其他正常操作相比,甚至在某种情况下都会发生异常时,我什至会感觉到。
NoChance 2012年

2
@EmmadKareem仅当您连接了调试器时。没有调试器,您每秒可以抛出数千个。
CodesInChaos

@CodeInChaos,您是正确的。我怎么知道,我写的干净的代码在运行时从未得到任何帮助:)
NoChance 2012年

@EmmadKareem取决于编写网络应用程序的方式,处理连接或协议失败的情况除外。在这样的应用程序中,异常的发生速度必须相当快,以避免普通的DoS攻击。
CodesInChaos 2012年

@CodeInChaos,这是一个很好的认识。我在最后一次评论中开玩笑。
NoChance 2012年

9

这不是考虑try / catch块的最佳方法。考虑try / catch块的方法是这样的:发生了故障,哪里是处理此特定故障的最佳位置。那可能是代码的下一行,可能是调用链的二十个级别。无论在哪里,它都应该在哪里。

catch块的作用取决于错误以及您可以采取的措施。有时可以简单地将其忽略(无法删除其中没有大量数据的暂存文件),有时将其用于将函数返回值设置为true或false(java的int parse方法),有时您将退出程序。所有这些都取决于错误。

要理解的重要部分是:catch block ==我知道如何处理。


6

捕获块仅应用于处理异常,而不能用于控制流。在任何情况下,如果您想使用异常来管理流程,最好检查一下先决条件。

处理带有异常的流很慢并且在语义上是不正确的。


4
我知道您在说什么,但值得注意的是,该异常所做的全部只是处理程序流-只是它应仅用于控制异常错误流……
2012年

何时在catch块中编写逻辑以及何时不编写逻辑没有任何具体准则?
HashimR 2012年

4
Java的Number解析器和Text格式化程序是有问题的异常用法的著名示例:检查先决条件(字符串代表可解析的数字)的唯一合理方法是尝试对其进行解析,如果它失败并显示异常,则您有什么选择?在实践中?您必须捕获异常并使用它来管理流程,无论它在语义上是不正确的。
乔纳斯·普拉卡

@JoonasPulakka,我认为您的评论带有数字解析器和文本格式化程序,可以完全解决该问题
gnat 2012年

5

应该使用catch块来编写逻辑,即处理流控制等吗?还是仅仅为了抛出异常?它会影响代码的效率或可维护性吗?

首先,忘记“例外应在特殊情况下使用”的概念。也不要担心效率,直到您的代码执行得令人无法接受并且已经确定要知道问题出在哪里。

在没有条件的情况下以简单的顺序执行操作时,代码最容易理解。异常通过从常规流中删除错误检查来提高可维护性。执行是遵循99.9%的时间还是50%的时间还是20%的时间正常流都没有关系。

当函数无法返回值,过程无法完成预期的操作或构造函数无法生成可用对象时,引发异常。这使程序员可以假设函数总是返回可用的结果,过程总是完成请求的操作,构造的对象总是处于可用状态。

我在异常处理代码中看到的最大问题是,程序员应该在不应该编写try / catch块的情况下编写它们。例如,在大多数Web应用程序中,如果数据库请求引发异常,则控制器中无法执行任何操作。最高级别的一个通用catch子句就足够了。然后,控制器代码可以很高兴地忽略磁盘崩溃或数据库脱机等问题的可能性。


1

捕获块不应用于编写代码逻辑。它们仅应用于处理错误。一个示例是(1)清理所有分配的资源,(2)打印有用的消息,(3)正常退出。

确实可以滥用异常并导致不良goto后果。但这并不会使它们无用。如果使用得当,它们可以改善代码的许多方面。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.