En fait, il y a des situations où le throw
ne préservera pas l'information StackTrace. Par exemple, dans le code ci-dessous :
try
{
int i = 0;
int j = 12 / i; // Line 47
int k = j + 1;
}
catch
{
// do something
// ...
throw; // Line 54
}
Le StackTrace indiquera que la ligne 54 a déclenché l'exception, alors qu'elle a été déclenchée à la ligne 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
Dans des situations comme celle décrite ci-dessus, il existe deux options pour préserver le StackTrace original :
Appel de la fonction Exception.InternalPreserveStackTrace
Comme il s'agit d'une méthode privée, elle doit être invoquée en utilisant la réflexion :
private static void PreserveStackTrace(Exception exception)
{
MethodInfo preserveStackTrace = typeof(Exception).GetMethod("InternalPreserveStackTrace",
BindingFlags.Instance | BindingFlags.NonPublic);
preserveStackTrace.Invoke(exception, null);
}
J'ai l'inconvénient de m'appuyer sur une méthode privée pour préserver l'information StackTrace. Elle peut être modifiée dans les futures versions de .NET Framework. L'exemple de code ci-dessus et la solution proposée ci-dessous ont été extraits de Blog de Fabrice MARGUERIE .
Appel de Exception.SetObjectData
La technique ci-dessous a été suggérée par Anton Tykhyy comme réponse à En C#, comment puis-je relancer une InnerException sans perdre la trace de la pile ? question.
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
}
Bien qu'elle ait l'avantage de s'appuyer uniquement sur des méthodes publiques, elle dépend également du constructeur d'exceptions suivant (que certaines exceptions développées par des tiers ne mettent pas en œuvre) :
protected Exception(
SerializationInfo info,
StreamingContext context
)
Dans ma situation, j'ai dû choisir la première approche, car les exceptions soulevées par une bibliothèque tierce que j'utilisais n'implémentaient pas ce constructeur.