Empty catch blocks

本文关键字:blocks catch Empty | 更新日期: 2023-09-27 18:15:05

我有时会遇到这样的情况:如果异常被抛出,我需要捕获异常,但从不对它做任何事情。换句话说,异常可能发生,但是否发生并不重要。

我最近读了一篇关于类似事情的文章:http://c2.com/cgi/wiki?EmptyCatchClause

这个人谈到

的注释
// should never occur 

是一种代码气味,不应该出现在代码中。然后他们解释了注释

// don't care if it happens

是完全不同的,我自己也遇到过这样的情况。例如,当我发送电子邮件时,我做了类似的事情:

var addressCollection = new MailAddressCollection();
foreach (string address in addresses)
{
    try
    {
        addressCollection.Add(address);
    }
    catch (Exception)
    {
        // Do nothing - if an invalid email occurs continue and try to add the rest
    }
}

现在,您可能认为这样做是一个坏主意,因为您希望返回用户并解释无法将一条或多条消息发送给收件人。但如果只是抄送地址呢?这并不重要,即使其中一个地址无效(可能只是一个打字错误),您可能仍然想要发送消息。

那么我使用空catch块是否正确,或者是否有我不知道的更好的替代方案?

Empty catch blocks

如果您真的想在发生某种类型的异常时什么都不做,那么使用空catch块是完全正确的。您可以通过只捕获您期望发生的并且您知道可以安全地忽略的异常类型来改进示例。通过捕获Exception,您可以隐藏错误,并使您自己更难调试程序。

关于异常处理,有一件事要记住:用于指示程序外部错误条件的异常(预期至少有时会发生)与指示编程错误的异常之间有很大的区别。第一种情况的一个例子是一个异常,表明由于连接超时而无法发送电子邮件,或者由于没有磁盘空间而无法保存文件。第二个例子是一个异常,表明你试图传递错误类型的参数给一个方法,或者你试图访问一个越界的数组元素。

对于第二个(编程错误),仅仅"吞下"异常将是一个很大的错误。最好的做法通常是记录堆栈跟踪,然后弹出一个错误消息,告诉用户发生了内部错误,他们应该将他们的日志发送给开发人员(即您)。或者在开发过程中,您可以让它向控制台输出堆栈跟踪信息,并使程序崩溃。

对于第一个(外部问题),没有规则规定什么是"正确"的事情。这完全取决于应用程序的细节。如果您想忽略某个条件并继续,那么就这样做。

一般

:

很好,你正在阅读技术书籍和文章。你可以从中学到很多东西。但请记住,当你阅读时,你会发现很多人的建议说,做这样那样的事情总是错,或者总是对。这些观点往往与宗教有关。永远不要认为做某件事是绝对"正确的",因为一本书或一篇文章(或关于某某的答案)……我早就告诉过你了。每个规则都有例外,而撰写这些文章的人并不了解您的应用程序的细节。你做的事情。确保你所读的内容是有意义的,如果没有,相信自己。

一个空的catch块在正确的地方是可以的——尽管从你的示例中,我想说你应该使用而不是 catch (Exception)。相反,您应该捕获预期发生的显式异常。

这样做的原因是,如果您吞下了所有的东西,那么您也将吞下您没有预料到的关键缺陷。"我无法发送到这个电子邮件地址"answers"你的计算机磁盘空间不足"之间有天壤之别。如果磁盘空间不足,您肯定不想继续尝试发送接下来的10000封电子邮件吧!

"不应该发生"answers"不在乎它是否发生"之间的区别在于,如果它"不应该发生",那么当它确实发生时,你不想默默地接受它!如果这是您从未预料到的情况,您通常会希望应用程序崩溃(或者至少干净地终止并大量记录发生的情况),以便您可以识别这种不可能的情况。

如果一个异常永远不应该被抛出,那么捕捉它就没有意义——它永远不应该发生,如果发生了,你需要知道它。

如果存在可以导致失败的特定场景,您可以接受,那么您应该捕获并测试这些特定场景,并在所有其他情况下重新抛出,例如

foreach (string address in addresses)
{
    try
    {
        addressCollection.Add(address);
    }
    catch (EmailNotSentException ex)
    {
        if (IsCausedByMissingCcAddress(ex))
        {
            // Handle this case here e.g. display a warning or just nothing
        }
        else
        {
            throw;
        }
    }
}

注意上面的代码捕获特定的(如果是虚构的)异常,而不是捕获Exception。我能想到的极少数情况下,捕获Exception是合法的,而不是捕获您期望被抛出的某些特定异常类型。

许多其他答案给出了很好的理由,说明什么时候可以捕获异常,尽管许多类支持根本不抛出异常的方法。

通常这些方法的前面会有前缀Try。该函数不抛出异常,而是返回一个布尔值,指示任务是否成功。

一个很好的例子是Parse vs TryParse
string s = "Potato";
int i;
if(int.TryParse(s, out i))
{
    //This code is only executed if "s" was parsed succesfully.
    aCollectionOfInts.Add(i);
}

如果您在循环中尝试上述函数,并将其与Parse + Catch等效方法进行比较,则TryParse方法将快得多。

使用空catch块只是吞下异常,我总是会处理它,即使它向您报告发生了Exception

捕获通用的Exception也是不好的做法,因为它可以隐藏应用程序中的错误。例如,你可能已经捕获了一个ArgumentOutOfRange异常,你没有意识到正在发生,然后吞下它(即没有对它做任何事情)。