这些尝试/捕获量是否相同?

时间:2017-01-26 12:14:22

标签: c# .net exception exception-handling

方案

我有一个执行数据库操作的方法(比方说)。如果在该操作期间引发任何异常,我只想将该异常抛给调用者。我不想在catch块中执行任何特定任务,假设调用者将对该异常执行任何操作。在这种情况下,哪一种是适当的异常处理技术?

try
{
    // Some work that may generate exception
}
catch(Exception)
{
    throw;
}
finally
{
    // Some final work
}

以上是否等同于以下try / catch / finally?

try
{
    // Some work that may generate exception
}
catch
{
    throw;
}
finally
{
    // Some final work
}

以上是否等同于以下的try / finally?

try
{
    // Some work that may generate exception
}
finally
{
    // Some final work
}

哪一个比另一个好?应该使用哪一个?

3 个答案:

答案 0 :(得分:32)

不,他们不等同。在某些情况下,可能相同,但一般的答案是否定的。

不同种类的catch

具有指定异常类型的

catch

以下内容仅捕获从System.Exception继承的托管异常,然后执行finally块,无论是否抛出异常,都会发生这种情况。

try
{
   // Some work that may generate exception
}
catch (Exception)
{
   throw;
}
finally
{
   // Some final work
}
没有指定异常类型的

catch

没有类型说明符的以下catch块也将捕获不一定由托管System.Exception对象表示的非托管异常,然后执行finally块,无论是否抛出异常都会发生。

try
{
   // Some work that may generate exception
}
catch
{
   throw;
}
finally
{
   // Some final work
}

finally阻止没有catch

如果您根本没有catch阻止,无论是否发生异常,您的finally仍会被执行。

try
{
   // Some work that may generate exception
}
finally
{
  // Some final work
}

它们何时相同?

如果您的catch块未指定异常且仅包含throw;语句,则最后两个确实等效。如果您不关心非托管异常,并且catch块仅包含throw;语句,则所有这三个都可以视为等效。

注释

关于throw

的说明

以下两段代码包含细微差别。后者将重新抛出异常,这意味着它将重写异常的堆栈跟踪,因此这些肯定是等效:

catch (Exception e)
{
    throw;
}

catch (Exception e)
{
    throw e;
}

如果您将finallyIDisposable一起使用,则以下两段代码几乎相同,但有一些细微差别:

  • 当对象为空时,using语句不会为您提供NullReferenceException
  • 使用try - finally技术时,变量仍保留在范围内,但在处置完毕后不建议非常使用任何对象。但是,您仍然可以将变量重新分配给其他内容。

    Something obj = null; 尝试 {     obj = new Something()     // 做一点事 } 最后 {     obj.Dispose(); }

using (var obj = new Something())
{
    // Do something
}

答案 1 :(得分:10)

到目前为止,你有一些很好的答案,但到目前为止他们没有提到有一个有趣的区别。考虑:

try { ImpersonateAdmin(); DoWork(); } 
finally { RevertImpersonation(); }

VS

try { ImpersonateAdmin(); DoWork(); }
catch { RevertImpersonation(); throw; }
finally { RevertImpersonation(); }

假设DoWork抛出。

现在第一个问题是"是否有一个可以处理此异常的catch块",因为如果答案是" no"那么程序的行为是实现定义的。运行时可能会选择立即终止进程,它可能会选择在终止进程之前运行finally块,它可能会选择在未处理的异常点启动调试器,它可能会选择执行任何喜欢的操作。具有未处理异常的程序可以执行任何操作。

因此运行时开始寻找catch块。这个try语句中没有,所以它查找调用堆栈。假设它找到一个带有异常过滤器的。它需要知道过滤器是否允许处理异常,因此过滤器在模拟恢复之前运行 。如果过滤器意外或故意做了只有管理员可以做的事情,它就会成功!这可能不是你想要的。

在第二个示例中,catch立即捕获异常,恢复模拟,抛出,现在运行时开始寻找catch块来处理重新抛出。现在,如果有一个过滤器,则在之后运行,然后恢复模拟。 (当然最终会再次恢复;我认为恢复模仿在这里是幂等的。)

这是这两个代码段之间的重要区别。如果绝对肯定禁止 任何代码,以查看由try尝试搞砸并被finally清理的全局状态,那么你必须在最终之前捕获。 "最后"并不意味着"立即",这意味着"最终"。

答案 2 :(得分:4)

两个try / catch语句都是等价的,因为它们重新抛出了捕获的原始异常。然而,空catch更宽泛(正如Venemo已经说过的那样,捕捉非托管异常)。如果要catch异常并将其捕获到变量中,则可以将其用于日志记录,或者在将原始异常作为参数传递时可以throw新异常 - 使其成为{{3} }。

finally无论如何都将起作用。

  

应该使用哪一个,在我们不需要记录异常的情况下,我们明确假设调用者将处理异常,例如在文件流中写入或发送电子邮件。

如果调用者将处理异常并且您不需要在此级别记录异常的发生,那么您根本不应该捕获。如果调用者将处理抛出的异常,则不需要捕获异常只是为了重新抛出异常。

捕获将被重新抛出的异常的有效理由:

  • throw new Exception("WTF Happened", ex); // Use as inner exception
  • 记录例外
  • 使用finally块执行一些清理代码