除錯時快速定位錯誤行號能大幅縮短排查時間。C# Exception.StackTrace 包含完整呼叫堆疊,但預設輸出冗長。本文示範如何擷取關鍵行號資訊,並介紹更現代化的 CallerLineNumber 屬性與結構化日誌最佳實踐。
try
{
int num = Convert.ToInt32(""); // 故意觸發 FormatException
}
catch (Exception ex)
{
MessageBox.Show(ex.StackTrace);
}
輸出範例:
位於 System.Number.ThrowOverflowOrFormatException(ParsingStatus status, TypeCode type)
位於 System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info)
位於 System.Convert.ToInt32(String value)
位於 ConsoleApp.Program.Main() 位於 D:\Projects\Test\Program.cs:行 14
缺點:資訊過多、格式隨 .NET 版本/語系變化、難以程式化解析。
catch (Exception ex)
{
string trace = ex.StackTrace;
int idx = trace.IndexOf("行號"); // 繁體中文環境為 "行號",簡體為 "行号"
if (idx >= 0)
MessageBox.Show(trace.Substring(idx));
}
僅顯示「行號 14」等關鍵片段,適合快速除錯,但相依當地語系字串,移植性差。
using System.Diagnostics;
catch (Exception ex)
{
var st = new StackTrace(ex, true); // true = 捕獲檔案行號
var frame = st.GetFrame(0); // 0 = 拋出例外的當前幀
string file = frame.GetFileName();
int line = frame.GetFileLineNumber();
int col = frame.GetFileColumnNumber();
string method = frame.GetMethod()?.Name;
MessageBox.Show($"錯誤位置:{file}:{line}:{col} 方法:{method}");
}
優點:強型別、不受語系影響、可取得檔案、行、欄、方法完整資訊。需編譯為 Debug 模式 或開啟 <DebugSymbols>true</DebugSymbols> / <DebugType>full</DebugType>,Release 建構預設不包含 PDB 行號資訊。
using System.Runtime.CompilerServices;
public static void LogError(string message,
[CallerFilePath] string file = "",
[CallerLineNumber] int line = 0,
[CallerMemberName] string member = "")
{
Debug.WriteLine($"[{file}:{line} {member}] {message}");
}
// 使用時完全不需傳參數
try { ... }
catch (Exception ex)
{
LogError(ex.Message); // 自動帶入呼叫端檔案、行號、方法名
}
優點:零效能損耗、Release 也能拿到行號、程式碼極乾淨。缺點:只能拿到「呼叫 LogError 那一行」,而非「例外拋出那一行」。
生產環境建議引入 Serilog / NLog + Seq / ELK,配合 LogContext 豐富上下文:
Log.Error(ex, "轉換失敗 | Input={Input}", input);
自動附加完整 StackTrace、機器名、執行緒 ID、相關性 ID,支援全文檢索與告警。
| 方法 | 適用場景 | 優點 | 缺點 |
|---|---|---|---|
| ex.StackTrace 字串 | 快速 Console 除錯 | 零依賴 | 冗長、語系相依、難解析 | 字串截取「行號」 | 臨時修補 | 一行程式碼 | 極脆弱、不跨語系 | StackTrace + StackFrame | 庫開發、需精確定位 | 標準 API、強型別 | 需 PDB、Release 預設無行號 | CallerLineNumber | 日誌封裝、現代專案 | 編譯期注入、Release 可用 | 只能拿到呼叫端行號 |
StackFrame + Debug 建構即時定位。CallerLineNumber 封裝日誌助手 + 結構化日誌框架,確保 Release 也能追蹤。MessageBox.Show,改用統一日誌管線,便於集中監控。