304 votes

Meilleures pratiques pour attraper et relancer les exceptions .NET

Quelles sont les meilleures pratiques à prendre en compte pour attraper les exceptions et les relancer ? Je veux m'assurer que le Exception de l'objet InnerException et la trace de la pile sont préservées. Y a-t-il une différence entre les blocs de code suivants dans la façon dont ils gèrent cette situation ?

try
{
    //some code
}
catch (Exception ex)
{
    throw ex;
}

Vs :

try
{
    //some code
}
catch
{
    throw;
}

277voto

Darren Kopp Points 27704

Le moyen de préserver la trace de la pile est d'utiliser la fonction throw; Ceci est également valable

try {
  // something that bombs here
} catch (Exception ex)
{
    throw;
}

throw ex; revient à lancer une exception à partir de ce point, de sorte que le suivi de la pile n'ira que jusqu'à l'endroit où vous émettez l'instruction throw ex; déclaration.

Mike est également correct, en supposant que l'exception vous permette de passer une exception (ce qui est recommandé).

Karl Seguin a un excellent article sur la gestion des exceptions dans son fondations de la programmation e-book également, qui est une grande lecture.

Edit : Lien fonctionnel vers Fondements de la programmation pdf. Il suffit de chercher le mot "exception" dans le texte.

11 votes

Je ne suis pas sûr que cet article soit merveilleux, il suggère try { // ... } catch(Exception ex) { throw new Exception(ex.Message + "other stuff") ; } est bon. Le problème est que vous êtes complètement incapable de traiter cette exception plus haut dans la pile, à moins que vous n'attrapiez toutes les exceptions, ce qui est un gros non-non (vous êtes sûr de vouloir traiter cette OutOfMemoryException ?).

2 votes

@ljs L'article a-t-il changé depuis votre commentaire car je ne vois pas de section où il recommande cela. En fait, c'est plutôt le contraire, il dit de ne pas le faire et demande si vous voulez aussi gérer l'exception OutOfMemoryException !?

6 votes

Parfois jeter ; n'est pas suffisant pour préserver la trace de la pile. Voici un exemple https://dotnetfiddle.net/CkMFoX

106voto

Mike Points 978

Si vous lancez une nouvelle exception avec l'exception initiale, vous conserverez également la trace de la pile initiale

try{
} 
catch(Exception ex){
     throw new MoreDescriptiveException("here is what was happening", ex);
}

1 votes

Peu importe ce que j'essaie, throw new Exception("message", ex) lève toujours ex et ignore le message personnalisé. throw new Exception("message", ex.InnerException) fonctionne cependant.

0 votes

Si aucune exception personnalisée n'est nécessaire, on peut utiliser AggregateException (.NET 4+). msdn.microsoft.com/fr/us/library/

0 votes

AggregateException ne doit être utilisé que pour les exceptions sur les opérations agrégées. Par exemple, elle est levée par la fonction ParallelEnumerable y Task des classes du CLR. L'utilisation devrait probablement suivre cet exemple.

29voto

Carlos Loth Points 2083

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.

1 votes

Vous pouvez attraper l'exception et publier cette exception où vous voulez. Puis en lancer une nouvelle expliquant ce qui s'est passé à l'utilisateur. De cette façon, vous pouvez voir ce qui s'est passé au moment où l'exception a été attrapée, l'utilisateur peut se moquer de ce qu'était l'exception réelle.

3 votes

Avec .NET 4.5, il existe une troisième option, plus propre à mon avis, qui consiste à utiliser ExceptionDispatchInfo. Voir la réponse de Tragedian à une question connexe ici : stackoverflow.com/a/17091351/567000 pour plus d'informations.

0 votes

Qu'un simple throw; ici montrait la ligne 54 au lieu de la ligne 47 devrait probablement être considéré comme un bogue de longue date dans .NET Framework, et a été corrigé dans .NET Core 2.1 (github.com/dotnet/runtime/issues/9518). Vous pouvez utiliser ExceptionDispatchInfo mais ce n'est pas l'un de ses principaux cas d'utilisation (l'un d'entre eux est illustré à l'annexe 1). stackoverflow.com/a/17091351 ), et le fait de le suggérer brouille les pistes et rend le code moins lisible. Cela dit, le fait d'avoir la ligne de la capture et tous les numéros de ligne supplémentaires de la pile d'appels a toujours été suffisant pour moi.

22voto

Quand vous throw ex vous lancez essentiellement une nouvelle exception et vous n'obtiendrez pas les informations de la trace de la pile originale. throw est la méthode préférée.

13voto

swilliams Points 19415

La règle de base est d'éviter d'attraper et de lancer le basique Exception objet. Cela vous oblige à être un peu plus intelligent en ce qui concerne les exceptions ; en d'autres termes, vous devriez avoir un catch explicite pour une SqlException afin que votre code de manipulation ne fasse pas quelque chose de mal avec une NullReferenceException .

Dans le monde réel, cependant, attraper et l'enregistrement l'exception de base est également une bonne pratique, mais n'oubliez pas de parcourir l'ensemble pour obtenir toute InnerExceptions ça aurait pu être le cas.

2 votes

Je pense qu'il est préférable de traiter les exceptions non gérées à des fins de journalisation en utilisant les exceptions AppDomain.CurrentDomain.UnhandledException et Application.ThreadException. Utilisation de gros try { ... } catch(Exception ex) { ... } partout signifie beaucoup de duplication. Tout dépend si vous souhaitez consigner les exceptions gérées, auquel cas une duplication (au moins minimale) est inévitable.

0 votes

De plus, utiliser ces événements signifie que vous hacer enregistrer toutes les exceptions non gérées, alors que si vous utilisez le bon vieux try { ... } catch(Exception ex) { ... } vous risquez d'en manquer.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X