Comment utiliser try try pour la gestion des exceptions est la meilleure pratique

Tout en conservant le code de mon collègue, même celui qui prétend être un développeur senior, je vois souvent le code suivant:

try { //do something } catch { //Do nothing } 

ou parfois ils écrivent des informations de journalisation pour enregistrer des fichiers comme suit try catch block

 try { //do some work } catch(Exception exception) { WriteException2LogFile(exception); } 

Je me demande si ce qu’ils ont fait est la meilleure pratique? Cela me rend confus car dans mon esprit, les utilisateurs doivent savoir ce qui se passe avec le système.

S’il vous plaît donnez-moi des conseils.

Ma stratégie de gestion des exceptions est la suivante:

  • Pour intercepter toutes les exceptions non gérées en accrochant à l’ Application.ThreadException event , décidez:

    • Pour une application d’interface utilisateur: pour la présenter à l’utilisateur avec un message d’excuse (winforms)
    • Pour une application Service ou Console: connectez-le à un fichier (service ou console)

Ensuite, je joins toujours chaque morceau de code exécuté en externe dans try/catch :

  • Tous les événements déclenchés par l’infrastructure Winforms (Load, Click, SelectedChanged …)
  • Tous les événements déclenchés par des composants tiers

Puis j’inclus dans “try / catch”

  • Toutes les opérations que je connais peuvent ne pas fonctionner tout le temps (opérations IO, calculs avec une division zéro potentielle …). Dans un tel cas, je lance une nouvelle ApplicationException("custom message", innerException) pour suivre ce qui s’est réellement passé

De plus, je fais de mon mieux pour sortinger correctement les exceptions . Il y a des exceptions qui:

  • besoin d’être montré à l’utilisateur immédiatement
  • requièrent un traitement supplémentaire pour mettre les choses en ordre lorsqu’elles se produisent pour éviter des problèmes en cascade (par exemple, mettre .EndUpdate dans la section finally pendant un remplissage TreeView )
  • l’utilisateur ne s’en soucie pas, mais il est important de savoir ce qui s’est passé. Donc je les connecte toujours:

    • Dans le journal des événements
    • ou dans un fichier .log sur le disque

Il est recommandé de concevoir des méthodes statiques pour gérer les exceptions dans les gestionnaires d’erreurs de niveau supérieur de l’application.

Je me force aussi à essayer de:

  • Rappelez-vous que toutes les exceptions sont passées au niveau supérieur . Il n’est pas nécessaire de placer des gestionnaires d’exception partout.
  • Les fonctions réutilisables ou appelées en profondeur n’ont pas besoin d’afficher ou de consigner des exceptions: elles sont soit mises en boucle automatiquement, soit relancées avec des messages personnalisés dans mes gestionnaires d’exceptions.

Donc enfin:

Mal:

 // DON'T DO THIS, ITS BAD try { ... } catch { // only air... } 

Inutile:

 // DONT'T DO THIS, ITS USELESS try { ... } catch(Exception ex) { throw ex; } 

Avoir un essai enfin sans prise est parfaitement valide:

 try { listView1.BeginUpdate(); // If an exception occurs in the following code, then the finally will be executed // and the exception will be thrown ... } finally { // I WANT THIS CODE TO RUN EVENTUALLY REGARDLESS AN EXCEPTION OCCURED OR NOT listView1.EndUpdate(); } 

Ce que je fais au plus haut niveau:

 // ie When the user clicks on a button try { ... } catch(Exception ex) { ex.Log(); // Log exception -- OR -- ex.Log().Display(); // Log exception, then show it to the user with apologies... } 

Ce que je fais dans certaines fonctions appelées:

 // Calculation module try { ... } catch(Exception ex) { // Add useful information to the exception throw new ApplicationException("Something wrong happened in the calculation module :", ex); } // IO module try { ... } catch(Exception ex) { throw new ApplicationException(ssortingng.Format("I cannot write the file {0} to {1}", fileName, directoryName), ex); } 

Il y a beaucoup à faire avec la gestion des exceptions (Custom Exceptions), mais ces règles que je tente de garder à l’esprit sont suffisantes pour les applications simples que je fais.

Voici un exemple de méthodes d’extension pour gérer les exceptions capturées d’une manière confortable. Ils sont implémentés de manière à pouvoir être enchaînés, et il est très facile d’append votre propre traitement des exceptions capturées.

 // Usage: try { // boom } catch(Exception ex) { // Only log exception ex.Log(); -- OR -- // Only display exception ex.Display(); -- OR -- // Log, then display exception ex.Log().Display(); -- OR -- // Add some user-friendly message to an exception new ApplicationException("Unable to calculate !", ex).Log().Display(); } // Extension methods internal static Exception Log(this Exception ex) { File.AppendAllText("CaughtExceptions" + DateTime.Now.ToSsortingng("yyyy-MM-dd") + ".log", DateTime.Now.ToSsortingng("HH:mm:ss") + ": " + ex.Message + "\n" + ex.ToSsortingng() + "\n"); return ex; } internal static Exception Display(this Exception ex, ssortingng msg = null, MessageBoxImage img = MessageBoxImage.Error) { MessageBox.Show(msg ?? ex.Message, "", MessageBoxButton.OK, img); return ex; } 

La meilleure pratique est que la gestion des exceptions ne doit jamais masquer les problèmes . Cela signifie que try-catch blocs d’ try-catch doivent être extrêmement rares.

Il y a 3 circonstances dans lesquelles l’utilisation d’un try-catch sens.

  1. Toujours traiter les exceptions connues aussi bas que possible. Cependant, si vous attendez une exception, il est généralement préférable de la tester en premier. Par exemple, l’parsing syntaxique, la mise en forme et les exceptions arithmétiques sont presque toujours mieux gérées par les vérifications logiques, plutôt que par une try-catch spécifique.

  2. Si vous devez faire quelque chose sur une exception (par exemple, enregistrer ou annuler une transaction), relancez l’exception.

  3. Traitez toujours les exceptions inconnues aussi haut que possible – le seul code qui devrait consumr une exception et ne pas le relancer devrait être l’interface utilisateur ou l’API publique.

Supposons que vous vous connectiez à une API distante, vous devez vous attendre à certaines erreurs (et avoir des choses dans ces circonstances), donc c’est le cas 1:

 try { remoteApi.Connect() } catch(ApiConnectionSecurityException ex) { // User's security details have expired return false; } return true; 

Notez qu’aucune autre exception n’est interceptée, car elles ne sont pas attendues.

Supposons maintenant que vous essayiez d’enregistrer quelque chose dans la firebase database. Nous devons le réduire s’il échoue, nous avons donc le cas 2:

 try { DBConnection.Save(); } catch { // Roll back the DB changes so they aren't corrupted on ANY exception DBConnection.Rollback(); // Re-throw the exception, it's critical that the user knows that it failed to save throw; } 

Notez que nous relançons l’exception – le code supérieur doit encore savoir que quelque chose a échoué.

Enfin, nous avons l’interface utilisateur – ici nous ne voulons pas avoir d’exceptions complètement non gérées, mais nous ne voulons pas non plus les cacher. Nous avons ici un exemple de cas 3:

 try { // Do something } catch(Exception ex) { // Log exception for developers WriteException2LogFile(ex); // Display message to users DisplayWarningBox("An error has occurred, please contact support!"); } 

Cependant, la plupart des frameworks API ou UI ont des méthodes génériques de casse 3. Par exemple, ASP.Net a un écran d’erreur jaune qui vide les détails de l’exception, mais qui peut être remplacé par un message plus générique dans l’environnement de production. Suivre ces règles est la meilleure pratique, car cela vous permet d’économiser beaucoup de code, mais aussi parce que la journalisation et l’affichage des erreurs doivent être des décisions de configuration plutôt que codées en dur.

Tout cela signifie que le cas 1 (exceptions connues) et le cas 3 (traitement de l’interface utilisateur unique) ont tous deux de meilleurs modèles (évitez l’erreur attendue ou la gestion des erreurs manuelles sur l’interface utilisateur).

Même le cas 2 peut être remplacé par de meilleurs modèles, par exemple les scopes de transaction (en using blocs qui annulent une transaction non validée pendant le blocage) rendent plus difficile pour les développeurs d’obtenir les meilleures pratiques.

Par exemple, supposons que vous ayez une application ASP.Net à grande échelle. La journalisation des erreurs peut se faire via ELMAH , l’affichage des erreurs peut être un YSoD informatif localement et un bon message localisé en production. Les connexions de firebase database peuvent toutes être effectuées via des scopes de transaction et using blocs. Vous n’avez pas besoin d’un seul bloc try-catch .

TL; DR: La meilleure pratique consiste en fait à ne pas utiliser de blocs try-catch .

Une exception est une erreur de blocage .

Tout d’abord, la meilleure pratique devrait être de ne pas lancer d’exceptions pour tout type d’erreur, à moins que ce ne soit une erreur de blocage .

Si l’erreur est bloquante , lancez l’exception. Une fois que l’exception est déjà lancée, il n’est plus nécessaire de la cacher car elle est exceptionnelle. informez-en l’utilisateur (vous devez reformater toute l’exception pour qu’elle soit utile à l’utilisateur dans l’interface utilisateur).

Votre travail en tant que développeur de logiciels est d’essayer d’empêcher un cas exceptionnel où certains parameters ou situations d’exécution peuvent se terminer par une exception. Autrement dit, les exceptions ne doivent pas être neutralisées, mais elles doivent être évitées .

Par exemple, si vous savez que certaines entrées entières peuvent être int.TryParse à un format non valide, utilisez int.TryParse au lieu de int.Parse . Il y a beaucoup de cas où vous pouvez faire cela au lieu de simplement dire “si cela échoue, lancez simplement une exception”.

Lancer des exceptions coûte cher.

Si, après tout, une exception est levée, au lieu d’écrire l’exception dans le journal une fois qu’elle a été lancée, l’une des meilleures pratiques est de l’attraper dans un gestionnaire d’exception de première chance . Par exemple:

  • ASP.NET: Global.asax Application_Error
  • Autres: événement AppDomain.FirstChanceException .

Mon sharepoint vue est que les essais / captures locaux sont mieux adaptés au traitement de cas particuliers dans lesquels vous pouvez traduire une exception dans une autre, ou lorsque vous souhaitez la désactiver pour un cas très très très très particulier (un bogue de bibliothèque). lancer une exception sans rapport que vous devez désactiver pour contourner le bogue entier).

Pour le rest des cas:

  • Essayez d’éviter les exceptions.
  • Si ce n’est pas possible: gestionnaires d’exception de première chance.
  • Ou utilisez un aspect PostSharp (AOP).

Répondre à @thewhiteambit sur un commentaire …

@thewhiteambit a dit:

Les exceptions ne sont pas des erreurs fatales, ce sont des exceptions! Parfois, ils ne sont même pas des erreurs, mais les considérer comme des erreurs fatales est une compréhension complètement fausse de ce que sont les exceptions.

Tout d’abord, comment une exception ne peut même pas être une erreur?

  • Aucune connexion à la firebase database => exception.
  • Format de chaîne non valide pour parsingr un type => exception
  • Essayer d’parsingr JSON et que l’entrée n’est pas JSON => exception
  • Argument null alors que l’object était attendu => exception
  • Une bibliothèque a un bogue => lance une exception inattendue
  • Il y a une connexion socket et celle-ci est déconnectée. Ensuite, vous essayez d’envoyer un message => exception

Nous pourrions lister 1k cas où une exception est levée et après tout, l’un des cas possibles sera une erreur .

Une exception est une erreur, car au bout du compte, c’est un object qui collecte des informations de diagnostic – il a un message et cela se produit lorsque quelque chose ne va pas.

Personne ne ferait exception quand il n’y a pas de cas exceptionnel. Les exceptions doivent bloquer les erreurs car, une fois qu’elles sont lancées, si vous n’essayez pas de faire appel à try / catch et aux exceptions pour implémenter le stream de contrôle, cela signifie que votre application / service arrêtera l’opération entrée dans un cas exceptionnel .

De plus, je suggère à tout le monde de vérifier le paradigme rapide publié par Martin Fowler (et écrit par Jim Shore) . C’est comme ça que j’ai toujours compris comment gérer les exceptions, même avant que j’arrive à ce document il y a quelque temps.

[…] considérez-les comme des erreurs fatales, c’est une compréhension complètement fausse des exceptions.

Généralement, les exceptions réduisent certains stream d’opérations et elles sont traitées pour les convertir en erreurs compréhensibles par l’homme. Ainsi, il semble qu’une exception soit un meilleur paradigme pour gérer les cas d’erreur et travailler sur eux pour éviter un plantage complet de l’application / service et informer l’utilisateur / consommateur que quelque chose a mal tourné.

Plus de réponses à propos des préoccupations du propriétaire

Par exemple, en cas de connexion à une firebase database manquante, le programme pourrait continuer exceptionnellement à écrire dans un fichier local et à envoyer les modifications à la firebase database une fois qu’il sera à nouveau disponible. Votre casting invalide de Ssortingng-To-Number pourrait être essayé à nouveau avec une interprétation locale de la langue sur Exception, comme lorsque vous essayez d’utiliser l’anglais par défaut (“1,5”) et que vous réessayez complètement avec l’interprétation allemande bien parce que nous utilisons la virgule au lieu du point comme séparateur. Vous voyez que ces exceptions ne doivent même pas être bloquantes, elles nécessitent seulement une gestion des exceptions.

  1. Si votre application peut fonctionner hors connexion sans conserver les données à la firebase database, vous ne devez pas utiliser les exceptions , car l’implémentation du stream de contrôle à l’aide de try/catch est considérée comme un anti-pattern. Le travail hors ligne est un cas d’utilisation possible. Vous implémentez donc le stream de contrôle pour vérifier si la firebase database est accessible ou non, vous n’attendez pas qu’elle soit inaccessible .

  2. L’ parsing est aussi un cas attendu ( pas un CAS EXCEPTIONNEL ). Si vous vous y attendez, vous n’utilisez pas les exceptions pour contrôler le stream! . Vous obtenez des métadonnées de l’utilisateur pour savoir quelle est sa culture et vous utilisez des formateurs pour cela! .NET prend également en charge cet environnement et d’autres, et constitue une exception, car le formatage des nombres doit être évité si vous prévoyez une utilisation spécifique à votre culture de votre application / service .

Une exception non gérée devient généralement une erreur, mais les exceptions elles-mêmes ne sont pas codeproject.com/Articles/15921/Not-All-Exceptions-Are-Errors

Cet article n’est qu’une opinion ou un sharepoint vue de l’auteur.

Puisque Wikipedia ne peut être que l’opinion des auteurs de l’article, je ne dirais pas que c’est le dogme , mais vérifiez ce que l’article Coding by exception dit quelque part dans un paragraphe:

[…] L’utilisation de ces exceptions pour gérer des erreurs spécifiques qui surviennent pour continuer le programme s’appelle le codage par exception. Cet anti-pattern peut rapidement dégrader les performances et la maintenance du logiciel.

Il dit aussi quelque part:

Utilisation d’exception incorrecte

Souvent, le codage par exception peut entraîner d’autres problèmes dans le logiciel avec une utilisation incorrecte des exceptions. Outre l’utilisation de la gestion des exceptions pour un problème unique, une utilisation incorrecte des exceptions va plus loin en exécutant du code même après que l’exception ait été déclenchée. Cette méthode de programmation médiocre ressemble à la méthode goto dans de nombreux langages logiciels, mais elle ne se produit qu’une fois un problème détecté dans le logiciel.

Honnêtement, je pense que les logiciels ne peuvent pas être développés, ne prenons pas les cas d’utilisation au sérieux. Si vous le savez …

  • Votre firebase database peut être déconnectée …
  • Certains fichiers peuvent être verrouillés …
  • Certains formats peuvent ne pas être supportés …
  • Une validation de domaine pourrait échouer …
  • Votre application devrait fonctionner en mode hors connexion …
  • quel que soit le cas d’utilisation

vous n’utiliserez pas d’exceptions pour cela . Vous prendrez en charge ces cas d’utilisation en utilisant un stream de contrôle régulier.

Et si un cas d’utilisation inattendu n’est pas couvert, votre code échouera rapidement, car il lancera une exception . Bien, car une exception est un cas exceptionnel .

D’un autre côté, et finalement, vous couvrez parfois des cas exceptionnels en lançant des exceptions attendues , mais vous ne les lancez pas pour implémenter des stream de contrôle. Vous le faites parce que vous souhaitez notifier les couches supérieures que vous ne prenez pas en charge certains cas d’utilisation ou que votre code ne fonctionne pas avec certains arguments ou données / propriétés d’environnement donnés.

Le seul moment où vous devriez inquiéter vos utilisateurs à propos de quelque chose qui est arrivé dans le code est de savoir s’ils peuvent ou doivent faire quelque chose pour éviter le problème. S’ils peuvent modifier les données d’un formulaire, appuyez sur un bouton ou modifiez un paramètre d’application afin d’éviter le problème, puis faites-le savoir. Mais les avertissements ou les erreurs que l’utilisateur ne peut pas éviter les font perdre confiance dans votre produit.

Les exceptions et les journaux sont pour vous, le développeur et non votre utilisateur final. Il est préférable de comprendre la bonne chose à faire lorsque vous attrapez chaque exception que d’appliquer simplement une règle d’or ou de vous fier à un filet de sécurité à l’échelle de l’application.

Le codage sans esprit est le SEUL type de mauvais code. Le fait que vous sentiez que quelque chose de mieux peut être fait dans ces situations montre que vous êtes investi dans un bon codage, mais évitez d’essayer d’imposer une règle générique dans ces situations et de comprendre la raison de quelque chose à jeter en premier lieu. vous pouvez faire pour vous en remettre.

Je sais que c’est une vieille question, mais personne ici n’a mentionné l’article MSDN, et c’est le document qui a été effacé pour moi, MSDN a un très bon document à ce sujet, vous devriez intercepter des exceptions lorsque les conditions suivantes sont remplies:

  • Vous comprenez bien pourquoi l’exception peut être levée et vous pouvez implémenter une récupération spécifique, par exemple en invitant l’utilisateur à entrer un nouveau nom de fichier lorsque vous interceptez un object FileNotFoundException.

  • Vous pouvez créer et lancer une nouvelle exception plus spécifique.

 int GetInt(int[] array, int index) { try { return array[index]; } catch(System.IndexOutOfRangeException e) { throw new System.ArgumentOutOfRangeException( "Parameter index is out of range."); } } 
  • Vous souhaitez gérer partiellement une exception avant de la transmettre pour un traitement supplémentaire. Dans l’exemple suivant, un bloc catch est utilisé pour append une entrée à un journal d’erreurs avant de relancer l’exception.
  try { // Try to access a resource. } catch (System.UnauthorizedAccessException e) { // Call a custom error logging procedure. LogError(e); // Re-throw the error. throw; } 

Je suggère de lire l’intégralité de la section « Exceptions and Exception Handling » ainsi que les meilleures pratiques pour les exceptions .

La meilleure approche est la seconde (celle dans laquelle vous spécifiez le type d’exception). L’avantage de ceci est que vous savez que ce type d’exception peut se produire dans votre code. Vous gérez ce type d’exception et vous pouvez reprendre. Si d’autres exceptions venaient, cela signifie que quelque chose ne va pas, ce qui vous aidera à trouver des bogues dans votre code. L’application finira par tomber en panne, mais vous saurez qu’il y a quelque chose que vous avez manqué (bug) qui doit être corrigé.

La deuxième approche est bonne.

Si vous ne voulez pas afficher l’erreur et confondre l’utilisateur de l’application en affichant une exception d’exécution (c’est-à-dire une erreur) qui ne lui est pas associée, il suffit de consigner l’erreur et l’équipe technique peut rechercher le problème et le résoudre.

 try { //do some work } catch(Exception exception) { WriteException2LogFile(exception);//it will write the or log the error in a text file } 

Je vous recommande d’aller pour la deuxième approche pour l’ensemble de votre application.

Laisser un bloc de capture vide est la pire chose à faire. S’il y a une erreur, la meilleure façon de le gérer est de:

  1. Connectez-le dans le fichier \ firebase database, etc.
  2. Essayez de le réparer à la volée (essayez peut-être une autre façon de faire cette opération)
  3. Si nous ne pouvons pas résoudre ce problème, avertissez l’utilisateur qu’il y a une erreur et bien sûr annulez l’opération.

Pour moi, la gestion des exceptions peut être considérée comme une règle métier. De toute évidence, la première approche est inacceptable. La seconde est la meilleure et peut être correcte à 100% si le contexte le dit. Maintenant, par exemple, vous développez un complément Outlook. Si vous ajoutez une exception non gérée, l’utilisateur Outlook peut maintenant le savoir, car Outlook ne se détruira pas à cause d’un échec du plug-in. Et vous avez du mal à comprendre ce qui a mal tourné. Par conséquent, la deuxième approche dans ce cas, pour moi, c’est une bonne. Outre la journalisation de l’exception, vous pouvez décider d’afficher un message d’erreur pour l’utilisateur – je le considère comme une règle métier.

La meilleure pratique consiste à lancer une exception lorsque l’erreur se produit. Parce qu’une erreur s’est produite et qu’elle ne doit pas être masquée.

Mais dans la vraie vie, vous pouvez avoir plusieurs situations où vous voulez cacher cela

  1. Vous comptez sur un composant tiers et vous souhaitez poursuivre le programme en cas d’erreur.
  2. Vous avez une parsing de rentabilisation que vous devez poursuivre en cas d’erreur

Vous devriez considérer ces directives de conception pour les exceptions

  • Exception Lancer
  • Utilisation de types d’exception standard
  • Exceptions et performance

https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/exceptions

La catch sans aucun argument consum simplement l’exception et ne sert à rien. Que se passe-t-il si une erreur fatale se produit? Il n’y a aucun moyen de savoir ce qui s’est passé si vous utilisez catch sans argument.

Une instruction catch devrait capturer des exceptions plus spécifiques, telles que FileNotFoundException et à la toute fin, vous devriez intercepter une Exception qui intercepterait toute autre exception et les consignerait.

Parfois, vous devez traiter les exceptions qui ne disent rien aux utilisateurs.

Mon chemin est:

  • Attraper des exceptions non capturées au niveau de l’application (c.-à-d. Dans global.asax) pour des exceptions critiques (l’application peut ne pas être utile). Ces exeptions je ne suis pas attraper sur la place. Il suffit de les connecter au niveau de l’application et de laisser le système faire son travail.
  • Catch “on place” et affiche des informations utiles pour l’utilisateur (entré un numéro erroné, impossible d’parsingr).
  • Catch on place et ne faites rien sur les problèmes marginaux comme “Je vérifierai les informations de mise à jour en arrière-plan, mais le service ne fonctionne pas”.

Il n’est certainement pas nécessaire que ce soit la meilleure pratique. 😉

Avec les exceptions, j’essaie ce qui suit:

Tout d’abord, j’attrape des types spéciaux d’exceptions comme la division par zéro, les opérations IO, etc., et écris du code en fonction de cela. Par exemple, une division par zéro, en fonction de la provenance des valeurs, je pourrais alerter l’utilisateur (par exemple, une simple calculasortingce dans laquelle dans un calcul intermédiaire (pas les arguments) arrive dans une division par zéro) ou traiter cette exception en silence et continuer le traitement.

Ensuite, j’essaie d’attraper les exceptions restantes et de les enregistrer. Si possible, autorisez l’exécution du code, sinon avertissez l’utilisateur qu’une erreur s’est produite et demandez-lui d’envoyer un rapport d’erreur.

Dans le code, quelque chose comme ça:

 try{ //Some code here } catch(DivideByZeroException dz){ AlerUserDivideByZerohappened(); } catch(Exception e){ treatGeneralException(e); } finally{ //if a IO operation here i close the hanging handlers for example }