Je suis actuellement en train d'écrire ma première application Windows Forms. J'ai lu quelques livres sur C # maintenant, donc j'ai une assez bonne compréhension des fonctionnalités du langage que C # doit gérer avec les exceptions. Cependant, ils sont tous assez théoriques, donc je n'ai pas encore une idée de la façon de traduire les concepts de base en un bon modèle de gestion des exceptions dans mon application.
Quelqu'un aimerait-il partager des perles de sagesse sur le sujet? Publiez toutes les erreurs courantes que vous avez vues des débutants comme moi faire, et tout conseil général sur la gestion des exceptions d'une manière qui rendra mon application plus stable et plus robuste.
Les principales choses que j'essaye actuellement de travailler sont:
- Quand dois-je relancer une exception?
- Dois-je essayer d'avoir un mécanisme central de gestion des erreurs?
- La gestion des exceptions qui pourraient être levées a-t-elle un impact sur les performances par rapport au test préventif de choses comme l'existence d'un fichier sur le disque?
- Tout le code exécutable doit-il être inclus dans des blocs try-catch-finally?
- Y a-t-il des moments où un bloc de capture vide pourrait être acceptable?
Tous les conseils reçus avec gratitude!
la source
ApplicationException
pour les cas d'échec que l'application devrait raisonnablement être en mesure de différencier et de gérer.Il y a un excellent article CodeProject de code ici . Voici quelques faits saillants:
la source
Notez que Windows Forms possède son propre mécanisme de gestion des exceptions. Si un bouton du formulaire est cliqué et que son gestionnaire lève une exception qui n'est pas interceptée dans le gestionnaire, Windows Forms affichera sa propre boîte de dialogue d'exception non gérée.
Pour empêcher l'affichage de la boîte de dialogue d'exception non gérée et intercepter ces exceptions pour la journalisation et / ou pour fournir votre propre boîte de dialogue d'erreur, vous pouvez attacher à l'événement Application.ThreadException avant l'appel à Application.Run () dans votre méthode Main ().
la source
Tous les conseils publiés ici jusqu'à présent sont bons et méritent d'être écoutés.
Une chose sur laquelle je voudrais développer est votre question "La gestion des exceptions qui pourraient être levées a-t-elle un impact sur les performances par rapport au test préventif de choses comme l'existence d'un fichier sur le disque?"
La règle empirique naïve est que «les blocs try / catch sont chers». Ce n'est pas vraiment vrai. Essayer n'est pas cher. C'est la capture, où le système doit créer un objet Exception et le charger avec la trace de pile, c'est cher. Il y a de nombreux cas dans lesquels l'exception est, eh bien, suffisamment exceptionnelle pour qu'il soit parfaitement correct d'encapsuler le code dans un bloc try / catch.
Par exemple, si vous remplissez un dictionnaire, ceci:
est souvent plus rapide que de faire cela:
pour chaque élément que vous ajoutez, car l'exception n'est levée que lorsque vous ajoutez une clé en double. (Les requêtes agrégées LINQ le font.)
Dans l'exemple que vous avez donné, j'utiliserais try / catch presque sans réfléchir. Premièrement, ce n'est pas parce que le fichier existe lorsque vous le vérifiez qu'il existera lorsque vous l'ouvrirez, vous devriez donc vraiment gérer l'exception de toute façon.
Deuxièmement, et je pense que plus important encore, à moins que vous a) votre processus ouvre des milliers de fichiers et b) les chances qu'un fichier qu'il essaie d'ouvrir ne soit pas existant ne sont pas trivialement faibles, la performance de la création de l'exception n'est pas quelque chose que vous '' re va jamais remarquer. De manière générale, lorsque votre programme essaie d'ouvrir un fichier, il n'essaie d'ouvrir qu'un seul fichier. C'est un cas où l'écriture de code plus sûr sera presque certainement meilleure que l'écriture du code le plus rapide possible.
la source
dict[key] = value
ce qui devrait être aussi rapide sinon plus rapide ..Voici quelques directives que je suis
Fail-Fast: Il s'agit davantage d'une directive de génération d'exceptions.Pour chaque hypothèse que vous faites et chaque paramètre que vous entrez dans une fonction, effectuez une vérification pour vous assurer que vous commencez avec les bonnes données et que les hypothèses que vous faites sont corrects. Les vérifications typiques incluent, argument non nul, argument dans la plage attendue, etc.
Lors de la relance, conserve la trace de la pile - Cela se traduit simplement par l'utilisation de throw lors de la relance au lieu de lancer une nouvelle Exception (). Sinon, si vous pensez pouvoir ajouter plus d'informations, encapsulez l'exception d'origine en tant qu'exception interne. Mais si vous attrapez une exception uniquement pour la consigner, utilisez définitivement throw;
N'attrapez pas les exceptions que vous ne pouvez pas gérer, alors ne vous inquiétez pas des choses comme OutOfMemoryException, car si elles se produisent, vous ne pourrez pas faire grand-chose de toute façon.
Accrochez les gestionnaires d'exceptions globales et assurez-vous de consigner autant d'informations que possible. Pour winforms, accrochez à la fois les événements d'exception non gérés de domaine d'application et de thread.
Les performances ne doivent être prises en considération que lorsque vous avez analysé le code et constaté qu'il provoque un goulot d'étranglement des performances, par défaut, optimisez la lisibilité et la conception. Donc, à propos de votre question initiale sur la vérification de l'existence du fichier, je dirais que cela dépend.Si vous pouvez faire quelque chose pour que le fichier ne soit pas là, alors oui, faites cette vérification sinon si tout ce que vous allez faire est de lever une exception si le fichier est pas là alors je ne vois pas le point.
Il y a certainement des moments où des blocs catch vides sont nécessaires, je pense que les gens qui disent le contraire n'ont pas travaillé sur des bases de code qui ont évolué au cours de plusieurs versions. Mais ils devraient être commentés et examinés pour s'assurer qu'ils sont vraiment nécessaires. L'exemple le plus typique est que les développeurs utilisent try / catch pour convertir une chaîne en entier au lieu d'utiliser ParseInt ().
Si vous vous attendez à ce que l'appelant de votre code puisse gérer les conditions d'erreur, créez des exceptions personnalisées qui détaillent la situation inattendue et fournissent des informations pertinentes. Sinon, respectez le plus possible les types d'exceptions intégrés.
la source
Application.ThreadException
événement lorsque vous avez fait référence à l'événement "exception de thread non gérée"?J'aime la philosophie de ne rien attraper que je n'ai pas l'intention de manipuler, quel que soit le sens de manipulation dans mon contexte particulier.
Je déteste quand je vois du code tel que:
J'ai vu cela de temps en temps et il est assez difficile de trouver des problèmes quand quelqu'un «mange» les exceptions. Un collègue que j'ai eu fait cela et cela a tendance à contribuer à un flux constant de problèmes.
Je relance s'il y a quelque chose que ma classe particulière doit faire en réponse à une exception mais que le problème doit être évacué, quelle que soit la méthode où cela s'est produit.
Je pense que le code devrait être rédigé de manière proactive et que les exceptions devraient concerner des situations exceptionnelles, et non éviter de tester les conditions.
la source
Je suis juste sur le point de sortir, mais je vais vous expliquer brièvement où utiliser la gestion des exceptions. J'essaierai d'aborder vos autres points à mon retour :)
*Raisonnable. Il n'est pas nécessaire de vérifier si, par exemple, un rayon cosmique a frappé vos données, provoquant le retournement de quelques bits. Comprendre ce qui est «raisonnable» est une compétence acquise pour un ingénieur. C'est difficile à quantifier, mais facile à comprendre. Autrement dit, je peux facilement expliquer pourquoi j'utilise un try / catch dans un cas particulier, mais j'ai du mal à en imprégner un autre avec cette même connaissance.
Pour ma part, j'ai tendance à m'éloigner des architectures fortement basées sur les exceptions. try / catch n'a pas de hit de performance en tant que tel, le hit arrive lorsque l'exception est levée et le code devra peut-être remonter plusieurs niveaux de la pile d'appels avant que quelque chose ne le gère.
la source
La règle d'or à laquelle on a essayé de s'en tenir est de gérer l'exception aussi près que possible de la source.
Si vous devez relancer une exception, essayez d'y ajouter, relancer une FileNotFoundException n'aide pas beaucoup, mais lancer une ConfigurationFileNotFoundException lui permettra d'être capturée et d'agir quelque part dans la chaîne.
Une autre règle que j'essaie de suivre est de ne pas utiliser try / catch comme une forme de flux de programme, donc je vérifie les fichiers / connexions, je m'assure que les objets ont été lancés, etc. avant de les utiliser. Try / catch devrait être pour les exceptions, des choses que vous ne pouvez pas contrôler.
Comme pour un bloc catch vide, si vous faites quelque chose d'important dans le code qui a généré l'exception, vous devez relancer l'exception au minimum. S'il n'y a aucune conséquence du code qui a jeté l'exception ne s'exécutant pas, pourquoi l'avez-vous écrit en premier lieu.
la source
vous pouvez intercepter l'événement ThreadException.
Sélectionnez un projet d'application Windows dans l'Explorateur de solutions.
Ouvrez le fichier Program.cs généré en double-cliquant dessus.
Ajoutez la ligne de code suivante en haut du fichier de code:
Dans la méthode Main (), ajoutez ce qui suit comme première ligne de la méthode:
Ajoutez ce qui suit sous la méthode Main ():
Ajoutez du code pour gérer l'exception non gérée dans le gestionnaire d'événements. Toute exception qui n'est gérée nulle part ailleurs dans l'application est gérée par le code ci-dessus. Le plus souvent, ce code doit consigner l'erreur et afficher un message à l'utilisateur.
référence: https://blogs.msmvps.com/deborahk/global-exception-handler-winforms/
la source
Les exceptions sont coûteuses mais nécessaires. Vous n'avez pas besoin de tout emballer dans une tentative de capture, mais vous devez vous assurer que les exceptions sont toujours interceptées éventuellement. Cela dépendra en grande partie de votre conception.
Ne relancez pas si laisser l'exception augmenter fera tout aussi bien. Ne laissez jamais les erreurs passer inaperçues.
exemple:
Si DoStuff tourne mal, vous voudrez quand même qu'il soit libéré sous caution. L'exception sera lancée à main et vous verrez le train d'événements dans la trace de pile de ex.
la source
Partout, mais les méthodes de l'utilisateur final ... comme les gestionnaires de clic de bouton
J'écris un fichier journal ... assez facile pour une application WinForm
Je ne suis pas sûr de cela, mais je pense que c'est une bonne pratique de faire des exceptions ... Je veux dire que vous pouvez demander si un fichier existe et s'il ne lance pas une FileNotFoundException
ouais
Oui, disons que vous voulez afficher une date, mais vous n'avez aucune idée de la façon dont cette date a été stockée (jj / mm / aaaa, mm / jj / aaaa, etc.), vous essayez de l'analyser, mais si elle échoue, continuez. Si cela ne vous concerne pas ... je dirais que oui, il y a
la source
La seule chose que j'ai apprise très rapidement a été de renfermer absolument chaque morceau de code qui interagit avec tout ce qui se trouve en dehors du flux de mon programme (c'est-à-dire le système de fichiers, les appels de base de données, l'entrée utilisateur) avec des blocs try-catch. Try-catch peut entraîner un impact négatif sur les performances, mais généralement à ces endroits de votre code, cela ne sera pas perceptible et il se rentabilisera en toute sécurité. vraiment besoin de poster un message, mais si vous ne l'attrapez pas, il lancera une erreur d'exception non gérée à l'utilisateur.
J'ai utilisé des blocs de capture vides dans des endroits où l'utilisateur peut faire quelque chose qui n'est pas vraiment "incorrect", mais cela peut lever une exception ... un exemple qui me vient à l'esprit est dans un GridView si l'utilisateur DoubleClique sur l'espace réservé gris cellule en haut à gauche, il déclenchera l'événement CellDoubleClick, mais la cellule n'appartient pas à une ligne. Dans ce cas, vous ne
la source
Lors de la relance d'une exception, le mot clé jeté par lui-même. Cela lèvera l'exception interceptée et pourra toujours utiliser la trace de pile pour voir d'où elle vient.
cela entraînera la fin de la trace de pile dans l'instruction catch. (éviter ça)
la source
Dans mon expérience, j'ai jugé bon d'attraper des exceptions quand je sais que je vais les créer. Pour les cas où je suis dans une application Web et que je fais une Response.Redirect, je sais que je vais recevoir une exception System.ThreadAbortException. Comme c'est intentionnel, j'ai juste une prise pour le type spécifique et je l'avale.
la source
Je suis profondément d'accord avec la règle de:
La raison en est que:
ForceAssert.AlwaysAssert est ma manière personnelle de Trace.Assert, que la macro DEBUG / TRACE soit définie ou non.
Le cycle de développement peut-être: j'ai remarqué le vilain dialogue Assert ou quelqu'un d'autre m'en a parlé, puis je reviens au code et trouve la raison de lever l'exception et décide comment la traiter.
De cette façon, je peux écrire MON code en peu de temps et me protéger du domaine inconnu, mais étant toujours remarqué si des choses anormales se sont produites, de cette façon, le système est devenu sûr et plus sûr.
Je sais que beaucoup d'entre vous ne seront pas d'accord avec moi car un développeur devrait connaître chaque détail de son code, franchement, je suis aussi un puriste dans l'ancien temps. Mais aujourd'hui, j'ai appris que la politique ci-dessus est plus pragmatique.
Pour le code WinForms, une règle d'or à laquelle j'obéis toujours est:
cela protégera votre interface utilisateur en étant toujours utilisable.
Pour l'atteinte des performances, la pénalité de performances se produit uniquement lorsque le code atteint catch, l'exécution du code try sans l'exception réelle déclenchée n'a pas d'effet significatif.
L'exception devrait se produire avec peu de chance, sinon ce ne sont pas des exceptions.
la source
Vous devez penser à l'utilisateur. Le crash de l'application est le dernierchose que l'utilisateur veut. Par conséquent, toute opération qui peut échouer doit avoir un bloc try catch au niveau de l'interface utilisateur. Il n'est pas nécessaire d'utiliser try catch dans chaque méthode, mais chaque fois que l'utilisateur fait quelque chose, il doit être capable de gérer des exceptions génériques. Cela ne vous libère en aucun cas de tout vérifier pour éviter les exceptions dans le premier cas, mais il n'y a pas d'application complexe sans bogues et le système d'exploitation peut facilement ajouter des problèmes inattendus, vous devez donc anticiper l'inattendu et vous assurer si un utilisateur souhaite en utiliser un opération, il n'y aura pas de perte de données car l'application se bloque. Il n'est pas nécessaire de laisser votre application planter, si vous rencontrez des exceptions, elle ne sera jamais dans un état indéterminé et l'utilisateur est TOUJOURS gêné par un crash. Même si l'exception est au plus haut niveau, ne pas planter signifie que l'utilisateur peut rapidement reproduire l'exception ou au moins enregistrer le message d'erreur et donc grandement vous aider à résoudre le problème. Certainement beaucoup plus que d'obtenir un simple message d'erreur et de ne voir que la boîte de dialogue d'erreur Windows ou quelque chose du genre.
C'est pourquoi vous ne devez JAMAIS être vaniteux et penser que votre application ne présente aucun bogue, ce n'est pas garanti. Et c'est un très petit effort pour envelopper quelques blocs try catch sur le code approprié et afficher un message d'erreur / enregistrer l'erreur.
En tant qu'utilisateur, je suis vraiment énervé chaque fois qu'un navigateur ou une application de bureau ou quoi que ce soit se bloque. Si l'exception est si élevée que l'application ne peut pas continuer, il est préférable d'afficher ce message et de dire à l'utilisateur quoi faire (redémarrer, corriger certains paramètres du système d'exploitation, signaler le bogue, etc.) que de simplement planter et c'est tout.
la source