CodeGym /課程 /C# SELF /finally 區塊和 throw 運算子

finally 區塊和 throw 運算子

C# SELF
等級 13 , 課堂 2
開放

1. 認識 finally 區塊

清理和釋放資源

想像一下:你在化學實驗室做實驗,結束後(就算你炸掉幾個燒瓶),你還是得收拾工作區、沖洗化學品、關燈。這就是 finally 區塊的用途——它總是會在 trycatch 之後執行,不管有沒有發生例外。


try
{
    // 這裡寫「危險」的程式碼,可能會丟出例外
}
catch
{
    // 這裡捕捉並處理例外
}
finally
{
    // 這段程式碼一定會執行,不管有沒有例外
}
C# 的 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. finallythrow 怎麼一起用?

就算丟例外,finally 還是會執行

來看看,如果 try 裡面有 throw,但我們也有 finally,會發生什麼事:


try
{
    Console.WriteLine("丟錯誤前...");
    throw new Exception("try 裡發生錯誤!");
}
catch
{
    Console.WriteLine("Catch 捕捉到錯誤。");
}
finally
{
    Console.WriteLine("Finally 執行了。");
}
就算丟例外,finally 還是會執行

結果:


丟錯誤前...
Catch 捕捉到錯誤。
Finally 執行了。

如果沒有合適的 catchfinally 還是會在程式崩潰前執行。

細節:如果 finally 裡也丟 throw 會怎樣?

如果 finally 裡面也發生 throw,這個例外會取代前面的例外。也就是說 try/catch 發生什麼錯誤都會被蓋掉。所以不建議在 finally 裡再丟 throw,如果前面已經有例外了。


try
{
    throw new Exception("try 裡的錯誤");
}
finally
{
    throw new Exception("finally 裡的錯誤");
}
// 結果:外面只會看到 "finally 裡的錯誤"
finally 的例外會蓋掉前面的例外

5. 實用建議和常見錯誤

新手最常忘記的事

  • 釋放資源時沒用 finally 區塊,只靠 catch
  • 把可能會丟新例外的程式碼寫進 finally——這會造成意外的錯誤。
  • 忘記 returntry 裡不會「跳過」 finally——它還是會執行。

finally 的替代方案:該選哪個?

自從有了 using(我們之後會詳細講),釋放資源變得更方便,但其實 using 底層還是用 finally。遇到特殊情況(像是解鎖或發送錯誤訊息)——還是得用 finally

為什麼面試官都愛問 finally

每個寫過高負載 server 的面試官都愛問 finally。通常會問:「如果 try 裡有 returnfinally 裡有 throw 會怎樣?」或「發生例外時資源一定會被釋放嗎?」現在你不只會回答,還真的懂了。你不只可以解釋 finally 怎麼運作,還能說明它的用途、什麼時候該用,還有為什麼沒有它就沒辦法寫出嚴謹的程式碼。

2
任務
C# SELF, 等級 13, 課堂 2
上鎖
簡單使用 finally 區塊
簡單使用 finally 區塊
2
任務
C# SELF, 等級 13, 課堂 2
上鎖
再次拋出例外
再次拋出例外
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION