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

437

Es gibt einige Beiträge, in denen gefragt wird, was der Unterschied zwischen diesen beiden bereits ist.
(Warum muss ich das überhaupt erwähnen ...)

Aber meine Frage ist anders in einer Weise , dass ich „throw ex“ nenne in einem anderen Fehler gottähnlichen Verfahren handhaben .

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

    private static void HandleException(Exception ex) {
        if (ex is ThreadAbortException) {
            // ignore then,
            return;
        }
        if (ex is ArgumentOutOfRangeException) { 
            // Log then,
            throw ex;
        }
        if (ex is InvalidOperationException) {
            // Show message then,
            throw ex;
        }
        // and so on.
    }
}

Wenn try & catchin der verwendet würde Main, würde ich verwenden throw;, um den Fehler erneut zu werfen. Im oben genannten vereinfachten Code werden jedoch alle Ausnahmen durchlaufenHandleException

Hat throw ex;das den gleichen Effekt wie ein Anruf, throwwenn er im Inneren angerufen wird HandleException?

dance2die
quelle
3
Es gibt einen Unterschied, es hat damit zu tun, ob oder wie die Stapelverfolgung in der Ausnahme angezeigt wird, aber ich kann mich nicht erinnern, welche gerade welche ist, daher werde ich hier keine Antwort auflisten.
Joel Coehoorn
@ Joel: Danke. Ich denke, die Verwendung der HandleError-Ausnahme ist eine schlechte Idee. Ich wollte nur einen Fehlerbehandlungscode überarbeiten.
Dance2die
1
Der dritte Weg ist, eine neue Ausnahme einzuschließen und timwise.blogspot.co.uk/2014/05/…
Tim Abell
Mögliches Duplikat des Unterschieds zwischen Wurf und Wurf neue Ausnahme ()
Michael Freidgeim

Antworten:

679

Ja, es gibt einen Unterschied.

  • throw exsetzt den Stack-Trace zurück (sodass Ihre Fehler anscheinend von stammen HandleException)
  • throw nicht - der ursprüngliche Täter würde erhalten bleiben.

    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 resets the stack trace Coming from Method 1 and propogates it to the caller(Main)
            throw ex;
        }
    }
    
    private static void Method1()
    {
        try
        {
            throw new Exception("Inside Method1");
        }
        catch (Exception)
        {
            throw;
        }
    }
    
Marc Gravell
quelle
28
Um Marc's Antwort ein wenig zu erweitern, finden Sie weitere Details hier: geekswithblogs.net/sdorman/archive/2007/08/20/…
Scott Dorman
3
@Shaul; Nein, das ist es nicht. Ich habe Details in einem Kommentar zu Ihrem Beitrag angegeben.
Marc Gravell
1
@ Marc Gravell - ich entschuldige mich, Sie hatten Recht. Entschuldigung für die Ablehnung; Es ist zu spät für mich rückgängig zu machen ... :(
Shaul Behr
3
@Marc: Es scheint, dass der Wurf den ursprünglichen Täter NUR bewahrt, wenn der Wurf nicht in der Methode ist, in der die ursprüngliche Ausnahme ausgelöst wurde (siehe diese Frage: stackoverflow.com/questions/5152265/… )
Brann
3
@ScottDorman Es sieht so aus, als würde Ihr Link nach einer Blog-Migration nicht richtig weitergeleitet. Sieht so aus, als würde es jetzt hier leben . Edit: Hey, warte, das ist dein Blog! Repariere deine eigenen Links! ; ^ D
Ruffin
96

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

Hier ist eine Demonstration des Unterschieds:

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

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

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

und hier ist die Ausgabe:

Exception 1:
   at UnitTester.Program.DivByZero() in <snip>\Dev\UnitTester\Program.cs:line 49
   at UnitTester.Program.ThrowException1() in <snip>\Dev\UnitTester\Program.cs:line 36
   at UnitTester.Program.TestExceptions() in <snip>\Dev\UnitTester\Program.cs:line 19

Exception 2:
   at UnitTester.Program.ThrowException2() in <snip>\Dev\UnitTester\Program.cs:line 43
   at UnitTester.Program.TestExceptions() in <snip>\Dev\UnitTester\Program.cs:line 25

Sie können sehen, dass in Ausnahme 1 die Stapelverfolgung auf die DivByZero()Methode zurückgeht, in Ausnahme 2 jedoch nicht.

Beachten Sie jedoch, dass die in ThrowException1()und gezeigte ThrowException2()Zeilennummer die Zeilennummer der throwAnweisung ist, nicht die Zeilennummer des Anrufs DivByZero(), was jetzt wahrscheinlich Sinn macht, da ich ein wenig 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 die ursprüngliche stackTrace nur im Debug-Modus beibehalten?

Shaul Behr
quelle
1
Dies liegt daran, dass der Optimierungsprozess des Compilers kurze Methoden wie z. B. enthält DevideByZero, sodass der Stack-Trace derselbe ist. Vielleicht sollten Sie dies als eigene Frage posten
Menahem
42

Die anderen Antworten sind völlig richtig, aber diese Antwort bietet 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("Your stack trace:");
      Console.WriteLine(e.StackTrace);
      Console.WriteLine();
      if (e.InnerException == null) {
        Console.WriteLine("No inner exception.");
      } else {
        Console.WriteLine("Stack trace of your inner exception:");
        Console.WriteLine(e.InnerException.StackTrace);
      }
    }
  }

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

      //   uncomment EITHER
      //throw arithExc;
      //   OR
      //throw;
      //   OR
      //throw new Exception("We handled and wrapped your exception", 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 throw arithExc;Zeile auskommentieren , lautet Ihre Ausgabe:

Handling a DivideByZeroException.
Your stack trace:
   at Program.ThrowTest() in c:\somepath\Program.cs:line 44
   at Program.Main() in c:\somepath\Program.cs:line 9

No inner exception.

Sicherlich haben Sie Informationen darüber verloren, wo diese Ausnahme aufgetreten ist. Wenn Sie stattdessen die throw;Leitung verwenden, erhalten Sie Folgendes:

Handling a DivideByZeroException.
Your stack trace:
   at System.Decimal.FCallDivide(Decimal& d1, Decimal& d2)
   at System.Decimal.Divide(Decimal d1, Decimal d2)
   at Program.Div(Decimal x, Decimal y) in c:\somepath\Program.cs:line 58
   at Program.ThrowTest() in c:\somepath\Program.cs:line 46
   at Program.Main() in c:\somepath\Program.cs:line 9

No inner exception.

Das ist viel besser, denn jetzt sehen Sie, dass es die Program.DivMethode war, die Ihnen Probleme verursacht hat. Es ist jedoch immer noch schwer zu erkennen, ob dieses Problem von Zeile 35 oder Zeile 37 im tryBlock herrührt.

Wenn Sie die dritte Alternative verwenden, eine äußere Ausnahme einschließen, verlieren Sie keine Informationen:

Handling a DivideByZeroException.
Your stack trace:
   at Program.ThrowTest() in c:\somepath\Program.cs:line 48
   at Program.Main() in c:\somepath\Program.cs:line 9

Stack trace of your inner exception:
   at System.Decimal.FCallDivide(Decimal& d1, Decimal& d2)
   at System.Decimal.Divide(Decimal d1, Decimal d2)
   at Program.Div(Decimal x, Decimal y) in c:\somepath\Program.cs:line 58
   at Program.ThrowTest() in c:\somepath\Program.cs:line 35

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

In diesem Blog-Beitraginternal behalten sie die Zeilennummer (Zeile des try-Blocks) bei, indem sie (durch Reflexion) die Intance-Methode InternalPreserveStackTrace()für das ExceptionObjekt aufrufen . Es ist jedoch nicht schön, eine solche Reflexion zu verwenden (.NET Framework könnte internaleines Tages seine Mitglieder ohne Vorwarnung ändern ).

Jeppe Stig Nielsen
quelle
6

Lassen Sie uns den Unterschied zwischen Wurf und Wurf Ex verstehen. Ich habe gehört, dass in vielen .net-Interviews diese häufig gestellte Frage gestellt wird.

Um einen Überblick über diese beiden Begriffe zu geben, werden sowohl throw als auch throw ex verwendet, um zu verstehen, wo die Ausnahme aufgetreten ist. Throw ex schreibt die Stapelverfolgung der Ausnahme neu, unabhängig davon, wo sie tatsächlich ausgelöst wurde.

Lassen Sie uns mit einem Beispiel verstehen.

Lass uns zuerst Throw verstehen.

static void Main(string[] args) {
    try {
        M1();
    } catch (Exception ex) {
        Console.WriteLine(" -----------------Stack Trace Hierarchy -----------------");
        Console.WriteLine(ex.StackTrace.ToString());
        Console.WriteLine(" ---------------- Method Name / Target Site -------------- ");
        Console.WriteLine(ex.TargetSite.ToString());
    }
    Console.ReadKey();
}

static void M1() {
    try {
        M2();
    } catch (Exception ex) {
        throw;
    };
}

static void M2() {
    throw new DivideByZeroException();
}

Die Ausgabe der oben genannten ist unten.

Zeigt die vollständige Hierarchie und den Methodennamen an, bei denen die Ausnahme tatsächlich ausgelöst wurde. Es ist M2 -> M2. zusammen mit Zeilennummern

Geben Sie hier die Bildbeschreibung ein

Zweitens .. lassen Sie uns durch Wurf ex verstehen. Ersetzen Sie einfach throw durch throw ex im Fangblock der M2-Methode. wie nachstehend.

Geben Sie hier die Bildbeschreibung ein

Die Ausgabe des Wurf-Ex-Codes ist wie folgt.

Geben Sie hier die Bildbeschreibung ein

Sie können den Unterschied in der Ausgabe sehen. Throw ex ignoriert einfach die gesamte vorherige Hierarchie und setzt die Stapelverfolgung mit der Zeile / Methode zurück, in die throw ex geschrieben ist.

Mahesh
quelle
5

Wenn Sie dies tun throw ex, wird diese ausgelöste Ausnahme zur "ursprünglichen". Der gesamte vorherige Stack-Trace ist also nicht vorhanden.

