Dot-Net
在 .NET 異常中保留原始 StackTrace/LineNumbers
了解throw ex和throw的區別,為什麼在這個例子中保留了原始的 StackTrace:
static void Main(string[] args) { try { LongFaultyMethod(); } catch (System.Exception ex) { Console.WriteLine(ex.StackTrace); } } static void LongFaultyMethod() { try { int x = 20; SomethingThatThrowsException(x); } catch (Exception) { throw; } } static void SomethingThatThrowsException(int x) { int y = x / (x - x); }但不是在這個:
static void Main(string[] args) { try { LongFaultyMethod(); } catch (System.Exception ex) { Console.WriteLine(ex.StackTrace); } } static void LongFaultyMethod() { try { int x = 20; int y = x / (x - 20); } catch (Exception) { throw; } }第二種情況是產生與throw ex相同的輸出?
在這兩種情況下,人們都希望看到初始化 y 的行號。
我不確定此限制是否在 C# 語言、CLI 或這些的 Microsoft 實現中,但您的第二個範例是
Exception.InternalPreserveStackTrace需要顯式呼叫的情況,如以下文章中所述。由於這個方法是internal,所以一般要通過反射來呼叫。通過為呼叫創建一個幾乎可以完全緩解其中涉及的性能問題Action<Exception>,如本答案末尾所示。**編輯:**在重新檢查 ECMA-335 Partition I §12.4.2(異常處理)和 Partition III §4.24(重新拋出)之後,我現在認為您看到的行為是 CLR(Microsoft 的 CLI 實現)中的語義錯誤。對該行為的唯一具體引用是“A
rethrow不會更改對像中的堆棧跟踪”。在此處描述的情況下,重新拋出實際上是在更改堆棧跟踪,使PreserveStackTrace黑客成為已知 CLR 缺陷的解決方法。static void LongFaultyMethod() { try { int x = 20; int y = x / (x - 20); } catch (Exception ex) { PreserveStackTrace(ex); // <-- add this line throw; } }
PreserveStackTrace這是對該部落格條目的優化:private static readonly Action<Exception> _internalPreserveStackTrace = (Action<Exception>)Delegate.CreateDelegate( typeof(Action<Exception>), typeof(Exception).GetMethod( "InternalPreserveStackTrace", BindingFlags.Instance | BindingFlags.NonPublic)); public static void PreserveStackTrace(Exception e) { _internalPreserveStackTrace(e); }