web-dev-qa-db-fra.com

Pourquoi essayer {...} finalement {...} c'est bien; essayer {...} attraper {} mauvais?

J'ai vu des gens dire qu'il est mauvais d'utiliser un catch sans argument, surtout si ce catch ne fait rien:

StreamReader reader=new  StreamReader("myfile.txt");
try
{
  int i = 5 / 0;
}
catch   // No args, so it will catch any exception
{}
reader.Close();

Cependant, cela est considéré comme une bonne forme:

StreamReader reader=new  StreamReader("myfile.txt");
try
{
  int i = 5 / 0;
}
finally   // Will execute despite any exception
{
  reader.Close();
}

Autant que je sache, la seule différence entre le code de nettoyage dans un bloc finally et le code de nettoyage après les blocs try..catch est que si vous avez des instructions de retour dans votre bloc try (dans ce cas, le code de nettoyage dans exécuter, mais le code après le try..catch ne sera pas).

Sinon, qu'est-ce qui a de si spécial enfin?

193
mbeckish

La grande différence est que try...catch va avaler l'exception en masquant le fait qu'une erreur s'est produite. try..finally exécutera votre code de nettoyage, puis l'exception continuera, pour être gérée par quelque chose qui sait quoi faire avec.

349
Khoth

"Enfin" indique "Vous devez toujours faire quelque chose pour vous assurer que l'état du programme est sain". En tant que tel, il est toujours bon d’en avoir un, s’il existe une possibilité que des exceptions gâchent le programme. Le compilateur déploie également beaucoup d'efforts pour s'assurer que votre code Finally est exécuté.

"Catch" est une déclaration de "Je peux récupérer de cette exception". Vous ne devriez récupérer que des exceptions que vous pouvez vraiment corriger - une prise sans arguments indique "Hé, je peux récupérer de n'importe quoi!", Ce qui est presque toujours faux.

Si il était possible de récupérer de chaque exception, alors ce serait vraiment un problème sémantique, à propos de ce que vous déclarez être votre intention. Cependant, ce n’est pas le cas, et il est presque certain que les cadres supérieurs aux vôtres seront mieux équipés pour gérer certaines exceptions. En tant que tel, utilisez enfin, faites exécuter votre code de nettoyage gratuitement, mais laissez tout de même des gestionnaires plus compétents s’occuper du problème.

59
Adam Wright

Parce que quand cette seule ligne lève une exception, vous ne le sauriez pas.

Avec le premier bloc de code, l'exception sera simplement absorbée, le programme continuera à s'exécuter même si l'état du programme est peut-être incorrect.

Avec le deuxième bloc, l'exception sera levée et les bulles monteront mais la reader.Close() est toujours garantie courir.

Si une exception n'est pas attendue, ne mettez pas un bloc try..catch de la sorte, il sera difficile de déboguer plus tard, lorsque le programme passera à un mauvais état et vous ne saurez pas pourquoi.

32
chakrit

Enfin est exécuté quoi qu'il arrive. Donc, si votre bloc try a réussi, il s'exécutera. Si votre bloc try échoue, il exécutera ensuite le bloc catch, puis le bloc finally.

En outre, il est préférable d'essayer d'utiliser la construction suivante:

using (StreamReader reader=new  StreamReader("myfile.txt"))
{
}

Comme l'instruction using est automatiquement encapsulée dans un try/finally et le flux sera automatiquement fermé. (Vous aurez besoin de mettre/essayer autour de l'instruction using si vous voulez réellement attraper l'exception).

21
Mark Ingram

Bien que les 2 blocs de code suivants soient équivalents, ils ne sont pas égaux.

try
{
  int i = 1/0; 
}
catch
{
  reader.Close();
  throw;
}

try
{
  int i = 1/0;
}
finally
{
  reader.Close();
}
  1. "Enfin" est un code révélateur d'intention. Vous déclarez au compilateur et aux autres programmeurs que ce code doit être exécuté quoi qu'il se passe.
  2. si vous avez plusieurs blocs catch et que vous avez le code de nettoyage, vous avez enfin besoin. Sans cela, vous dupliqueriez votre code de nettoyage dans chaque bloc catch. (Principe DRY)

