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