可以使用空的catch语句吗?


Answers:


77

我一直用D中的转换错误之类的方法来做:

import std.conv, std.stdio, std.exception;

void main(string[] args) {  
    enforce(args.length > 1, "Usage:  foo.exe filename");

    double[] nums;

    // Process a text file with one number per line into an array of doubles,
    // ignoring any malformed lines.
    foreach(line; File(args[1]).byLine()) {
        try {
            nums ~= to!double(line);
        } catch(ConvError) {
            // Ignore malformed lines.
        }
    }

    // Do stuff with nums.
}

就是说,我认为所有catch块中都应包含某些内容,即使这些内容只是解释为什么忽略该异常的注释。

编辑:我还想强调一点,如果您要执行类似的操作,则应注意仅捕获要忽略的特定异常。做一个普通的老人catch {}几乎总是一件坏事。


15
+1为解释评论。
Michael K

某些IDE允许您指定异常的特定名称(通常为“忽略”),表示您有意忽略了该异常,从而消除了否则将显示的警告。这代替了评论。
Alex Feinman

我不熟悉D,但是我认为可以做类似bool TryParse(字符串行,输出为double valToParse)之类的东西,这可能会更干净。
ysolik 2010年

4
哇,实际使用D的人!:-P
James McNellis

4
就像@Michael所说的那样- 因为您在这里有评论,所以它不是空;如果没有声明,则必须添加“此捕获有意留为空白”的注释
STW 2010年

50

我认为可以在不做任何事情的情况下吞下异常,甚至不记录异常的一个例子是在记录代码本身内部。

如果您尝试记录某些内容并遇到异常,则您无能为力:

  • 您当然不能登录;
  • 您也许可以使用备用日志记录机制,但是大多数应用程序并不那么复杂,对于足够复杂的应用程序,备用日志记录机制将出现相同的设计问题;
  • 快速失败-对于大多数应用程序来说太过激进了;
  • 抛出异常并没有多大用处,因为它将最终导致堆栈中的catch语句,几乎可以肯定地尝试记录它。

这给您留下了“轻描淡写”的选项,可以悄悄地吞下它,使应用程序能够继续执行其主要任务,但是却损害了对其进行故障排除的能力。


10
好点。调试调试代码始终是一个挑战。
DevSolo

7
这,一百万次。尤其是当您考虑到要登录时,您可能会处于可怕的状态。您可能内存不足,可能崩溃了,也可能是任何东西。在这一点上,当您记录错误时,如果那失败了,那么唯一要做的事就是什么都不做;您无法保证您所做的任何尝试都不会使情况变得更糟。
GWLlosa

好点。在我的日志记录代码中,我还想为异常添加InnerMessage。很多时候它为空,因此尝试添加它会破坏日志记录本身。
Tangurena

@Tangurena如果它通常为null,请处理它,不要吹牛。在C#中,我经常通过??来保护这些东西。“”;
罗兰·佩希特尔

8

如果异常是由可选操作抛出的,则可能无事可做。记录它可能没有用。在这种情况下,您可能只想抓住它而什么也不做。

如果要执行此操作,请添加评论。始终注释任何看起来像错误的内容(switch语句中的错误,空catch块,条件中的赋值)。

您可能会认为这不是对异常的正确使用,但这是另一个问题。


6

catch块是大多数语言中的代码味道。主要思想是在异常情况下使用异常,而不是将其用于逻辑控制。所有异常都必须在某个地方处理。

采用这种方法的结果是,在对一个特定的应用程序层进行编程时,您有多种选择:

  • 对异常不做任何事情。它将被另一层捕获和处理
  • 抓住并执行纠正措施。
  • 抓住它,做点什么,但是将它重新扔给另一层处理

这实际上并没有留下任何可以做的空catch块的空间。

编辑添加:假设您正在使用一种语言来编程,其中抛出异常是控制程序逻辑的常规方法(是的替代方法之一goto)。然后,catch用这种语言编写的程序中的空else块非常类似于传统语言中的空块(或根本没有else块)。但是,我相信这不是C#,Java或C ++开发社区推荐的编程风格。


我认为使用异常作为逻辑控制已变得相当普遍,因此我发现自己经常会遇到空或几乎空的catch块。
whatsisname 2010年

+1表示认识到空的catch块可能表示使用异常进行流控制。
John M Gant

