例外狀況可用來指出執行程式時發生錯誤。 會建立描述錯誤的例外狀況物件,。 運行時間接著會搜尋最相容的例外狀況處理程式。
當下列一或多個條件成立時,程式設計人員應該擲回例外狀況:
方法無法完成其定義的功能。 例如,如果方法的參數具有無效的值:
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的屬性。 此字串包含目前呼叫堆疊上方法的名稱,以及每個方法擲回例外狀況的檔名和行號。 共通語言執行平台 (CLR) 會從 StackTrace 語句所在的點自動建立 throw
物件,因此例外狀況必須從堆疊追蹤應該開始的點擲回。
所有例外狀況都包含名為 Message的屬性。 此字串應設定為說明例外狀況的原因。 對安全性敏感的信息不應該放在消息正文中。 除了 Message之外,ArgumentException 還包含名為 ParamName 的屬性,該屬性應設定為造成擲回例外狀況的自變數名稱。 在屬性 setter 中,ParamName 應設定為 value
。
每當公共和受保護的方法無法完成其預定的功能時,就會擲回例外狀況。 擲回的例外狀況類別是符合錯誤條件的最特定例外狀況。 這些例外狀況應記錄為類別功能的一部分,而原始類別的衍生類別或更新應保留相同的行為,以保持回溯相容性。
拋出例外狀況時應避免的事項
下列清單會識別擲回例外狀況時應避免的做法:
- 請勿使用例外狀況來變更程式流程,作為一般執行的一部分。 使用例外狀況來報告及處理錯誤狀況。
- 例外狀況不應以傳回值或參數的形式傳回,而不是擲回。
- 請勿在您自己的原始程式碼中故意擲回 System.Exception、System.SystemException、System.NullReferenceException或 System.IndexOutOfRangeException。
- 請勿建立可在偵錯模式中擲回但無法以發行模式擲回的例外狀況。 若要在開發階段識別運行時間錯誤,請改用偵錯判斷提示。
工作傳回方法中的例外狀況
使用 async
修飾詞宣告的方法在例外狀況方面有一些特殊考慮。
async
方法中擲回的例外狀況會儲存在傳回的工作中,並且直到等待該工作時才會出現。 如需預存例外狀況的詳細資訊,請參閱 異步例外狀況。
建議您先驗證自變數,並擲回任何對應的例外狀況,例如 ArgumentException 和 ArgumentNullException,再輸入方法的異步部分。 也就是說,這些驗證例外狀況應該會在工作開始之前同步出現。 下列程式碼片段展示了一個範例,其中,若拋出例外狀況,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來建立自己的例外狀況類別。 衍生類別應該至少定義三個建構函式:一個無參數建構函式、一個設定訊息屬性,另一個同時設定 Message 和 InnerException 屬性。 例如:
[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# 語法和使用方式的最終來源。