在维护我同事的代码时,甚至有人声称自己是高级开发人员,我经常看到以下代码:

try
{
  //do something
}
catch
{
  //Do nothing
}

或者有时他们将日志信息写入日志文件,例如下面的try catch块

try
{
  //do some work
}
catch(Exception exception)
{
   WriteException2LogFile(exception);
}

我只是想知道他们所做的是不是最好的做法?这让我很困惑,因为在我看来,用户应该知道系统发生了什么。


当前回答

留下空白的catch块是最糟糕的做法。如果出现错误,最好的处理方法是:

登录到文件数据库等。 尝试在飞行中修复它(可能尝试做该操作的替代方式) 如果我们不能修复这个问题,那么就通知用户有错误,当然还要中止操作

其他回答

对于异常,我尝试如下:

首先,我捕获特殊类型的异常,如除零、IO操作等,并根据这些编写代码。例如,除以0,这取决于我可以提醒用户的值的出处(例如,一个简单的计算器在中间计算(而不是参数)中到达一个除以0),或者静默地处理这个异常,记录它并继续处理。

然后我尝试捕获剩余的异常并记录它们。如果可能,允许执行代码,否则提醒用户发生错误,并要求他们发送错误报告。

在代码中,是这样的:

try{
    //Some code here
}
catch(DivideByZeroException dz){
    AlerUserDivideByZerohappened();
}
catch(Exception e){
    treatGeneralException(e);
}
finally{
    //if a IO operation here i close the hanging handlers for example
}

我可以告诉你:

代码片段#1是不可接受的,因为它忽略了异常。(它像什么都没发生一样吞下去)。

所以不要添加不做任何事情或只是重新抛出的catch块。

Catch块应该增加一些价值。例如,向最终用户输出消息或记录错误。

不要对正常的流程序逻辑使用异常。例如:

例如输入验证。<-这不是有效的例外情况,而是你应该写方法IsValid(myInput);检查输入项是否有效。

设计代码以避免异常。例如:

int Parse(string input);

如果我们传递一个不能解析为int的值,这个方法会抛出一个异常,我们可以这样写:

bool TryParse(字符串输入,输出int结果);<-该方法将返回布尔值,表示解析是否成功。

也许这有点超出了这个问题的范围,但我希望这将帮助您在涉及try {} catch(){}和异常时做出正确的决定。

你唯一应该让用户担心代码中发生的事情的时候是他们是否可以或需要做一些事情来避免这个问题。如果他们可以更改表单上的数据、按下按钮或更改应用程序设置以避免这个问题,请让他们知道。但是用户无法避免的警告或错误只会让他们对你的产品失去信心。

异常和日志是为您,即开发人员,而不是最终用户准备的。当您捕捉到每个异常时,了解正确的做法要比仅仅应用一些黄金规则或依赖于应用程序范围的安全网要好得多。

盲目的编码是唯一一种错误的编码。事实上,您觉得在这些情况下可以做一些更好的事情,这表明您对良好的编码进行了投资,但避免试图在这些情况下确定一些通用规则,并首先了解抛出某些东西的原因,以及您可以做些什么来从中恢复。

我知道这是一个老问题,但这里没有人提到MSDN的文章,实际上是文档为我澄清了这个问题,MSDN在这方面有一个非常好的文档,当以下条件为真时,你应该捕获异常:

您已经很好地理解了异常可能被抛出的原因,并且可以实现特定的恢复,例如在捕获FileNotFoundException对象时提示用户输入一个新的文件名。 您可以创建并抛出一个新的、更具体的异常。

int GetInt(int[] array, int index)
{
    try
    {
        return array[index];
    }
    catch(System.IndexOutOfRangeException e)
    {
        throw new System.ArgumentOutOfRangeException(
            "Parameter index is out of range.");
    }
}

您希望在将异常传递给其他处理之前对其进行部分处理。在下面的示例中,catch块用于在重新抛出异常之前向错误日志添加条目。

    try
{
    // Try to access a resource.
}
catch (System.UnauthorizedAccessException e)
{
    // Call a custom error logging procedure.
    LogError(e);
    // Re-throw the error.
    throw;     
}

我建议阅读整个“异常和异常处理”部分以及异常的最佳实践。

最佳实践是异常处理永远不应该隐藏问题。这意味着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块。