enfin les blocs sont spéciaux. Le CLR reconnaît et traite le code avec un bloc finally séparément des blocs catch, et le CLR déploie beaucoup d'efforts pour garantir qu'un bloc final sera toujours exécuté. Ce n'est pas juste du sucre syntaxique du compilateur.

8
Robert Paulson

Je suis d'accord avec ce qui semble être le consensus ici: une "capture" vide est mauvaise car elle masque toute exception qui aurait pu se produire dans le bloc try.

En outre, du point de vue de la lisibilité, lorsque je vois un bloc "try", je suppose qu’il y aura une déclaration "catch" correspondante. Si vous utilisez uniquement un 'essai' afin de vous assurer que les ressources sont désallouées dans le bloc 'enfin', vous pouvez envisager l'instruction 'utiliser' ' :

using (StreamReader reader = new StreamReader('myfile.txt'))
{
    // do stuff here
} // reader.dispose() is called automatically

Vous pouvez utiliser l'instruction 'using' avec n'importe quel objet implémentant IDisposable. La méthode dispose () de l'objet est appelée automatiquement à la fin du bloc.

5
Chris Lawlor

Le bloc try..finally lancera toujours les exceptions déclenchées. Tout ce que finally fait est de s'assurer que le code de nettoyage est exécuté avant que l'exception ne soit levée.

Le try..catch avec une capture vide consommera complètement toute exception et cachera le fait que cela s'est produit. Le lecteur sera fermé, mais rien ne dit si la bonne chose s’est produite. Et si votre intention était d'écrire i dans le fichier? Dans ce cas, vous ne pourrez pas accéder à cette partie du code et myfile.txt sera vide. Est-ce que toutes les méthodes en aval gèrent cela correctement? Quand vous verrez le fichier vide, serez-vous capable de deviner correctement qu'il est vide parce qu'une exception a été levée? Mieux vaut jeter l'exception et faire savoir que vous faites quelque chose de mal.

Une autre raison est le try..catch fait comme ceci est complètement incorrect. Ce que vous dites en faisant ceci est, "Peu importe ce qui se passe, je peux le gérer." Qu'en est-il de StackOverflowException, pouvez-vous nettoyer par la suite? Qu'en est-il de OutOfMemoryException? En général, vous ne devez gérer que les exceptions que vous attendez et savez comment gérer.

3
OwenP

Utilisation Try..Catch..Finally, si votre méthode sait comment gérer l'exception localement. L'exception se produit dans Try, Handled in Catch et ensuite, le nettoyage est terminé dans Finally.

Si votre méthode ne sait pas comment gérer l'exception mais a besoin d'un nettoyage une fois qu'elle est produite, utilisez Try..Finally

Ainsi, l’exception est propagée aux méthodes d’appel et gérée s’il existe des instructions Catch appropriées dans les méthodes d’appel. Si aucun gestionnaire d’exception ne figure dans la méthode en cours ou si aucune des méthodes d’appel ne s’applique, l’application se bloque.

Par Try..Finally _ il est garanti que le nettoyage local est effectué avant de propager l'exception aux méthodes d'appel.

3
manjuv

Si vous ne savez pas quel type d'exception capturer ou quoi en faire, il est inutile de disposer d'une instruction catch. Vous devriez simplement le laisser à un appelant plus haut placé qui peut avoir plus d'informations sur la situation pour savoir quoi faire.

Vous devriez toujours avoir une dernière instruction là-bas au cas où il y aurait une exception, afin de pouvoir nettoyer les ressources avant que cette exception ne soit renvoyée à l'appelant.

2
Mark Cidade

Du point de vue de la lisibilité, il indique de manière plus explicite aux futurs lecteurs de code: "ces éléments ici sont importants, ils doivent être exécutés quoi qu’il se passe". C'est bon.

En outre, les déclarations de captures vides ont tendance à avoir une certaine "odeur". Ils peuvent indiquer que les développeurs ne réfléchissent pas aux diverses exceptions pouvant se produire et à la manière de les gérer.

