Capture de la classe d’exception de base dans .NET

Je continue à entendre ça

catch (Exception ex) 

C’est une mauvaise pratique, cependant, je l’utilise souvent dans les gestionnaires d’événements où une opération peut par exemple aller au réseau, permettant ainsi la possibilité de nombreux types de défaillances. Dans ce cas, j’attrape toutes les exceptions et affiche le message d’erreur à l’utilisateur dans une boîte de message.

Est-ce considéré comme une mauvaise pratique? Il n’y a plus rien que je puisse faire à l’exception: je ne veux pas que l’application s’arrête, l’utilisateur doit savoir ce qui s’est passé et je suis au niveau le plus élevé de mon code. Que devrais-je faire d’autre?

MODIFIER:

Les gens disent que je devrais regarder dans la stack d’appels et gérer les erreurs de manière spécifique, car par exemple, une exception StackOverflow ne peut pas être gérée de manière significative. Cependant, arrêter le processus est le pire résultat, je veux éviter cela à tout prix. Si je ne peux pas gérer un StackOverflow, qu’il en soit, le résultat ne sera pas pire que de ne pas attraper d’exceptions du tout, et dans 99% des cas, informer l’utilisateur est la moins mauvaise option en ce qui me concerne.

En outre, malgré tous mes efforts pour résoudre toutes les exceptions possibles, il est probable que dans une grande base de code, j’en manquerais quelques-unes. Et pour la plupart d’entre eux, la meilleure défense consiste toujours à informer l’utilisateur.

La mauvaise pratique est

 catch (Exception ex){} 

et variantes:

 catch (Exception ex){ return false; } 

etc.

Saisir toutes les exceptions au niveau supérieur et les transmettre à l’utilisateur (en les enregistrant ou en les affichant dans une boîte de message, selon que vous écrivez un serveur ou une application client) est exactement ce qu’il vous faut. faire.

Je trouve que les arguments selon lesquels les captures génériques sont toujours mauvaises sont excessivement dogmatiques. Comme tout le rest, ils ont leur place.

Cet endroit n’est pas votre code de bibliothèque, ni les classes que vous avez développées sur mesure pour votre application. Comme beaucoup l’ont mentionné, cet endroit est le niveau le plus élevé de l’application. En cas d’ exception, le cas est probablement inattendu.

Voici ma règle générale (et comme toutes les règles, elle est conçue pour être enfreinte si nécessaire):

J’utilise des classes et des bibliothèques personnalisées pour la majorité des opérations de levage dans une application. Ceci est l’architecture de base de l’application – vraiment basique, remarquez. Ces personnes essaient de gérer autant d’exceptions que possible et, si elles ne peuvent vraiment pas continuer, renvoient le type le plus spécifique disponible dans l’interface utilisateur.

À l’interface utilisateur, j’ai toujours tendance à attraper tout ce que font les gestionnaires d’événements. S’il existe une attente raisonnable d’attraper une exception spécifique et que je peux faire quelque chose à ce sujet, j’attrape l’exception et la gère avec élégance. Cela doit venir avant tout, cependant. NET n’utilisera que le tout premier gestionnaire d’exceptions correspondant à votre exception. (Commandez toujours du plus spécifique au plus générique!)

Si je ne peux rien faire à propos de l’exception autre que la sortie d’erreur (disons, la firebase database est hors ligne), ou si l’exception est vraiment inattendue, catch all le prendra, le consignera et échouera rapidement, avec un message d’erreur général affiché à l’utilisateur avant de mourir. (Bien sûr, il y a certaines classes d’erreurs qui échoueront presque toujours sans grâce – OutOfMemory, StackOverflow, etc. Je suis assez chanceux pour ne pas avoir eu à traiter avec celles du code de niveau prod … jusqu’à présent!)

Catch tout a sa place. Cet endroit n’est pas destiné à masquer l’exception, cet endroit n’est pas à essayer de récupérer (parce que si vous ne savez pas ce que vous avez attrapé, comment pouvez-vous éventuellement le récupérer), cet endroit n’est pas destiné à empêcher les erreurs de s’afficher pour l’utilisateur tout en permettant votre application pour continuer à exécuter dans un état inconnu et mauvais.

Catch all est une solution de dernier recours, un piège permettant de s’assurer que, si vos défenses sont bien conçues et bien gardées, elles seront au moins consignées de manière appropriée et pourront être sorties proprement. C’est une mauvaise pratique si vous n’avez pas de défenses bien conçues et bien gardées en place aux niveaux inférieurs, et c’est une très mauvaise pratique aux niveaux inférieurs, mais en dernier recours, c’est (à mon avis) non seulement acceptable. , mais souvent la bonne chose à faire.

Quand je vois

 catch (Exception ex) 

ma main commence à tâtonner pour un marteau. Il n’y a presque aucune excuse pour attraper une exception de base. Seuls les cas valables qui me viennent à l’esprit sont:
1) Le composant tiers jette Exception (sois mon auteur)
2) Traitement des exceptions de très haut niveau (en dernier recours) (par exemple, gérer les exceptions “non gérées” dans l’application WinForms)

