1. 認識 finally 區塊
清理和釋放資源
想像一下:你在化學實驗室做實驗,結束後(就算你炸掉幾個燒瓶),你還是得收拾工作區、沖洗化學品、關燈。這就是 finally 區塊的用途——它總是會在 try 和 catch 之後執行,不管有沒有發生例外。
try
{
// 這裡寫「危險」的程式碼,可能會丟出例外
}
catch
{
// 這裡捕捉並處理例外
}
finally
{
// 這段程式碼一定會執行,不管有沒有例外
}
這有什麼用? 最主要就是保證一定會釋放資源:像是關閉檔案、資料庫連線、解鎖伺服器房的門...如果沒有 finally,發生錯誤時有些資源可能會「卡住」——這就很麻煩了。比如檔案被鎖住,連管理員都打不開。
為什麼不能全部寫在 catch?
我們能不能直接在 catch 釋放資源?理論上可以。但如果一切順利,catch 根本不會執行。如果你想確保資源一定會被釋放,就得用 finally。
現實中常常有這種情況,不管成功還是失敗——都一定要收拾。這就是 finally 存在的理由。
2. finally 區塊的特點
什麼時候 finally 不會執行?
這題有陷阱!finally 幾乎一定會執行。就算 try 裡面有 return(提前離開方法),或是丟出新的例外,finally 還是會被執行。
static void Test()
{
try
{
Console.WriteLine("До return");
return;
}
finally
{
Console.WriteLine("finally всё равно сработает!");
}
}
//呼叫 Test()
Test();
輸出結果:
// До return
// finally всё равно сработает!
不過如果你的應用程式突然「硬掛」了(像是電腦被關機、process 被 kill 或整個 CLR 掛掉),那 finally 就不會執行。這種情況也沒辦法啦。
finally 運算子和呼叫堆疊
呼叫堆疊我們下堂課會細講,這裡先簡單說:它就像一疊被呼叫的方法,程式會「往下走」,如果找不到合適的 catch。
還有一點很重要:如果 try 發生例外,但 catch 沒有處理到(比如沒有對應的 handler),程式就會離開目前的方法,繼續往呼叫堆疊上層找合適的 catch。但在這之前,每一層都會先執行 finally 區塊。
3. throw 運算子:自己丟例外
什麼是 throw,為什麼要用它
有時候光是捕捉例外還不夠——你可能需要自己產生一個錯誤,把它「丟」出去。這時就要用 throw 運算子。
throw new Exception("這是我的特製錯誤!");
throw 就是在跟 CLR 說:「我這裡發現很嚴重的問題,要丟一個 Exception,剩下的讓呼叫這段程式的人去處理」。
throw 不建立新例外的用法
你可以在 catch 區塊裡用 throw;,把剛剛捕捉到的例外再丟出去——比如你處理了一部分錯誤,但剩下的想交給上層程式碼處理。
try
{
DangerousOperation();
}
catch (Exception ex)
{
LogError(ex);
throw; // 再次丟出目前的例外,呼叫堆疊資訊會保留
}
如果你寫 throw ex;,呼叫堆疊的資訊就會不見——這是個壞習慣。
4. finally 跟 throw 怎麼一起用?
就算丟例外,finally 還是會執行
來看看,如果 try 裡面有 throw,但我們也有 finally,會發生什麼事:
try
{
Console.WriteLine("丟錯誤前...");
throw new Exception("try 裡發生錯誤!");
}
catch
{
Console.WriteLine("Catch 捕捉到錯誤。");
}
finally
{
Console.WriteLine("Finally 執行了。");
}
結果:
丟錯誤前...
Catch 捕捉到錯誤。
Finally 執行了。
如果沒有合適的 catch,finally 還是會在程式崩潰前執行。
細節:如果 finally 裡也丟 throw 會怎樣?
如果 finally 裡面也發生 throw,這個例外會取代前面的例外。也就是說 try/catch 發生什麼錯誤都會被蓋掉。所以不建議在 finally 裡再丟 throw,如果前面已經有例外了。
try
{
throw new Exception("try 裡的錯誤");
}
finally
{
throw new Exception("finally 裡的錯誤");
}
// 結果:外面只會看到 "finally 裡的錯誤"
5. 實用建議和常見錯誤
新手最常忘記的事
- 釋放資源時沒用 finally 區塊,只靠 catch。
- 把可能會丟新例外的程式碼寫進 finally——這會造成意外的錯誤。
- 忘記 return 在 try 裡不會「跳過」 finally——它還是會執行。
finally 的替代方案:該選哪個?
自從有了 using(我們之後會詳細講),釋放資源變得更方便,但其實 using 底層還是用 finally。遇到特殊情況(像是解鎖或發送錯誤訊息)——還是得用 finally。
為什麼面試官都愛問 finally
每個寫過高負載 server 的面試官都愛問 finally。通常會問:「如果 try 裡有 return,finally 裡有 throw 會怎樣?」或「發生例外時資源一定會被釋放嗎?」現在你不只會回答,還真的懂了。你不只可以解釋 finally 怎麼運作,還能說明它的用途、什麼時候該用,還有為什麼沒有它就沒辦法寫出嚴謹的程式碼。
GO TO FULL VERSION