Frage

Welche Best Practices sind beim Abfangen und erneuten Auslösen von Ausnahmen zu berücksichtigen?Ich möchte sicherstellen, dass die Exception Objekt InnerException und Stacktrace bleiben erhalten.Gibt es einen Unterschied zwischen den folgenden Codeblöcken in der Art und Weise, wie sie damit umgehen?

try
{
    //some code
}
catch (Exception ex)
{
    throw ex;
}

Gegenüber:

try
{
    //some code
}
catch
{
    throw;
}
War es hilfreich?

Lösung

Die Möglichkeit, den Stack-Trace beizubehalten, ist die Verwendung von throw; Dies gilt auch

try {
  // something that bombs here
} catch (Exception ex)
{
    throw;
}

throw ex; ist im Grunde so, als würde man von diesem Punkt aus eine Ausnahme auslösen, sodass der Stack-Trace nur dorthin geht, wo Sie ihn ausgeben throw ex; Stellungnahme.

Mike ist auch richtig, vorausgesetzt, die Ausnahme erlaubt Ihnen das Übergeben einer Ausnahme (was empfohlen wird).

Karl Seguin hat ein Toller Artikel zur Ausnahmebehandlung in seinem Grundlagen der Programmierung E-Book Auch das ist eine tolle Lektüre.

Bearbeiten:Arbeitslink zu Grundlagen der Programmierung pdf.Suchen Sie einfach im Text nach „Ausnahme“.

Andere Tipps

Wenn Sie mit der ersten Ausnahme eine neue Ausnahme auslösen, bleibt auch der anfängliche Stack-Trace erhalten.

try{
} 
catch(Exception ex){
     throw new MoreDescriptiveException("here is what was happening", ex);
}

Tatsächlich gibt es einige Situationen, in denen die throw Die Aussage behält die StackTrace-Informationen nicht bei.Zum Beispiel im folgenden Code:

try
{
  int i = 0;
  int j = 12 / i; // Line 47
  int k = j + 1;
}
catch
{
  // do something
  // ...
  throw; // Line 54
}

Der StackTrace zeigt an, dass Zeile 54 die Ausnahme ausgelöst hat, obwohl sie in Zeile 47 ausgelöst wurde.

Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero.
   at Program.WithThrowIncomplete() in Program.cs:line 54
   at Program.Main(String[] args) in Program.cs:line 106

In Situationen wie der oben beschriebenen gibt es zwei Möglichkeiten, den ursprünglichen StackTrace beizubehalten:

Aufruf von Exception.InternalPreserveStackTrace

Da es sich um eine private Methode handelt, muss sie mithilfe von Reflection aufgerufen werden:

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

Ich habe den Nachteil, dass ich mich auf eine private Methode verlassen muss, um die StackTrace-Informationen zu bewahren.Es kann in zukünftigen Versionen von .NET Framework geändert werden.Das Codebeispiel oben und die vorgeschlagene Lösung unten wurden extrahiert Weblog von Fabrice MARGUERIE.

Aufruf von Exception.SetObjectData

Die folgende Technik wurde von vorgeschlagen Anton Tykhyy als Antwort auf Wie kann ich in C# InnerException erneut auslösen, ohne den Stack-Trace zu verlieren? Frage.

static void PreserveStackTrace (Exception e) 
{ 
  var ctx = new StreamingContext  (StreamingContextStates.CrossAppDomain) ; 
  var mgr = new ObjectManager     (null, ctx) ; 
  var si  = new SerializationInfo (e.GetType (), new FormatterConverter ()) ; 

  e.GetObjectData    (si, ctx)  ; 
  mgr.RegisterObject (e, 1, si) ; // prepare for SetObjectData 
  mgr.DoFixups       ()         ; // ObjectManager calls SetObjectData 

  // voila, e is unmodified save for _remoteStackTraceString 
} 

Obwohl es den Vorteil hat, nur auf öffentliche Methoden zurückzugreifen, ist es auch auf den folgenden Ausnahmekonstruktor angewiesen (den einige von Drittanbietern entwickelte Ausnahmen nicht implementieren):

protected Exception(
    SerializationInfo info,
    StreamingContext context
)

In meiner Situation musste ich den ersten Ansatz wählen, da die von einer von mir verwendeten Bibliothek eines Drittanbieters ausgelösten Ausnahmen diesen Konstruktor nicht implementierten.

Wenn du throw ex, lösen Sie im Wesentlichen eine neue Ausnahme aus und verpassen die ursprünglichen Stack-Trace-Informationen. throw ist die bevorzugte Methode.

Als Faustregel gilt, das Fangen und Werfen zu vermeiden Exception Objekt.Dies zwingt Sie dazu, bei Ausnahmen etwas schlauer vorzugehen;Mit anderen Worten: Sie sollten einen expliziten Fang für a haben SqlException damit Ihr Handhabungscode mit a nichts falsch macht NullReferenceException.

In der realen Welt jedoch fangend und Protokollierung Die Basisausnahme ist ebenfalls eine gute Praxis, aber vergessen Sie nicht, die ganze Strecke zu laufen, um welche zu bekommen InnerExceptions es könnte sein.

Niemand hat den Unterschied erklärt ExceptionDispatchInfo.Capture( ex ).Throw() und eine Ebene throw, hier ist es also.Einige Leute haben jedoch das Problem mit bemerkt throw.

