【问题标题】:Empty catch blocks空捕获块
【发布时间】:2013-05-18 21:58:14
【问题描述】:

我有时会遇到需要捕获异常但从不对其进行任何处理的情况。换句话说,可能会发生异常,但是否发生并不重要。

我最近读到了一篇关于类似事情的文章: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 块,或者是否有更好的替代方案,我不知道?

【问题讨论】:

  • 你怎么知道Exception与抄送地址有关,而不是e.g.到内存不足的情况?你打算如何从中恢复?
  • 在你的例子中,我仍然会对异常做一些事情,即使只是记录它 - 甚至可能不会让用户选择要做什么失败电子邮件地址,但记录它以便您知道这已经发生了,也很好。
  • 我怀疑作者的意图是说“这个错误永远不应该发生”的评论意味着编码人员没有考虑过如果错误违背预期并且无论如何发生该怎么办。第二条评论是“更好”,但更好的评论会解释为什么你不关心错误。最终,最好的异常处理取决于你的应用程序应该做什么。在某些情况下,吞下异常而不采取进一步行动是可以的。在其他情况下,您可能需要先记录它,而在某些情况下,您实际上需要尝试恢复。
  • 感谢 cmets。我同意你的观点,我应该捕捉与地址格式相关的特定例外情况,并让其他人冒出来。我会做出这个改变
  • 即使我们假设我们不在乎,因为电子邮件无效,最好记录异常并记下无效的电子邮件。空异常只是lazy编程的借口。

标签: c# .net exception error-handling


【解决方案1】:

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

关于异常处理需要牢记的一点:用于指示程序外部错误条件的异常(至少有时预计会发生)和表示编程错误的异常。第一个示例是异常,表明由于连接超时而无法发送电子邮件,或者由于没有磁盘空间而无法保存文件。第二个例子是一个异常,表明您尝试将错误类型的参数传递给方法,或者您尝试越界访问数组元素。

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

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

一般:

很高兴您正在阅读技术书籍和文章。你可以从中学到很多东西。但是请记住,当您阅读时,您会发现很多人的建议说,做某事是总是错误或总是正确。这些观点常常与宗教接壤。 从不相信以某种方式做事绝对是“正确的”,因为一本书或文章(或关于 SO... 的答案)告诉过你。每条规则都有例外,撰写这些文章的人不知道您的应用程序的详细信息。你做。确保您阅读的内容有意义,如果没有,请相信自己。

【讨论】:

  • 非常感谢您提供的信息。我同意你的最后一点,我更有可能从已发布的来源而不是博客或互联网文章中获取信息。
  • 哈哈,对绝对陈述做出绝对陈述。
  • 我不同意你的一般观点。是的,您是对的,它使调试变得更容易,但是忽略捕获只是草率的编程,并显示了程序员的技能。我知道有些人是狂热分子,他们争论空间应该放在这里还是那里,但是捕捉所有异常只是好的编程。总是有非常罕见的异常,但您的第一个异常示例也应该被捕获,即使要记录。我想知道我调用的函数是否无法写入磁盘或发送电子邮件,它将帮助我确定下一步行动以确保数据完整性。
【解决方案2】:

许多其他答案给出了很好的理由何时可以捕获异常,但是许多类支持根本不抛出异常的方法。

这些方法通常会在它们前面加上前缀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 方法会快得多。

【讨论】:

  • 同意我很喜欢 Try 方法
  • 是的,错误的用户输入不是例外情况,而是预期的情况。人类不像计算机那样可预测。在这种情况下抛出异常在我看来比其他任何事情都更重要的是Vexing Exception
【解决方案3】:

一个空的 catch 块很好在正确的位置 - 虽然从你的示例中我会说你应该明确地使用catch (Exception)。相反,您应该捕获您期望发生的显式异常。

这样做的原因是,如果你吞下一切,你也会吞下你没有预料到的严重缺陷。 “我无法发送到此电子邮件地址”和“您的计算机磁盘空间不足”之间存在天壤之别。如果您的磁盘空间不足,您不想继续尝试发送接下来的 10000 封电子邮件!

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

【讨论】:

  • +1 表示最后一段。 “不应该发生”的错误有一种违背期望并无论如何都会发生的绝妙习惯。尤其是在应用程序已经存在一段时间并且可能经过一些修改之后。
  • 同意“不应该发生”。如果它们不应该发生,你可能会意识到你毕竟在乎。
【解决方案4】:

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

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

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 是合法的,而不是捕获您期望抛出的某些特定异常类型。

【讨论】:

  • 我明白你在说什么。我猜您的 EmailNotSendException 是将与无效电子邮件地址相关的异常归为一组的想法?
  • @Serberuss 是的 - 捕获最具体的异常
【解决方案5】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-09-28
    • 1970-01-01
    • 1970-01-01
    • 2011-08-23
    • 1970-01-01
    • 2021-09-21
    相关资源
    最近更新 更多