下列指导方针有助于确保你的库适当地处理了异常。
不要在框架的代码中通过捕获非特殊的异常(如 System.Exception、System.SystemException,以及等等)来压制异常。
在捕获异常的目标是用来重新抛出异常或者把异常转移到一个不同的线程中的时候,你可以捕获异常。下列代码范例示范了错误的异常处理。
C#
public class BadExceptionHandlingExample1 { public void DoWork() { // 完成一些可能会抛出异常的任务。 } public void MethodWithBadHandler() { try { DoWork(); } catch (Exception e) { // 压制异常并且继续执行。 } } }
避免在应用程序的代码中通过捕获非特殊的异常(如 System.Exception、System.SystemException,以及等等)来压制错误。在有些情况下,压制应用程序中的错误是可以被接受的,但是这种情况是非常罕见的。
应用程序不应该压制能够导致意外的状态或者可利用状态的异常。如果你不能够预知所有可能被导致的异常并且能够确保恶意的代码无法使用到应用程序状态的结果,那么你就应该允许应用程序被终止而不是对异常进行压制。
在为转移异常的目标而进行捕获的时候,不要排斥任何特殊的异常。
在你的 catch 子句中创建特殊的异常列表,你应该只对那些你能够合理地进行处理的异常进行捕获。当然,不能够被你处理的异常也不应该被视为为非特殊异常处理器中的特殊情况。下列代码范例示范了为重新抛出特殊异常的目的而进行的错误测试。
C#
public class BadExceptionHandlingExample2 { public void DoWork() { // 执行一些可能会抛出异常的任务。 } public void MethodWithBadHandler() { try { DoWork(); } catch (Exception e) { if (e is StackOverflowException || e is OutOfMemoryException) throw; // 处理异常并且继续执行。 } } }
在你理解为什么它会从一个给定环境中被抛出的时候,考虑抛出特殊的异常。
你应该只能够捕获那些你能够进行恢复的异常。例如,尝试打开一个并不存在的文件时所产生的 FileNotFoundException 异常就能够通过应用程序而被处理,因为它能够把问题传达给用户并且允许用户指定另外的文件名称或者直接创建该文件。而文件打开请求所产生的 ExecutionEngineException 异常就是不应该被处理的,因为这个异常的根本原因不能够以任何的可确认度而为人所知,并且应用程序也无法确保继续执行将会是安全的。
不要过度地使用捕获。异常应该经常允许被传播到调用堆栈之外。
捕获你不能够进行合理处理的异常来隐藏重要的调试信息。
为用来清理资源的代码而使用 try-finally 并且避免使用 try-catch。在一个编写良好的异常处理代码中,try-finally 要比 try-catch 来得更加通俗。
catch 子句的用途就是允许你处理异常(例如,记录一个非致命的错误)。而 finally 子句则用来允许你执行与清理有关的代码,并且不管是否已经抛出了一个异常。如果你分配了昂贵的或者有限的资源(如数据库连接或者数据流),那么你就应该把释放它们的代码存放到一个 finally 代码区中。
在捕获并且重新抛出一个异常的时候,宁愿使用一个空的 throw。这是保护异常调用堆栈的最佳方式。
下列代码范例说明了一个能够抛出异常的方法。这个方法将在后面的范例中被引用。
C#
public void DoWork(Object anObject) { // 执行一些可能会抛出异常的任务。 if (anObject == null) { throw new ArgumentNullException("anObject", "Specify a non-null argument."); } // 操作 o。 }
下列代码范例示范了一个异常的捕获并且在重新抛出这个异常的时候错误地对它进行了指定。这将导致堆栈追踪把重新抛出异常的位置定位成产生错误的位置,而不是定位到 DoWork 方法。
C#
public void MethodWithBadCatch(Object anObject) { try { DoWork(anObject); } catch (ArgumentNullException e) { System.Diagnostics.Debug.Write(e.Message); // 这是错误的。 throw e; // 应该是这样: // throw; } }
不要使用一个无参数的 catch 代码区来处理非 CLS 兼容的异常(不是继承自 System.Exception 的异常)。支持非继承自 Exception 的异常的编程语言会自由地对这些非 CLS 兼容的异常进行处理。
.NET Framework 2.0 把非 CLS 兼容的异常包装进了一个派生自 Exception 的类中。