基本語法結構
try
{
// 可能拋出例外的程式碼
}
catch (ExceptionType1 ex)
{
// 處理 ExceptionType1 及其衍生類別
}
catch (ExceptionType2 ex)
{
// 處理 ExceptionType2
}
catch // 無參數 catch:捕獲所有非 CLS 例外 (極少見)
{
}
finally
{
// 保證執行:資源清理、鎖釋放、關閉連線
}
執行流程圖解
try
{
int n = 10, m = 0;
float f = n / m; // 拋出 DivideByZeroException
}
catch (DivideByZeroException ex)
{
Console.WriteLine($"除零錯誤:{ex.Message}");
}
catch (Exception ex) // 捕獲其他所有例外
{
Console.WriteLine($"其他錯誤:{ex.Message}");
}
finally
{
Console.WriteLine("finally 區塊永遠執行");
}
Console.WriteLine("finally 之後的程式碼");
關鍵行為驗證
| 情況 | catch 執行 | finally 執行 | 後續程式碼執行 |
| try 正常結束 | 否 | 是 | 是 |
| catch 符合並處理 | 是 | 是 | 是 |
| catch 符合但重新拋出 | 是 | 是 | 否 (傳遞給上層) |
| 無符合 catch | 否 | 是 | 否 (傳遞給上層) |
| try 中 return | 視情況 | 是 (return 前) | 否 |
多重捕獲順序:由具體到一般
// 錯誤順序:Exception 先捕獲,DivideByZeroException 永不會被執行
catch (Exception ex) { }
catch (DivideByZeroException ex) { } // 編譯器警告:無法到達
// 正確順序
catch (DivideByZeroException ex) { }
catch (ArithmeticException ex) { } // 父類別
catch (Exception ex) { }
捕獲所有例外的三種寫法
// 1. 指定 Exception (捕獲所有 CLS-compliant 例外)
catch (Exception ex) { }
// 2. 無參數 catch (捕獲所有,包含非 CLS 例外,極少用)
catch { }
// 3. catch (Exception ex) when (true) - 等同 1,可加篩選
catch (Exception ex) when (true) { }
主動拋出例外:throw
string s = null;
if (s == null)
throw new ArgumentNullException(nameof(s), "輸入不可為空");
// 重新拋出原始例外 (保留堆疊追蹤)
try { Risky(); }
catch (Exception ex)
{
Log(ex);
throw; // 不是 throw ex!throw ex 會重設堆疊追蹤
}
常見例外類別速查
| 例外類別 | 觸發情境 | 繼承自 |
| ArgumentNullException | 參數為 null | ArgumentException |
| ArgumentOutOfRangeException | 參數超出有效範圍 | ArgumentException |
| InvalidOperationException | 物件狀態不適合該操作 | Exception |
| NullReferenceException | 存取 null 參考成員 | Exception |
| DivideByZeroException | 整數除以零 | ArithmeticException |
| IndexOutOfRangeException | 陣列索引越界 | Exception |
| FileNotFoundException | 檔案不存在 | IOException |
| FormatException | 格式轉換失敗 | Exception |
| StackOverflowException | 堆疊溢位 (無法 catch) | Exception |
| OutOfMemoryException | 記憶體不足 (難以復原) | Exception |
常見新手錯誤
- 吞沒例外:
catch { } 或 catch (Exception) { } 不記錄、不處理、不重拋
- throw ex 而非 throw:遺失原始堆疊追蹤,除錯困難
- catch 範圍過大:
catch (Exception) 捕獲本應讓上層處理的嚴重錯誤 (如 OutOfMemoryException)
- 在 finally 拋出例外:會遮蔽 try/catch 中的原始例外
- 用例外控制流程:效能差、語意不清
- 忘記釋放資源:未使用
using 或 finally 關閉 Stream/Connection
最佳實踐速查
- ✅ 使用
using / IDisposable 管理資源
- ✅ 捕獲具體例外類別,頂層再捕獲
Exception 記錄
- ✅ 重新拋出用
throw; 保留堆疊
- ✅ 自訂例外繼承
Exception,加 [Serializable]
- ✅
finally 只做清理,不拋出例外、不返回
- ✅ 非同步方法用
try/catch 包裹 await