处理exception,这是一个好方法吗?

我们正在努力处理在我们的应用程序中正确处理exception的策略。 这是我们的目标(总结):

  • 只处理特定的exception。
  • 只处理可以纠正的exception
  • 只logging一次。

我们已经提出了一个解决scheme,涉及一个通用的特定于应用程序的例外,并在一段代码中这样工作:

try { // Do whatever } catch(ArgumentNullException ane) { // Handle, optinally log and continue } catch(AppSpecificException) { // Rethrow, don't log, don't do anything else throw; } catch(Exception e) { // Log, encapsulate (so that it won't be logged again) and throw Logger.Log("Really bad thing", e.Message, e); throw new AppSpecificException(e) } 

logging所有exception,然后转向AppSpecificException,以便不会再次logging。 最终,如果必要的话,它将会到达最后的处理事件处理程序。

我没有太多的exception处理模式的经验…这是一个很好的方式来解决我们的目标? 它有什么重大的缺点或大的红色警告?

注意:这样做的缺点之一是,在第一次捕获之后,你将失去处理特定exception的能力(如果调用一个方法调用另一个方法,而第二个抛出一个exception则无法处理)我发现我从来没有这样做过…我只处理一个深度的exception…

如果在第一次抛出exception的时候loggingexception,则不会logging完整的堆栈跟踪。

处理exception(即修复它们),尽可能接近抛出时间。 尽可能快地收集关于上下文的信息。 但是允许exception传播到实际可以处理的地方。 日志logging是最后一种处理,所以它应该发生在应用子系统的外层。

这应该不需要使用特定于应用程序的exception作为标记来logging不应该被捕获的exception。

不要loggingexception,然后重新抛出exception – 呼叫者有责任处理/logging您生成的任何exception。

只捕获一个exception来处理它(例如logging它),或添加上下文特定的信息。

这是解决exception处理问题的一个相当常见的方法(从更具体到更具体)。

只要记住,如果要捕获特定的问题,使用一个通用的ApplicationSpecificexception来捕获该应用程序/方法中发生的所有事情并不是一个好主意。 最后尝试扩展更具体的例外。

重新抛出exception是好的,更好的是声明方法抛出一些exception,并让调用者处理它们。 这样你就不得不创build更less的代码,你可以集中一些控件。

解决堆栈跟踪问题的第一个选项:

 class AppSpecificException : ApplicationException { public string SpecificTrace { get; private set; } public string SpecificMessage { get; private set; } public AppSpecificException(string message, Exception innerException) { SpecificMessage = message; SpecificTrace = innerException.StackTrace; } } 

我不得不写一个例子来理解这个问题,并检查stacktrace问题,这是我的代码,注意button2_click方法,最后我的文本框显示崩溃string和stacktrace:

  private String internalValue; private void Operation1(String pField) { if (pField == null) throw new ArgumentNullException("pField"); internalValue = pField; } private void Operation2(Object pField) { if (pField == null) throw new ArgumentNullException("pField"); internalValue = Convert.ToInt32(pField).ToString(); } private void Operation3(String pField) { if (pField == null) throw new ArgumentNullException("pField"); internalValue = pField; Operation2(-1); } /// <exception cref="AppSpecificException"><c>AppSpecificException</c>.</exception> private void button1_Click(object sender, EventArgs e) { try { Operation1("One"); Operation2("Two"); Operation3("Three"); MessageBox.Show(internalValue); } catch (ArgumentNullException ex) { textBoxException.Text = ex.Message + (char) 13 + (char) 10 + ex.StackTrace; } catch (AppSpecificException ex) { //textBoxException.Text = ex.Message + (char)13 + (char)10 + ex.StackTrace; throw; } catch (Exception ex) { textBoxException.Text = ex.Message + (char)13 + (char)10 + ex.StackTrace; throw new AppSpecificException("crash", ex); } } private void button2_Click(object sender, EventArgs e) { try { button1_Click(sender, e); } catch (AppSpecificException ex) { textBoxException.Text = ex.SpecificMessage + (char) 13 + (char) 10 + ex.SpecificTrace; } } 

尽量避免创build新的exception并重新抛出,因为抛出一个exception会将堆栈跟踪设置为引发exception的地方。 只是做一个普通的投掷。 请参阅Eric Lippert博客上的重复使用 。

我build议多想一下你想用什么样的模式来“处理”

如果您的处理模式logging或重新抛出,则重新抛出的错误最终将被logging下来。 所以最后,这只是错误logging。 如果你使用ASP.NET,使用elmah,那么至less你的代码不会被try / catch-and-log锅炉覆盖。

只有几个方法来“处理”不仅仅是日志logging而终止的错误。

重试。 (注意无限循环)

等待,然后重试。

尝试不同但等同的技术(不能连接上http?尝试连接上https)。

build立缺less的条件(创build抛出FolderNotFoundException的文件夹)

忽略错误 – 再三考虑一下,只有当错误不是真正的问题时才有意义,比如第三方库警告你不适用的情况。

关于exception处理的一个很好的解决scheme是使用拦截。 但是,您必须validation此模式是否可以应用于您的应用程序,具体取决于架构:拦截需要一个容器。

其原理是通过使用属性(自定义)对方法外的方法进行分解,然后使用容器初始化实例。 容器将通过reflection代理这些实例:它的被调用的实例拦截器。 您只需像往常一样通过这些实例调用您的方法,让拦截机制完成您在方法之前或之后编码的工作。

请注意,您可以在方法中添加try catch来pipe理拦截器中未pipe理的特定exception。

统一拦截: http : //msdn.microsoft.com/en-us/library/dd140045.aspx