Frage

ich eine Ausnahme mit rethrow "werfen;", aber die Stacktrace ist falsch:

static void Main(string[] args) {
    try {
        try {
            throw new Exception("Test"); //Line 12
        }
        catch (Exception ex) {
            throw; //Line 15
        }
    }
    catch (Exception ex) {
        System.Diagnostics.Debug.Write(ex.ToString());
    }
    Console.ReadKey();
}

Das Recht stacktrace sollte sein:

System.Exception: Test
   at ConsoleApplication1.Program.Main(String[] args) in Program.cs:Line 12

Aber ich bekomme:

System.Exception: Test
   at ConsoleApplication1.Program.Main(String[] args) in Program.cs:Line 15

Aber Linie 15 ist die Position der „werfen;“. Ich habe getestet dies mit .NET 3.5.

War es hilfreich?

Lösung

zweimal Werfen in der gleichen Methode ist wahrscheinlich ein Sonderfall - ich habe nicht in der Lage gewesen, einen Stack-Trace zu schaffen, wo verschiedene Linien in dem gleichen Verfahren aufeinander folgen. Wie das Wort sagt, eine „Stack-Trace“ zeigen Ihnen den Stack-Frames, dass eine Ausnahme verfahren. Und es gibt nur einen Stack-Frame pro Methodenaufruf!

Wenn Sie von einer anderen Methode zu werfen, throw; den Eintrag für Foo() nicht entfernen, wie zu erwarten:

  static void Main(string[] args)
  {
     try
     {
        Rethrower();
     }
     catch (Exception ex)
     {
        Console.Write(ex.ToString());
     }
     Console.ReadKey();
  }

  static void Rethrower()
  {
     try
     {
        Foo();
     }
     catch (Exception ex)
     {
        throw;
     }

  }

  static void Foo()
  {
     throw new Exception("Test"); 
  }

Wenn Sie Rethrower() ändern und ersetzen throw; durch throw ex;, der Foo() Eintrag in dem Stack-Trace verschwindet. Auch das ist das erwartete Verhalten.

Andere Tipps

Es ist etwas, das wie erwartet in Betracht gezogen werden kann. Ändern Stack-Trace ist üblich Fall, wenn Sie throw ex; angeben, FxCop werden Sie als benachrichtigen, dass Stapel geändert wird. Falls Sie throw; machen, wird keine Warnung erzeugt, aber immer noch, wird die Spur geändert werden. Also leider jetzt ist es die beste nicht die Ex oder wirft es als Innen zu fangen. Ich denke, es sollte als Windows Auswirkung oder smth wie die in Betracht gezogen werden - editierte . Jeff Richter beschreibt diese Situation genauer in seiner "CLR via C #" :

  

Der folgende Code führt die gleichen   Ausnahmeobjekt, dass es gefangen und   bewirkt, dass der CLR seinen Ausgang zurücksetzen   Punkt für die Ausnahme:

private void SomeMethod() {
  try { ... }
  catch (Exception e) {
    ...
    throw e; // CLR thinks this is where exception originated.
    // FxCop reports this as an error
  }
}
     

Im Gegensatz dazu, wenn Sie wieder wirft eine   Ausnahmeobjekt durch den Ball mit   Stichwort selbst, hat die CLR nicht   zurückgesetzt, um den Startpunkt des Stapels. Das   folgenden Code wieder wirft die gleiche   Ausnahmeobjekt, dass es gefangen,   wodurch die CLR nicht zurückgesetzt sein   Punkt für die Ausnahme Start:

private void SomeMethod() {
  try { ... }
  catch (Exception e) {
    ...
    throw; // This has no effect on where the CLR thinks the exception
    // originated. FxCop does NOT report this as an error
  }
}
     