将异常用于编程逻辑通常被认为是不好的做法。但是,令人惊讶的是(我最初反对例外的说法)例外不会引起明显的性能损失。请参阅codeproject.com/KB/exception/ExceptionPerformance.aspx
Evan Plaice

6

在某些情况下,Java要求您处理绝对不会发生的异常。考虑以下代码:

try {
   bytes[] hw = "Hello World".getBytes("UTF-8");
}
catch(UnsupportedCodingException e) {
}

Java平台的每个实现都需要支持以下标准字符集。

...

UTF-8

在不破坏Java平台的情况下,根本没有办法导致此异常。那么,为什么还要打扰呢?但是您不能简单地忽略try-catch-clause。


1
在这种情况下,我很想补充一点throw new Error(e),以确保绝对不会遗漏任何东西(或报告实际损坏的平台)。
哈雷·纳斯特

@HalleKnast是的。:这完全这里详细讨论softwareengineering.stackexchange.com/questions/122233/...
user281377

5

作为一般规则,不可以。您要么将异常抛出堆栈,要么记录发生的事情。

但是,在解析字符串并将其转换为数字时,我经常这样做(尤其是在映射具有一次性使用功能并丢弃各种功能的项目中)。

boolean isNonZeroNumber = false;

try {
   if(StringUtils.isNotBlank(s) && new BigDecimal(s).compareTo(BigDecimal.ZERO) != 0) {
     b = true;
   }
} catch(NumberFormatException e) {
//swallow this exception; b stays false.
}

return b;

3

在某些情况下,该异常并非完全例外。实际上,这是预料之中的。考虑以下示例:

    /* Check if the process is still alive; exitValue() throws an exception if it is */
    try {
        p.exitValue();
        processPool.remove(p);
    }
    catch (IllegalThreadStateException e) { /* expected behaviour */ }

由于java.lang.Process()中没有isAlive()方法,因此检查它是否仍然存在的唯一方法是调用exitValue(),如果进程仍在运行,则会抛出IllegalThreadStateException。


2

以我卑微的经历,这很少是一件好事。您的里程可能会有所不同。

几乎每次我在我们的代码库中看到此内容时,都是因为我已经找到了一个被吃掉的异常隐藏的错误。换句话说,通过不提供任何代码,应用程序无法指示错误或执行其他操作。

有时,例如在API层,您可能不希望或无法让异常过去,因此在这种情况下,我倾向于尝试捕获最通用的异常,然后将其记录下来。再一次,至少给调试/诊断会话一个机会。

通常,如果必须为空,则我要做的至少是放某种类型的断言,以便至少在调试会话期间发生某些事情。就是说,如果我不能处理,我倾向于完全忽略它们。


2

IMO在一个地方可以使用空catch语句。在预期例外的测试案例中:

try
{
   DoSomething(); //This should throw exception if everything works well
   Assert.Fail('Expected exception was not thrown');
}
catch(<whatever exception>)
{
    //Expected to get here
}

+1,我恨,我这样做,但它在JUnit的工作
SAL

@sal:@Test(expected = Exception.class)呢?
ysolik

@ysolik,坏习惯
SAL

1
@sal:为什么这是个坏习惯?
亚当李尔

嗯。使用单元测试框架可以做到这一点。例如 NUnit。在某些情况下,测试某些可预见的故障可能会有用。虽然,我永远不会在生产代码中这样做。
Evan Plaice

2

我相信在某些情况下有一个空的catch子句是合理的-这些情况是您希望代码在特定操作失败的情况下继续运行的情况。但是,我确实认为这种模式很容易过度使用,因此应仔细检查每次使用,以确保没有更好的方法来处理它。特别是,如果它使程序处于不一致状态或以后可能导致代码的其他部分失败,则永远不要这样做。


1

我不相信发生异常时不会发出某种程度的警报-编程的目的之一不是改善代码吗?如果异常发生的时间是10%,并且您永远都不知道,那么异常总是那么糟糕,甚至更糟。

但是,我确实看到了另一面,如果异常在代码处理中没有真正的危害,那么为什么还要让用户接触它。我通常实现的解决方案是由我的logger类(即我在工作中写过的每个类)记录下来。

如果这是一个良性异常,那么我将调用Logger的.Debug()方法;否则,Logger.Error()(并且(可能)抛出)。

try
{
   doWork();
}
catch(BenignException x)
{
  _log.Debug("Error doing work: " + x.Message);
}