Si vous trouvez un cas où de nombreux types d’exceptions peuvent se produire, c’est un bon signe de mauvaise conception.

Je ne suis pas d’accord avec Armin Ronacher. Comment vous comporteriez-vous si une exception StackOverflow était déclenchée? Essayer de réaliser des actions supplémentaires peut avoir des conséquences encore pires. Ne capturez une exception que si vous pouvez la gérer de manière significative et sûre. Attraper System.Exception pour couvrir une gamme d’exceptions possibles est terriblement faux. Même lorsque vous le relancez.

Il est parfaitement logique d’attraper l’exception au plus haut niveau de votre code. Capturer le type d’exception de base est acceptable tant que vous n’avez pas besoin de faire une logique différente en fonction du type de l’exception.

Assurez-vous également que vous affichez un message d’erreur convivial et général et que vous ne présentez pas le message de l’exception réelle. Cela peut conduire à des vulnérabilités de sécurité.

C’est une mauvaise pratique en ce sens que vous ne devriez pas le faire partout.

Dans ce cas, je considérerais cela comme la seule solution raisonnable, car votre exception pourrait être vraiment n’importe quoi. La seule amélioration possible consisterait à append des gestionnaires supplémentaires avant de tout intercepter pour des cas d’erreur spécifiques dans lesquels vous pourriez faire quelque chose à propos de l’exception.

Oui, il est bon d’attraper l’execption de base au plus haut niveau de l’application , ce que vous faites.

Les fortes réactions que vous obtenez sont probablement dues au fait qu’à un autre niveau , il est presque toujours erroné d’attraper l’exception de Base. Dans une bibliothèque en particulier, ce serait une très mauvaise pratique.

Cela convient parfaitement si vous relancez des exceptions que vous ne pouvez pas gérer correctement. Si vous attrapez juste les exceptions, vous pourriez cacher des bogues dans le code que vous ne vous attendez pas. Si vous interceptez des exceptions pour les afficher (et contourner le comportement de l’outil de mémorisation et d’impression-en arrière de stderr), c’est parfaitement acceptable.

Je pense que l’affiche fait référence à la gestion des exceptions comme ceci:

 try {something} catch (SqlException) {do stuff} catch (Exception) {do other stuff} 

L’idée ici est que vous souhaitez détecter les erreurs plus spécifiques (telles que SqlException) en premier et les traiter de manière appropriée, plutôt que de toujours vous baser sur l’exception générale fourre-tout.

La sagesse conventionnelle dit que c’est la bonne façon de gérer les exceptions (et qu’un Catch solo (Exception ex) est mauvais). En pratique, cette approche ne fonctionne pas toujours, en particulier lorsque vous utilisez des composants et des bibliothèques écrits par quelqu’un d’autre.

En production, ces composants génèrent souvent un type d’exception différent de celui attendu par votre code, en fonction du comportement du composant dans votre environnement de développement, même si le problème sous-jacent est identique dans les deux environnements. C’est un problème très courant dans ASP.NET et m’a souvent amené à utiliser un bloc Catch (Exception) nu, qui ne tient pas compte du type d’exception levée.

La gestion des exceptions structurées est une excellente idée en théorie. En pratique, cela peut toujours être une excellente idée dans le domaine de code que vous contrôlez. Une fois que vous présentez des éléments tiers, cela ne fonctionne parfois pas très bien.

Nous utilisons assez souvent Catch ex comme Exception (variante VB.Net). Nous l’enregistrons et examinons nos journaux régulièrement. Traquez les causes et résolvez-les.

Je pense que Catch ex as Exception est complètement acceptable une fois que vous avez affaire à un code de production ET vous avez un moyen général de gérer les exceptions inconnues avec élégance. Personnellement, je ne mets pas le verrou générique avant d’avoir terminé un module / une nouvelle fonctionnalité et mis en place un traitement spécialisé pour toutes les exceptions que j’ai trouvées lors des tests. Cela semble être le meilleur des deux mondes.

Non; dans ce cas, si vous ne voulez pas arrêter le programme, vous ne pouvez rien faire d’autre. Au plus haut niveau, c’est le bon endroit pour le faire, tant que vous vous connectez correctement et que vous ne le cachez pas dans un sourire espoir.

L’important est de comprendre le chemin des exceptions dans votre application, et pas simplement de les lancer ou de les intercepter arbitrairement. Par exemple, que se passe-t-il si l’exception que vous attrapez est Out-Of-Memory? Êtes-vous sûr que votre boîte de dialog va s’afficher dans ce cas? Mais il est certainement utile de définir un point d’exception ultime et de dire que vous ne voulez jamais que les erreurs se propagent au-delà de ce point.

