Erreurs de programmation courantes dans .Net lors de la gestion des exceptions?

Quelles sont les erreurs les plus courantes que vous avez vues lors de la gestion des exceptions?

Il semble que la gestion des exceptions peut être l’une des choses les plus difficiles à apprendre à faire «bien» dans .Net. Surtout compte tenu de la réponse actuellement classée numéro 1 aux erreurs de programmation communes que les développeurs .NET doivent éviter? est liée à la gestion des exceptions.

Espérons qu’en listant certaines des erreurs les plus courantes, nous pouvons tous apprendre à mieux gérer les exceptions.

Quelles sont les erreurs les plus courantes que vous avez vues lors de la gestion des exceptions?

Je peux penser à beaucoup.

Lisez d’abord mon article sur la catégorisation des exceptions en vexant , sans tête , fatal et exogène :

http://ericlippert.com/2008/09/10/vexing-exceptions/

Quelques erreurs communes:

  • Ne pas gérer les exceptions exogènes.
  • Échec de la gestion des exceptions vexantes.
  • Construction de méthodes qui jettent des exceptions frustrantes.
  • Gestion des exceptions que vous ne pouvez réellement pas gérer, comme des exceptions fatales.
  • Gestion des exceptions qui cachent des bogues dans votre code; ne gérez pas une exception boneheaded, corrigez le bogue pour qu’il ne soit pas lancé en premier lieu

  • Erreur de sécurité: échec du mode non sécurisé

    try { result = CheckPassword(); if (result == BadPassword) throw BadPasswordException(); } catch(BadPasswordException ex) { ReportError(ex); return; } catch(Exception ex) { LogException(ex); } AccessUserData(); 

    Voir ce qui s’est passé Nous avons échoué au mode non sécurisé. Si CheckPassword a lancé NetworkDriverIsAllMessedUpException, nous l’avons détecté, enregistré et accédé aux données de l’utilisateur, que le mot de passe soit correct ou non . Échec au mode sans échec; Lorsque vous obtenez une exception, supposez le pire.

  • Erreur de sécurité: production d’exceptions qui génèrent des informations sensibles , directement ou indirectement.

    Il ne s’agit pas exactement de gérer des exceptions dans votre code, il s’agit de produire des exceptions qui sont gérées par du code hostile.

    Histoire drôle. Avant que .NET 1.0 ne soit livré aux clients, nous avons trouvé un bogue dans lequel il était possible d’appeler une méthode qui présentait l’exception “l’assembly qui appelait cette méthode n’a pas l’autorisation de déterminer le nom du fichier C: \ foo.txt”. Génial. Merci de me le faire savoir. Qu’est-ce qui empêche ledit assembly d’attraper l’exception et d’interroger son message pour obtenir le nom du fichier? Rien. Nous avons corrigé cela avant l’expédition.

    C’est un problème direct. Un problème indirect serait un problème que j’ai implémenté dans LoadPicture , dans VBScript. Cela a donné un message d’erreur différent selon que l’argument incorrect est un répertoire, un fichier qui n’est pas une image ou un fichier qui n’existe pas. Ce qui signifie que vous pouvez l’utiliser comme un navigateur de disque très lent! En essayant un tas de choses différentes, vous pouvez progressivement créer une image de quels fichiers et répertoires étaient sur le disque dur de quelqu’un. Les exceptions doivent être conçues de telle sorte que si elles sont gérées par un code non fiable, ce code n’apprend aucune des informations privées de l’utilisateur sur ce qu’elles ont fait pour provoquer l’exception. (LoadPicture donne désormais des messages d’erreur beaucoup moins utiles.)

  • Erreur de gestion de la sécurité et des ressources: les gestionnaires qui ne nettoient pas les ressources sont des fuites de ressources en attente. Les fuites de ressources peuvent être utilisées comme des attaques par déni de service par un code de confiance partiel hostile qui crée délibérément des situations générasortingces d’exceptions.

  • Erreur de robustesse: les gestionnaires doivent supposer que l’état du programme est perturbé à moins de gérer une exception exogène spécifique. Cela est particulièrement vrai pour les blocs finalement. Lorsque vous gérez une exception imprévue, il est tout à fait possible et même probable que quelque chose soit profondément perturbé dans votre programme. Vous ne savez pas si certains de vos sous-systèmes fonctionnent et, le cas échéant, si les appeler amélioreront ou aggraveront la situation. Concentrez-vous sur l’enregistrement de l’erreur et, si possible, enregistrez les données utilisateur et arrêtez-vous aussi proprement que possible. Supposons que rien ne fonctionne bien.

  • Erreur de sécurité: les mutations d’état globales temporaires qui ont des impacts sur la sécurité doivent être annulées avant qu’un code pouvant être hostile puisse s’exécuter. Le code hostile peut s’exécuter avant que les blocs ne soient enfin exécutés! Voir mon article à ce sujet pour plus de détails:

http://blogs.msdn.com/ericlippert/archive/2004/09/01/224064.aspx

