代码之家  ›  专栏  ›  技术社区  ›  stephenbayer

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

  •  49
  • stephenbayer  · 技术社区  · 16 年前

    哇,我刚刚从外包开发人员那里得到了一个大型的C语言项目,在我进行代码审查时,我的分析工具发现了它认为是不好的东西。更令人沮丧的信息之一是:

    Exceptions.DontSwallowErrorsCatchingNonspecificExceptionsRule  : 2106 defects 
    

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

    24 回复  |  直到 9 年前
        1
  •  55
  •   Community CDub    8 年前

    虽然有一些合理的理由可以忽略异常;但是,通常只有特定的异常才可以安全地忽略。如所指出的 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”,即使有异常也不是很好的标志。

        2
  •  16
  •   itsmatt    16 年前

    除非我打算做些什么,否则我不会抓住例外。忽视他们并不是在做什么。

        3
  •  12
  •   DavidRR Chris Brandsma    9 年前

    我有时使用一个网页显示时不强制使用的WebControl。如果失败,我不想阻止页面显示。非关键WebControl的一个例子是显示广告的WebControl。

    但是,我会记录错误。我只是不把它再扔了。

        4
  •  11
  •   Clayton    16 年前

    我觉得任何空的catch块都需要注释。

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

    此外,您也不希望将其设置为通用的“catch(exception e)”。

    您应该只捕获预期存在的特定错误类型,并且可以安全地忽略它。

        5
  •  8
  •   Cruachan    16 年前

    一般不会,事实上99%的情况下不会,但是

    也有例外。我做过一个项目,我们使用第三方库来处理一个twain设备。这是错误的,在某些硬件组合下会抛出一个空指针错误。然而,我们从来没有发现过这样的情况,即在文档扫描之前,它实际上没有扫描文档——因此捕获异常是完全合理的。

    所以我认为,如果是你的代码引发了异常,那么你应该经常检查它,但是如果你坚持使用第三方代码,那么在某些情况下,你可能会被迫接受异常并继续前进。

        6
  •  8
  •   belugabob    16 年前

    另一种情况是,当您进行单元测试时,可以原谅捕获和忽略异常。

    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");
    }
    
        7
  •  7
  •   stephenbayer    16 年前

    我想从我收集到的最好的答案是它可以被接受,但应该是有限的。您应该尝试使用另一种替代方法,如果您找不到另一种替代方法,那么您应该对代码的工作方式有足够的了解,这样您就可以期望特定的异常类型,而不仅仅是使用覆盖的捕获所有“异常”。忽略此异常的原因的文档应以可理解的注释的形式包含在内。

        8
  •  6
  •   Dustin Getz sunsations    16 年前

    在关键代码中,可能不是,因为程序的状态必须始终被精确定义。类似于数据库调用示例。

    当然,在非关键代码中,我们也会这样做(我们只是在消息框中显示捕获的异常并继续运行)。可能插件或模块出现故障,但主程序不受影响。可能是词法转换失败了,屏幕上出现了文本问题。不需要停止这个过程。

        9
  •  5
  •   Kena    16 年前

    我认为这一点可以接受的一个例子是一些关键应用的非关键模块(例如,航天飞机导航系统的声音反馈模块),对于不应该发生的、不能更干净地处理的例外情况。

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

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

        10
  •  5
  •   osp70    16 年前

    我认为如果您有一个空的catch块,您需要记录为什么它是空的,以便下一个开发人员知道。例如,在server.transfer上,有时会引发Web异常。我注意到了这一点,并评论说我们可以因为转接呼叫而忽略它。

        11
  •  5
  •   Robert Rossney    16 年前

    在某些情况下,可以捕获特定的异常而不做任何事情。下面是一个简单的例子:

        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 布洛克要扔了。幼稚的开发人员给出的捕获异常的第一个原因是,“但是我不知道会抛出什么类型的异常”,这就是这个问题的答案。

    如果您不知道会抛出什么类型的异常,那么您就不知道代码会如何失败。如果您不知道代码是如何失败的,那么您就没有理由假定如果代码失败了就继续处理。

        12
  •  4
  •   StingyJack    16 年前

    是的,在某些情况下,按照马克西姆的职位,这是可以接受的(不可避免的,必要的)。这并不意味着你必须喜欢它。2106个冲突可能太多了,至少他们应该在catch块中添加一个注释,说明为什么可以接受这个异常。

    @达斯廷 向公共用户显示任何异常详细信息(堆栈跟踪、行号等)是不好的做法。您可能应该记录异常并显示一般性错误。

        13
  •  4
  •   Even Mien    16 年前

    当涉及到例外情况时, 总有例外 .

        14
  •  3
  •   Konrad Rudolph    16 年前

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

        15
  •  2
  •   Clayton    16 年前

    我认为最初的问题已经得到了很好的回答,但我想补充的一点是,如果你认为这些外包/合同开发人员的工作质量很差,你应该确保你公司的合适人员知道这一点。

    可能有一个机会,它可以送回返工,付款可以部分扣款,或同一家公司将不再使用。如果您的公司再次使用承包商,他们可能会找到一种方法将质量要求构建到协议中,前提是这并不总是存在的。

    如果这是内部工作,会对产生不合格工作的团队/个人造成后果。也许这仅仅意味着他们必须在晚上和周末工作来解决问题,但他们无论如何都会为此陷入困境。同样的情况也应该适用于承包商,甚至可能更多。

    只是要小心地专业地解释你的职位,重点是什么对公司/产品最有利。你不想让它看起来像你只是在抱怨,或者你对外包有某种政治上的反对。别为你着想。关于成本、上市时间、客户满意度等。你知道,管理层关心的所有事情。

        16
  •  2
  •   Bill the Lizard    16 年前

    我很想知道这个具体的问题。

    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?
        }
    }
    

    我不知道在最后一个关卡里该怎么办。我以前从来没有见过close()抛出异常,它是如此的不可能,以至于我不担心它。我只需记录异常并继续。

        17
  •  2
  •   bashmohandes    16 年前

    除非你的捕获码

    1. 记录异常
    2. 将异常重新打包为另一个匹配相同抽象的异常。再次投掷
    3. 处理异常 你认为合适的方式

    您可以忽略异常,但至少在方法文档中提到预期的异常,这样消费者可以在必要时进行预期和处理。

        18
  •  2
  •   Jon Bright    16 年前

    举一个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();
    }
    

    如果虚拟机将要疯狂/规范破坏,我几乎无能为力,但是有了堆栈跟踪,我至少能注意到它何时开始陷入疯狂……

        19
  •  1
  •   Zebra North    16 年前

    这真是件坏事。

    虽然那里 你可能想忽略例外的正当理由——如果在某种程度上是可以预料到的,而且没有必要做任何事情——然而,做2000次似乎他们只是想把例外扫到地毯下。

    在哪些地方可以接受异常的例子可能是在探索一些事情…你向某个设备或模块发送消息,但你不在乎它是否真的到达那里。

        20
  •  1
  •   user28205    16 年前

    以下仅适用于检查异常的语言,例如Java:

    有时一个方法抛出一个选中的异常, 知道 不会发生,例如一些Java API希望将一个编码名称作为字符串,如果不支持给定的编码,则抛出一个UnFuteldDeCudIdExct异常。但通常我传递一个我知道支持的文本“utf-8”,这样理论上我可以在那里编写一个空catch。

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

        21
  •  1
  •   Aaron Palmer    16 年前

    我喜欢让几乎所有的异常冒泡到一个应用程序处理程序中,在那里它们被记录下来,并向最终用户显示一条一般性的错误消息。但这里要注意的是,实际发生的异常不应该太多。如果您的应用程序抛出了许多异常,那么可能存在一些错误,或者某些代码可能编写得更好。在大多数情况下,我会尽力确保我的代码在高级阶段检查异常情况,因为生成异常非常昂贵。

    另外,外包编码通常是个坏主意。从我的经验来看,通常他们是顾问,他们只负责支付薪水,与项目的成功没有利害关系。另外,你也会屈服于他们缺乏的编码标准(除非你在合同中加入了这一标准)。

        22
  •  1
  •   David Robbins    16 年前

    这样想吧——如果您花费CPU周期来捕获异常,但又吞咽了异常,那么您将忽略一个潜在的问题并浪费CPU。正如许多人所说,应用程序不应该抛出那么多异常,除非您有一些构造不好的东西。

        23
  •  1
  •   GWLlosa    16 年前

    我们有一个应用程序可以代表其他应用程序进行大量处理,在这个应用程序中,您将一些作业(配置集合)插入到数据库中,应用程序将接受它并在适当的时间运行它。我们倾向于在该应用程序中接受许多异常,因为即使Job1以可怕的方式死亡并出现灾难性错误,我们也希望该应用程序在处理Job2时保持活力。

        24
  •  0
  •   Davy8    16 年前

    我认为最好的经验法则是,只有当你完全了解异常的含义和可能的后果时,才忽略异常。在某些独立的模块不会影响系统的其他部分的情况下,我认为只要您知道其他任何事情都不会发生任何错误,就可以捕获一般的异常。

    IMO更容易知道Java中的分支,因为每种方法都需要声明它可以抛出的所有异常,这样你就知道会发生什么,但是在C语言中,即使没有被记录,也会抛出异常,所以很难知道一个方法可以抛出的所有可能的异常,而且缺乏这些知识通常是个坏主意。抓住一切。

    推荐文章