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

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

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

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

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


当前回答

我知道这是一个老问题,但这里没有人提到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;     
}

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

其他回答

To me, handling exception can be seen as business rule. Obviously, the first approach is unacceptable. The second one is better one and it might be 100% correct way IF the context says so. Now, for example, you are developing an Outlook Addin. If you addin throws unhandled exception, the outlook user might now know it since the outlook will not destroy itself because of one plugin failed. And you have hard time to figure out what went wrong. Therefore, the second approach in this case, to me, it is a correct one. Beside logging the exception, you might decide to display error message to user - i consider it as a business rule.

没有任何参数的捕获只是简单地吃掉异常,没有任何用处。如果发生致命错误怎么办?如果不带参数地使用catch,就无法知道发生了什么。

catch语句应该捕获更具体的异常,如FileNotFoundException,然后在最后捕获Exception,它将捕获任何其他异常并记录它们。

我的异常处理策略是:

通过连接到应用程序来捕获所有未处理的异常。线程异常事件,然后决定: 对于UI应用程序:向用户弹出一个道歉消息(WinForms) 对于服务或控制台应用程序:将其记录到文件(服务或控制台)

然后我总是在try/catch中附上外部运行的每一段代码:

WinForms基础架构触发的所有事件(加载、单击、选定更改…) 由第三方组件触发的所有事件

然后加上try/catch

我所知道的所有操作可能并不总是有效(IO操作,潜在的零除法计算……)。在这种情况下,我抛出一个新的ApplicationException(“自定义消息”,innerException)来跟踪实际发生的事情

此外,我尽最大努力对异常进行正确排序。但也有例外:

需要立即显示给用户 当它们发生时,需要一些额外的处理来将它们放在一起,以避免级联问题(例如:在TreeView填充期间将. endenddate放在finally部分) 用户并不关心,但重要的是要知道发生了什么。所以我总是记录它们: 在事件日志中 或者在磁盘上的.log文件中

在应用程序顶级错误处理程序中设计一些静态方法来处理异常是一个很好的实践。

我还强迫自己试着:

记住,所有的异常都被冒泡到顶层。没有必要把异常处理程序放在任何地方。 可重用的或深度调用的函数不需要显示或记录异常:它们要么自动生成,要么在异常处理程序中使用一些自定义消息重新抛出。

最后:

Bad:

// DON'T DO THIS; ITS BAD
try
{
    ...
}
catch 
{
   // only air...
}

用处:

// DON'T DO THIS; IT'S USELESS
try
{
    ...
}
catch(Exception ex)
{
    throw ex;
}

have a try finally without a catch是完全正确的:

try
{
    listView1.BeginUpdate();

    // If an exception occurs in the following code, then the finally will be executed
    // and the exception will be thrown
    ...
}
finally
{
    // I WANT THIS CODE TO RUN EVENTUALLY REGARDLESS AN EXCEPTION OCCURRED OR NOT
    listView1.EndUpdate();
}

我在顶层做的是:

// i.e When the user clicks on a button
try
{
    ...
}
catch(Exception ex)
{
    ex.Log(); // Log exception

    -- OR --
    
    ex.Log().Display(); // Log exception, then show it to the user with apologies...
}

我在一些被调用的函数中所做的:

// Calculation module
try
{
    ...
}
catch(Exception ex)
{
    // Add useful information to the exception
    throw new ApplicationException("Something wrong happened in the calculation module:", ex);
}

// IO module
try
{
    ...
}
catch(Exception ex)
{
    throw new ApplicationException(string.Format("I cannot write the file {0} to {1}", fileName, directoryName), ex);
}

异常处理(自定义异常)有很多事情要做,但我试图记住的那些规则对于我做的简单应用程序已经足够了。

下面是一个扩展方法的例子,它以一种舒适的方式处理捕获的异常。它们以一种可以链接在一起的方式实现,并且非常容易添加您自己捕获的异常处理。

// Usage:

try
{
    // boom
}
catch(Exception ex)
{
    // Only log exception
    ex.Log();

    -- OR --

    // Only display exception
    ex.Display();

    -- OR --

    // Log, then display exception
    ex.Log().Display();

    -- OR --

    // Add some user-friendly message to an exception
    new ApplicationException("Unable to calculate !", ex).Log().Display();
}

// Extension methods

internal static Exception Log(this Exception ex)
{
    File.AppendAllText("CaughtExceptions" + DateTime.Now.ToString("yyyy-MM-dd") + ".log", DateTime.Now.ToString("HH:mm:ss") + ": " + ex.Message + "\n" + ex.ToString() + "\n");
    return ex;
}

internal static Exception Display(this Exception ex, string msg = null, MessageBoxImage img = MessageBoxImage.Error)
{
    MessageBox.Show(msg ?? ex.Message, "", MessageBoxButton.OK, img);
    return ex;
}

有时需要处理与用户无关的异常。

我的方法是:

在应用程序级别捕获未捕获的异常。在global.asax中)用于关键异常(应用程序可能没有用处)。这些例外情况我不太了解。只要在应用程序级别上登录它们,让系统完成它的工作。 捕捉“on place”并向用户显示一些有用的信息(输入错误的数字,无法解析)。 抓住位置,对边缘问题什么都不做,比如“我将在后台检查更新信息,但服务没有运行”。

它绝对不需要成为最佳实践。: -)

更好的方法是第二种(指定异常类型的方法)。这样做的好处是,您知道这种类型的异常可以发生在您的代码中。您正在处理这种类型的异常,您可以继续。如果出现任何其他异常,那么这意味着有错误,这将帮助您找到代码中的错误。应用程序最终会崩溃,但你会知道你错过了什么(bug),需要修复。