560 Stimmen

Gibt es einen Unterschied zwischen "throw" und "throw ex"?

Es gibt bereits einige Beiträge, die nach dem Unterschied zwischen den beiden fragen.
(Warum muss ich das überhaupt erwähnen...)

Aber meine Frage unterscheidet sich insofern, als dass ich "throw ex" in einer anderen Fehler gottgleichen Behandlungsmethode aufrufe.

public class Program {
    public static void Main(string[] args) {
        try {
            // etwas
        } catch (Exception ex) {
            HandleException(ex);
        }
    }

    private static void HandleException(Exception ex) {
        if (ex is ThreadAbortException) {
            // dann ignorieren,
            return;
        }
        if (ex is ArgumentOutOfRangeException) { 
            // dann protokollieren,
            throw ex;
        }
        if (ex is InvalidOperationException) {
            // dann Nachricht anzeigen,
            throw ex;
        }
        // und so weiter.
    }
}

Wenn try & catch im Main verwendet wurden, würde ich throw; verwenden, um den Fehler erneut zu werfen. Aber im obigen vereinfachten Code durchlaufen alle Ausnahmen HandleException.

Hat throw ex; den gleichen Effekt wie das Aufrufen von throw, wenn es innerhalb von HandleException aufgerufen wird?

5 Stimmen

Es gibt einen Unterschied, der damit zu tun hat, ob oder wie der Stapelrückverfolgungsverlauf in der Ausnahme erscheint, aber ich erinnere mich nicht daran, welcher jetzt welcher ist, also werde ich dies nicht als Antwort auflisten.

0 Stimmen

@Joel: Danke. Ich glaube, die Verwendung der HandleError-Ausnahme ist keine gute Idee. Ich wollte nur etwas Fehlerbehandlungscode umstrukturieren.

1 Stimmen

Der dritte Weg besteht darin, eine neue Ausnahme zu umschließen und erneut zu werfen timwise.blogspot.co.uk/2014/05/…

842voto

Marc Gravell Punkte 970173

Ja, es gibt einen Unterschied.

  • throw ex setzt den Stack-Trace zurück (so dass Ihre Fehler scheinbar von HandleException ausgehen würden)

  • throw tut das nicht - der ursprüngliche Verursacher bleibt erhalten.

     static void Main(string[] args)
     {
         try
         {
             Method2();
         }
         catch (Exception ex)
         {
             Console.Write(ex.StackTrace.ToString());
             Console.ReadKey();
         }
     }
    
     private static void Method2()
     {
         try
         {
             Method1();
         }
         catch (Exception ex)
         {
             //throw ex setzt den Stack-Trace zurück, der von Method 1 stammt und gibt ihn an den Aufrufer (Main) weiter
             throw ex;
         }
     }
    
     private static void Method1()
     {
         try
         {
             throw new Exception("Innerhalb von Method1");
         }
         catch (Exception)
         {
             throw;
         }
     }

0 Stimmen

@Scott: Vielen Dank für den Link. Und ich habe auch herausgefunden, wie man den Fehlerhandler in der Nachfolgefrage extrahiert: stackoverflow.com/questions/730300/…

6 Stimmen

@Marc : Es scheint, dass throw den ursprünglichen Übeltäter NUR dann beibehält, wenn das throw nicht in der Methode ist, in der die ursprüngliche Ausnahme geworfen wurde (siehe diese Frage: stackoverflow.com/questions/5152265/…)

0 Stimmen

@Xenan - du bist nicht der Einzige - ich verwechsle es auch immer. Es sieht einfach seltsam aus.

118voto

Shaul Behr Punkte 35201

(Ich habe früher einen Beitrag gepostet, und @Marc Gravell hat mich korrigiert)

Hier ist eine Demonstration des Unterschieds:

static void Main(string[] args) {
    try {
        ThrowException1(); // Zeile 19
    } catch (Exception x) {
        Console.WriteLine("Ausnahme 1:");
        Console.WriteLine(x.StackTrace);
    }
    try {
        ThrowException2(); // Zeile 25
    } catch (Exception x) {
        Console.WriteLine("Ausnahme 2:");
        Console.WriteLine(x.StackTrace);
    }
}

