为什么空捕获阻止了一个坏主意?

时间:2009-08-05 16:31:56

标签: exception-handling try-catch

我刚刚看到一个question on try-catch,人们(包括Jon Skeet)说空挡块是个坏主意?为什么这个?是否有空的捕获不是错误的设计决定?

我的意思是,例如,有时你想从某个地方(网络服务,数据库)获得一些额外的信息,你真的不在乎你是否会收到这些信息。所以你试图得到它,如果发生任何事情,那没关系,我只会添加一个“catch(忽略异常){}”,这就是全部

20 个答案:

答案 0 :(得分:284)

通常空的try-catch是个坏主意,因为你正在静默地吞下一个错误条件,然后继续执行。偶尔这可能是正确的事情,但通常这是一个迹象,开发人员看到一个例外,不知道该怎么做,所以使用空捕获来解决问题。

这相当于将黑色胶带放在发动机警告灯上。

我相信您处理异常的方式取决于您所使用的软件层:Exceptions in the Rainforest

答案 1 :(得分:35)

他们总的来说是一个坏主意,因为这是一个真正罕见的情况,其中失败(异常情况,更一般地)正确地满足了无响应。最重要的是,空catch块是使用异常引擎进行错误检查的人使用的常用工具,他们应该先发制人。

总是坏是不真实的......这是非常的。在某些情况下,您可能不关心是否存在错误,或者错误的存在以某种方式表明您无法对其执行任何操作(例如,将以前的错误写入文本日志文件时你得到一个IOException,这意味着你无论如何都无法写出新的错误。)

答案 2 :(得分:10)

我不会伸出一些东西,只要说使用空挡块是一个坏程序员而且不知道他在做什么......

如果需要,我会使用空的catch块。有时我正在消费的图书馆程序员不知道他在做什么,即使在没人需要的情况下也会抛出异常。

例如,考虑一些http服务器库,如果服务器因为客户端已断开连接而无法发送index.html而引发异常,我不在乎。

答案 3 :(得分:8)

在极少数情况下可以证明其合理性。在Python中,您经常会看到这种结构:

try:
    result = foo()
except ValueError:
    result = None

所以可以(根据你的应用程序)做:

result = bar()
if result == None:
    try:
        result = foo()
    except ValueError:
        pass # Python pass is equivalent to { } in curly-brace languages
 # Now result == None if bar() returned None *and* foo() failed

在最近的.NET项目中,我不得不编写代码来枚举插件DLL以查找实现某个接口的类。相关的代码(在VB.NET中,抱歉)是:

    For Each dllFile As String In dllFiles
        Try
            ' Try to load the DLL as a .NET Assembly
            Dim dll As Assembly = Assembly.LoadFile(dllFile)
            ' Loop through the classes in the DLL
            For Each cls As Type In dll.GetExportedTypes()
                ' Does this class implement the interface?
                If interfaceType.IsAssignableFrom(cls) Then

                    ' ... more code here ...

                End If
            Next
        Catch ex As Exception
            ' Unable to load the Assembly or enumerate types -- just ignore
        End Try
    Next

虽然在这种情况下,我承认在某处记录故障可能会有所改善。

答案 4 :(得分:6)

通常会放入空的catch块,因为编码器并不真正知道它们在做什么。在我的组织中,一个空的catch块必须包含一个注释,说明为什么对异常做任何事情都是个好主意。

在相关的说明中,大多数人都不知道try {}块可以跟随catch {}或finally {},只需要一个。

答案 5 :(得分:6)

只有在存在真正异常的情况下才会抛出异常 - 超出常规的事情。一个空的挡块基本上说“发生了一些不好的事情,但我只是不在乎”。这是一个坏主意。

如果您不想处理异常,请让它向上传播,直到它到达一些可以处理异常的代码。如果没有任何东西可以处理异常,则应该关闭应用程序。

答案 6 :(得分:6)

我认为如果您捕获特定的异常类型是可以的,您知道它只会针对一个特定原因引发,并且您期望该异常并且真的不需要做任何事情。

但即使在这种情况下,调试消息也可能是有序的。

答案 7 :(得分:5)

Per Josh Bloch - 第65项:不要忽略 Effective Java 的例外情况

  1. 一个空的捕获区阻止了异常的目的
  2. 至少,catch块应该包含一个注释,解释为什么忽略该异常是合适的。

答案 8 :(得分:3)

这与“不要使用例外来控制程序流程”,以及“仅针对特殊情况使用例外”相辅相成。如果这些已经完成,则只有在出现问题时才会发生异常。如果出现问题,你不想默默地失败。在罕见的异常情况下,没有必要处理问题,您至少应该记录异常,以防异常变得不再是异常现象。唯一比失败更糟的是默默地失败。

答案 9 :(得分:3)

一个空的catch块基本上是在说“我不想知道抛出了什么错误,我只是想忽略它们。”

它类似于VB6的On Error Resume Next,除了抛出异常后try块中的任何内容都将被跳过。

当某些事情破裂时,这无济于事。

答案 10 :(得分:2)

我认为一个完全空的catch块是一个坏主意,因为没有办法推断忽略该异常是代码的预期行为。在某些情况下,吞下异常并返回false或null或其他值并不一定是坏事。 .net框架有许多行为方式的“尝试”方法。根据经验,如果您吞下异常,请在应用程序支持记录时添加注释和日志语句。

答案 11 :(得分:1)

这可能永远不是正确的事情,因为你默默地传递每个可能的异常。如果你期望有一个特定的例外,那么你应该测试它,如果不是你的例外则重新抛出。