顺便说一下,在我的记录器实现中,仅当我的App.Config文件指定程序执行日志级别处于“调试”模式时,对.Debug()方法的调用才会记录该日志。


如果_log.Debug抛出异常(出于某种原因)怎么办?
JohnL

然后.Debug()吞噬了异常,而我却一无所知。但是我对记录器的信心比对经常引发异常的代码的信心要大。如果我对此感到担心,我想我可以实现某种模式,以监视是否进入catch块并将其保存以备后用。无论如何-要点
蒂姆·克拉森

1

存在检查是一个很好的用例:

// If an exception happens, it doesn't matter. Log the initial value or the new value

function logId(person) {
    let id = 'No ID';
    try {
        id = person.data.id;
    } catch {}
    console.log(id);
}

另一种情况是使用finally子句时:

 function getter(obj){}

 function objChecker()
   {
   try
     {
     /* Check argument length and constructor type */
     if (getter.length === 1 && getter.constructor === Function)
       {
       console.log("Correct implementation");
       return true;
       }
     else
       {
       console.log("Wrong implementation");
       return false;
       }
     }
   catch(e)
     {
     }
   finally
     {
     console.log(JSON.stringify(getter))
     }
   }

 // Test the override of the getter method 
 getter = getter;
 objChecker();
 getter = [];
 objChecker();
 getter = {};
 objChecker();
 getter = RegExp;
 objChecker();
 getter = Function;
 objChecker();

有一种建议允许在ECMAScript中省略与该案例和类似用例有关的catch绑定

参考文献


另一个示例:在nodejs中,不赞成使用fs.exists方法(返回true或false)(nodejs.org/dist/latest-v10.x/docs/api/…),而赞成使用fs.access,该方法会在文件不存在的情况。如果仅在文件存在时才想做某事,则catch子句完全没有必要。
systemovich

0

我真正需要做的事情只有一次是使用控制台应用程序的日志记录类。有一个顶级全局捕获处理程序,该处理程序将错误发送到STDOUT,将错误记录到文件中,然后使用> 0错误代码关闭应用程序。

这里的问题是有时记录到文件失败。目录权限使您无法写文件,该文件被独占锁定,无论如何。如果发生这种情况,我将无法以与其他地方相同的方式处理异常(捕获,记录相关详细信息,包装更好的异常类型等),因为记录异常详细信息会导致记录器执行相同的操作(尝试写入文件)已失败。当他们再次失败时...您会看到我要去的地方。它陷入无限循环。

如果删除了一些try {}块,最终可能会在消息框中显示异常(而不是无声配置应用所需的消息)。因此,在写入日志文件的方法中,我有一个空的catch处理程序。这不会掩埋错误,因为您仍然会得到> 0错误代码,它只会停止无限循环并允许应用正常退出。

当然有一条评论,解释为什么它是空的。

(我本来打算早些发布,但是我只是害怕被遗忘。因为我不是唯一的一个人...)


0

在某些情况下,无论最关键的部分放在最后,都必须执行某些顺序是可以的。

例如。在主机与控制器的运动控制通信中。在紧急情况下,有许多准备步骤来中止运动,停止轨迹生成,使电动机减速,启用中断,禁用放大器,然后必须切断总线电源。所有步骤必须按顺序进行,并且即使其中一个步骤失败,也必须执行其余步骤,即使存在损坏硬件的风险也是如此。说,即使以上所有方法都失败了,仍然必须关闭总线电源。


这并不意味着您什么也不做,只是您所做的不会中止流程。捕获异常,将其保存在内存中(无磁盘写入延迟),然后在finally语句中关闭电源。然后对保存的信息进行处理。
罗兰·佩希特尔

0

优雅地关闭系统资源以从错误中恢复

例如。如果您在后台运行的服务无法向用户提供反馈,则可能不希望向用户推送错误指示,但是您确实需要以优雅的方式关闭正在使用的资源。

try
{
    // execution code here
}
catch(Exception e)
{
    // do nothing here
}
finally
{
    // close db connection
    // close file io resource
    // close memory stream
    // etc...
}

理想情况下,您可以在调试模式下使用catch来捕获错误并将其打印到控制台或日志文件中以进行测试。在生产环境中,通常期望后台服务在引发错误时不会中断用户,因此应抑制错误输出。

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.