.NET的一般异常处理策略

时间:2009-06-26 17:32:15

标签: c# .net exception-handling exception-logging

我习惯在每种方法中使用try / catch块。这样做的原因是我可以在违规点捕获每个异常并记录它。从我的阅读和与他人的对话中我理解,这不是一种流行的观点。人们应该只抓住人们准备处理的事情。但是,如果我没有抓到违规点,那么就永远不可能记录那次违规行为并了解它。注意:当我抓住并且不处理时,我仍然会抛出。这允许我让异常传播到将处理它的东西,但仍然允许我在违规点记录它。

那么......如何在每个方法中避免try / catch,但仍然在错误发生时记录错误?

11 个答案:

答案 0 :(得分:18)

不,不要抓住一切。异常在堆栈上向上传播。您所要做的就是确保在异常到达堆栈顶部之前捕获异常。

这意味着,例如,您应该使用try / catch块包围事件处理程序的代码。事件处理程序可能是“堆栈顶部”。对于ThreadStart处理程序或来自异步方法的回调也是如此。

您还希望捕获图层边界上的异常,但在这种情况下,您可能只想将异常包装在特定于图层的异常中。

对于ASP.NET,您可以决定允许ASP.NET Health Monitoring为您记录异常。

但是你肯定不需要在每种方法中捕获异常。这是一个重要的反模式。我会大声反对你用这种异常处理来检查代码。

答案 1 :(得分:9)

您可以在堆栈跟踪中查看所有内容 - 无需尝试/捕获每个方法。

坚持几条规则:

  1. 仅当您要使用自定义例外类型
  2. 时才使用try / catch
  3. 仅当上层需要知道
  4. 时才定义新的例外类型
  5. 尝试/捕获顶级而不是为每个方法执行此操作

答案 2 :(得分:4)

好的,在阅读了所有答案,说你应该在顶层进行一次尝试/捕获,我将会考虑另一种观点。

我不会在每个方法中放置一个try / catch,远离它。但是我使用try / catch来解决我预期会失败的代码段(例如打开文件),以及我想在异常中添加其他信息的地方(要在链中更高的位置记录) )。

堆栈跟踪说明和“拒绝权限”的消息可能足以让您作为程序员找出问题所在,但我的目标是为用户提供有意义的信息,例如“无法打开文件'C:\ lockedfile.txt'。权限被拒绝。”。

如:

private void DoSomethingWithFile(string filename)
{
    // Note: try/catch doesn't need to surround the whole method...

    if (File.Exists(filename))
    {
        try
        {
            // do something involving the file
        }
        catch (Exception ex)
        {
            throw new ApplicationException(string.Format("Cannot do something with file '{0}'.", filename), ex);
        }
    }
}

我还想提一下,即使那些说“只有一次尝试/捕获”的人可能仍会在整个代码中使用try / finally,因为这是保证正确清理等的唯一方法。

答案 3 :(得分:1)

要在发生时这样做,你仍然需要一个try / catch。但是你并不一定需要到处捕捉异常。它们向上传播调用堆栈,当它们被捕获时,您将获得堆栈跟踪。因此,如果出现问题,您可以根据需要添加更多尝试/捕获。

考虑检出可用的众多日志框架之一。

答案 4 :(得分:1)

我认为你不需要在违规时抓住一切。您可以冒泡出异常,然后使用StackTrace找出实际发生的违规点。

另外,如果你需要一个try catch块,我听过的最好的方法是在一个方法中隔离它,以免用巨大的try catch块混乱代码。另外,在try语句中尽可能少地生成语句。

当然,重申一下,将您的异常冒充到顶层并记录堆栈跟踪比在整个代码中嵌套try-catch-log-throw块更好。

答案 5 :(得分:1)

我会考虑使用ELMAH进行异常处理,这几乎是“让异常发生”的概念。 ELMAH将负责记录它们,您甚至可以将其设置为在特定项目的例外达到或超过特定阈值时通过电子邮件发送给您。在我的部门,我们尽可能远离try / catch块。如果应用程序出现问题,我们想立即知道问题是什么,以便我们可以修复它,而不是压缩异常并在代码中处理它。

