建立和擲回例外狀況

例外狀況是用來表示執行程式時發生錯誤。 建立描述錯誤的例外狀況物件,然後使用 throw 陳述式或運算式擲回。 執行階段接著會搜尋最相容的例外狀況處理常式。

符合下列其中一或多個條件時,程式設計人員應該會擲回例外狀況:

  • 此方法無法完成其定義的功能。 例如,如果方法的參數具有無效值︰

    static void CopyObject(SampleClass original)
    {
        _ = original ?? throw new ArgumentException("Parameter cannot be null", nameof(original));
    }
    
  • 根據物件狀態,對物件進行不適當的呼叫。 其中一個範例可能會嘗試寫入至唯讀檔案。 如果物件狀態不允許作業,則會根據這個類別的衍生來擲回 InvalidOperationException 執行個體或物件。 下列程式碼為擲回 InvalidOperationException 物件的方法範例:

    public class ProgramLog
    {
        FileStream logFile = null!;
        public void OpenLog(FileInfo fileName, FileMode mode) { }
    
        public void WriteLog()
        {
            if (!logFile.CanWrite)
            {
                throw new InvalidOperationException("Logfile cannot be read-only");
            }
            // Else write data to the log and return.
        }
    }
    
  • 方法的引數造成例外狀況時。 在此情況下,應該會攔截到原始例外狀況,並且應該會建立 ArgumentException 執行個體。 應該將原始例外狀況傳遞至 ArgumentException 的建構函式,作為 InnerException 參數:

    static int GetValueFromArray(int[] array, int index)
    {
        try
        {
            return array[index];
        }
        catch (IndexOutOfRangeException e)
        {
            throw new ArgumentOutOfRangeException(
                "Parameter index is out of range.", e);
        }
    }
    

    注意

    上述範例示範如何使用 InnerException 屬性。 其已刻意簡化。 在實務上,您應該先檢查索引是否在範圍內,再使用它。 當參數的成員擲回呼叫成員之前無法預期的例外狀況時,您可以使用這個技術來包裝例外狀況。

例外狀況包含名為 StackTrace 的屬性。 這個字串包含目前呼叫堆疊上的方法名稱,以及擲回每個方法之例外狀況的檔案名稱和行號。 Common Language Runtime (CLR) 會從 throw 陳述式的位置自動建立 StackTrace 物件,因此必須從堆疊追蹤應該開始的位置擲回例外狀況。

所有例外狀況包含名為 Message 的屬性。 此字串應該設為說明例外狀況的原因。 安全性敏感資訊不應該放在訊息文字中。 除了 Message 之外,ArgumentException 還會包含一個名為 ParamName 的屬性,該屬性應設定為導致擲回例外狀況的引數名稱。 在屬性 setter 中,ParamName 應設定為 value

只要 public 和 protected 方法無法完成其預期函式,就會擲回例外狀況。 擲回的例外狀況類別為可符合錯誤狀況的最特定例外狀況。 這些例外狀況應該記錄為類別功能的一部分,而且原始類別的衍生類別或更新應該會保留相同的行為,以進行回溯相容性。

擲回例外狀況時要避免的事項

下列清單可識別擲回例外狀況時要避免的作法:

  • 請勿使用例外狀況,將程式流程變更為一般執行的一部分。 使用例外狀況回報及處理錯誤條件。
  • 例外狀況不應以傳回值或參數形式傳回,而不是加以擲回。
  • 請勿刻意從您自己的原始程式碼擲回 System.ExceptionSystem.SystemExceptionSystem.NullReferenceException 或 System.IndexOutOfRangeException
  • 請勿建立可由偵錯模式擲回,但無法由發行模式擲回的例外狀況。 若要在開發階段期間識別執行階段錯誤,請改用「偵錯判斷提示」。

工作傳回方法中的例外狀況

使用 async 修飾元宣告的方法在例外狀況方面有一些特殊考量。 在 async 方法中擲回的例外狀況會儲存在傳回的工作中,而且要等到等候工作之前才會出現。 如需預存例外狀況的詳細資訊,請參閱非同步例外狀況

建議您先驗證引數,並擲回任何對應的例外狀況,例如 ArgumentExceptionArgumentNullException,再輸入方法的非同步部分。 也就是說,這些驗證例外狀況應該會在工作開始之前同步出現。 下列程式碼片段示範範例,其中如果擲回例外狀況,則 ArgumentException 例外狀況會同步出現,而 InvalidOperationException 會儲存在傳回的工作中。

// Non-async, task-returning method.
// Within this method (but outside of the local function),
// any thrown exceptions emerge synchronously.
public static Task<Toast> ToastBreadAsync(int slices, int toastTime)
{
    if (slices is < 1 or > 4)
    {
        throw new ArgumentException(
            "You must specify between 1 and 4 slices of bread.",
            nameof(slices));
    }

    if (toastTime < 1)
    {
        throw new ArgumentException(
            "Toast time is too short.", nameof(toastTime));
    }

    return ToastBreadAsyncCore(slices, toastTime);

    // Local async function.
    // Within this function, any thrown exceptions are stored in the task.
    static async Task<Toast> ToastBreadAsyncCore(int slices, int time)
    {
        for (int slice = 0; slice < slices; slice++)
        {
            Console.WriteLine("Putting a slice of bread in the toaster");
        }
        // Start toasting.
        await Task.Delay(time);

        if (time > 2_000)
        {
            throw new InvalidOperationException("The toaster is on fire!");
        }

        Console.WriteLine("Toast is ready!");

        return new Toast();
    }
}

定義例外狀況類別

程式可以擲回 System 命名空間中預先定義的例外狀況類別 (但先前註明的項目除外),或藉由衍生自 Exception 來建立自己的例外狀況類別。 衍生類別應至少定義三個建構函式:一個無參數建構函式、一個設定訊息屬性,而另一個設定 MessageInnerException 屬性。 例如:

[Serializable]
public class InvalidDepartmentException : Exception
{
    public InvalidDepartmentException() : base() { }
    public InvalidDepartmentException(string message) : base(message) { }
    public InvalidDepartmentException(string message, Exception inner) : base(message, inner) { }
}

新屬性所提供的資料可用於解決例外狀況時,則應將新屬性新增至例外狀況類別。 如果將新屬性新增至衍生例外狀況類別,則應該覆寫 ToString() 來傳回已新增的資訊。

C# 語言規格

如需詳細資訊,請參閱 C# 語言規格例外狀況throw 陳述式。 語言規格是 C# 語法及用法的限定來源。

另請參閱