是否有任何有效理由忽略捕获的异常

时间:2008-10-15 14:02:40

标签: c# exception

哇,我刚从外包开发人员那里获得了一个C#的大项目,在我的代码审查过程中,我的分析工具揭示了它被认为是坏东西的一堆。其中一个令人沮丧的消息是:

Exceptions.DontSwallowErrorsCatchingNonspecificExceptionsRule  : 2106 defects 

开发人员向我保证他们有充分的理由使用所有空的catch块,有时使用空catch块的尝试只是忽略无用的异常并使应用程序不会崩溃。我觉得这是一个警察和完整的BS。我实际查找的一些示例是数据库调用,其中记录被保存到数据库,在这种情况下,如果忽略异常,用户将返回一个正常的提示,认为一切正常,并继续他们的工作。实际上,他们的工作从未得到保存。我认为这绝对是最可怕的错误。在这种情况下,使用空catch块在try中抛出代码是完全错误的。但我的问题是,“这种情况在任何情况下都可以接受吗?”我想不是,但我知道错了。

24 个答案:

答案 0 :(得分:55)

虽然有一些合理的理由可以忽略例外情况;但是,通常只有您能够安全忽略的特定例外情况。如Konrad Rudolph所述,您可能必须捕获并吞下错误作为框架的一部分;正如osp70所指出的那样,您可以忽略的框架可能会产生异常。

在这两种情况下,您可能都知道异常类型,如果您知道类型,那么您应该具有类似于以下内容的代码:

try {
  // Do something that might generate an exception
} catch (System.InvalidCastException ex) {
  // This exception is safe to ignore due to...
} catch (System.Exception ex) {
  // Exception handling
}

对于您的申请,在某些情况下听起来像类似的东西;但是你提供的数据库示例保存返回“OK”,即使存在异常也不是一个好兆头。

答案 1 :(得分:16)

除非我打算对他们做些什么,否则我不会抓住异常。忽略它们并没有对它们做些什么。

答案 2 :(得分:12)

我有时会使用一个WebControl,它不是页面显示的必需条件。如果失败,我不想阻止页面显示。非关键WebControl的一个示例是显示广告的示例。

但是,我记录了错误。我只是不要重新抛出它。

答案 3 :(得分:11)

我的感觉是任何空的Catch Block都需要评论。

忽略某些错误可能是有效的,但您需要记录原因。

此外,您不希望将其设为通用的“catch(Exception e){}”。

您应该只捕获那里预期的特定错误类型,并且已知安全地被忽略。

答案 4 :(得分:8)

另一种情况是,您可以免除捕获和忽略异常,这是您进行单元测试的时候。

public void testSomething(){
    try{
        fooThatThrowsAnException(parameterThatCausesTheException);
        fail("The method didn't throw the exception that we expected it to");
    } catch(SomeException e){
        // do nothing, as we would expect this to happen, if the code works OK.
    }
}

请注意,即使catch块什么也不做,它解释了原因。

话虽如此,最近的测试框架(Junit4& TestNG)允许您指定预期的异常 - 这导致这样的事情...

@Test(expected = SomeException.class)
public void testSomething(){
    fooThatThrowsAnException(parameterThatCausesTheException);
    fail("The method didn't throw the exception that we expected it to");
}

答案 5 :(得分:8)

一般情况下没有,实际上99%的情况都没有,但是

有例外。我工作的一个项目是使用第三方库来处理TWAIN设备。它是错误的,并且在某些硬件组合下会抛出空指针错误。但是,在它没有实际设法扫描文档之前我们从未发现任何情况 - 因此捕获异常是完全合理的。

所以我认为如果你的代码是抛出异常,那么你应该经常检查它,但如果你遇到第三方代码,那么在某些情况下你可能会被迫吃掉异常并继续前进。

答案 6 :(得分:7)

我想从我收集到的最好的答案是,它可以在某种程度上被接受,但应该是有限的。您应该尝试使用另一种替代方案,如果您找不到其他替代方案,您应该充分了解代码如何工作,您可以期望特定的异常类型,而不仅仅是使用全局捕获所有“异常”。忽略此异常的原因的文档应包含在可理解的注释中。

答案 7 :(得分:6)

在关键代码中,可能不是,因为必须始终精确定义程序的状态。就像你的数据库调用示例一样。