如果发生异常,则意味着某些事情不对。我们的想法是让您的应用程序只执行它应该执行的操作。如果它正在做一些不同的事情并导致异常,那么您的响应应该是解决它发生的原因,而不是让它发生并在代码中处理它。这只是我/我们的理念,并不适合所有人。但是,由于某种原因“吃”异常并且没有人知道出现问题,我们都被烧毁了太多次。

从来没有,永远都会遇到一般的例外。始终,总是,总是捕获最具体的异常,以便如果抛出异常,但它不是您期望的类型,您再次知道,因为应用程序将崩溃。如果您只是捕获(Exception e),那么无论抛出什么类型的异常,您的catch块现在都将负责响应可能抛出的每种类型的异常。如果没有,那么你会遇到整个“吃”的例外情况,在这种情况下出现问题,但你可能永远都不知道,直到可能为时已晚。

答案 6 :(得分:0)

  

如何在每个方法中避免try / catch,但仍然在错误发生时记录错误?

这取决于托管环境。 Asp.Net,WinForms和WPF都有不同的方法来捕获未处理的异常。但是,一旦全局处理程序传递了异常实例,您就可以确定异常的抛出点,因为每个异常都包含一个堆栈跟踪。

答案 7 :(得分:0)

  1. 捕获并重新抛出您无法处理的异常只不过是浪费处理器时间。如果您无法对异常执行任何操作,请忽略它并让调用者响应它。
  2. 如果要记录每个异常,全局异常处理程序就可以了。在.NET中,堆栈跟踪是一个对象;它的属性可以像任何其他属性一样进行检查。您可以将堆栈跟踪的属性(甚至以字符串形式)写入您选择的日志。
  3. 如果要确保捕获每个异常,则应使用全局异常处理程序。事实上,没有应用程序应该没有应用程序。
  4. 您的catch块应捕获您知道可以正常恢复的异常。也就是说,如果您可以做一些事情,请抓住它。否则,让来电者担心。如果没有调用者可以对它做任何事情,让全局异常处理程序捕获它,并记录它。

答案 8 :(得分:0)

我绝对不会在每个方法周围使用try catch包装器(奇怪的是,我在第一次启动时就这样做了,但那是在我学会更好的方法之前)。

1)为了防止程序崩溃和用户丢失信息,我这样做

                runProgram:
                try
                {
                    container.ShowDialog();
                }
                catch (Exception ex)
                {
                    ExceptionManager.Publish(ex);
                    if (MessageBox.Show("A fatal error has occurred.  Please save work and restart program.  Would you like to try to continue?", "Fatal Error", MessageBoxButtons.YesNo) == DialogResult.Yes)
                        goto runProgram;
                    container.Close();
                }

容器是我的应用程序启动的地方所以这基本上会在我的整个应用程序周围放置一个包装器,这样就不会导致无法恢复的崩溃。这是我不太介意使用goto的罕见情况之一(它是一小部分代码并且仍然可读)

2)我只会在我预期会出现问题的方法中捕获异常(例如超时)。

3)作为一个可读性点,如果你在try部分中有一堆try代码的try catch块和catch部分中的一堆代码,那么最好将该代码提取到一个命名良好的方法。

   public void delete(Page page) 
   {
      try 
      {
         deletePageAndAllReferences(page)
      }
      catch (Exception e) 
      {
         logError(e);
      }
   }

答案 9 :(得分:0)

实际上,避免粒度尝试/捕获。允许异常在堆栈中向上遍历并尽可能高地捕获。如果您有一个特定的关注点,那么如果您担心异常级联,请将日志记录放在即时捕获中 - 尽管您仍然可以通过钻取内部异常来解决这些问题。

异常处理不应该是事后的想法。确保始终如一地执行此操作。我看到很多人从每个方法的开头到结尾放置了一个广泛的try / catch并捕获了一般异常。人们认为这有助于他们获得更多信息,而事实上并非如此。在某些情况下,越多越少,越少越好。我从不厌倦“应该用异常来记录异常行为的公理”。如果可以,请进行恢复,并尝试减少总体异常的数量。当您尝试解决问题并在出现问题时看到数百个相同的NullReferenceException或类似内容时,没有什么比这更令人沮丧了。

答案 10 :(得分:0)

实施例外,除非抛出它们,否则它们没有成本。

这对我来说,表现的影响不是一个强有力的论据。特殊条件通常是......例外。