C# 中 throw 用於**拋出**異常,try-catch 用於**捕獲並處理**異常。兩者分工明確,配合使用構建健壯的錯誤處理體系。本文透過實例說明何時用哪個。
| 關鍵字 | 角色 | 執行流向 | 典型使用場景 |
|---|---|---|---|
throw | 拋出者 | 向上傳遞,尋找最近的 catch | 驗證失敗、守護條件不滿足、重新拋出 |
try-catch | 捕獲者 | 攔截異常,執行恢復/記錄邏輯 | 外部呼叫、資源釋放、轉換為業務異常 |
finally | 清理者 | 無論是否異常皆執行 | 關閉檔案、釋放連線、歸還鎖 |
public class Person {
private int _age;
public int Age {
get => _age;
set {
if (value < 0)
throw new ArgumentOutOfRangeException(nameof(value), "年齡不可為負數");
_age = value;
}
}
}
// 呼叫端捕獲處理
try {
person.Age = -5; // 觸發 throw
} catch (ArgumentOutOfRangeException ex) {
Log.Error(ex.Message);
MessageBox.Show("輸入錯誤:" + ex.Message);
}
ArgumentNullException, ArgumentOutOfRangeException)。InsufficientStockException。catch 記錄日誌後 throw; 保留堆疊追蹤,讓上層決定最終處理。SqlException → 包裝為 DataAccessException 拋出,隱藏實作細節。using 區塊隱式 try-finally;手動管理時需 try { } finally { resource.Dispose(); }。UseExceptionHandler、WinForms Application.ThreadException、WPF DispatcherUnhandledException。| 反模式 | 問題 | 改進 |
|---|---|---|
捕獲 Exception 後吞沒 | 掩蓋真實錯誤,難以除錯 | 至少記錄日誌,或僅捕獲具體異常型別 |
| 用異常控制正常流程 | 效能差、語意不清 | 用布林返回值、TryParse 模式、Option/Maybe 型別 |
throw ex; 而非 throw; | 丟失原始堆疊追蹤 | 永遠用 throw; 重新拋出 |
[Serializable]
public class BusinessException : Exception {
public string ErrorCode { get; }
public BusinessException(string code, string message) : base(message) {
ErrorCode = code;
}
public BusinessException(string code, string message, Exception inner)
: base(message, inner) { ErrorCode = code; }
protected BusinessException(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { }
}
總結:throw 是「發現問題、拋出問題」,try-catch 是「面對問題、解決問題」。合理分工,讓異常在正確層級被處理,是寫出可維護 C# 程式的關鍵。