共用方式為


處理例外狀況的最佳作法

更新:2007 年 11 月

設計良好的錯誤處理程式碼區塊可以讓程式更為穩定,並且更不容易因為應用程式處理此類錯誤而當機。下列清單包含有關處理例外狀況的最佳作法的建議:

  • 要知道何時設定 Try/Catch 區塊。例如,您可以利用程式設計方式檢查可能發生的情況,而不需使用例外處理。在其他情形中,使用例外處理來攔截錯誤情況則是適當的。

    下列範例使用 if 陳述式來檢查連接是否關閉。您可以使用這個方法代替擲回例外狀況,如果連接沒有關閉的話。

       If conn.State <> ConnectionState.Closed Then
          conn.Close()
       End If
    
       if(conn.State != ConnectionState.Closed)
          conn.Close();
    

    在下列範例中,如果連接未關閉,例外狀況會擲回。

       Try
          conn.Close()
       Catch ex As InvalidOperationException
          'Do something with the error or ignore it.
       End Try
    
       try {
         conn.Close();
       }
       catch(InvalidOperationException ex) {
         //Do something with the error or ignore it.
       }
    

    您選擇的方法取決於您預期事件會發生的頻率。如果事件的確是異常的,並且是個錯誤 (例如意料不到的檔案結尾),最好使用例外處理,因為在正常情況中執行的程式碼比較少。如果事件常態性地發生,使用程式設計方法來檢查錯誤會比較好。在這個情形中,如果例外狀況發生,例外狀況將花較久時間來處理。

  • 在可能產生例外狀況的程式碼周圍使用 Try/Finally 區塊,並在一個位置上集中管理您的 Catch 陳述式。以這樣的方式,Try 陳述式產生例外狀況、Finally 陳述式關閉或解除資源配置,而 Catch 陳述式從中央位置處理例外狀況。

  • 固定地將 Catch 區塊中的例外狀況從最特殊的排列到最不特殊的。這個技術在特定例外狀況傳遞至更為一般的 Catch 區塊之前來處理它。

  • 以文字 "Exception" 做為例外狀況類別名稱的結尾。例如:

    Public Class EmployeeListNotFoundException
        Inherits Exception
    
    public class MyFileNotFoundException : Exception {
    }
    
  • 當建立使用者定義的例外狀況時,您必須確保例外狀況的中繼資料可供程式碼作遠端執行,包括當例外狀況會跨應用程式定義域發生時例如,假定應用程式定義域建立應用程式定義域 B,它執行會擲回例外狀況的程式碼。如果要讓應用程式定義域 A 正確地攔截並處理例外狀況,它必須能夠尋找含有應用程式定義域 B 擲回之例外狀況的組件。如果應用程式定義域 B 擲回例外狀況,包含於其應用程式基底之下,但不是在應用程式定義域 A 的應用程式基底之下的組件,應用程式定義域 A 將無法尋找例外狀況,而 Common Language Runtime 則將擲回 FileNotFoundException。若要避免這個情形,您可以用兩種方式部署含有例外狀況資訊的組件:

    • 將組件置入兩個應用程式定義域都共用的通用應用程式基底

      -或-

    • 如果定義域不共用通用應用程式基底,則以強式名稱 (Strong Name) 簽署含有例外狀況資訊的組件,並將組件部署到全域組件快取中。

  • 在 C# 和 C++ 中,建立您自己的例外狀況類別時,至少要使用三個通用建構函式。如需範例,請參閱 HOW TO:建立使用者定義的例外狀況

  • 在大部分情形中,使用預先定義的例外狀況類型。只針對程式設計案例定義新的例外狀況類型。引進新例外狀況類別,以使程式設計人員能夠根據例外狀況類別在程式碼中採取不同動作。

  • 對於大多數應用程式,請從 Exception 類別衍生自訂例外狀況。原先一般認為自訂例外狀況應該衍生自 ApplicationException 類別,然而實際上這樣做的幫助不大。

  • 將當地語系化的說明字串包含於一切例外狀況中。當使用者看到錯誤訊息時,它乃是衍生自擲回的例外狀況的說明字串,而非衍生自例外狀況類別。

  • 使用文法正確的錯誤訊息,包括結束的標點符號。例外狀況說明字串中的每一句應該以句號結束。

  • 提供 Exception 屬性以供程式設計方式存取。只有當額外的資訊在某程式設計案例時有用到時,才要將額外資訊包含於例外狀況 (除了說明字串之外) 中。

  • 傳回 Null 給甚為平常的錯誤情形。例如,如果找不到檔案,Open 會傳回 null,如果檔案被鎖定則擲回例外狀況。

  • 設計類別以讓例外狀況在正常使用時決不會擲回。例如,FileStream 類別公開另一種判斷是否已經抵達檔案結尾的方式。這避免萬一您讀取超過檔案結尾時,會擲回例外狀況。下列範例示範如何讀取至檔案結尾。

    Class FileRead
    
        Public Sub Open(ByVal fileToRead As FileStream)
    
            ' This If statement is optional
            ' as it is very unlikely that
            ' the stream would ever be null
            If IsDBNull(fileToRead) Then
                Throw New System.ArgumentNullException()
            End If
    
            Dim b As Integer
    
            ' Set the stream position to the beginning of the file.
            fileToRead.Seek(0, SeekOrigin.Begin)
    
            ' Read each byte to the end of the file.
            For i As Integer = 0 To fileToRead.Length
                b = fileToRead.ReadByte()
                Console.Write(b.ToString())
                ' Or do something else with the byte.
            Next
        End Sub
    
    End Class
    class FileRead {
        public void Open(FileStream fileToRead) 
        {
    
            // This if statement is optional
            // as it is very unlikely that
            // the stream would ever be null.
            if (fileToRead == null)
            {
                throw new System.ArgumentNullException();
            }
    
            int b;
    
            // Set the stream position to the beginning of the file.
            fileToRead.Seek(0, SeekOrigin.Begin);
    
            // Read each byte to the end of the file.
            for (int i = 0; i < fileToRead.Length; i++)
            {
                b = fileToRead.ReadByte();
                Console.Write(b.ToString());
                // Or do something else with the byte.
            }
        }
    } 
    
  • 如果屬性集或方法呼叫對物件的目前狀態而言並不適當,就會擲回 InvalidOperationException

  • 如果傳遞無效的參數,就會擲回 ArgumentException 或從 ArgumentException 衍生的類別。

  • 堆疊追蹤在擲回例外狀況的陳述式開始,並在攔截例外狀況的 Catch 陳述式結束。在決定將 Throw 陳述式放置何處時,請留意這個事實。

  • 使用例外狀況產生器 (Builder) 方法。類別在它的實作中從不同的地方擲回相同的例外狀況是很常見的。若要避免過多的程式碼,請使用 Helper 方法,以建立例外狀況並將它傳回。例如:

    Class File
       Private fileName As String
    
       Public Function Read(bytes As Integer) As Byte()
          If Not ReadFile(handle, bytes) Then
             Throw NewFileIOException()
          End If
       End Function 'Read
    
       Function NewFileIOException() As FileException
          Dim description As String = __unknown ' Build localized string, including fileName.
          Return New FileException(description) '
       End Function 'NewFileIOException
    End Class 'File
    
    class File {
        string fileName;
        public byte[] Read(int bytes) {
            if (!ReadFile(handle, bytes))
                throw NewFileIOException();
        }
        FileException NewFileIOException() {
            string description = // Build localized string, including fileName.
            return new FileException(description);
         }
    }
    

    此外,使用例外狀況的建構函式來建置例外狀況。這對全域例外狀況類別而言更為適當,例如 ArgumentException

  • 擲回例外狀況來代替傳回錯誤碼或 HRESULT。

  • 在擲回例外狀況時清除中繼結果。呼叫端應該能夠假設,在例外狀況自方法擲回時不會有不良的後果。

請參閱

其他資源

處理和擲回例外狀況