private static void ThrowException1() {
    try {
        DivByZero(); // Zeile 34
    } catch {
        throw; // Zeile 36
    }
}
private static void ThrowException2() {
    try {
        DivByZero(); // Zeile 41
    } catch (Exception ex) {
        throw ex; // Zeile 43
    }
}

private static void DivByZero() {
    int x = 0;
    int y = 1 / x; // Zeile 49
}

und hier ist die Ausgabe:

Ausnahme 1:
   at UnitTester.Program.DivByZero() in \Dev\UnitTester\Program.cs:line 49
   at UnitTester.Program.ThrowException1() in \Dev\UnitTester\Program.cs:line 36
   at UnitTester.Program.TestExceptions() in \Dev\UnitTester\Program.cs:line 19

Ausnahme 2:
   at UnitTester.Program.ThrowException2() in \Dev\UnitTester\Program.cs:line 43
   at UnitTester.Program.TestExceptions() in \Dev\UnitTester\Program.cs:line 25

Sie können sehen, dass bei Ausnahme 1 der Stack-Trace bis zur Methode DivByZero() zurückverfolgt wird, während dies bei Ausnahme 2 nicht der Fall ist.

Beachten Sie jedoch, dass die Zeilennummer, die in ThrowException1() und ThrowException2() angezeigt wird, die Zeilennummer der throw-Anweisung ist, nicht die Zeilennummer des Aufrufs von DivByZero() ist, was vermutlich Sinn macht, wenn ich darüber nachdenke...

Ausgabe im Release-Modus

Ausnahme 1:

at ConsoleAppBasics.Program.ThrowException1()
at ConsoleAppBasics.Program.Main(String[] args)

Ausnahme 2:

at ConsoleAppBasics.Program.ThrowException2()
at ConsoleAppBasics.Program.Main(String[] args)

Wird der ursprüngliche Stack-Trace nur im Debug-Modus beibehalten?

3 Stimmen

Es liegt daran, dass der Optimierungsprozess des Compilers kurze Methoden wie DevideByZero inline einbettet, sodass der Stack-Trace DER gleiche ist. Möglicherweise sollten Sie dies als eigene Frage veröffentlichen.

0 Stimmen

Ursprünglicher Stack-Trace nur im Debug-Modus. Standardmäßig neigt eine Anwendung im Release-Modus zur Leistungsoptimierung. Im Rahmen dieses Optimierungsprozesses wird der Stack-Trace reduziert, da im Stack-Trace im Release-Modus (Produktion) potenziell sensible Informationen der Anwendung offenbart werden können.

66voto

Shivprasad Koirala Punkte 25296

Throw bewahrt den Stack-Trace. Angenommen, Source1 wirft Error1, wird es von Source2 abgefangen und Source2 sagt throw, dann wird der Fehler von Source1 + der Fehler von Source2 im Stack-Trace verfügbar sein.

Throw ex bewahrt den Stack-Trace nicht. Alle Fehler von Source1 werden also gelöscht und nur der Fehler von Source2 wird an den Client gesendet.

Manchmal sind Dinge durch bloßes Lesen nicht klar, daher empfehle ich, dieses Video anzusehen, um mehr Klarheit zu erhalten, Throw vs Throw ex in C#.

Throw vs Throw ex

47voto

Jeppe Stig Nielsen Punkte 57056

Die anderen Antworten sind völlig korrekt, aber diese Antwort enthält meiner Meinung nach einige zusätzliche Details.

Betrachten Sie dieses Beispiel:

using System;

static class Program {
  static void Main() {
    try {
      ThrowTest();
    } catch (Exception e) {
      Console.WriteLine("Ihr Stack-Trace:");
      Console.WriteLine(e.StackTrace);
      Console.WriteLine();
      if (e.InnerException == null) {
        Console.WriteLine("Keine innere Ausnahme.");
      } else {
        Console.WriteLine("Stack-Trace Ihrer inneren Ausnahme:");
        Console.WriteLine(e.InnerException.StackTrace);
      }
    }
  }