In der Tat, der einzige Unterschied zwischen   diese beiden Codefragmente sind, was die   CLR denkt, ist der ursprüngliche Standort   wo war die Ausnahme ausgelöst.    Leider, wenn Sie werfen oder   rethrow eine Ausnahme Windows tut   Reset der Ausgangspunkt des Stapels. So   wenn die Ausnahme wird nicht behandelte,   der Stack-Position, über die berichtet wird   Windows-Fehlerberichterstattung ist die   Position des letzten Wurfes oder   Wiederwurf, auch wenn die CLR weiß   der Stapel Ort, an dem das Original   Ausnahme geworfen wurde. Das ist   bedauerlich, weil es das Debuggen macht   Anwendungen, die in die es versäumt haben,   Feld viel schwieriger. Etwas   Entwickler haben diese gefunden, so   unerträglich, dass sie gewählt, um eine   unterschiedliche Art und Weise ihren Code zu implementieren   um sicherzustellen, dass der Stack-Trace wirklich   spiegelt den Ort, an dem ein   Ausnahme war ursprünglich geworfen:

private void SomeMethod() {
  Boolean trySucceeds = false;
  try {
    ...
    trySucceeds = true;
  }
  finally {
    if (!trySucceeds) { /* catch code goes in here */ }
  }
}

Dies ist eine bekannte Einschränkung in der Windows-Version der CLR. Es verwendet Windows' eingebaute Unterstützung für die Ausnahmebehandlung (SEH). Das Problem ist, ist es Stapelrahmen basiert und ein Verfahren hat nur einen Stapelrahmen. Sie können ganz einfach das Problem, indem Sie den inneren try / catch-Block in einer anderen Hilfsmethode lösen, damit ein weiteren Stapelrahmen zu schaffen. Eine weitere Folge dieser Einschränkung ist, dass der JIT-Compiler wird nicht inline jede Methode, die eine try-Anweisung enthält.

  

Wie kann ich die REAL stacktrace erhalten?

werfen Sie eine neue Ausnahme, und auch die ursprüngliche Ausnahme als die innere Ausnahme.

  

, aber das ist hässlich ... Längerer ... Machen Sie die rigth Ausnahme Wahl zu werfen ....

Sie sind falsch über die hässlich aber direkt über die anderen beiden Punkte. Die Faustregel: nicht fangen, wenn Sie mit ihm etwas tun werden, wie es einpacken, ändern sie, schlucken, oder es loggt sein. Wenn Sie catch entscheiden und dann wieder throw, stellen Sie sicher, dass Sie etwas damit tun, sonst lass es einfach sprudeln.

Sie können auch einen Haken einfach zu setzen versucht sein, so dass Sie innerhalb der Fangstopp-können, aber das Visual Studio-Debugger hat genug Möglichkeiten, dass die Praxis überflüssig zu machen, versuchen Sie stattdessen unter Verwendung eines ersten Chance Ausnahmen oder bedingte Haltepunkte.

Bearbeiten / Ersetzen

Das Verhalten ist tatsächlich anders, aber subtilely so. Wie bei Warum dem Verhalten, wenn andere, ich werde zu einem CLR-Experten verschieben müssen.

EDIT: AlexD Antwort scheint darauf hinzudeuten, dass dies durch Design ist.

Werfen die Ausnahme in der gleichen Methode, dass Fänge es die Situation ein wenig verwirrt, also lassen Sie sich eine Ausnahme von einer anderen Methode zu werfen:

class Program
{
    static void Main(string[] args)
    {
        try
        {
            Throw();
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

    public static void Throw()
    {
        int a = 0;
        int b = 10 / a;
    }
}

Wenn throw; verwendet wird, ist die Aufrufliste (Zeilennummern mit Code ersetzt):

at Throw():line (int b = 10 / a;)
at Main():line (throw;) // This has been modified

Wenn throw ex; verwendet wird, die Aufrufliste ist:

at Main():line (throw ex;)

Wenn Ausnahme nicht abgefangen wird, die Aufrufliste ist:

at Throw():line (int b = 10 / a;)
at Main():line (Throw())

Getestet in .NET 4 / VS 2010

Es gibt eine doppelte Frage href="https://stackoverflow.com/questions/4217616/incorrect-stacktrace-by-rethrow">.

Wie ich es verstehe - Wurf; kompiliert in ‚rethrow‘ MSIL Anweisung und modifiziert das letzte Frame des Stack-Trace.

Ich würde erwarten, dass es den ursprünglichen Stack-Trace zu halten und die Zeile, wo es wieder geworfen wurde, aber anscheinend es kann nur ein Stack-Frame pro Methodenaufruf sein .

Fazit: Vermeiden Sie die Verwendung throw; und wickeln Sie Ihre Ausnahme in einem neuen auf Wieder throwing -. es ist nicht hässlich, es ist am beste Praxis

Sie können Stack-Trace erhalten mit

ExceptionDispatchInfo.Capture(ex);

Hier ist Codebeispiel:

    static void CallAndThrow()
    {
        throw new ApplicationException("Test app ex", new Exception("Test inner ex"));
    }

    static void Main(string[] args)
    {
        try
        {
            try
            {
                try
                {
                    CallAndThrow();
                }
                catch (Exception ex)
                {
                    var dispatchException = ExceptionDispatchInfo.Capture(ex);

                    // rollback tran, etc

                    dispatchException.Throw();
                }
            }
            catch (Exception ex)
            {
                var dispatchException = ExceptionDispatchInfo.Capture(ex);

                // other rollbacks

                dispatchException.Throw();
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
            Console.WriteLine(ex.InnerException.Message);
            Console.WriteLine(ex.StackTrace);
        }

        Console.ReadLine();
    }

Der Ausgang so etwas wie sein:

Test app ex
Test inner ex
   at TestApp.Program.CallAndThrow() in D:\Projects\TestApp\TestApp\Program.cs:line 19
   at TestApp.Program.Main(String[] args) in D:\Projects\TestApp\TestApp\Program.cs:line 30
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at TestApp.Program.Main(String[] args) in D:\Projects\TestApp\TestApp\Program.cs:line 38
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at TestApp.Program.Main(String[] args) in D:\Projects\TestApp\TestApp\Program.cs:line 47

OK, es scheint ein Fehler in dem .NET Framework zu sein, wenn Sie eine Ausnahme auslösen, und rethrow es in der gleichen Methode, die ursprüngliche Zeilennummer verloren geht (es wird die letzte Zeile des Verfahrens sein).

Fortunatelly, ein kluger Mann namens Fabrice MARGUERIE gefunden eine Lösung zu diesem Fehler. Im Folgenden finden Sie meine Version, die Sie in diesem .NET Fiddle testen können.

private static void RethrowExceptionButPreserveStackTrace(Exception exception)
{
    System.Reflection.MethodInfo preserveStackTrace = typeof(Exception).GetMethod("InternalPreserveStackTrace",
      System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
    preserveStackTrace.Invoke(exception, null);
      throw exception;
}

fängt nun die Ausnahme als Regel, aber statt throw; nur diese Methode aufrufen, und voila, wird die ursprüngliche Zeilennummer beibehalten werden!

Nicht sicher, ob dies durch Design, aber ich denke, es ist immer so gewesen ist.

Wenn die ursprüngliche Wurf neue Exception in einem separaten Verfahren ist, dann ist das Ergebnis für throw sollte die ursprünglichen Methode hat Namen und die Zeilennummer und dann die Zeilennummer in Haupt, wo die Ausnahme erneut ausgelöst wird.

Wenn Sie throw ex verwenden, dann wird das Ergebnis nur die Linie in Haupt sein, wo die Ausnahme rethrow ist.

Mit anderen Worten, throw ex verliert alle die Stacktrace, während throw bewahrt den Stack-Trace Geschichte (dh Details der unteren Ebene Methoden). Aber wenn Ihre Ausnahme nach der gleichen Methode wie Ihre rethrow erzeugt wird, dann können Sie einige Informationen verlieren.

NB. Wenn Sie ein sehr einfaches und kleines Testprogramm schreiben, kann der Rahmen optimiert manchmal Dinge und ein Verfahren ändern Inline-Code zu sein, was bedeutet, dass die Ergebnisse von einem ‚echten‘ Programm unterscheiden.

Haben Sie Ihre rechte Zeilennummer wollen? Verwenden Sie einfach ein try / catch pro Methode. In Systemen, gut ... nur in der UI-Ebene, nicht in der Logik oder Datenzugriff, das ist sehr ärgerlich, denn wenn man Datenbank-Transaktionen benötigen, na ja, sollten sie in der UI-Ebene nicht sein, und Sie werden nicht haben die richtige Liniennummer, aber wenn Sie sie nicht brauchen, nicht erneut auslösen nicht mit noch ohne Ausnahme in Fang ...

5 Minuten Beispielcode:

Menü Datei -> Neues Projekt , Platz drei Tasten, und rufen Sie den folgenden Code in jedem:

private void button1_Click(object sender, EventArgs e)
{
    try
    {
        Class1.testWithoutTC();
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message + Environment.NewLine + ex.StackTrace + Environment.NewLine + Environment.NewLine + "In. Ex.: " + ex.InnerException);
    }
}

private void button2_Click(object sender, EventArgs e)
{
    try
    {
        Class1.testWithTC1();
    }
    catch (Exception ex)
    {
            MessageBox.Show(ex.Message + Environment.NewLine + ex.StackTrace + Environment.NewLine + Environment.NewLine + "In. Ex.: " + ex.InnerException);
    }
}

private void button3_Click(object sender, EventArgs e)
{
    try
    {
        Class1.testWithTC2();
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message + Environment.NewLine + ex.StackTrace + Environment.NewLine + Environment.NewLine + "In. Ex.: " + ex.InnerException);
    }
}

Erstellen Sie jetzt eine neue Klasse:

class Class1
{
    public int a;
    public static void testWithoutTC()
    {
        Class1 obj = null;
        obj.a = 1;
    }
    public static void testWithTC1()
    {
        try
        {
            Class1 obj = null;
            obj.a = 1;
        }
        catch
        {
            throw;
        }
    }
    public static void testWithTC2()
    {
        try
        {
            Class1 obj = null;
            obj.a = 1;
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }
}

Ausführen ... die erste Taste ist schön!

Ich denke, das ist weniger ein Fall von Stack-Trace zu ändern und mehr mit der Art und Weise für den Stack-Trace die Zeilennummer zu tun bestimmt wird. Der Versuch, es aus in Visual Studio 2010 ist das Verhalten ähnlich dem, was Sie von der MSDN-Dokumentation erwarten: „throw ex“; baut den Stack-Trace von dem Punkt dieser Aussage, „werfen“; Blätter des Stack-Trace, da es als Ausnahme, dass, wo immer die Ausnahme erneut ausgelöst ist, ist die Zeilennummer die Position des rethrow und der Anruf nicht die Ausnahme kam durch.

So mit "werfen"; der Aufruf der Methode Baum bleibt unverändert, aber die Zeilennummern ändern können.

Ich habe über diese ein paar Mal kommen, und es durch Design sein kann und einfach nicht vollständig dokumentiert. Ich kann verstehen, warum sie dies als die rethrow Lage getan haben konnte, ist sehr nützlich zu wissen, und wenn Ihre Methoden sind einfach genug, um die ursprüngliche Quelle in der Regel offensichtlich ohnehin wäre.

Wie viele andere Leute schon gesagt haben, ist es in der Regel am besten nicht die Ausnahme zu fangen, wenn Sie wirklich haben, und / oder Sie gehen mit ihm an diesem Punkt befassen.

Interessante Randnotiz:. Visual Studio 2010 wird nicht einmal lassen Sie mich den Code bauen, wie in der Frage vorgelegt, wie es die Division durch Null Fehler bei der Kompilierung aufgreift

Das ist, weil Sie die Exception von catched Zeile 12 und haben erneut ausgelöst es auf Zeile 15 , so dass der Stack-Trace nimmt es als Bargeld, dass die Exception geworfen wurde aus es.

Um besser in den Griff Ausnahmen, sollten Sie einfach try...finally verwenden, und lassen Sie die nicht behandelte Exception sprudeln.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top