空的异常捕获块为什么是一个坏主意?

214

我刚刚看到一个try-catch问题,人们(包括Jon Skeet)说空的catch块是一个非常糟糕的想法?为什么会这样?难道没有任何情况下空的catch不是错误的设计决策吗?

我的意思是,例如,有时您想要从某个地方(webservice、数据库)获取一些附加信息,您真的不在乎是否能够获取到这些信息。所以您尝试去获取它,如果发生了任何事情,没关系,我只需添加一个“catch(Exception ignored){}”,就可以了。


61
我会尽力进行翻译,以下是内容:我会写一条评论来解释为什么它应该为空。 - Mehrdad Afshari
5
或者至少记录下它被捕捉了的信息。 - matt b
2
@ocdecio:避免在清理代码中使用它,而不是完全避免使用它。 - John Saunders
1
@ocdecio - try..finally 比 try..空 catch 更好,因为错误会继续向上堆栈传递,要么由框架处理,要么终止程序并显示给用户 - 这两种结果都比“静默失败”更好。 - Nate
1
我同意在你的情况下完全抑制异常可能是正确的,但如果你能捕获到预期要抑制的实际异常子类型(例如IOException),那将更加清晰。像你一样,我也将其命名为“ignored”而不是“ex”。 - Nelson
显示剩余15条评论
20个回答

321
通常情况下,空的try-catch是一个不好的做法,因为你在默默地忽略一个错误条件,然后继续执行。偶尔这可能是正确的做法,但往往表明开发人员看到了异常,不知道该怎么处理,所以使用空catch来消除问题。
这相当于在引擎警告灯上贴上黑色胶带的编程等效物。
我认为,如何处理异常取决于您正在工作的软件层次结构:Exceptions in the Rainforest

4
在那些真正罕见的情况下,如果我真的不需要例外,并且由于某些原因无法进行日志记录,我会确保注释说明这是有意为之——以及为什么不需要它,并重申如果在该环境中可行的话,我仍然更喜欢将其记录下来。基本上,我的注释让工作量比在该情况下进行日志记录所需的工作还要多。幸运的是,只有两个客户会遇到这个问题,而且只有在从他们的网站发送非关键电子邮件时才会出现。 - John Rudy
41
也喜欢这个比喻-就像所有规则一样,总有例外。例如,如果您从不打算进行定时录制(对于那些记得什么是VCR的老年人),在VCR上用黑色胶带覆盖闪烁的时钟完全没有问题。 - Michael Burr
3
像任何偏离通行做法的行为一样,如果适当就去做,并记录下来,以便下一个人知道你做了什么和为什么这样做。 - David Thornley
6
至少,你应该包含一条详细的评论,解释为什么要屏蔽这些异常情况。 - Ned Batchelder
2
你曾经说过try-catch是个坏主意!实际上它是个好主意。想象一下一个没有用户交互的系统!而且应该保持运行!在我的情况下,我的系统应该始终保持工作状态,24/7。所以它不能停机。在某些情况下,我尝试写入文件,我不关心是否成功完成,我关心的是整个系统的持续运行。 - MBH
显示剩余8条评论

38

它们一般来说是个糟糕的想法,因为很少有情况下一个故障(更普遍的是异常条件)不需要任何响应来妥善处理。此外,空的catch块是使用异常引擎进行错误检查的人常用的工具,而他们本应该采取预防措施。

说它总是不好是不正确的...很少有事情是总是如此。可能有某些情况下你并不关心是否发生了错误,或者错误的存在表示你根本无法采取任何措施(例如,在将先前的错误写入文本日志文件时,如果出现IOException, 表示您无论如何都无法写出新错误)。


12

我不会夸大地说那些使用空catch块的人是糟糕的程序员并且不知道自己在做什么...

如果需要,我会使用空catch块。有时候我正在使用的库的程序员并不知道自己在做什么,并在没有必要的情况下抛出异常。

例如,考虑一些http服务器库,如果客户端断开连接并且无法发送index.html,那么服务器抛出异常对我来说并不重要。


8
你肯定过于概括了。仅仅因为不需要它,并不意味着其他人都不需要。即使绝对没有办法对此进行响应,也有某个地方的某个人需要收集有关废弃连接的统计数据。所以妄下断言“库的程序员不知道自己在做什么”是相当无礼的。 - Ben Voigt

11

只有在真的发生异常或者出现超出正常情况的情况下,才应该抛出异常。一个空的catch块基本上是在说“出现了糟糕的情况,但我并不关心。” 这是一个不好的想法。

如果你不想处理异常,请让它向上传递,直到达到某个可以处理它的代码。如果没有任何代码可以处理异常,那么它应该导致应用程序崩溃。


