iT邦幫忙

2025 iThome 鐵人賽

DAY 27
0
Software Development

30天快速上手製作WPF選股工具 — 從C#基礎到LiteDB與Web API整合系列 第 27

Day 27 — 開發者閒聊:Try-Catch 怎麼寫才算「有用」?

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20251008/20121224UpTGXEhkJc.png
今天我們暫時放下程式功能開發,聊聊一個每位 C# 工程師都會遇到的問題:
try-catch 要怎麼寫才寫得好?

很多人以為「有捕例外就安全」,但實際上,
不完整的例外處理往往讓 問題更難排查
尤其在系統上線後、使用者回報「某功能無法使用」時,
你打開 Log,發現只有一句:

Exception: Object reference not set to an instance of an object.

這時你應該會想拿鍵盤砸人。


1. 問題不是「沒捕」,而是「沒記」

許多初學者寫的程式可能長這樣:

try
{
    DoWork();
}
catch (Exception)
{
    Console.WriteLine("發生錯誤!");
}

這種寫法的問題是:

  • 完全不知道是哪裡錯。
  • 完全不知道當時傳入的參數是什麼。
  • 完全不知道程式執行到哪一步。

結果是:錯誤發生當下你忽略了,隔天你要查問題時一無所獲。


2. 正確的觀念:Log 下「上下文」

當發生例外時,你需要留下「能讓人重現現場」的資訊。

例如:

try
{
    var result = ProcessStock(code, startDate, endDate);
}
catch (Exception ex)
{
    _logger.LogError(ex,
        "ProcessStock failed. Code={Code}, Start={Start}, End={End}",
        code, startDate, endDate);
    throw; // 保留堆疊
}

這樣做的好處是:

  • 例外類型與訊息會被完整記錄。
  • 參數(例如股票代號、日期區間)也被寫進 Log。
  • 後續排查時能根據這些資訊精準重現問題。

3. 在 Runtime 的「參數」比 Stack Trace 更重要

Stack trace 只告訴你哪一行出錯,
但真正能解決問題的,是當時輸入的參數

舉例來說,你收到錯誤訊息:

ArgumentOutOfRangeException: startDate is later than endDate.

如果沒有記錄參數,你只知道「某個人」輸入錯誤;
但若 Log 中有這樣的訊息:

ProcessStock failed. Code=2330, Start=2025-10-01, End=2025-09-30

你馬上知道錯誤的實際場景(日期順序反了),
不需要連接 DB 或回朔 Request。


4. 不要濫用空的 catch

許多程式為了「不讓畫面壞掉」,會這樣寫:

try
{
    SaveData();
}
catch { }

這是最糟糕的情況,因為:

  • 你失去了所有除錯線索;
  • 可能導致後續邏輯錯亂;
  • 程式表面沒壞,但資料已經不一致。

正確做法:

  • 若不想讓 UI crash,至少記 Log;
  • 若有多層呼叫,盡可能在上層集中記錄;
  • 若不需要重新拋出,也應清楚說明原因。

5. 把 Exception 當成「事件」記錄

例外發生時,除了 Log 下 Exception 本身,也應加上「當下狀態」。

例如在金融系統中:

catch (Exception ex)
{
    _logger.LogError(ex,
        "PriceService.GetQuote failed. Stock={Stock}, API={Api}, Attempt={Attempt}",
        stockCode, apiName, retryCount);
}

這樣當未來出現類似錯誤時,
你可以透過 log 分析「哪個 API 常錯」「哪個代號有問題」「是否出現重試行為」。


6. 例外應該在哪一層處理?

原則上:

  • 底層(Repository、Service):捕例外、記 Log,然後重新拋出。
  • 中層(Use Case / Domain Service):判斷是否屬於可預期錯誤(例如資料重複)。
  • UI 層或最外層:顯示人類可理解的訊息。

簡化流程如下:

Repository → 捕例外 + Log
Service → 捕例外 + 決定是否重試
ViewModel → 捕例外 + 顯示錯誤訊息

7. 小結

寫好 try-catch 並不是防止例外,而是保留足夠的調查線索
一個好的例外處理應該:

  • 記錄所有重要的輸入參數
  • 包含完整的 Exception 與 StackTrace
  • 保持拋出(throw;)以維持原始堆疊
  • 讓後續開發者能重現現場

延伸思考

有時候你會看到團隊導入 全域例外處理(Global Exception Handler)
例如 ASP.NET WebAPI 的 UseExceptionHandler、WPF 的 AppDomain.CurrentDomain.UnhandledException
但即使有全域攔截,也不能取代在關鍵流程中記錄詳細參數的必要性。

全域 Handler 負責「收屍」,
而每個 try-catch 才是「現場記錄員」。



上一篇
Day 26 — 安全儲存 API Token:以 FinMind 為例
系列文
30天快速上手製作WPF選股工具 — 從C#基礎到LiteDB與Web API整合27
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言