try/catch/finally 是 C# 結構化異常處理的基石。熟練掌握這三個區塊的協作方式,能讓程式在發生錯誤時既能妥善處理,又能確保資源正確釋放。本文透過實際程式碼範例,示範如何將底層異常轉換為更具語意的高層異常,以及 finally 在資源管理上的關鍵角色。
try-catch 的核心目的是攔截工作程式碼產生的異常。部分異常可在 catch 中完全處理並恢復執行;更多情況下,我們需要將其轉換為更適合呼叫端理解的異常類型。
當方法接收索引參數存取陣列時,若發生越界,原始的 IndexOutOfRangeException 屬於實作細節,對呼叫者而言,ArgumentOutOfRangeException 更能精確表達「參數值不合法」的語意。
class TestTryCatch
{
static int GetInt(int[] array, int index)
{
try
{
return array[index];
}
catch (IndexOutOfRangeException e)
{
Console.WriteLine(e.Message);
// 將原始異常包裝為 InnerException,保留完整錯誤鏈
throw new ArgumentOutOfRangeException(
"index 參數超出範圍", e);
}
}
}
try 區塊。catch 指定要處理的具體異常類型(IndexOutOfRangeException)。InnerException,提供呼叫端完整診斷脈絡。InnerException 為唯讀屬性,必須在建構時指定。finally 的存在意義在於「保證執行」。即使 try 區塊中發生異常、catch 處理中途再次擲回、或使用 return 離開方法,finally 區塊仍會執行。這使其成為釋放非受控資源(檔案控制代碼、資料庫連線、網路通訊端等)的最佳位置。
static void CodeWithoutCleanup()
{
FileStream file = null;
FileInfo fileInfo = new FileInfo(@"C:\file.txt");
file = fileInfo.OpenWrite();
file.WriteByte(0xF);
file.Close(); // 若 WriteByte 擲回異常,Close 永不會被呼叫
}
若 WriteByte 擲回異常,file.Close() 不會執行,檔案控制代碼洩漏,直到記憶體回收器介入(時機不可預期)。
static void CodeWithCleanup()
{
FileStream file = null;
FileInfo fileInfo = null;
try
{
fileInfo = new FileInfo(@"C:\file.txt");
file = fileInfo.OpenWrite();
file.WriteByte(0xF);
}
catch (UnauthorizedAccessException e)
{
Console.WriteLine(e.Message);
}
finally
{
// OpenWrite 可能失敗,file 可能為 null,必須檢查
if (file != null)
{
file.Close();
}
}
}
OpenWrite() 可能在建立 FileStream 前失敗,導致 file 保持 null。直接呼叫 file.Close() 會擲回 NullReferenceException,汙染原始錯誤資訊。finally 區塊應盡量純粹執行清理,若清理邏輯本身可能失敗,需內部自行處理。SqlConnection.Close(),確保連線儘快歸還連線池,避免耗盡可用連線數。對於實作 IDisposable 的物件,C# 提供 using 語法糖,編譯器會自動展開為 try/finally 模式,程式碼更簡潔:
static void CodeWithUsing()
{
using (FileStream file = new FileInfo(@"C:\file.txt").OpenWrite())
{
file.WriteByte(0xF);
} // 編譯器保證這裡呼叫 file.Dispose()(相當於 Close())
}
這是現代 C# 開發中推薦的資源管理方式。
try 定義受保護區域。catch 處理特定異常,必要時包裝重新擲回並保留 InnerException。finally 確保資源釋放,務必檢查 null。using 簡化 IDisposable 物件的清理。掌握這些模式,能讓您的 C# 程式在面對錯誤與資源管理時,展現專業級的穩健度。