筆記目錄

Skip to content

淺談 throw 與 throw ex 的差異

TLDR

  • 使用 throw; 可以保留原始的 Stack Trace,確保除錯時能追蹤到錯誤發生的原始位置。
  • 使用 throw ex; 會重置 Stack Trace,導致錯誤堆疊資訊遺失,應避免使用。
  • 若需拋出新的 Exception,應將原始 Exception 作為 InnerException 傳入,以保留錯誤上下文。
  • catch 區塊內僅執行 throw; 而無其他邏輯,則應移除該 try...catch 區塊。
  • .NET 5 以上版本提供 CA2200 規則,會自動偵測並警告 throw ex 的不當使用。

throw 與 throw ex 的差異分析

在 C# 的 try...catch 區塊中,throw;throw ex; 的行為有顯著差異。

使用 throw 保留堆疊資訊

什麼情況下會遇到這個問題:當需要在 catch 區塊中攔截例外,進行記錄或處理後,再將原始例外繼續向上拋出時。

使用 throw; 可以重新拋出捕捉到的 Exception,並完整保留原始的 Exception 堆疊資訊。這對於除錯至關重要,因為它能提供正確的錯誤發生路徑。

csharp
try {
    try {
        int result = Divide(1, 0);
    } catch {
        throw;
    }
} catch (Exception e) {
    Console.WriteLine(e.ToString());
}

static int Divide(int numerator, int denominator) {
    return numerator / denominator;
}

執行結果顯示,錯誤路徑明確指向第 3 行的 Divide 方法:

text
System.DivideByZeroException: Attempted to divide by zero.
   at Program.<<Main>$>g__Divide|0_0(Int32 numerator, Int32 denominator) in D:\Programming\Projects\TestThrow\TestThrow\Program.cs:line 12
   at Program.<Main>$(String[] args) in D:\Programming\Projects\TestThrow\TestThrow\Program.cs:line 3

使用 throw ex 導致堆疊遺失

什麼情況下會遇到這個問題:開發者誤以為 throw ex 是重新拋出例外的一般寫法,卻忽略了它會將例外物件視為一個「新的」錯誤來源。

使用 throw ex 會建立一個新的 Exception 並拋出,這會重置 Exception 的堆疊資訊,使得追蹤變得困難。

csharp
try {
    try {
        int result = Divide(1, 0);
    } catch (Exception ex) {
        throw ex;
    }
} catch (Exception e) {
    Console.WriteLine(e.ToString());
}

執行結果顯示,錯誤發生位置變成了 throw ex 所在的行數,原始的錯誤路徑已遺失:

text
System.DivideByZeroException: Attempted to divide by zero.
   at Program.<Main>$(String[] args) in D:\Programming\Projects\TestThrow\TestThrow\Program.cs:line 5

TIP

測試結果顯示,使用 throw ex 重新拋出的 Exception,其 InnerExceptionnull,這證實了原始的堆疊資訊確實會遺失。

最佳實踐與建議

正確封裝 Exception

什麼情況下會遇到這個問題:當需要拋出自定義的 Exception,同時又想保留原始錯誤資訊時。

我們應該拋出一個新的 Exception,並將原始 Exception 作為 innerException 參數傳遞。這樣既能增加新的上下文資訊,又能保留原始的堆疊追蹤。

csharp
public class CustomException : Exception {
    public CustomException(string message, Exception innerException)
        : base(message, innerException) {
    }
}

try {
    // 可能發生 Exception 的程式碼...
} catch (Exception ex) {
    // ex 要作為 InnerException 傳入,才能保留資訊
    throw new CustomException("額外的資訊", ex);
}

避免無效的 try...catch

什麼情況下會遇到這個問題:在程式碼中撰寫了 try...catch 區塊,但 catch 內部僅執行 throw; 而未進行任何額外處理。

catch 區塊內沒有任何處理邏輯,僅僅是 throw;,則應直接移除該 try...catch 區塊,因為它對程式執行沒有任何正面影響。

csharp
// 建議移除此類無意義的 catch
try {
    // 可能發生 Exception 的程式碼...
} catch {
    throw;
}

異動歷程

  • 2025-07-31 初版文件建立。