方案
我有一个执行数据库操作的方法(比方说)。如果在该操作期间引发任何异常,我只想将该异常抛给调用者。我不想在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
}
哪一个比另一个好?应该使用哪一个?
答案 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;
}
如果您将finally
与IDisposable
一起使用,则以下两段代码几乎相同,但有一些细微差别:
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
块执行一些清理代码