例外處理
更新: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,則不應該加以處理,因為此例外狀況的根本原因完全不知道,且應用程式無法確知是否可以安全地繼續執行。
請勿過度使用攔截,應該經常允許例外狀況散佈到呼叫堆疊上。
攔截您無法合法處理的例外狀況將會隱藏重大的偵錯資訊。
請使用 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 版會將不符合 CLS 標準的例外狀況包裝在衍生自 Exception 的類別中。
Portions Copyright 2005 Microsoft Corporation.All rights reserved.
Portions Copyright Addison-Wesley Corporation.All rights reserved.
如需設計方針的詳細資訊,請參閱由 Krzysztof Cwalina 和 Brad Abrams 所著,並由 Addison-Wesley 於 2005 年發行的「Framework 設計方針:可重複使用之 .NET 程式庫的慣例、慣用語法和模式」一書。