哇,我刚从外包开发人员那里拿回了一个C#的大项目,在进行我的代码审查时,我的分析工具揭示了它被认为是坏东西的一堆.其中一个令人沮丧的消息是:
Exceptions.DontSwallowErrorsCatchingNonspecificExceptionsRule : 2106 defects
开发人员向我保证他们有充分的理由使用所有空的catch块,有时候使用空catch块的尝试只是忽略无用的异常并使应用程序不会崩溃.我觉得这是一个警察和完整的BS.我实际查找的一些示例是数据库调用,其中记录被保存到数据库,在这种情况下,如果忽略异常,用户将返回一个正常的提示,认为一切正常,并继续他们的工作.实际上,他们的工作从未得到保存.我认为这绝对是最可怕的错误.在这种情况下,在使用空catch块的try中抛出该代码是完全错误的.但我的问题是,"这种情况在任何情况下都可以接受吗?" 我想不是,但我知道错了.
虽然有一些合理的理由可以忽略例外情况; 但是,通常只有您能够安全忽略的特定例外情况.如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",即使存在异常也不是一个好兆头.
除非我打算对它们采取一些措施,否则我不会捕获异常.忽略它们并没有对它们做些什么.
我有时使用WebControl,这不是页面显示的必需条件.如果失败,我不想阻止页面显示.非关键WebControl的示例将是显示广告的示例.
但是,我确实记录了错误.我只是不要重新抛出它.
我的感觉是任何空的Catch Block都需要评论.
可能忽略某些错误是有效的,但您需要记录您的原因.
此外,您不希望将其设为通用的"catch(Exception e){}".
您应该只捕获那里预期的特定错误类型,并且已知安全地被忽略.
一般来说没有,事实上99%的情况都没有,但是
也有例外.我工作的一个项目是使用第三方库来处理TWAIN设备.它是错误的,并且在某些硬件组合下会抛出空指针错误.然而,在它没有实际设法扫描文档之前我们从未发现任何情况 - 因此捕获异常是完全合理的.
所以我认为如果你的代码是抛出异常,那么你应该经常检查它,但如果你遇到第三方代码,那么在某些情况下你可能会被迫吃掉异常并继续前进.
另一种情况是,您可以免除捕获和忽略异常,这是您进行单元测试的时候.
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"); }
我想从我收集到的最好的答案是,它可以在某种程度上被接受,但应该是有限的.您应该尝试使用另一种替代方案,如果您找不到其他替代方案,您应该充分了解代码如何工作,您可以期望特定的异常类型,而不仅仅是使用全局捕获所有"异常".忽略此异常的原因的文档应包含在可理解的注释中.
在关键代码中,可能不是,因为必须始终精确定义程序的状态.像你的数据库调用示例.
在非关键代码中,当然,我们也这样做(我们只是在消息框中显示捕获的异常并继续运行).也许插件或模块失败,但主程序不受影响.也许lexical_cast失败了,并且有一个文本故障呈现在屏幕上.无需停止该过程.
我认为这是可接受的一个例子是关键应用的一些非关键模块(例如,在航天飞机导航系统的声音反馈模块中),用于不应该发生的异常,并且不能更干净地处理.
在这些情况下,您不希望让该异常传播并导致整个应用程序失败(抱歉,没有更多的导航系统,我们的哔哔声模块崩溃了,我们真的无能为力).
编辑说,在任何这些情况下,你至少想要在某个地方记录事件.
我认为如果你有一个空的catch块,你需要记录它为什么是空的,以便下一个开发人员知道.例如,在server.transfer上,有时会抛出Web异常.我抓住了这个并评论我们可以忽略它,因为转移呼叫.
在某些情况下,可以捕获特定的异常并且什么都不做.这是一个简单的例子:
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
块中的代码将要抛出的特定类型异常.天真的开发人员为了捕获异常而不管类型的第一个原因,"但我不知道可能会抛出什么类型的异常",这就是问题的答案.
如果您不知道可能抛出的异常类型,则不知道代码如何失败.如果您不知道代码如何失败,那么您没有理由认为只要继续处理就可以了.