Die vollständige Möglichkeit, eine abgefangene Ausnahme erneut auszulösen, ist die Verwendung von ExceptionDispatchInfo.Capture( ex ).Throw() (nur ab .Net 4.5 verfügbar).

Nachfolgend sind die Fälle aufgeführt, die zum Testen erforderlich sind:

1.

void CallingMethod()
{
    //try
    {
        throw new Exception( "TEST" );
    }
    //catch
    {
    //    throw;
    }
}

2.

void CallingMethod()
{
    try
    {
        throw new Exception( "TEST" );
    }
    catch( Exception ex )
    {
        ExceptionDispatchInfo.Capture( ex ).Throw();
        throw; // So the compiler doesn't complain about methods which don't either return or throw.
    }
}

3.

void CallingMethod()
{
    try
    {
        throw new Exception( "TEST" );
    }
    catch
    {
        throw;
    }
}

4.

void CallingMethod()
{
    try
    {
        throw new Exception( "TEST" );
    }
    catch( Exception ex )
    {
        throw new Exception( "RETHROW", ex );
    }
}

In Fall 1 und Fall 2 erhalten Sie einen Stack-Trace, in dem die Zeilennummer des Quellcodes angegeben ist CallingMethod Methode ist die Zeilennummer der throw new Exception( "TEST" ) Linie.

In Fall 3 erhalten Sie jedoch einen Stack-Trace, in dem die Zeilennummer des Quellcodes angegeben ist CallingMethod Methode ist die Zeilennummer der throw Anruf.Das heißt, wenn die throw new Exception( "TEST" ) Wenn die Zeile von anderen Vorgängen umgeben ist, wissen Sie nicht, bei welcher Zeilennummer die Ausnahme tatsächlich ausgelöst wurde.

Fall 4 ähnelt Fall 2, da die Zeilennummer der ursprünglichen Ausnahme erhalten bleibt, es sich jedoch nicht um einen echten Rethrow handelt, da dadurch der Typ der ursprünglichen Ausnahme geändert wird.

Ein paar Leute haben tatsächlich einen sehr wichtigen Punkt übersehen – „throw“ und „throw ex“ bewirken zwar das Gleiche, liefern Ihnen aber keine entscheidende Information, nämlich die Zeile, an der die Ausnahme aufgetreten ist.

Betrachten Sie den folgenden Code:

static void Main(string[] args)
{
    try
    {
        TestMe();
    }
    catch (Exception ex)
    {
        string ss = ex.ToString();
    }
}

static void TestMe()
{
    try
    {
        //here's some code that will generate an exception - line #17
    }
    catch (Exception ex)
    {
        //throw new ApplicationException(ex.ToString());
        throw ex; // line# 22
    }
}

Wenn Sie entweder „throw“ oder „throw ex“ ausführen, erhalten Sie den Stack-Trace, aber die Zeilennummer wird #22 sein, sodass Sie nicht herausfinden können, welche Zeile genau die Ausnahme ausgelöst hat (es sei denn, Sie haben nur eine oder wenige). Codezeilen im Try-Block).Um die erwartete Zeile Nr. 17 in Ihrer Ausnahme zu erhalten, müssen Sie eine neue Ausnahme mit dem ursprünglichen Ausnahme-Stack-Trace auslösen.

Sie sollten immer "Wurf" verwenden; " Um die Ausnahmen in .NET zu überdenken,

Verweisen Sie darauf,http://weblogs.asp.net/bhouse/archive/2004/11/30/272297.aspx

Grundsätzlich verfügt MSIL (CIL) über zwei Anweisungen: „Throw“ und „Rethrow“:

  • C#'s "throw ex;" wird in MSILs "Wurf" zusammengestellt
  • C#'s "Wurf;" - In MSIL "Rethrow"!

Grundsätzlich kann ich den Grund erkennen, warum „throw ex“ den Stack-Trace überschreibt.

Sie können auch Folgendes verwenden:

try
{
// Dangerous code
}
finally
{
// clean up, or do nothing
}

Und alle ausgelösten Ausnahmen werden auf die nächste Ebene übertragen, die sie behandelt.

Ich würde auf jeden Fall verwenden:

try
{
    //some code
}
catch
{
    //you should totally do something here, but feel free to rethrow
    //if you need to send the exception up the stack.
    throw;
}

Dadurch wird Ihr Stack geschont.

Zu Ihrer Information Ich habe dies gerade getestet und die Stapelspur von 'Throw; ist keine völlig korrekte Stapelspur.Beispiel:

    private void foo()
    {
        try
        {
            bar(3);
            bar(2);
            bar(1);
            bar(0);
        }
        catch(DivideByZeroException)
        {
            //log message and rethrow...
            throw;
        }
    }

    private void bar(int b)
    {
        int a = 1;
        int c = a/b;  // Generate divide by zero exception.
    }

Der Stack-Trace zeigt korrekt auf den Ursprung der Ausnahme (gemeldete Zeilennummer), aber die für foo() gemeldete Zeilennummer ist die Zeile des Wurfs;Anweisung, daher können Sie nicht sagen, welcher der Aufrufe von bar() die Ausnahme verursacht hat.

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