在非关键代码中,当然,我们也是这样做的(我们只是在消息框中显示捕获的异常并继续运行)。也许插件或模块失败,但主程序不受影响。也许lexical_cast失败了,并且有一个文本故障呈现在屏幕上。无需停止该过程。

答案 8 :(得分:5)

在某些情况下,可以捕获特定的异常并且不执行任何操作。这是一个简单的例子:

    public FileStream OpenFile(string path)
    {
        FileStream f = null;
        try
        {
            f = new FileStream(path, FileMode.Open, FileAccess.ReadWrite);
        }
        catch (FileNotFoundException)
        {
        }
        return f;
    }

您也可以这样编写方法:

    public FileStream OpenFile(string path)
    {
        FileStream f = null;
        FileInfo fi = new FileInfo(path);
        if (fi.Exists)
        {
            f = new FileStream(path, FileMode.Open, FileAccess.ReadWrite);                
        }
        return f;
    }

在这种情况下,捕获异常(非常)稍微安全一些,因为文件可能会在您检查其存在与打开它之间被删除。

肯定有的原因。在.NET中,异常在计算上是昂贵的,所以你想要避免抛出大量异常的东西。 (在Python中,异常很便宜,使用异常来执行诸如摆脱循环之类的事情是一种常见的习惯。)

但这忽略了特定的异常。这段代码:

catch
{
}

是不可原谅的。

没有理由不捕获try块中的代码将抛出的特定类型异常。天真的开发人员为了捕捉异常而不管类型的第一个原因,“但我不知道可能会抛出什么类型的异常”,这就是问题的答案。

如果您不知道可能会抛出什么类型的异常,则您不知道代码如何失败。如果您不知道代码如何失败,那么您没有理由认为如果可以继续处理就可以。

答案 9 :(得分:5)

我认为如果你有一个空的catch块,你需要记录它为空的原因,以便下一个开发人员知道。例如,在server.transfer上,有时会抛出Web异常。我抓住了这个并评论我们可以忽略它,因为转移呼叫。

答案 10 :(得分:5)

我认为这是可接受的一个例子是关键应用的一些非关键模块(例如,在航天飞机导航系统的声音反馈模块中),对于不应该发生的异常,并且不能处理得更干净。

在这些情况下,您不希望让该异常传播并导致整个应用程序失败(抱歉,没有更多导航系统,我们的哔哔声模块崩溃了,我们真的无能为力)。

编辑说,在任何这些情况下,你至少想要在某个地方记录事件。

答案 11 :(得分:4)

说到例外,总有例外

答案 12 :(得分:4)

是的,在Maxim的帖子中,在某些情况下它是可接受的(不可避免的,必要的)。这并不意味着你必须喜欢它。 2106违规可能太多了,至少他们应该在catch块中添加一条注释,说明为什么可以吞下这个例外。

@Dustin 向公共用户显示任何异常详细信息(堆栈跟踪,行号等)的不良做法。您应该记录异常并显示一般错误。

答案 13 :(得分:3)

这取决于框架。实施不当的框架实际上可能需要这样做。我记得VB6中的一个黑客攻击,无法确定一个集合是否包含一个元素。唯一的方法是尝试检索元素并吞下错误。

答案 14 :(得分:2)

以Java世界为例,可以忽略异常:

String foo="foobar";
byte[] foobytes;

try
{
    foobytes=foo.getBytes("UTF-8");
}
catch (UnsupportedEncodingException uee)
{
    // This is guaranteed by the Java Language Specification not to occur, 
    // since every Java implementation is required to support UTF-8.
}

即使在这种情况下,我也会经常使用

...
catch (UnsupportedEncodingException uee)
{
    // This is guaranteed by the Java Language Specification not to occur, 
    // since every Java implementation is required to support UTF-8.
    uee.printStackTrace();
}

如果虚拟机会疯狂/破坏规则,那么我无能为力,但是对于堆栈跟踪,我至少会注意到它开始下降到疯狂......

答案 15 :(得分:2)

除非你的捕获代码要么

  1. 记录例外
  2. 将异常重新打包到另一个匹配相同抽象的异常中。再扔一次
  3. 以您认为合适的
  4. 的方式处理异常

    您可以忽略该异常,但至少在方法文档中提及预期的异常,因此消费者可以在必要时期望和处理

答案 16 :(得分:2)

我认为最初的问题已经得到了很好的解答,但我想补充的一点是,如果您认为这些外包/签约开发人员的工作质量很差,那么您应该确保您公司的合适人员了解它