4
有时候你知道做某件事情不会对任何事情产生影响,那么就去做吧,并留下评论,这样下一个人就不会认为你是无能而把它看作错误。 - David Thornley
1
是的,每件事都有其时机和场合。总的来说,我认为空的 catch 块是好的这种情况只是例外 - 这是一个罕见的情况,你真正想要忽略一个异常(通常,在我看来,这是异常使用不当的结果)。 - Reed Copsey
2
@David Thornley:如果您不检查异常以确定它是预期且无意义的错误还是应该向上传播的错误,那么您怎么知道它不会影响任何东西呢? - Dave Sherohman
2
@Dave:虽然 catch (Exception) {} 是个不好的想法,但是 catch (SpecificExceptionType) {} 可能完全没问题。程序员使用 catch 子句中的类型信息检查了异常。 - Ben Voigt

10

我认为如果你捕获了一个你知道只针对特定原因而引发的特定异常类型,并且你期望那个异常,而且确实不需要对它做任何处理,那么这是可以的。

但即使是在这种情况下,也可能需要一个调试信息。


9

有些情况下这样做是可以被证明是合理的。在Python中,您经常看到这种构造:

try:
    result = foo()
except ValueError:
    result = None

因此,根据您的应用程序,这样做可能是可以接受的:

result = bar()
if result == None:
    try:
        result = foo()
    except ValueError:
        pass # Python pass is equivalent to { } in curly-brace languages
 # Now result == None if bar() returned None *and* foo() failed

在最近的.NET项目中,我需要编写代码来枚举插件DLL以找到实现特定接口的类。相关的代码片段(使用VB.NET编写)如下:
    For Each dllFile As String In dllFiles
        Try
            ' Try to load the DLL as a .NET Assembly
            Dim dll As Assembly = Assembly.LoadFile(dllFile)
            ' Loop through the classes in the DLL
            For Each cls As Type In dll.GetExportedTypes()
                ' Does this class implement the interface?
                If interfaceType.IsAssignableFrom(cls) Then

                    ' ... more code here ...

                End If
            Next
        Catch ex As Exception
            ' Unable to load the Assembly or enumerate types -- just ignore
        End Try
    Next

即使在这种情况下,我也承认将失败记录在某个地方可能会有所改善。

在看到你的回答之前,我曾经提到过log4net作为其中一个实例。 - Scott Lawrence

9

根据Josh BlochEffective Java的第65条建议: 不要忽略异常:

  1. 一个空的catch块会使异常处理失去意义。
  2. 至少,catch块应包含注释,解释为什么可以忽略该异常。

7

空的catch块通常是由于编码人员不知道自己在做什么。在我们的组织中,空的catch块必须包含一条注释,说明为什么对异常不做任何处理是一个好主意。

另外,大多数人不知道try{}块可以跟随catch{}或finally{},只需要一个。


1
+1 我会强调最后一句话中的“或”以增加影响力。 - MPritchard
第二段可能与语言有关,对吧? - David Z
3
当然,除非您在谈论IE6或IE7;-)...在这种情况下,catch是必需的,或者finally不会执行。(顺便说一句,这个问题在IE8中得到了修复) - scunliffe
Bug 184: 在try-catch-finally中使用catch语句 - scunliffe
非常正确。可能值得强调一下编程语言。我认为 .net 是可以的。 - MPritchard
问题或标签中都没有列出任何语言。在C++中,你是错的。C++中没有try {} finally {},这主要是因为C++有更好的清理方式。 - David Thornley

4

如果您不知道在catch块中该怎么做,您可以将此异常记录下来,但不要让它保持空白。

        try
        {
            string a = "125";
            int b = int.Parse(a);
        }
        catch (Exception ex)
        {
            Log.LogError(ex);
        }

3
一个空的catch块本质上是在说“我不想知道会抛出什么错误,我只是要忽略它们。”这与VB6的On Error Resume Next类似,但是在异常抛出后try块中的任何内容都将被跳过。这并不能帮助解决任何问题。

我实际上会说,“On Error Resume Next”更糟糕——它只是尝试执行下一行代码。而一个空的catch块会跳过try语句块的结束括号。 - MPritchard
好的,我应该在我的回复中注明这一点。 - Powerlord
1
这并不完全正确,如果你有一个空的try...catch块,并且指定了特定的异常类型,那么它将只忽略这种类型的异常,这可能是合理的... - Meta-Knight
但是,如果您知道正在抛出特定类型的异常,似乎您可能已经有足够的信息以一种避免使用try-catch来处理情况的方式编写逻辑。 - Scott Lawrence
@Scott:某些语言(比如Java)具有已检查异常......强制你编写catch块的异常。 - Powerlord
显示剩余2条评论

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