In einigen Situationen throw
werden die StackTrace-Informationen in der Anweisung nicht beibehalten. 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
}
Die 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, die ursprüngliche StackTrace zu erhalten:
Aufruf der 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 verlasse, um die StackTrace-Informationen zu erhalten. Es kann in zukünftigen Versionen von .NET Framework geändert werden. Das obige Codebeispiel und die unten vorgeschlagene Lösung wurden aus dem Weblog von Fabrice MARGUERIE extrahiert .
Aufruf von Exception.SetObjectData
Die folgende Technik wurde von Anton Tykhyy als Antwort auf In C # vorgeschlagen. Wie kann ich InnerException erneut auslösen, ohne die Frage nach der Stapelverfolgung zu verlieren?
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, sich nur auf öffentliche Methoden zu verlassen, hängt es auch vom folgenden Ausnahmekonstruktor ab (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 Ausnahmen, die von einer von mir verwendeten Drittanbieter-Bibliothek ausgelöst wurden, diesen Konstruktor nicht implementiert haben.