J'y ai pensé et je n'ai pas pu donner d'exemple. Pourquoi quelqu'un voudrait-il attraper une exception sans rien faire? Pouvez-vous donner un exemple? Peut-être que c'est juste quelque chose qui ne devrait jamais être fait.
Je le fais tout le temps avec des choses comme des erreurs de conversion en D:
import std.conv, std.stdio, std.exception;
void main(string[] args) {
enforce(args.length > 1, "Usage: foo.exe filename");
double[] nums;
// Process a text file with one number per line into an array of doubles,
// ignoring any malformed lines.
foreach(line; File(args[1]).byLine()) {
try {
nums ~= to!double(line);
} catch(ConvError) {
// Ignore malformed lines.
}
}
// Do stuff with nums.
}
Cela dit, je pense que tous les blocs catch devraient avoir quelque chose en eux, même si ce quelque chose n'est qu'un commentaire expliquant pourquoi vous ignorez l'exception.
Edit: je tiens également à souligner que, si vous allez faire quelque chose comme ça, vous devez faire attention à ne saisir que l'exception spécifique que vous souhaitez ignorer. Faire un vieux simple catch {}
est presque toujours une mauvaise chose.
Un exemple où je pense qu'il est juste d'avaler une exception sans rien faire, même en enregistrant l'exception, est à l'intérieur du code de journalisation lui-même.
Si vous avez essayé d'enregistrer quelque chose et que vous avez obtenu une exception, vous ne pouvez pas faire grand-chose:
Cela vous laisse l'option "le moins du mal" de l'avaler tranquillement, permettant à l'application de continuer à effectuer son travail principal, mais compromettant la capacité de le dépanner.
Si une exception est levée par une opération qui était de toute façon facultative, il n'y a peut-être rien à faire. Il peut ne pas être utile de le consigner. Dans ce cas, vous voudrez peut-être simplement l'attraper et ne rien faire.
Si vous faites cela, ajoutez un commentaire. Toujours commenter tout ce qui ressemble à un bogue (passage dans une instruction switch
, bloc catch
vide, affectation dans une condition).
Vous pourriez dire que ce n'est pas une utilisation appropriée des exceptions, mais c'est pour une autre question.
Dans certains cas, Java vous oblige à gérer une exception qui ne peut, en aucun cas, se produire. Considérez ce code:
try {
bytes[] hw = "Hello World".getBytes("UTF-8");
}
catch(UnsupportedCodingException e) {
}
Chaque implémentation de la plate-forme Java est requise pour prendre en charge les jeux de caractères standard suivants.
...
UTF-8
Sans casser votre Java, il n'y a tout simplement aucun moyen de provoquer cette exception. Alors, pourquoi prendre la peine de la gérer? Mais vous ne pouvez pas simplement annuler la clause try-catch.
En règle générale, non. Vous souhaitez soit lever l'exception dans la pile, soit consigner ce qui s'est passé.
Cependant, je le fais souvent lorsque j'analyse des chaînes et convertis en nombres (en particulier dans les projets de mappage qui sont de la variété à usage unique et jetables).
boolean isNonZeroNumber = false;
try {
if(StringUtils.isNotBlank(s) && new BigDecimal(s).compareTo(BigDecimal.ZERO) != 0) {
b = true;
}
} catch(NumberFormatException e) {
//swallow this exception; b stays false.
}
return b;
Les blocs catch
vides sont une odeur de code dans la plupart des langues. L'idée principale est d'utiliser des exceptions pour des situations exceptionnelles et non de les utiliser pour un contrôle logique. Toutes les exceptions doivent être gérées quelque part.
En conséquence de cette approche, lorsque vous programmez une couche d'application particulière, vous avez plusieurs choix:
Cela ne laisse pas vraiment de place pour les blocs catch
vides.
MODIFIÉ POUR AJOUTER: supposez que vous programmez dans un langage où lever des exceptions est le moyen normal de contrôler la logique du programme (une des alternatives à goto
). Ensuite, un bloc catch
vide dans un programme écrit dans ce langage ressemble beaucoup à un bloc else
vide dans un langage traditionnel (ou pas de bloc else
du tout). Cependant, je crois que ce n'est pas le style de programmation recommandé par C #, Java ou les communautés de développement C++.
Dans certains cas, l'exception n'est pas du tout exceptionnelle; en fait, c'est prévu. Considérez cet exemple:
/* Check if the process is still alive; exitValue() throws an exception if it is */
try {
p.exitValue();
processPool.remove(p);
}
catch (IllegalThreadStateException e) { /* expected behaviour */ }
Puisqu'il n'y a pas de méthode isAlive () dans Java.lang.Process (), la seule façon de vérifier si elle est toujours vivante est d'appeler exitValue (), qui lève une exception IllegalThreadStateException si le processus est toujours en cours d'exécution.
Dans mon humble expérience, c'est rarement cette bonne chose. Votre kilométrage peut cependant varier.
Presque chaque fois que j'ai vu cela dans notre base de code, c'est parce que j'ai trouvé un bug que l'exception mangée a caché. En d'autres termes, en ne fournissant aucun code, l'application n'a aucun moyen d'indiquer une erreur ou d'effectuer une autre action.
Parfois, au niveau des couches API par exemple, vous ne voudrez peut-être pas ou ne pourrez pas laisser passer les exceptions, alors dans ce cas, j'ai tendance à essayer d'attraper les plus génériques, puis à les enregistrer. Encore une fois, pour donner au moins une chance à une session de débogage/diagnostic.
En règle générale, si elle doit être vide, le moins que je fasse est de mettre une assertion quelconque, donc au moins quelque chose se produit pendant une session de débogage. Cela dit, si je ne peux pas le gérer, j'ai tendance à les omettre complètement.
OMI, il y a un endroit où les déclarations de capture vides sont OK. Dans les cas de test où vous attendez une exception:
try
{
DoSomething(); //This should throw exception if everything works well
Assert.Fail('Expected exception was not thrown');
}
catch(<whatever exception>)
{
//Expected to get here
}
Je crois qu'il y a certains cas où il est justifié d'avoir une clause catch vide - ce sont des cas où vous voulez que le code continue simplement si une opération particulière échoue. Cependant, je pense que ce modèle peut être facilement surutilisé, donc chaque utilisation doit être examinée de près pour s'assurer qu'il n'y a pas de meilleure façon de le gérer. En particulier, cela ne devrait jamais être fait s'il laisse le programme dans un état incohérent ou s'il peut entraîner l'échec d'une autre partie du code plus tard.
La vérification d'existence est un bon cas d'utilisation:
// If an exception happens, it doesn't matter. Log the initial value or the new value
function logId(person) {
let id = 'No ID';
try {
id = person.data.id;
} catch {}
console.log(id);
}
Un autre cas est lorsqu'une clause finally
est utilisée:
function getter(obj){}
function objChecker()
{
try
{
/* Check argument length and constructor type */
if (getter.length === 1 && getter.constructor === Function)
{
console.log("Correct implementation");
return true;
}
else
{
console.log("Wrong implementation");
return false;
}
}
catch(e)
{
}
finally
{
console.log(JSON.stringify(getter))
}
}
// Test the override of the getter method
getter = getter;
objChecker();
getter = [];
objChecker();
getter = {};
objChecker();
getter = RegExp;
objChecker();
getter = Function;
objChecker();
Il est proposé d'autoriser omission de la liaison de capture dans ECMAScript qui se rapporte à ce cas d'utilisation et à des cas similaires.
Références
Je ne crois pas à ne pas avoir un certain niveau d'alerte lorsqu'une exception se produit - l'un des objectifs de la programmation ne devrait-il pas être d'améliorer le code? Si une exception se produit 10% du temps, et que vous ne le savez jamais, alors l'exception sera toujours aussi mauvaise, ou pire.
Cependant, je vois l'autre côté, que si l'exception ne fait pas de mal réel dans le traitement du code, alors pourquoi y exposer l'utilisateur. La solution que j'implémente généralement est de l'avoir enregistré par ma classe d'enregistreurs (c'est-à-dire dans chaque classe que j'écris au travail).
Si c'est une exception bénigne, alors j'appelle la méthode .Debug () de mon enregistreur; sinon, Logger.Error () (et (peut-être) throw).
try
{
doWork();
}
catch(BenignException x)
{
_log.Debug("Error doing work: " + x.Message);
}
Soit dit en passant, dans mon implémentation de mon enregistreur, effectuer un appel à la méthode .Debug () ne l'enregistrera que si mon fichier App.Config spécifie que le niveau du journal d'exécution du programme est en mode débogage.
C'est correct dans la situation où une séquence doit être exécutée, peu importe ce qui se passe avec la pièce la plus critique placée à la fin.
Par exemple. En communication de contrôle de mouvement de l'hôte au contrôleur. Dans les situations d'urgence, il existe un certain nombre d'étapes de préparation pour interrompre le mouvement, arrêter la génération de trajectoire, ralentir les moteurs, activer les pauses, désactiver l'amplificateur et après cela, vous devez tuer l'alimentation du bus. Tout doit se produire séquentiellement et si l'une des étapes échoue, le reste des étapes doit être exécuté de toute façon, même avec un risque accepté d'endommagement du matériel. Disons que même si tout ce qui précède échoue, l'alimentation du bus de mise à mort doit quand même se produire.
La seule fois où j'ai vraiment eu besoin de faire quelque chose comme ça, c'était avec une classe de journalisation pour une application console. Il y avait un gestionnaire de capture global de niveau supérieur qui a émis l'erreur vers STDOUT, a enregistré l'erreur dans un fichier, puis a fermé l'application avec un code d'erreur> 0.
Le problème ici était que parfois, la journalisation dans le fichier échouait. Les autorisations de répertoire vous ont empêché d'écrire le fichier, le fichier était exclusivement verrouillé, peu importe. Si cela se produisait, je ne pourrais pas gérer l'exception de la même manière que je l'ai fait ailleurs (capture, consigner les détails pertinents, encapsuler dans un meilleur type d'exception, etc.) car la consignation des détails de l'exception a provoqué l'enregistreur pour passer par les mêmes actions (essayer d'écrire dans un fichier) qui avaient déjà échoué. Quand ils ont encore échoué ... Vous voyez où je vais. Il entre dans une boucle infinie.
Si vous supprimez certains des blocs try {}, vous pouvez vous retrouver avec l'exception apparaissant dans une boîte de message (pas ce que vous voulez pour une application de configuration silencieuse). Donc, dans cette méthode qui écrit dans le fichier journal, j'ai un gestionnaire de capture vide. Cela n'enterre pas l'erreur car vous obtenez toujours le code d'erreur> 0, cela arrête simplement la boucle infinie et permet à l'application de se terminer correctement.
Il y a un commentaire bien sûr, expliquant pourquoi il est vide.
(J'allais poster ça plus tôt, j'avais juste peur de m'enflammer dans l'oubli. Comme je ne suis pas le seul, cependant ...)
Fermeture des ressources système gracieusement pour récupérer d'une erreur
Par exemple. Si vous exécutez un service en arrière-plan qui ne fournit pas de commentaires à l'utilisateur, vous ne voudrez peut-être pas pousser une indication de l'erreur à l'utilisateur, mais vous faire devez fermer les ressources être utilisé d'une manière gracieuse.
try
{
// execution code here
}
catch(Exception e)
{
// do nothing here
}
finally
{
// close db connection
// close file io resource
// close memory stream
// etc...
}
Idéalement, vous utiliseriez la capture en mode débogage pour détecter les erreurs et les imprimer sur la console ou dans un fichier journal pour les tests. Dans un environnement de production, les services d'arrière-plan ne sont généralement pas censés interrompre l'utilisateur lorsqu'une erreur est générée, de sorte que la sortie d'erreur doit être supprimée.