Share via


處理例外狀況的最佳作法

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

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

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

If conn.State <> ConnectionState.Closed Then
    conn.Close()
End IF
if (conn.State != ConnectionState.Closed)
{
    conn.Close();
}
if (conn->State != ConnectionState::Closed)
{
    conn->Close();
}

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

Try
    conn.Close()
Catch ex As InvalidOperationException
    Console.WriteLine(ex.GetType().FullName)
    Console.WriteLine(ex.Message)
End Try
try
{
    conn.Close();
}
catch (InvalidOperationException ex)
{
    Console.WriteLine(ex.GetType().FullName);
    Console.WriteLine(ex.Message);
}
try
{
    conn->Close();
}
catch (InvalidOperationException^ ex)
{
    Console::WriteLine(ex->GetType()->FullName);
    Console::WriteLine(ex->Message);
}

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

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

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

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

Public Class MyFileNotFoundException
    Inherits Exception
End Class
public class MyFileNotFoundException : Exception
{
}
public ref class MyFileNotFoundException : public 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 ReadAll(fileToRead As FileStream)
        ' This if statement is optional
        ' as it is very unlikely that
        ' the stream would ever be null.
        If fileToRead Is Nothing 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 - 1
            b = fileToRead.ReadByte()
            Console.Write(b.ToString())
            ' Or do something else with the byte.
        Next i
    End Sub
End Class
class FileRead
{
    public void ReadAll(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.
        }
    }
}
class FileRead
{
public:
    void ReadAll(FileStream^ fileToRead)
    {
        // This if statement is optional
        // as it is very unlikely that
        // the stream would ever be null.
        if (fileToRead == nullptr)
        {
            throw gcnew 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 FileReader
    Private fileName As String


    Public Sub New(path As String)
        fileName = path
    End Sub

    Public Function Read(bytes As Integer) As Byte()
        Dim results() As Byte = FileUtils.ReadFromFile(fileName, bytes)
        If results Is Nothing
            Throw NewFileIOException()
        End If
        Return results
    End Function

    Function NewFileIOException() As FileReaderException
        Dim description As String = "My NewFileIOException Description"

        Return New FileReaderException(description)
    End Function
End Class
class FileReader
{
    private string fileName;

    public FileReader(string path)
    {
        fileName = path;
    }

    public byte[] Read(int bytes)
    {
        byte[] results = FileUtils.ReadFromFile(fileName, bytes);
        if (results == null)
        {
            throw NewFileIOException();
        }
        return results;
    }

    FileReaderException NewFileIOException()
    {
        string description = "My NewFileIOException Description";

        return new FileReaderException(description);
    }
}
ref class FileReader
{
private:
    String^ fileName;

public:
    FileReader(String^ path)
    {
        fileName = path;
    }

    array<Byte>^ Read(int bytes)
    {
        array<Byte>^ results = FileUtils::ReadFromFile(fileName, bytes);
        if (results == nullptr)
        {
            throw NewFileIOException();
        }
        return results;
    }

    FileReaderException^ NewFileIOException()
    {
        String^ description = "My NewFileIOException Description";

        return gcnew FileReaderException(description);
    }
};

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

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

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

請參閱

概念

處理和擲回例外狀況