try
{
    // Do some processing.
}
catch (FileNotFound fnf)
{
    HandleFileNotFound(fnf);
}
catch (Exception e)
{
    if (!IsGenericButExpected(e))
        throw;
}

public bool IsGenericButExpected(Exception exception)
{
    var expected = false;
    if (exception.Message == "some expected message")
    {
        // Handle gracefully ... ie. log or something.
        expected = true;
    }

    return expected;
}

答案 12 :(得分:1)

空catch块表示程序员不知道如何处理异常。他们正在抑制异常冒泡并被另一个try块正确处理。总是尝试做一些你正在捕捉的例外情况。

答案 13 :(得分:1)

我发现最烦人的是空的catch语句是其他程序员做的时候。我的意思是当你需要从其他人调试代码时,任何空的catch语句都会使得这样的事务变得更加困难。 IMHO catch语句应该总是显示某种错误消息 - 即使错误未被处理,它至少应该检测到它(仅在调试模式下为alt。)

答案 14 :(得分:1)

因为如果抛出异常 你将永远不会看到它 - 默默地失败是最糟糕的选择 - 你会得到错误的行为并且不知道它在哪里发生。至少在那里放一条日志消息!即使它是'永远不会发生'的东西!

答案 15 :(得分:1)

通常,您应该只捕获实际可以处理的异常。这意味着在捕获异常时尽可能具体。捕获所有异常很少是一个好主意,忽略所有异常几乎总是一个非常糟糕的主意。

我只能想到一些空的catch块有一些有意义的目的。如果任何特定的异常,你只是通过重新尝试动作来“处理”,就不需要在catch块中做任何事情。但是,记录异常发生的事实仍然是一个好主意。

另一个例子:CLR 2.0改变了终结器线程上未处理的异常的处理方式。在2.0之前,该过程被允许在这种情况下生存。在当前的CLR中,如果终结器线程上出现未处理的异常,则终止进程。

请记住,如果你确实需要一个终结器,你应该只实现一个终结器,即便如此,你应该在终结器中做一点点。但是,如果你的终结者必须做的任何工作都可以抛出异常,你需要在两个邪恶中较小的一个之间进行选择。您是否因为未处理的异常而关闭应用程序?或者您想要处于或多或少的未定义状态?至少在理论上,后者在某些情况下可能是两个邪恶中的较小者。在这种情况下,空的catch块会阻止进程终止。

答案 16 :(得分:1)

我的意思是,例如,有时你想从某个地方(网络服务,数据库)获得一些额外的信息,你真的不在乎你是否会得到这些信息。所以你试图得到它,如果发生任何事情,那没关系,我只会添加一个“catch(忽略异常){}”,这就是

所以,按照你的例子,在这种情况下这是一个坏主意,因为你正在捕捉并忽略所有异常。如果你只抓住EInfoFromIrrelevantSourceNotAvailable并忽略它,那就没问题,但你不是。你也忽略了ENetworkIsDown,这可能是也可能不重要。你忽略了ENetworkCardHasMeltedEFPUHasDecidedThatOnePlusOneIsSeventeen,这几乎肯定很重要。

如果空捕获块设置为仅捕获(并忽略)您知道不重要的某些类型的异常,则它不是问题。抑制和默默忽略所有异常是一个好主意的情况,在不停止检查它们以确定它们是否正常/正常/不相关的情况下极为罕见。

答案 17 :(得分:1)

有些情况下你可能会使用它们,但它们应该很少见。我可以使用的情况包括:

  • 异常记录;根据上下文,您可能需要发布未处理的异常或消息。

  • 循环技术情况,如渲染或声音处理或列表框回调,其中行为本身将演示问题,抛出异常只会妨碍,并且记录异常可能只会导致1000的“未能通过XXX”消息。

  • 无法失败的程序,尽管它们至少仍应记录某些内容。

对于大多数winforms应用程序,我发现每个用户输入都有一个try语句就足够了。我使用以下方法:( AlertBox只是一个快速的MessageBox.Show包装器)

  public static bool TryAction(Action pAction)
  {
     try { pAction(); return true; }
     catch (Exception exception)
     {
        LogException(exception);
        return false;
     }
  }

  public static bool TryActionQuietly(Action pAction)
  {
     try { pAction(); return true; }
     catch(Exception exception)
     {
        LogExceptionQuietly(exception);
        return false;
     }
  }

  public static void LogException(Exception pException)
  {
     try
     {
        AlertBox(pException, true);
        LogExceptionQuietly(pException);
     }
     catch { }
  }

  public static void LogExceptionQuietly(Exception pException)
  {
     try { Debug.WriteLine("Exception: {0}", pException.Message); } catch { }
  }

然后每个事件处理程序都可以执行以下操作:

  private void mCloseToolStripMenuItem_Click(object pSender, EventArgs pEventArgs)
  {
     EditorDefines.TryAction(Dispose);
  }

  private void MainForm_Paint(object pSender, PaintEventArgs pEventArgs)
  {
     EditorDefines.TryActionQuietly(() => Render(pEventArgs));
  }

理论上,您可以使用TryActionSilently,这可能更适合渲染调用,以便异常不会生成无穷无尽的消息。

答案 18 :(得分:0)

你永远不应该有一个空的catch块。这就像隐藏了你所知道的错误。至少你应该写一个日志文件的例外,以便以后按时间查看。

答案 19 :(得分:-1)

如果你不知道在catch块中做什么,你可以记录这个例外,但不要把它留空。

        try
        {
            string a = "125";
            int b = int.Parse(a);
        }
        catch (Exception ex)
        {
            Log.LogError(ex);
        }