Relancer des exceptions comme ceci:

 try { // some code here } catch(Exception ex) { // logging, etc throw ex; } 

Cela tue la trace de la stack, rendant beaucoup moins utilisable. La manière correcte de relancer serait comme ceci:

 try { // some code here } catch(Exception ex) { // logging, etc throw; } 

Attraper toutes les exceptions lorsque, dans de nombreux cas, vous devez tenter de détecter des exceptions spécifiques:

 try { // Do something. } catch (Exception exc) { // Do something. } 

Plutôt que:

 try { // Do something. } catch (IOException exc) { // Do something. } 

Les exceptions doivent être commandées du plus spécifique au moins.

Relancer une exception avec un message sans signification.

 try { ... } catch (Exception ex) { throw new Exception("An error ocurred when saving database changes"). } 

Vous ne croirez pas combien de fois je vois du code comme celui-ci en cours de production.

Personne ne parle de voir des blocs de capture vides comme ceux-ci ….

  try{ //do something } catch(SQLException sqex){ // do nothing } 

N’utilisez également jamais la gestion des exceptions pour créer des stream de méthodes alternatifs …

  try{ //do something }catch(SQLException sqex){ //do something else } 

Ne pas utiliser sur des objects IDisposable :

 File myFile = File.Open("some file"); callSomeMethodWhichThrowsException(myFile); myFile.Close(); 

myFile n’est pas fermé tant que le finaliseur myFile n’est pas appelé (ce qui peut ne jamais être le cas) car une exception a été myFile.Close() avant l’ myFile.Close() .

La bonne façon de le faire est

 using(File myFile = File.Open("some file")) { callSomeMethodWhichThrowsException(myFile); } 

Cela est traduit par le compilateur en quelque chose comme:

 File myFile = File.Open("some file"); try { callSomeMethodWhichThrowsException(myFile); } finally { if(myFile != null) myFile.Dispose(); //Dispose() calls Close() } 

Le dossier est donc fermé même en présence d’exceptions.

Oubliez de définir l’exception interne lors du renvoi d’une exception capturée

 try { ... } catch (IOException ioException) { throw new AppSpecificException("It was not possible to save exportation file.") // instead of throw new AppSpecificException("It was not possible to save exportation file.", ioException); } 

Lorsque j’ai posté cette réponse, j’oublie de mentionner que nous devons toujours envisager d’inclure une exception interne ou non pour des raisons de sécurité. Comme Eric Lippert l’a souligné sur une autre réponse à ce sujet , certaines exceptions peuvent fournir des informations sensibles sur les détails d’implémentation du serveur. Ainsi, si l’appelant qui traitera l’exception ne sera pas approuvé, il n’est pas judicieux d’inclure les informations d’exception internes.

Prise vide:

 //What's the point? catch() {} 

Redémarrer:

 //Exceptions are for *adding* detail up the stack catch (Exception ex) {throw ex;} 

En supposant qu’une exception couvrant de nombreux scénarios était quelque chose de spécifique. Un scénario réel était une application Web où la gestion des exceptions supposait toujours que toutes les erreurs étaient des expirations de session et consignées et signalait toutes les erreurs comme des dépassements de délai de session.

Un autre exemple:

 try { Insert(data); } catch (SqlException e) { //oh this is a duplicate row, lets change to update Update(data); } 

Pour enregistrer Exception.Message au lieu d’Exception.ToSsortingng ()

Plusieurs fois, je vois du code enregistrant uniquement le message d’exception alors qu’il devrait enregistrer la méthode de retour de ToSsortingng. ToSsortingng fournit beaucoup plus d’informations sur l’exception que le message. Il comprend des informations telles que l’exception interne et la trace de stack en plus du message.

Essayer d’attraper OutOfMemoryException ou StackOverflowException – ceux-ci conduisent à un arrêt de l’exécution, donc à un moyen de les capturer dans le même processus (ou même à partir du CLR dans son ensemble?)

OutOfMemoryException: exception levée lorsque la mémoire est insuffisante pour poursuivre l’exécution d’un programme.

“À partir de .NET Framework version 2.0, un object StackOverflowException ne peut pas être intercepté par un bloc try-catch et le processus correspondant se termine par défaut. Par conséquent, les utilisateurs sont invités à écrire leur code pour détecter et empêcher un débordement de stack.”

Défaut d’attraper des exceptions possibles dans un gestionnaire de capture. Cela peut entraîner la propagation de la mauvaise exception vers le haut.

Par exemple:

 try { DoImportantWork(); } catch { Cleanup(); throw; } 

Que se passe-t-il si Cleanup() lance une exception? Vous ne voulez pas voir une exception pointant vers la méthode Cleanup () dans ce gestionnaire catch. Vous voulez l’erreur d’origine. Vous pouvez essayer de consigner l’erreur de nettoyage, mais même votre code de journalisation a besoin d’une gestion des exceptions pour éviter de générer des exceptions.

 try { DoImportantWork(); } catch { try { Cleanup(); } catch { // We did our best to clean up, and even that failed. // If you try to log this error, the logging may throw yet another Exception. } throw; } 

Faux

 try { // Do something stupid } catch { // ignore the resulting error because I'm lazy, and later spend // a week trying to figure out why my app is crashing all over // the place. } 

Meilleur

 try { /// do something silly. } catch (InvalidOperationException ex) { /// respond, or log it. } catch (Exception e) { /// log it. } 

Utilisation des exceptions pour le contrôle de stream normal. Les exceptions devraient être exceptionnelles. S’il s’agit d’une opération correcte / attendue, utilisez les valeurs de retour, etc.