2
Ryan

Enfin, c'est optionnel - il n'y a aucune raison d'avoir un bloc "Finally" s'il n'y a pas de ressources à nettoyer.

2
Guy Starbuck

Tiré de: ici

La levée et la capture d'exceptions ne doivent pas se produire systématiquement dans le cadre de l'exécution réussie d'une méthode. Lors du développement de bibliothèques de classes, le code client doit avoir la possibilité de tester une condition d'erreur avant d'entreprendre une opération susceptible de générer une exception. Par exemple, System.IO.FileStream fournit une propriété CanRead qui peut être vérifiée avant d'appeler la méthode Read, empêchant ainsi la génération d'une exception potentielle, comme illustré dans le fragment de code suivant:

Dim str As Stream = GetStream () If (str.CanRead) Then 'code permettant de lire le flux End If

La décision de vérifier ou non l'état d'un objet avant d'appeler une méthode particulière susceptible de déclencher une exception dépend de l'état attendu de l'objet. Si un objet FileStream est créé à l'aide d'un chemin de fichier qui devrait exister et d'un constructeur qui devrait renvoyer un fichier en mode lecture, la vérification de la propriété CanRead n'est pas nécessaire. l'incapacité de lire FileStream violerait le comportement attendu des appels de méthode effectués et une exception devrait être déclenchée. En revanche, si une méthode est documentée comme renvoyant une référence FileStream pouvant ou non être lisible, il est conseillé de vérifier la propriété CanRead avant de tenter de lire les données.

Pour illustrer l'impact sur les performances lié à l'utilisation d'une technique de codage "exécuter jusqu'à exception", les performances d'un transtypage, qui lève une exception InvalidCastException en cas d'échec de la transtypage, sont comparées à l'opérateur C # as, qui renvoie la valeur null en cas d'échec de la transtypage. La performance des deux techniques est identique pour le cas où la conversion est valide (voir Test 8.05), mais pour le cas où la conversion est invalide et où l'utilisation d'un cast provoque une exception, l'utilisation d'un cast est 600 fois plus lente que l'utilisation du script. en tant qu'opérateur (voir test 8.06). L'impact haute performance de la technique de lancement d'exception inclut le coût de l'allocation, du lancement et de la capture de l'exception, ainsi que le coût de la récupération de place ultérieure de l'objet exception, ce qui signifie que l'impact instantané du lancement d'une exception n'est pas si élevé. À mesure que davantage d'exceptions sont levées, le ramassage fréquent des ordures devient un problème, de sorte que l'impact global de l'utilisation fréquente d'une technique de codage avec levée des exceptions sera similaire à celui de l'essai 8.05.

2
SpoiledTechie.com

Si vous lisez C # pour les programmeurs , vous comprendrez que le dernier bloc a été conçu pour optimiser une application et éviter les fuites de mémoire.

Le CLR n'élimine pas complètement les fuites ... des fuites de mémoire peuvent survenir si le programme conserve par inadvertance des références à des objets indésirables.

Par exemple, lorsque vous ouvrez une connexion à un fichier ou à une base de données, votre ordinateur allouera de la mémoire pour gérer cette transaction. Cette mémoire ne sera conservée que si la commande disposée ou close a été exécutée. mais si au cours de la transaction, une erreur s'est produite, la commande en cours sera terminée sauf si elle se trouvait à l'intérieur du try.. finally.. bloquer.

catch était différent de finally en ce sens que, catch était conçu pour vous permettre de gérer/gérer ou d'interpréter l'erreur elle-même. Pensez-y comme à une personne qui vous dit "hé, j'ai attrapé des méchants, que voulez-vous que je leur fasse?" while finally a été conçu pour vous assurer que vos ressources ont été correctement placées. Pensez à quelqu'un qui, qu'il y ait ou non des méchants, s'assurera que votre propriété est toujours en sécurité.

Et vous devriez permettre à ces deux-là de travailler ensemble pour de bon.

par exemple:

try
{
  StreamReader reader=new  StreamReader("myfile.txt");
  //do other stuff
}
catch(Exception ex){
 // Create log, or show notification
 generic.Createlog("Error", ex.message);
}
finally   // Will execute despite any exception
{
  reader.Close();
}
2
dr.Crow

Il est déconseillé d’ajouter une clause catch juste pour réexaminer l’exception.

2
Bastien Vandamme

Enfin, vous pouvez nettoyer les ressources, même si votre instruction catch renvoie l’exception au programme appelant. Avec votre exemple contenant l'instruction de capture vide, il y a peu de différence. Cependant, si vous êtes pris au piège, si vous faites un peu de traitement et jetez l'erreur, ou même si vous n'avez même pas de piège, l'attaque sera toujours exécutée.

1
Kibbee

Tout d’abord, c’est une mauvaise pratique d’attraper des exceptions que vous ne prenez pas la peine de gérer. Consultez Chapitre 5 sur les performances .Net de Amélioration des performances et de l'évolutivité des applications .NET . Remarque secondaire, vous devriez probablement charger le flux à l'intérieur du bloc try. Ainsi, vous pouvez intercepter l'exception pertinente en cas d'échec. La création du flux en dehors du bloc try annule son objectif.

1
Factor Mystic

essayer {…} catch {} n'est pas toujours mauvais. Ce n'est pas un modèle courant, mais j'ai tendance à l'utiliser quand j'ai besoin d'arrêter des ressources quoi qu'il en soit, comme fermer des sockets (éventuellement) ouverts à la fin d'un thread.

0
Martin Liesén

Parmi les raisons probablement nombreuses, les exceptions sont très lentes à exécuter. Vous pouvez facilement paralyser vos temps d'exécution si cela se produit souvent.

0
lotsoffreetime

Le problème des blocs try/catch qui interceptent toutes les exceptions est que votre programme est maintenant dans un état indéterminé si une exception inconnue se produit. Cela va complètement à l'encontre de la règle d'échec rapide - vous ne voulez pas que votre programme continue si une exception se produit. L'essai/capture ci-dessus attraperait même OutOfMemoryExceptions, mais il s'agit certainement d'un état dans lequel votre programme ne s'exécutera pas.

Les blocs try/finally vous permettent d'exécuter du code de nettoyage tout en échouant rapidement. Dans la plupart des cas, vous souhaitez uniquement intercepter toutes les exceptions au niveau global afin de pouvoir les consigner, puis les quitter.

0
David Mohundro

La différence effective entre vos exemples est négligeable tant qu'aucune exception n'est levée.

Si, toutefois, une exception est levée dans la clause 'try', le premier exemple l'avalera complètement. Le deuxième exemple lève l’exception à l’étape suivante de la pile d’appel. La différence entre les exemples cités est que l’un occulte complètement les exceptions (premier exemple) et que l’autre (deuxième exemple) conserve les informations relatives aux exceptions pour une éventuelle gestion ultérieure. toujours en train d'exécuter le contenu de la clause 'finally'.

Si, par exemple, vous deviez insérer du code dans la clause 'catch' du premier exemple qui renvoyait une exception (celle qui avait été initialement déclenchée ou une nouvelle), le code de nettoyage du lecteur ne serait jamais exécuté. Enfin, exécute peu importe de ce qui se passe dans la clause 'catch'.

La différence principale entre "catch" et "finally" réside donc dans le fait que le contenu du bloc "finally" (à quelques rares exceptions près) peut être considéré comme garanti à exécuter, même en présence d'un exception inattendue, alors que tout code suivant une clause "catch" (mais en dehors d’une clause "finally") ne comporterait pas une telle garantie.

Incidemment, Stream et StreamReader implémentent tous deux IDisposable et peuvent être encapsulés dans un bloc 'using'. Les blocs 'using' sont l'équivalent sémantique de try/finally (no 'catch'), votre exemple pourrait donc être plus simplement exprimé comme suit:

using (StreamReader reader = new  StreamReader("myfile.txt"))
{
  int i = 5 / 0;
}

... qui ferme et supprime l'instance StreamReader lorsqu'elle sort de la portée. J'espère que cela t'aides.

0
Jared