Wenn Sie dies tun throw, geht die Ausnahme nur die ganze Linie hinunter und Sie erhalten den vollständigen Stack-Trace.

GR7
quelle
4

Nein, dies führt dazu, dass die Ausnahme einen anderen Stack-Trace hat. Nur wenn Sie ein throwObjekt ohne Ausnahme im catchHandler verwenden, bleibt die Stapelverfolgung unverändert.

Möglicherweise möchten Sie einen Booleschen Wert von HandleException zurückgeben, unabhängig davon, ob die Ausnahme erneut ausgelöst werden soll oder nicht.

Lucero
quelle
4

MSDN steht für :

Sobald eine Ausnahme ausgelöst wird, ist ein Teil der darin enthaltenen Informationen die Stapelverfolgung. Die Stapelverfolgung ist eine Liste der Methodenaufrufhierarchie, die mit der Methode beginnt, die die Ausnahme auslöst, und mit der Methode endet, die die Ausnahme abfängt. Wenn eine Ausnahme durch Angabe der Ausnahme in der throw-Anweisung erneut ausgelöst wird, wird die Stapelverfolgung bei der aktuellen Methode neu gestartet, und die Liste der Methodenaufrufe zwischen der ursprünglichen Methode, die die Ausnahme ausgelöst hat, und der aktuellen Methode geht verloren. Verwenden Sie die throw-Anweisung, ohne die Ausnahme anzugeben, um die ursprünglichen Stack-Trace-Informationen mit der Ausnahme beizubehalten.

WIE
quelle
2

Schauen Sie hier: http://blog-mstechnology.blogspot.de/2010/06/throw-vs-throw-ex.html

Werfen :

try 
{
    // do some operation that can fail
}
catch (Exception ex)
{
    // do some local cleanup
    throw;
}

Die Stapelinformationen bleiben mit Ausnahme erhalten

Dies wird als "Rethrow" bezeichnet.

Wenn Sie eine neue Ausnahme auslösen möchten,

throw new ApplicationException("operation failed!");

Werfen Sie Ex :

try
{
    // do some operation that can fail
}
catch (Exception ex)
{
    // do some local cleanup
    throw ex;
}

Es werden keine Stapelinformationen mit Ausnahme gesendet

Dies wird als "Breaking the Stack" bezeichnet.

Wenn Sie eine neue Ausnahme auslösen möchten,

throw new ApplicationException("operation failed!",ex);
Aaaaaaaa
quelle
0

Um Ihnen eine andere Perspektive zu geben, ist die Verwendung von throw besonders nützlich, wenn Sie einem Client eine API bereitstellen und ausführliche Stapelverfolgungsinformationen für Ihre interne Bibliothek bereitstellen möchten. Wenn Sie hier throw verwenden, wird in diesem Fall die Stapelverfolgung der System.IO.File-Bibliothek für File.Delete abgerufen. Wenn ich throw ex verwende, werden diese Informationen nicht an meinen Handler weitergegeben.

static void Main(string[] args) {            
   Method1();            
}

static void Method1() {
    try {
        Method2();
    } catch (Exception ex) {
        Console.WriteLine("Exception in Method1");             
    }
}

static void Method2() {
    try {
        Method3();
    } catch (Exception ex) {
        Console.WriteLine("Exception in Method2");
        Console.WriteLine(ex.TargetSite);
        Console.WriteLine(ex.StackTrace);
        Console.WriteLine(ex.GetType().ToString());
    }
}

static void Method3() {
    Method4();
}

static void Method4() {
    try {
        System.IO.File.Delete("");
    } catch (Exception ex) {
        // Displays entire stack trace into the .NET 
        // or custom library to Method2() where exception handled
        // If you want to be able to get the most verbose stack trace
        // into the internals of the library you're calling
        throw;                
        // throw ex;
        // Display the stack trace from Method4() to Method2() where exception handled
    }
}
Charles Owen
quelle
-1
int a = 0;
try {
    int x = 4;
    int y ;
    try {
        y = x / a;
    } catch (Exception e) {
        Console.WriteLine("inner ex");
        //throw;   // Line 1
        //throw e;   // Line 2
        //throw new Exception("devide by 0");  // Line 3
    }
} catch (Exception ex) {
    Console.WriteLine(ex);
    throw ex;
}
  1. wenn alle Zeilen 1, 2 und 3 kommentiert sind - Ausgabe - innere ex

  2. Wenn alle Zeilen 2 und 3 kommentiert sind - Ausgabe - inner ex System.DevideByZeroException: {"Versuch, durch Null zu teilen."} ---------

  3. wenn alle Zeilen 1 und 2 kommentiert sind - Ausgabe - inner ex System.Ausnahme: durch 0 teilen ----

  4. Wenn alle Zeilen 1 und 3 kommentiert sind - Ausgabe - inner ex System.DevideByZeroException: {"Versuch, durch Null zu teilen."} ---------

und StackTrace werden im Falle eines Wurf-Ex zurückgesetzt;

Bhanu Pratap
quelle