try/catch/throw和try/catch(e)/throw e的区别

106

What is the difference between

try { }
catch
{ throw; }

try { }
catch(Exception e)
{ throw e;}

何时应该使用其中一种?

4个回答

153

这些结构

try { ... }
catch () { ... } /* You can even omit the () here */

try { ... }
catch (Exception e) { ... }

try和catch在某些方面是相似的,因为两者都会捕获try块中抛出的每一个异常(除非你只是用它来记录异常,应该避免使用)。现在看看这些:

try { ... }
catch ()
{
    /* ... */
    throw;
}

try { ... }
catch (Exception e)
{
    /* ... */
    throw;
}

try { ... }
catch (Exception e)
{
    /* ... */
    throw e;
}

第一和第二个try-catch块完全相同,它们只是重新抛出当前异常,该异常将保留其“源”和堆栈跟踪。

第三个try-catch块不同。当它抛出异常时,它将更改来源和堆栈跟踪,以便看起来异常已经从这个方法中抛出,从包含该try-catch块的方法中的那行throw e抛出。

哪种应该使用?这实际上取决于每种情况。

假设你有一个名为Person的类,其中有一个.Save()方法,用于将其持久化到数据库。假设您的应用程序在某个地方执行Person.Save()方法。如果您的数据库拒绝保存Person,则.Save()会引发异常。在这种情况下,应该使用throw还是throw e?嗯,这要看具体情况。

我更喜欢这样做:

try {
    /* ... */
    person.Save();
}
catch(DBException e) {
    throw new InvalidPersonException(
       "The person has an invalid state and could not be saved!",
       e);
}

这应该将DBException作为新异常的“内部异常”抛出。因此,当您检查此InvalidPersonException时,堆栈跟踪将包含有关Save方法的信息(这可能足以解决问题),但仍然可以访问原始异常(如果需要)。

最后一点,当您期望发生异常时,应该真正捕捉到那个特定的异常,而不是一般的Exception,也就是说,如果您期望一个InvalidPersonException,应该优先使用:

try { ... }
catch (InvalidPersonException e) { ... }
try { ... }
catch (Exception e) { ... }

35

第一种方式保留了堆栈跟踪信息,而第二种方式重置了它。这意味着,如果您使用第二种方法,则异常的堆栈跟踪将始终从此方法开始,并且您将丢失原始异常跟踪信息,这对于阅读异常日志的人来说可能是灾难性的,因为他永远不会找到异常的根本原因。

当您想要向堆栈跟踪添加其他信息时,第二种方法可能是有用的,但它的使用方式如下:

try
{
    // do something
}
catch (Exception ex)
{
    throw new Exception("Additional information...", ex);
}

有一篇博客文章讨论了这些差异。


那真是很棒的知识! - Myles
那么为什么要使用第二个呢? 只使用第一个更好吗? - Karim
1
第二个在需要检查特定异常(如OutOfRangeException)或需要记录消息等情况下非常有用。第一个似乎是类似于C++中的try{}catch(...){}的通配符异常处理程序。 - 3Dave
1
David,那只适用于catch(Exception e)部分。而且那与throwthrow e是分开的。 - H H

6

你应该使用

try { }
catch(Exception e)
{ throw }

如果你想在重新抛出异常之前对其进行某些操作(例如记录日志),可以使用孤独的throw语句来保留堆栈跟踪。


如果我在这里用“throw e”代替“throw”,会发生什么? - Karim

5
没有参数的catch和catch(Exception e)之间的区别在于,后者可以获取对异常的引用。从框架版本2开始,非托管异常被包装在托管异常中,因此没有参数的异常已经不再有用。 throw;throw e;之间的区别在于前者用于重新抛出异常,而后者用于抛出新创建的异常。如果您使用第二个选项重新抛出异常,它将像新异常一样处理,并替换原始抛出位置的所有堆栈信息。
因此,在问题中都不应该使用任何一种选择。您不应该使用没有参数的catch,并且应该使用throw;重新抛出异常。
此外,在大多数情况下,您应该使用比所有异常的基类更具体的异常类。您只应该捕获预期的异常。
try {
   ...
} catch (IOException e) {
   ...
   throw;
}

如果您想在重新抛出异常时添加任何信息,可以创建一个新的异常,并将原始异常作为内部异常以保留所有信息:
try {
   ...
} catch (IOException e) {
   ...
   throw new ApplicationException("Some informative error message", e);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接