Dot-Net

在 .NET 異常中保留原始 StackTrace/LineNumbers

  • January 14, 2019

了解throw exthrow的區別,為什麼在這個例子中保留了原始的 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 實現)中的語義錯誤。對該行為的唯一具體引用是“Arethrow不會更改對像中的堆棧跟踪”。在此處描述的情況下,重新拋出實際上是在更改堆棧跟踪,使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);
}

引用自:https://stackoverflow.com/questions/1585082