Ich rufe durch Reflexion eine Methode auf, die eine Ausnahme verursachen kann. Wie kann ich die Ausnahme an meinen Anrufer weitergeben, ohne dass die Wrapper-Reflexion sie umgibt?
Ich werfe die InnerException erneut, aber dies zerstört die Stapelverfolgung.
Beispielcode:
public void test1()
{
// Throw an exception for testing purposes
throw new ArgumentException("test1");
}
void test2()
{
try
{
MethodInfo mi = typeof(Program).GetMethod("test1");
mi.Invoke(this, null);
}
catch (TargetInvocationException tiex)
{
// Throw the new exception
throw tiex.InnerException;
}
}
Antworten:
In .NET 4.5 gibt es jetzt die
ExceptionDispatchInfo
Klasse.Auf diese Weise können Sie eine Ausnahme erfassen und erneut auslösen, ohne die Stapelverfolgung zu ändern:
Dies funktioniert mit jeder Ausnahme, nicht nur
AggregateException
.Es wurde aufgrund der
await
C # -Sprachenfunktion eingeführt, die die inneren Ausnahmen vonAggregateException
Instanzen auspackt , um die asynchronen Sprachfunktionen den synchronen Sprachfunktionen ähnlicher zu machen.quelle
throw;
nach der Zeile .Throw () eine reguläre Zeile einfügen, da der Compiler nicht weiß, dass .Throw () immer eine Ausnahme auslöst.throw;
wird daher nie aufgerufen, aber zumindest wird sich der Compiler nicht beschweren, wenn Ihre Methode ein Rückgabeobjekt erfordert oder eine asynchrone Funktion ist.throw;
. Wenn Siethrow ex.InnerException;
den Stack-Trace verwenden, wird er an dem Punkt neu initialisiert, an dem er erneut ausgelöst wird.ExceptionDispatchInfo.Capture(ex.InnerException ?? ex).Throw();
Es ist möglich, die Stapelspur vor dem erneuten Werfen ohne Reflexion beizubehalten:
Dies verschwendet viele Zyklen im Vergleich zum Aufrufen
InternalPreserveStackTrace
über einen zwischengespeicherten Delegaten, hat jedoch den Vorteil, dass nur öffentliche Funktionen verwendet werden. Hier sind einige gängige Verwendungsmuster für Stack-Trace-Erhaltungsfunktionen:quelle
InternalPreserveStackTrace
(ungefähr 6% langsamer mit 10000 Iterationen). Der direkte Zugriff auf die Felder durch Reflexion ist etwa 2,5% schneller als das AufrufenInternalPreserveStackTrace
e.Data
Wörterbuch mit einer Zeichenfolge oder einem eindeutigen Objektschlüssel zu verwenden (static readonly object myExceptionDataKey = new object ()
aber tun Sie dies nicht, wenn Sie irgendwo Ausnahmen serialisieren müssen). Vermeiden Sie Änderungene.Message
, da Sie möglicherweise irgendwo Code haben, der analysiert wirde.Message
. Das Parsene.Message
ist böse, aber es gibt möglicherweise keine andere Wahl, z. B. wenn Sie eine Bibliothek eines Drittanbieters mit schlechten Ausnahmepraktiken verwenden müssen.Ich denke, Ihre beste Wette wäre, dies einfach in Ihren Fangblock zu legen:
Und extrahieren Sie später die innere Ausnahme.
quelle
Niemand hat den Unterschied zwischen
ExceptionDispatchInfo.Capture( ex ).Throw()
und einer Ebene erklärtthrow
, also hier ist es.Der vollständige Weg, um eine abgefangene Ausnahme erneut auszulösen, ist die Verwendung
ExceptionDispatchInfo.Capture( ex ).Throw()
(nur ab .NET 4.5 verfügbar).Nachfolgend sind die Fälle aufgeführt, die zum Testen erforderlich sind:
1.
2.
3.
4.
Fall 1 und Fall 2 geben Ihnen eine Stapelverfolgung, wobei die Quellcode-Zeilennummer für die
CallingMethod
Methode die Zeilennummer der istthrow new Exception( "TEST" )
Zeile ist.In Fall 3 erhalten Sie jedoch eine Stapelverfolgung, bei der die Quellcode-Zeilennummer für die
CallingMethod
Methode die Zeilennummer desthrow
Aufrufs ist. Dies bedeutet, dassthrow new Exception( "TEST" )
Sie keine Ahnung haben, bei welcher Zeilennummer die Ausnahme tatsächlich ausgelöst wurde , wenn die Zeile von anderen Operationen umgeben ist.Fall 4 ist ähnlich wie Fall 2, da die Zeilennummer der ursprünglichen Ausnahme beibehalten wird, jedoch kein echter Neuauswurf ist, da der Typ der ursprünglichen Ausnahme geändert wird.
quelle
Rufen Sie die Erweiterungsmethode für Ihre Ausnahme auf, bevor Sie sie auslösen. Dadurch bleibt die ursprüngliche Stapelverfolgung erhalten.
quelle
Action<Exception>
? Hier verwenden Sie statische MethodeNoch mehr Reflexion ...
Beachten Sie, dass dies jederzeit unterbrochen werden kann, da private Felder nicht Teil der API sind. Weitere Diskussion zu Mono Bugzilla .
quelle
InternalPreserveStackTrace
Methode aufzurufen wäre besser, da sie dasselbe tut und sich in Zukunft weniger wahrscheinlich ändert ...Erstens: Verlieren Sie nicht die TargetInvocationException - es sind wertvolle Informationen, wenn Sie Dinge debuggen möchten.
Zweitens: Wickeln Sie die TIE als InnerException in Ihren eigenen Ausnahmetyp ein und fügen Sie eine OriginalException-Eigenschaft ein, die mit dem verknüpft ist, was Sie benötigen (und den gesamten Callstack intakt halten).
Drittens: Lassen Sie die KRAWATTE aus Ihrer Methode heraussprudeln.
quelle
Leute, ihr seid cool. Ich werde bald ein Nekromant sein.
quelle
.Invoke()
.Ein weiterer Beispielcode, der die Serialisierung / Deserialisierung von Ausnahmen verwendet. Der tatsächliche Ausnahmetyp muss nicht serialisierbar sein. Außerdem werden nur öffentliche / geschützte Methoden verwendet.
quelle
Basierend auf der Antwort von Paul Turners habe ich eine Erweiterungsmethode erstellt
Das
return ex
ist nie erreicht, aber der Vorteil ist, dass ich esthrow ex.Capture()
als Einzeiler verwenden kann, damit der Compiler keinennot all code paths return a value
Fehler auslöst.quelle