2009-10-18 12 views
13

den Unterschied zwischen throw ex und Wurf, warum die ursprüngliche Stacktrace in diesem Beispiel erhaltenen Grundlegendesoriginal Stacktrace/Zeilennummern in .NET Ausnahmen Erhaltung

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; 
     } 
    } 

Das zweite Szenario produziert die gleiche Ausgabe wie werfen ex würde?

In beiden Fällen erwartet man die Zeilennummer, wo y initialisiert wird.

Antwort

14

Ich bin nicht sicher, ob diese Einschränkung innerhalb der C# -Sprache, der CLI oder der Microsoft-Implementierung dieser ist, aber Ihr zweites Beispiel ist ein Fall, in dem ein expliziter Aufruf an Exception.InternalPreserveStackTrace erforderlich ist, wie in dem folgenden Beitrag dokumentiert. Da diese Methode internal ist, muss sie im Allgemeinen durch Reflexion aufgerufen werden. Die damit verbundenen Leistungsprobleme können durch Erstellen einer Action<Exception> für den Anruf fast vollständig gemildert werden, wie am Ende dieser Antwort gezeigt.

Referenz: Rethrowing exceptions and preserving the full call stack trace

Edit: Nach Neuüberprüfen ECMA-335 Partition I §12.4.2 (Exception Handling) und Partition III §4.24 (rethrow), glaube ich jetzt, dass das Verhalten, das Sie sehen werden, ist ein semantischer Fehler in der CLR (Microsofts Implementierung der CLI). Der einzige spezifische Verweis auf das Verhalten ist "A rethrow ändert die Stack-Ablaufverfolgung in dem Objekt nicht." In dem hier beschriebenen Fall ändert der erneute Durchlauf tatsächlich die Stapelverfolgung, wodurch der PreserveStackTrace Hack ein Workaround für einen bekannten CLR-Fehler wird.

static void LongFaultyMethod() 
{ 
    try 
    { 
     int x = 20; 
     int y = x/(x - 20); 
    } 
    catch (Exception ex) 
    { 
     PreserveStackTrace(ex); // <-- add this line 
     throw; 
    } 
} 

PreserveStackTrace hier ist eine Optimierung des einer aus diesem Blog-Eintrag:

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); 
} 
+0

Das ist großartig, danke für die Info. – Nariman

3

Weil im zweiten Beispiel Ausnahme von derselben Methode erneut ausgelöst wird. In der ersten ist es von einer anderen Methode, warum geworfen. In einem Methodenumfang kann die Stapelverfolgung nur eine sein.

Gehen Sie folgendermaßen vor: Der beste Weg besteht darin, eine Ausnahme immer in eine neue Ausnahme zu schreiben, sodass die Ausnahmetiefe angezeigt wird.

„Wenn rethrow hat im gleichen Verfahren (Ausnahme-Stack-Trace in erteilt worden hat nur eine Zeilennummer pro Methode, die Sie nie Stack-Trace siehe , dass in Verfahren A, bei der Zeilennummer 2 Ausnahme geschleudert und dann in derselben wurde Verfahren A, wurde es von der Linie Nummer 17, wird es nur enthalten letzte Zeilennummer von wo Ausnahme war erneut ausgelöst“

try   
{    
    int x = 20;    
    int y = x/(x - 20);   
}   
catch (Exception ex)   
{    
    // do something here.. like log or something 
    throw new Exception("Internal Exception", ex);   
} 
erneut ausgelöst
+0

Ich verstehe, dass dies eine Work-around ist, aber ich bin immer noch nicht verstehen, warum die rethrow in Arbeit 2. Beispiel sollte nicht die richtige Zeilennummer haben. – Sam

+0

Es ist keine Problemumgehung. Es ist die richtige Art, es zu tun ... – awe

+0

@Akash Kava: Haben Sie eine Referenz?Ich bin nicht überzeugt, dass Sie die Frage des OP vollständig verstanden haben. –