W rzeczywistości istnieją pewne sytuacje, w których throw
statystyka nie zachowa informacji StackTrace. Na przykład w poniższym kodzie:
try
{
int i = 0;
int j = 12 / i; // Line 47
int k = j + 1;
}
catch
{
// do something
// ...
throw; // Line 54
}
StackTrace wskaże, że wiersz 54 zgłosił wyjątek, chociaż został zgłoszony w wierszu 47.
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
W sytuacjach takich jak ta opisana powyżej istnieją dwie opcje zachowania oryginalnego StackTrace:
Wywołanie wyjątku.InternalPreserveStackTrace
Ponieważ jest to metoda prywatna, należy ją wywołać za pomocą refleksji:
private static void PreserveStackTrace(Exception exception)
{
MethodInfo preserveStackTrace = typeof(Exception).GetMethod("InternalPreserveStackTrace",
BindingFlags.Instance | BindingFlags.NonPublic);
preserveStackTrace.Invoke(exception, null);
}
Mam tę wadę, że polegam na prywatnej metodzie w celu zachowania informacji StackTrace. Można go zmienić w przyszłych wersjach .NET Framework. Powyższy przykład kodu i zaproponowane poniżej rozwiązanie zostało wyodrębnione z bloga internetowego Fabrice MARGUERIE .
Wywołanie wyjątku.SetObjectData
Poniższa technika została zasugerowana przez Anton Tykhyy jako odpowiedź na In C #, w jaki sposób mogę ponownie wprowadzić wyjątek wewnętrzny bez utraty pytania śledzenia stosu .
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
}
Chociaż ma tę zaletę, że polega wyłącznie na metodach publicznych, zależy to również od następującego konstruktora wyjątków (którego niektóre wyjątki opracowane przez strony trzecie nie implementują):
protected Exception(
SerializationInfo info,
StreamingContext context
)
W mojej sytuacji musiałem wybrać pierwsze podejście, ponieważ wyjątki podniesione przez bibliotekę innej firmy, z której korzystałem, nie implementowały tego konstruktora.