Werfen VS rethrow: gleiches Ergebnis?
-
30-09-2019 - |
Frage
beziehe zu viel Dokumentation im Netz, vor allem auf SO, zum Beispiel: Was ist der richtige Weg, um erneut werfen eine Ausnahme in C #? sollte es einen Unterschied zwischen dem, „throw e;“ und "werfen";.
Aber von: http://bartdesmet.net /blogs/bart/archive/2006/03/12/3815.aspx ,
Mit diesem Code:
using System;
class Ex
{
public static void Main()
{
//
// First test rethrowing the caught exception variable.
//
Console.WriteLine("First test");
try
{
ThrowWithVariable();
}
catch (Exception ex)
{
Console.WriteLine(ex.StackTrace);
}
//
// Second test performing a blind rethrow.
//
Console.WriteLine("Second test");
try
{
ThrowWithoutVariable();
}
catch (Exception ex)
{
Console.WriteLine(ex.StackTrace);
}
}
private static void BadGuy()
{
//
// Some nasty behavior.
//
throw new Exception();
}
private static void ThrowWithVariable()
{
try
{
BadGuy();
}
catch (Exception ex)
{
throw ex;
}
}
private static void ThrowWithoutVariable()
{
try
{
BadGuy();
}
catch
{
throw;
}
}
}
ergibt folgendes Ergebnis:
$ /cygdrive/c/Windows/Microsoft.NET/Framework/v4.0.30319/csc.exe Test.cs
Microsoft (R) Visual C# 2010 Compiler version 4.0.30319.1
Copyright (C) Microsoft Corporation. All rights reserved.
$ ./Test.exe
First test
at Ex.ThrowWithVariable()
at Ex.Main()
Second test
at Ex.ThrowWithoutVariable()
at Ex.Main()
, die mit der Blog-Post in völligem Widerspruch ist.
Die gleiche Art von Ergebnis wird mit dem Code erhalten von: http://crazorsharp.blogspot.com/2009/08/rethrowing-exception-without-resetting.html
Original Frage : Was mache ich falsch
UPDATE : gleiches Ergebnis mit .Net 3.5 / csc.exe 3.5.30729.4926
SUMUP . Sie alle Ihre Antworten waren toll, danke wieder
So ist der Grund effektiv inlining aufgrund der 64-Bit-Jitters.
hatte ich nur eine Antwort zu wählen, und hier ist, warum ich gewählt habe LukeH Antwort:
-
vermutete er das inlining Problem und die Tatsache, es kann zu meiner 64-Bit-Architektur in Beziehung gesetzt werden,
-
er vorgesehen, um die NoInlining Flagge, die der einfachste Weg ist, um dieses Verhalten zu vermeiden.
Doch dieses Problem jetzt steigt eine andere Frage: Dieses Verhalten ist konform mit allen .NET-Spezifikationen: die CLR Einsen und die Programmiersprache C # diejenigen
UPDATE : diese Optimierung scheint konform nach: Werfen VS rethrow: gleiches Ergebnis (danke 0xA3 )
Vielen Dank im Voraus für Ihre Hilfe.
Lösung
Ich kann das Problem nicht replizieren -. Mit .NET 3.5 (32-Bit) gibt mir die gleichen Ergebnisse in Barts Artikel beschrieben
Meine Vermutung ist, dass die .NET-4-Compiler / Jitter - oder vielleicht ist es die 64-Bit-Compiler / Jitter wenn dies zu -3,5 geschieht - wird inlining die BadGuy
Methode in den Aufrufen von Methoden. Versuchen Sie, die folgenden MethodImpl
Attribut zu BadGuy
und sehen, ob das einen Unterschied macht:
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
private static void BadGuy()
{
//
// Some nasty behavior.
//
throw new Exception();
}
Andere Tipps
Ich habe versucht, diesen Code selbst ausgeführt wird und die Debug-Build funktioniert wie ich erwartet hatte, aber ich habe das gleiche Ergebnis wie Sie in den Release-Build.
vermute ich, was passiert ist, dass der Compiler inlining einfach die BadGuy () -Aufruf mit throw new Exception();
ersetzt hat, weil das die einzige Erklärung ist in BadGuy ().
Wenn Sie die ‚Optimieren Code‘ Option in den Projekteigenschaften ausschalten. -> Build-Bildschirm, dann beide Release- und Debug-Build erzeugt das gleiche Ergebnis, das zeigt BadGuy () an der Spitze des Stack-Trace
Es scheint, dass die JIT-Optimierer tut hier etwas Arbeit. Wie Sie der Call-Stack im zweiten Fall ist zu sehen, anders als im ersten Fall, wenn Sie das Debug-Build ausführen. Doch in dem Release-Build sind beide Call Stacks identisch aufgrund der Optimierung.
Um zu sehen, dass dies auf die Jitter zusammenhängt Sie die Methoden mit einem a href dekorieren <= „http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.methodimplattribute.aspx“ rel = "nofollow noreferrer"> MethodImplAttribute
Attribut:
[MethodImpl(MethodImplOptions.NoOptimization)]
private static void ThrowWithoutVariable()
{
try
{
BadGuy();
}
catch
{
throw;
}
}
Beachten Sie, dass die IL für ThrowWithoutVariable
noch anders und ThrowWithVariable
:
.method private hidebysig static void ThrowWithVariable() cil managed
{
// Code size 11 (0xb)
.maxstack 1
.locals init ([0] class [mscorlib]System.Exception ex)
.try
{
IL_0000: call void Ex::BadGuy()
IL_0005: leave.s IL_000a
} // end .try
catch [mscorlib]System.Exception
{
IL_0007: stloc.0
IL_0008: ldloc.0
IL_0009: throw
} // end handler
IL_000a: ret
} // end of method Ex::ThrowWithVariable
.method private hidebysig static void ThrowWithoutVariable() cil managed
{
// Code size 11 (0xb)
.maxstack 1
.try
{
IL_0000: call void Ex::BadGuy()
IL_0005: leave.s IL_000a
} // end .try
catch [mscorlib]System.Object
{
IL_0007: pop
IL_0008: rethrow
} // end handler
IL_000a: ret
} // end of method Ex::ThrowWithoutVariable
Aktualisieren Sie Ihre Follow-up-Frage zu beantworten, ob dies mit der CLI-konform ist Spezifikation
In der Tat gefällig es, und zwar für die JIT-Compiler zu ermöglichen, wichtige Optimierungen zu ermöglichen. Anhang F Staaten auf Seite 52 (Schwerpunkt von mir):
Einige CIL Befehle führen implizite Laufzeitprüfungen, die Speicher zu gewährleisten und Typsicherheit. Ursprünglich war die CLI garantiert, dass die Ausnahmen waren präzise , was bedeutet, dass Programmzustand erhalten wurde, als eine Ausnahme geworfen. jedoch die Durchsetzung präzise Ausnahmen für implizite Kontrollen Marken einige wichtige Optimierungen praktisch unmöglich, zu übernehmen. Programmierer können nun erklären, über ein benutzerdefiniertes Attribut, das ein Verfahren ist, „Entspannt“, die besagt, dass Ausnahmen die sich aus impliziten Laufzeitprüfungen muss nicht genau sein.
Relaxed Kontrollen bewahren Prüfbarkeit (durch die Erhaltung Speicher und Sicherheitstyp), während ermöglicht Optimierungen, die Neuordnungs Anleitung. Insbesondere ist es ermöglicht folgende Optimierungen:
- Hissen implizite Laufzeit Kontrollen von Schleifen.
- Die Neuanordnung Schleifeniterationen (Z.B. Vektorisierung und automatische Multithreading)
- Vertauschen Schleifen
- Inlining das macht eine inlined Verfahren nach mindestens so schnell wie die Äquivalent Makro
ein Debug-Build verwenden und Sie werden den Unterschied deutlicher sehen. Mit einem Debug-Build zeigt der erste Lauf den Standort als throw ex
Linie und die zweite als von dem eigentlichen Aufruf BadGuy
. Offensichtlich ist das ‚Problem‘ ist der Aufruf BadGuy -. Nicht der Wurf ab Zeile und Sie werden mit der direkten throw;
Aussage weniger Geister jagen
In einem Stapel verfolgt diese seicht die Vorteile sind nicht als immediatley offensichtlich, in einem sehr tief Sie stapeln würde die eigentliche Ursache des Problems Maske und einige Treue verlieren durch manuelle Ausnahme werfen, anstatt den eingebauten Wiederwurf Aussage.
Auf einer Seite zur Kenntnis, fand ich einen Hack auf einem Blog gepostet einmal (ich da die Referenz verloren haben), dass Sie den Anruf-Stack auf rethrow behalten können. Dies ist vor allem dann nützlich, wenn Sie eine Ausnahme in einem Kontext zu fangen (sagen wir, in einem Thread einen asynchronen Vorgang ausgeführt wird) und wollen es in einem anderen erneut auslösen (zum Beispiel in dem anderen Thread, der die asynchrone Operation gestartet). Es nutzt einige undokumentierte Funktionen Erhaltung von Stack-Traces über Remotegrenzen hinweg zu ermöglichen, enthalten.
//This terrible hack makes sure track trace is preserved if exception is re-thrown
internal static Exception AppendStackTrace(Exception ex)
{
//Fool CLR into appending stack trace information when the exception is re-thrown
var remoteStackTraceString = typeof(Exception).GetField("_remoteStackTraceString",
BindingFlags.Instance |
BindingFlags.NonPublic);
if (remoteStackTraceString != null)
remoteStackTraceString.SetValue(ex, ex.StackTrace + Environment.NewLine);
return ex;
}