  static void ThrowTest() {
    decimal a = 1m;
    decimal b = 0m;
    try {
      Mult(a, b);  // Zeile 34
      Div(a, b);   // Zeile 35
      Mult(b, a);  // Zeile 36
      Div(b, a);   // Zeile 37
    } catch (ArithmeticException arithExc) {
      Console.WriteLine("Behandlung einer {0}.", arithExc.GetType().Name);

      //   entweder auskommentieren
      //throw arithExc;
      //   oder
      //throw;
      //   oder
      //throw new Exception("Wir haben Ihre Ausnahme behandelt und verpackt", arithExc);
    }
  }

  static void Mult(decimal x, decimal y) {
    decimal.Multiply(x, y);
  }
  static void Div(decimal x, decimal y) {
    decimal.Divide(x, y);
  }
}

Wenn Sie die Zeile throw arithExc; auskommentieren, lautet Ihre Ausgabe wie folgt:

Behandlung einer DivideByZeroException.
Ihr Stack-Trace:
   bei Program.ThrowTest() in c:\somepath\Program.cs: Zeile 44
   bei Program.Main() in c:\somepath\Program.cs: Zeile 9

Keine innere Ausnahme.

Sie haben sicherlich Informationen darüber verloren, wo genau diese Ausnahme aufgetreten ist. Wenn Sie stattdessen die Zeile throw; verwenden, erhalten Sie folgendes:

Behandlung einer DivideByZeroException.
Ihr Stack-Trace:
   bei System.Decimal.FCallDivide(Decimal& d1, Decimal& d2)
   bei System.Decimal.Divide(Decimal d1, Decimal d2)
   bei Program.Div(Decimal x, Decimal y) in c:\somepath\Program.cs: Zeile 58
   bei Program.ThrowTest() in c:\somepath\Program.cs: Zeile 46
   bei Program.Main() in c:\somepath\Program.cs: Zeile 9

Keine innere Ausnahme.

Dies ist wesentlich besser, denn jetzt sehen Sie, dass es die Methode Program.Div war, die Probleme verursacht hat. Es ist jedoch immer noch schwer zu erkennen, ob dieses Problem von Zeile 35 oder Zeile 37 im try-Block kommt.

Wenn Sie die dritte Alternative verwenden und in eine äußere Ausnahme verpacken, verlieren Sie keine Informationen:

Behandlung einer DivideByZeroException.
Ihr Stack-Trace:
   bei Program.ThrowTest() in c:\somepath\Program.cs: Zeile 48
   bei Program.Main() in c:\somepath\Program.cs: Zeile 9

Stack-Trace Ihrer inneren Ausnahme:
   bei System.Decimal.FCallDivide(Decimal& d1, Decimal& d2)
   bei System.Decimal.Divide(Decimal d1, Decimal d2)
   bei Program.Div(Decimal x, Decimal y) in c:\somepath\Program.cs: Zeile 58
   bei Program.ThrowTest() in c:\somepath\Program.cs: Zeile 35

Insbesondere können Sie sehen, dass es Zeile 35 ist, die zu dem Problem führt. Dies erfordert jedoch, dass die Leute die InnerException durchsuchen, und es fühlt sich etwas indirekt an, innere Ausnahmen in einfachen Fällen zu verwenden.

In diesem Blog-Beitrag erhalten sie die Zeilennummer (Zeile des try-Blocks), indem sie (über Reflektion) die internal Instanzmethode InternalPreserveStackTrace() auf dem Exception-Objekt aufrufen. Aber es ist nicht schön, so Reflektion zu verwenden (das .NET-Framework könnte seine internal-Member eines Tages ohne Vorwarnung ändern).

11voto

GR7 Punkte 4963

Wenn Sie throw ex verwenden, wird diese geworfene Ausnahme zur "ursprünglichen" Ausnahme. Daher werden alle vorherigen Stack-Traces nicht vorhanden sein.

Wenn Sie throw verwenden, geht die Ausnahme einfach den ganzen Weg nach unten und Sie erhalten den vollständigen Stack-Trace.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X