可能有可能将其退回以进行返工,可以部分扣留付款,或者同一公司不会再次使用。如果您的公司再次使用承包商,他们可能会找到一种方法来在协议中建立质量要求,假设那里不是那样的。

如果这是内部工作,那么会对产生不合格工作的团队/个人产生影响。也许这只是意味着他们必须在晚上和周末工作来解决它,但他们会以这种或那种方式陷入困境。这同样适用于承包商,甚至可能更多。

请小心谨慎地解释您的职位,并专注于对公司/产品最有利的方面。你不希望它看起来像你只是在抱怨,或者你对外包有某种政治上的反对意见。不要为你做这件事。了解成本,上市时间,客户满意度等。您知道管理类型所关心的所有事情。

答案 17 :(得分:2)

我很想知道这个特定的一个。

Connection con = DriverManager.getConnection(url, "***", "***");

try {
    PreparedStatement pStmt = con.prepareStatement("your query here");

    ... // query the database and get the results
}
catch(ClassNotFoundException cnfe) {
    // real exception handling goes here
}
catch(SQLException sqle) {
    // real exception handling goes here
}
finally {
    try {
        con.close();
    }
    catch {
        // What do you do here?
    }
}

我永远不知道在finally块的最后一次捕获中该怎么做。我以前从未见过close()抛出异常,而且我不太担心它。我只是记录异常并继续前进。

答案 18 :(得分:1)

以这种方式思考 - 如果你花费CPU周期来捕获异常然后吞咽,你忽略了潜在的问题并浪费了CPU。正如许多人所说,应用程序不应该抛出那么多例外,除非你有一些构造不良的东西。

答案 19 :(得分:1)

我们有一个应用程序代表其他应用程序执行大量处理,您可以在其中将一些作业(配置集合)插入到数据库中,然后应用程序将在适当的时间运行它并运行它。我们倾向于在该应用程序中吞下很多异常,因为即使Job1以可怕的方式死于灾难性的错误,我们也希望应用程序能够继续存在以处理Job2。

答案 20 :(得分:1)

我喜欢让几乎所有的异常都冒泡到应用程序处理程序,在那里记录它们并向最终用户显示一般错误消息。但这里需要注意的是,实际上应该没有很多异常。如果您的应用程序抛出许多异常,那么可能存在错误或可以更好地编码的内容。在大多数情况下,我尝试确保我的代码检查高级的异常情况,因为生成异常是昂贵的。

顺便说一句,外包编码通常是一个坏主意。根据我的经验,通常他们是顾问,他们只是支付薪水,并且与项目的成功无关。此外,你放弃了他们的编码标准(除非你在合同中包括它)。

答案 21 :(得分:1)

以下内容仅适用于已检查异常的语言,例如:爪哇:

有时,某个方法会抛出一个已检查的异常,即知道不会发生,例如:某些java API期望编码名称为字符串,如果不支持给定的编码,则抛出UnsupportedEncodingException。但通常我会传递一个我知道支持的字面“UTF-8”,所以我理论上可以写一个空的捕获。

而不是那样做(空捕获)我通常会抛出一个包含“不可能”异常的通用未经检查的异常,或者我甚至声明了我抛出的类ImpossibleException。因为我关于错误条件不可能的理论可能是错误的,在这种情况下我不希望吞下这个例外。

答案 22 :(得分:1)

这是一件非常糟糕的事情。

虽然有效的原因,你可能想要忽略异常 - 如果它以某种方式预期,并且不需要对它做任何事情 - 但是这样做2000次似乎他们只是想要在地毯下扫除他们的例外。

可以吞下异常的示例可能是探测事物......你向某个设备或模块发送消息,但是你不关心它是否实际到达那里。

答案 23 :(得分:0)

我认为,如果您完全了解异常的含义及其可能的后果,那么最好的经验法则是忽略异常。在一些不影响系统其他部分的独立模块的情况下,我认为只要你知道其他任何事情都没有发生,就可以捕获通用的Exception。

IMO更容易知道Java中的后果,因为每个方法都需要声明它可以抛出的所有异常,所以你知道会发生什么,但是在C#中,即使没有记录,也会抛出异常,所以很难要知道一个方法可能抛出的所有可能的异常,并且缺乏这些知识,捕获所有异常通常是一个坏主意。