最佳实践是异常处理永远不应该隐藏问题。这意味着try-catch块应该非常少。
在三种情况下使用try-catch是有意义的。
Always deal with known exceptions as low-down as you can. However, if you're expecting an exception it's usually better practice to test for it first. For instance parse, formatting and arithmetic exceptions are nearly always better handled by logic checks first, rather than a specific try-catch.
If you need to do something on an exception (for instance logging or roll back a transaction) then re-throw the exception.
Always deal with unknown exceptions as high-up as you can - the only code that should consume an exception and not re-throw it should be the UI or public API.
假设你连接到一个远程API,在这里你知道预期某些错误(在这些情况下有一些事情),所以这是情况1:
try
{
remoteApi.Connect()
}
catch(ApiConnectionSecurityException ex)
{
// User's security details have expired
return false;
}
return true;
注意,没有捕获其他异常,因为它们不是预期的。
现在假设您试图将一些东西保存到数据库中。如果失败,我们必须回滚它,所以我们有情况2:
try
{
DBConnection.Save();
}
catch
{
// Roll back the DB changes so they aren't corrupted on ANY exception
DBConnection.Rollback();
// Re-throw the exception, it's critical that the user knows that it failed to save
throw;
}
注意,我们重新抛出了异常——更高级别的代码仍然需要知道某些事情失败了。
最后我们有了UI——这里我们不想有完全未处理的异常,但我们也不想隐藏它们。这里是情况3的例子:
try
{
// Do something
}
catch(Exception ex)
{
// Log exception for developers
WriteException2LogFile(ex);
// Display message to users
DisplayWarningBox("An error has occurred, please contact support!");
}
然而,大多数API或UI框架都有处理情形3的通用方法。例如ASP。Net有一个黄色的错误屏幕,用来转储异常详细信息,但是在生产环境中可以用更通用的消息代替。遵循这些是最佳实践,因为它节省了大量代码,但也因为错误记录和显示应该是配置决策,而不是硬编码。
这意味着情况1(已知异常)和情况3(一次性UI处理)都有更好的模式(避免预期错误或将错误处理交给UI)。
即使情况2也可以被更好的模式所取代,例如事务作用域(使用块回滚在块期间未提交的任何事务)使开发人员更难获得错误的最佳实践模式。
例如,假设你有一个大规模的ASP。网络应用程序。错误日志可以通过ELMAH进行记录,错误显示可以是本地的信息YSoD和生产中的漂亮的本地化消息。数据库连接都可以通过事务作用域并使用块。您不需要单个的try-catch块。
TL;DR:最佳实践实际上是根本不使用try-catch块。