Vous devriez attraper les exceptions liées à ce que vous faites. Si vous examinez les méthodes que vous appelez, vous verrez quelles exceptions elles génèrent et vous souhaitez restr plus spécifiques à celles-ci. Vous devriez avoir la possibilité de savoir quelles exceptions peuvent être émises par les méthodes que vous appelez et de les gérer correctement.

Et … mieux que d’avoir un seul gros coup d’essai, essayez de le faire là où vous en avez besoin.

 try { myThing.DoStuff(); } catch (StuffGoneWrongException ex) { //figure out what you need to do or bail } 

Peut-être pas tout à fait aussi serré, mais cela dépend de ce que vous faites. N’oubliez pas que le travail ne consiste pas seulement à le comstackr et à le placer sur le bureau de quelqu’un, vous voulez savoir ce qui ne fonctionne pas si quelque chose se produit et comment le réparer. (Insérer un discours sur le traçage ici)

bien souvent, les exceptions sont capturées par des ressources gratuites, ce n’est pas important si une exception est (re) levée. dans ces cas, vous pouvez éviter d’essayer d’attraper:

1) pour un object jetable, vous pouvez utiliser le mot-clé “using”: using (SqlConnection conn = new SqlConnection (connStr)) {// code} une fois que vous êtes sorti de la scope de using (normalement ou par une instruction de retour ou par exception), Dispsose La méthode est automatiquement appelée sur l’object. autrement dit, c’est comme essayer / finalement construire.

2) dans asp.net, vous pouvez intercepter l’événement Error ou UnLoad de l’object Page pour libérer votre ressource.

J’espère que je vous aide!

Je réponds à “Cependant, l’arrêt du processus est le pire résultat …”

Si vous pouvez gérer une exception en exécutant un code différent (en utilisant try / catch en tant que stream de contrôle), en essayant, en attendant et en essayant à nouveau, en utilisant une technique différente mais équivalente (c’est-à-dire une méthode de repli), vous devez le faire.

Il est également intéressant de remplacer les messages d’erreur et de les consigner, sauf s’il est pseudo-poli-passif-agressif-agressif “contactez votre administrateur” (lorsque vous savez qu’il n’existe pas d’administrateur et que, le cas échéant, il ne peut rien y faire! ) Mais après cela, l’application doit se terminer, c’est-à-dire que vous obtenez le même comportement avec une exception non gérée.

D’un autre côté, si vous avez l’intention de gérer l’exception en renvoyant l’utilisateur à un thread de code qui a potentiellement détruit son état, je dirais que c’est pire que de mettre fin à l’application et de laisser l’utilisateur recommencer. Est-il préférable que l’utilisateur redémarre au début ou mieux laisse l’utilisateur détruire les données?

Si je reçois une exception inattendue dans le module qui détermine les comptes sur lesquels je peux retirer de l’argent, est-ce que je veux vraiment enregistrer et signaler une exception et renvoyer l’utilisateur à l’écran de retrait d’argent? Nous soaps tous que nous venons de lui accorder le droit de retirer de l’argent de tous les comptes!

Tout ceci est utile pour intercepter des exceptions que vous pouvez gérer. Mais il arrive aussi parfois qu’en raison d’un environnement instable ou que les utilisateurs n’effectuent que le processus correctement, l’application se heurte à une exception inattendue. Ce que vous n’avez pas été répertorié ou manipulé dans le code. Existe-t-il un moyen de capturer l’exception non gérée à partir du fichier app.config et d’afficher une erreur commune?

Met également ce message d’exception de détails dans un fichier journal.

Je travaille pas mal avec des exceptions, et voici la structure de mise en œuvre que je suis en train de suivre:

  1. Tout réduire à Nothing / Ssortingng.Empty / 0 etc. en dehors de Try / Catch.
  2. Initialisez tout ce qui se trouve dans Try / Catch aux valeurs souhaitées.
  3. Commencez par capturer les exceptions les plus spécifiques, par exemple FormatException, mais laissez en dernier recours le traitement des exceptions (vous pouvez avoir plusieurs blocs catch, rappelez-vous)
  4. Presque toujours jeter des exceptions
  5. Laissez l’application_Error de global.asax gérer les erreurs correctement, par exemple, appelez une fonction personnalisée pour consigner les détails de l’erreur dans un fichier et redirigez-les vers une page d’erreur.
  6. Tue tous les objects que tu as dimedés dans un bloc Finally

Un exemple où je pensais qu’il était acceptable de ne pas traiter une exception ‘correctement’ travaillait récemment avec une chaîne GUID (strGuid) transmise via HTTP GET à une page. J’aurais pu implémenter une fonction pour vérifier la validité de la chaîne GUID avant d’appeler New Guid (strGuid), mais il semblait assez raisonnable de:

 Dim myGuid As Guid = Nothing Try myGuid = New Guid(strGuid) 'Some processing here... Catch ex As FormatException lblError.Text = "Invalid ID" Catch ex As Exception Throw Finally If myGuid IsNot Nothing Then myGuid = Nothing End If End Try