.NET的一般exception处理策略

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

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

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

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

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

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

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

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

坚持几条规则:

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

好的,已经阅读了所有答案,说你应该在顶层有一个尝试/捕获,我将用另一种观点来衡量。

我不会在每种方法中使用try / catch,远非如此。 但是我使用try / catch来解决我希望失败的代码段(例如打开文件),以及我想在exception中添加其他信息的位置(要在链中更高的位置记录)。

堆栈跟踪说法和“拒绝许可”的消息可能足以让您作为程序员找出问题所在,但我的目标是为用户提供有意义的信息,例如“无法打开文件”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,因为这是保证正确清理等的唯一方法。

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

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

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

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

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

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

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

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

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

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

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

我绝对不会在每个方法周围使用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)我只捕获我期望出现问题的方法中的exception(例如超时)。

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

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

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

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

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

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