it-swarm.com.de

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

Es gibt einige Posts, in denen gefragt wird, worin der Unterschied zwischen diesen beiden bereits besteht.
(warum muss ich das überhaupt erwähnen ...)

Aber meine Frage unterscheidet sich in einer Weise, die ich in einem anderen Fehler "throw ex" nenne gottähnliche Handhabungsmethode.

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 & catch wurden in Main verwendet, dann würde ich throw;, um den Fehler erneut auszulösen. Aber im oben beschriebenen vereinfachten Code durchlaufen alle Ausnahmen HandleException

Hat throw ex; hat den gleichen Effekt wie das Aufrufen von throw, wenn es in HandleException aufgerufen wird.

398
Sung M. Kim

Ja, da gibt es einen Unterschied.

  • throw ex setzt den Stack-Trace zurück (so dass Ihre Fehler anscheinend von HandleException stammen)
  • 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;
        }
    }
    
633
Marc Gravell

(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 der Stack-Trace auf die Methode DivByZero() zurückgeht, während dies in Ausnahme 2 nicht der Fall ist.

Beachten Sie jedoch, dass die in ThrowException1() und ThrowException2() angegebene Zeilennummer die Zeilennummer der Anweisung throw ist, nicht der Zeile Nummer des Aufrufs von DivByZero(), was jetzt wahrscheinlich Sinn macht, wenn ich ein bisschen darüber nachdenke ...

Ausgabe im Freigabemodus

Ausnahme 1:

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

Ausnahme 2:

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

Behält es das ursprüngliche stackTrace nur im Debug-Modus bei?

89
Shaul Behr

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 Zeile throw arithExc; Auskommentieren, erhalten Sie folgende 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.

Sicher haben Sie Informationen darüber verloren, wo diese Ausnahme aufgetreten ist. Wenn Sie stattdessen die Zeile throw; 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, weil Sie jetzt sehen, dass es die Program.Div - Methode war, die Ihnen Probleme bereitete. Es ist jedoch immer noch schwer zu erkennen, ob dieses Problem in Zeile 35 oder Zeile 37 des Blocks try auftritt.

Wenn Sie die dritte Alternative verwenden und 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 die Zeile 35 ist, die zu dem Problem führt. Dies erfordert jedoch die Suche nach InnerException, und es fühlt sich etwas indirekt an, in einfachen Fällen innere Ausnahmen zu verwenden.

In diesem Blogeintrag behalten sie die Zeilennummer (Zeile des try-Blocks) bei, indem sie (durch Reflektion) die internal intance-Methode InternalPreserveStackTrace() auf dem Exception Objekt. Aber es ist nicht schön, Reflection so zu verwenden (das .NET Framework könnte eines Tages seine internal -Mitglieder ohne Vorwarnung ändern).

39

lassen Sie uns den Unterschied zwischen werfen und ex werfen 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 den Stack-Trace der Exception neu, unabhängig davon, wo er tatsächlich ausgelöst wurde.

Lassen Sie uns mit einem Beispiel verstehen.

Lassen Sie 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();
}

ausgabe der oben ist unten.

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

enter image description here

Zweitens .. lasst uns verstehen, indem wir ex werfen. Ersetzen Sie einfach throw durch throw ex im catch-Block der M2-Methode. wie nachstehend.

enter image description here

die Ausgabe des Throw-Ex-Codes ist wie folgt.

enter image description here

Sie können den Unterschied in der Ausgabe erkennen. Throw ex ignoriert einfach die gesamte vorherige Hierarchie und setzt den Stack-Trace mit der Zeile/Methode zurück, in die throw ex geschrieben wurde.

5
Mahesh

Nein, dies führt dazu, dass die Ausnahme einen anderen Stack-Trace aufweist. Wenn Sie im throw -Handler nur ein catch -Objekt ohne Ausnahme verwenden, bleibt der Stack-Trace unverändert.

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

4
Lucero

Wenn Sie ex werfen, wird diese geworfene Ausnahme die "ursprüngliche". Alle vorherigen Stack-Traces sind also nicht vorhanden.

Wenn Sie werfen, geht die Ausnahme einfach auf der ganzen Linie und Sie erhalten den vollständigen Stack-Trace.

4
GR7

MSDN steht für:

Sobald eine Ausnahme ausgelöst wurde, ist ein Teil der darin enthaltenen Informationen der Stack-Trace. Der Stack-Trace 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 erneut ausgelöst wird, indem die Ausnahme in der throw-Anweisung angegeben wird, wird der Stack-Trace 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, um die ursprünglichen Stack-Trace-Informationen mit Ausnahme beizubehalten, ohne die Ausnahme anzugeben.

3
A.S.

Schau mal 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 Stack-Informationen bleiben mit Ausnahme erhalten

Dies wird als "Rethrow" bezeichnet.

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

throw new ApplicationException("operation failed!");

Ex werfen :

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

Stack-Informationen werden mit Ausnahme nicht gesendet

Dies wird als "Breaking the Stack" bezeichnet.

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

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

Um Ihnen eine andere Perspektive zu bieten, ist die Verwendung von throw besonders nützlich, wenn Sie einem Client eine API bereitstellen und ausführliche Stack-Trace-Informationen für Ihre interne Bibliothek bereitstellen möchten. Mit throw here würde ich in diesem Fall den Stack-Trace der System.IO.File-Bibliothek für File.Delete erhalten. 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
    }
}
0
Charles Owen