更新:2007 年 11 月
下面的准则有助于确保库正确处理异常。
不要通过在框架代码中捕捉非特定异常(如 System.Exception、System.SystemException 等)来处理错误。
如果捕捉异常是为了再次引发或传输给其他线程,则可以捕捉这些异常。下面的代码示例演示的异常处理是不正确的。
Public Class BadExceptionHandlingExample1
Public Sub DoWork()
' Do some work that might throw exceptions.
End Sub
Public Sub MethodWithBadHandler()
Try
DoWork()
Catch e As Exception
' Handle the exception and
' continue executing.
End Try
End Sub
End Class
public class BadExceptionHandlingExample1
{
public void DoWork()
{
// Do some work that might throw exceptions.
}
public void MethodWithBadHandler()
{
try
{
DoWork();
}
catch (Exception e)
{
// Handle the exception and
// continue executing.
}
}
}
避免通过在应用程序代码中捕捉非特定异常(如 System.Exception、System.SystemException 等)来处理错误。某些情况下,可以在应用程序中处理错误,但这种情况极少。
应用程序不应该处理异常,否则可能导致意外状态或可利用状态。如果不能预知所有可能的异常原因,也不能确保恶意代码不能利用产生的应用程序状态,则应该允许应用程序终止,而不是处理异常。
如果捕捉异常是为了传输异常,则不要排除任何特殊异常。
只捕捉能够合法处理的异常,而不要在 catch 子句中创建特殊异常的列表。在非特定异常处理程序中,不能处理的异常不应视为特殊处理的特殊情况。下面的代码示例演示对以再次引发为目的特殊异常进行的不正确测试。
Public Class BadExceptionHandlingExample2
Public Sub DoWork()
' Do some work that might throw exceptions.
End Sub
Public Sub MethodWithBadHandler()
Try
DoWork()
Catch e As Exception
If TypeOf e Is StackOverflowException Or _
TypeOf e Is OutOfMemoryException Then
Throw
End If
End Try
End Sub
End Class
public class BadExceptionHandlingExample2
{
public void DoWork()
{
// Do some work that might throw exceptions.
}
public void MethodWithBadHandler()
{
try
{
DoWork();
}
catch (Exception e)
{
if (e is StackOverflowException ||
e is OutOfMemoryException)
throw;
// Handle the exception and
// continue executing.
}
}
}
如果了解特定异常在给定上下文中引发的条件,请考虑捕捉这些异常。
应该只捕捉可以从中恢复的异常。例如,试图打开不存在的文件而导致的 FileNotFoundException 可以由应用程序处理,因为应用程序可以将问题传达给用户,并允许用户指定其他文件名或创建该文件。如果打开文件的请求会生成 ExecutionEngineException,则不应该处理该请求,因为没有任何把握可以了解该异常的基础原因,应用程序也无法确保继续执行是安全的。
不要过多使用 catch。通常应允许异常在调用堆栈中往上传播。
捕捉无法合法处理的异常会隐藏关键的调试信息。
使用 try-finally 并避免将 try-catch 用于清理代码。在书写规范的异常代码中,try-finally 远比 try-catch 更为常用。
使用 catch 子句是为了允许处理异常(例如,通过纪录非致命错误)。无论是否引发了异常,使用 finally 子句即可执行清理代码。如果分配了昂贵或有限的资源(如数据库连接或流),则应将释放这些资源的代码放置在 finally 块中。
捕捉并再次引发异常时,首选使用空引发。这是保留异常调用堆栈的最佳方式。
下面的代码示例演示一个可引发异常的方法。此方法在后面示例中引用。
Public Sub DoWork(ByVal anObject As Object)
' Do some work that might throw exceptions.
If (anObject = Nothing) Then
Throw New ArgumentNullException("anObject", "Specify a non-null argument.")
End If
' Do work with o.
End Sub
public void DoWork(Object anObject)
{
// Do some work that might throw exceptions.
if (anObject == null)
{
throw new ArgumentNullException("anObject",
"Specify a non-null argument.");
}
// Do work with o.
}
下面的代码示例演示捕捉一个异常,并在再次引发该异常时对它进行错误的指定。这会使堆栈跟踪指向再次引发作为错误位置,而不是指向 DoWork 方法。
Public Sub MethodWithBadCatch(ByVal anObject As Object)
Try
DoWork(anObject)
Catch e As ArgumentNullException
System.Diagnostics.Debug.Write(e.Message)
' This is wrong.
Throw e
' Should be this:
' throw
End Try
End Sub
public void MethodWithBadCatch(Object anObject)
{
try
{
DoWork(anObject);
}
catch (ArgumentNullException e)
{
System.Diagnostics.Debug.Write(e.Message);
// This is wrong.
throw e;
// Should be this:
// throw;
}
}
不要使用无参数 catch 块来处理不符合 CLS 的异常(不是从 System.Exception 派生的异常)。支持不是从 Exception 派生的异常的语言可以处理这些不符合 CLS 的异常。
.NET Framework 2.0 版在 Exception 的派生类中包装了不符合 CLS 的异常。
部分版权所有 2005 Microsoft Corporation。保留所有权利。
部分版权所有 Addison-Wesley Corporation。保留所有权利。
有关设计指南的更多信息,请参见 Krzysztof Cwalina 和 Brad Abrams 编著、Addison-Wesley 于 2005 年出版的“Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries”(《框架设计指南:可重用 .NET 库的约定、术语和模式》)。