Pourquoi les blocs de capture vides sont-ils une mauvaise idée? [fermé]

194

Je viens de voir une question sur try-catch , quelles personnes (y compris Jon Skeet) disent que les blocs catch vides sont une très mauvaise idée? Pourquoi ça? N'y a-t-il pas de situation où une prise vide n'est pas une mauvaise décision de conception?

Je veux dire, par exemple, parfois, vous voulez obtenir des informations supplémentaires de quelque part (service Web, base de données) et vous ne vous souciez vraiment pas de savoir si vous obtiendrez ces informations ou non. Donc vous essayez de l'obtenir, et si quelque chose arrive, c'est ok, je vais juste ajouter un "catch (Exception ignored) {}" et c'est tout

Samuel Carrijo
la source
53
J'écrirais un commentaire expliquant pourquoi il devrait être vide.
Mehrdad Afshari
5
... ou du moins enregistrer qu'il a été attrapé.
matt b
2
@ocdecio: évitez-le pour le code de nettoyage , ne l'évitez pas en général.
John Saunders
1
@ocdecio: Un autre cas à éviter d'utiliser try..catch est celui où vous interceptez des exceptions pour des types d'échecs connus. Par exemple, exceptions de conversion numérique
MPritchard
1
@ocdecio - try..finally vaut mieux que try..empty catch car l'erreur continue dans la pile - soit pour être gérée par le framework, soit pour tuer le programme et être affiché à l'utilisateur - les deux résultats sont meilleurs qu'un " échec silencieux ".
Nate

Réponses:

298

Habituellement, un try-catch vide est une mauvaise idée car vous avalez silencieusement une condition d'erreur et continuez ensuite l'exécution. Parfois, cela peut être la bonne chose à faire, mais c'est souvent le signe qu'un développeur a vu une exception, ne savait pas quoi faire à ce sujet et a donc utilisé une capture vide pour faire taire le problème.

C'est l'équivalent de programmation de mettre du ruban noir sur un voyant d'avertissement du moteur.

Je crois que la façon dont vous gérez les exceptions dépend de la couche du logiciel dans laquelle vous travaillez: Exceptions in the Rainforest .

Ned Batchelder
la source
4
Dans ces circonstances vraiment rares où je vraiment ne pas besoin de l'exception et l' exploitation forestière est indisponible pour une raison quelconque, je fais en sorte de commentaire que c'est intentionnel - et pourquoi il n'est pas nécessaire, et répéter que je serais encore préfère se connecter si c'était faisable dans cet environnement. Fondamentalement, je fais le commentaire PLUS de travail que la journalisation aurait été si cela avait été une option dans cette circonstance. Heureusement, je n'ai que 2 clients pour qui c'est un problème, et ce n'est que lors de l'envoi d'e-mails non critiques à partir de leurs sites Web.
John Rudy
37
J'adore aussi l'analogie - et comme toutes les règles, il y a des exceptions. Par exemple, il est parfaitement acceptable d'utiliser une bande noire sur l'horloge clignotante de votre magnétoscope si vous n'avez jamais l'intention de faire des enregistrements chronométrés (pour tous les vieux qui vous souvenez de ce qu'est un magnétoscope).
Michael Burr
3
Comme pour tout écart par rapport à la pratique acceptée, faites-le si nécessaire et documentez-le pour que le prochain homme sache ce que vous avez fait et pourquoi.
David Thornley
5
@Jason: à tout le moins, vous devriez inclure un commentaire détaillé expliquant pourquoi vous faites taire les exceptions.
Ned Batchelder
1
Cette réponse suppose deux choses: 1. Que l'exception lancée est en effet une exception significative et que celui qui a écrit le code qui lance savait ce qu'il faisait; 2. Que l'exception est en effet une "condition d'erreur" et n'est pas abusée comme flux de contrôle (bien que ce soit un cas plus spécifique de mon premier point).
Boris B.
38

C'est une mauvaise idée en général car c'est une condition vraiment rare où une panne (condition exceptionnelle, plus génériquement) est correctement rencontrée sans aucune réponse. En plus de cela, les catchblocs vides sont un outil couramment utilisé par les personnes qui utilisent le moteur d'exception pour vérifier les erreurs qu'elles devraient faire de manière préventive.

Dire que c'est toujours mauvais est faux ... c'est très peu vrai. Il peut y avoir des circonstances où vous ne vous souciez pas qu'il y ait eu une erreur ou que la présence de l'erreur indique d'une manière ou d'une autre que vous ne pouvez rien y faire de toute façon (par exemple, lors de l'écriture d'une erreur précédente dans un fichier journal texte et vous obtenez un IOException, ce qui signifie que vous ne pouvez pas écrire la nouvelle erreur de toute façon).

Adam Robinson
la source
11

Je n'irais pas jusqu'à dire que celui qui utilise des blocs catch vides est un mauvais programmeur et ne sait pas ce qu'il fait ...

J'utilise des blocs catch vides si nécessaire. Parfois, le programmeur de la bibliothèque que je consomme ne sait pas ce qu'il fait et lance des exceptions même dans des situations où personne n'en a besoin.

Par exemple, considérez une bibliothèque de serveur http, je m'en fiche si le serveur lève une exception car le client s'est déconnecté et index.htmln'a pas pu être envoyé.

lubos hasko
la source
6
Vous êtes certainement en train de trop généraliser. Ce n'est pas parce que vous n'en avez pas besoin que personne n'en a besoin. Même si absolument rien ne peut être fait en réponse, il y a quelqu'un quelque part qui a l'obligation de collecter des statistiques sur les connexions abandonnées. Il est donc assez impoli de supposer que "le programmeur de la bibliothèque ne sait pas ce qu'il fait".
Ben Voigt
8

Il existe de rares cas où cela peut être justifié. En Python, vous voyez souvent ce type de construction:

try:
    result = foo()
except ValueError:
    result = None

Il est donc possible (selon votre application) de faire:

result = bar()
if result == None:
    try:
        result = foo()
    except ValueError:
        pass # Python pass is equivalent to { } in curly-brace languages
 # Now result == None if bar() returned None *and* foo() failed

Dans un récent projet .NET, j'ai dû écrire du code pour énumérer les DLL de plug-in afin de trouver des classes qui implémentent une certaine interface. Le bit de code pertinent (dans VB.NET, désolé) est:

    For Each dllFile As String In dllFiles
        Try
            ' Try to load the DLL as a .NET Assembly
            Dim dll As Assembly = Assembly.LoadFile(dllFile)
            ' Loop through the classes in the DLL
            For Each cls As Type In dll.GetExportedTypes()
                ' Does this class implement the interface?
                If interfaceType.IsAssignableFrom(cls) Then

                    ' ... more code here ...

                End If
            Next
        Catch ex As Exception
            ' Unable to load the Assembly or enumerate types -- just ignore
        End Try
    Next

Bien que même dans ce cas, j'admets que la journalisation de l'échec quelque part serait probablement une amélioration.

Daniel Pryden
la source
J'ai fait référence à log4net comme l'une de ces instances avant de voir votre réponse.
Scott Lawrence
7

Des blocs de capture vides sont généralement placés parce que le codeur ne sait pas vraiment ce qu'il fait. Dans mon organisation, un bloc catch vide doit inclure un commentaire expliquant pourquoi ne rien faire à l'exception est une bonne idée.

Dans le même ordre d'idées, la plupart des gens ne savent pas qu'un bloc try {} peut être suivi par un catch {} ou un finally {}, un seul est requis.

Justin
la source
1
+1 Je soulignerais le 'ou' dans cette dernière phrase pour plus d'impact
MPritchard
Ce deuxième paragraphe peut cependant dépendre de la langue, n'est-ce pas?
David Z
3
à moins bien sûr que vous ne parlez d'IE6 ou d'IE7 ;-) ... où le catch EST requis ou finalement ne s'exécute pas. (cela a été corrigé dans IE8 btw)
scunliffe
Très vrai. Cela vaut peut-être la peine de souligner les langues. Je crois que .net va bien
MPritchard
7

Les exceptions ne devraient être levées que s'il y a vraiment une exception - quelque chose qui se passe au-delà de la norme. Un bloc de capture vide dit essentiellement "quelque chose de mauvais se passe, mais je m'en fiche". C'est une mauvaise idée.

Si vous ne souhaitez pas gérer l'exception, laissez-la se propager vers le haut jusqu'à ce qu'elle atteigne un code capable de la gérer. Si rien ne peut gérer l'exception, l'application doit être supprimée.

Reed Copsey
la source
3
Parfois, vous savez que cela n'affectera rien. Alors allez-y et faites-le, et commentez-le pour que le prochain ne pense pas que vous avez foiré parce que vous êtes incompétent.
David Thornley
Ouais, il y a un temps et un lieu pour tout. En général, cependant, je pense qu'un bloc catch vide étant bon est l'exception à la règle - c'est un cas rare où vous voulez vraiment ignorer une exception (généralement, l'OMI, c'est le résultat d'une mauvaise utilisation des exceptions).
Reed Copsey
2
@David Thornley: Comment savez-vous que cela n'affectera rien si vous n'inspectez pas au moins l'exception pour déterminer s'il s'agit d'une erreur attendue et dénuée de sens ou qui devrait plutôt être propagée vers le haut?
Dave Sherohman
2
@Dave: Bien que ce catch (Exception) {}soit une mauvaise idée, ça catch (SpecificExceptionType) {}pourrait être parfaitement bien. Le programmeur a DID inspecter l'exception, en utilisant les informations de type dans la clause catch.
Ben Voigt
7

Je pense que ce n'est pas grave si vous attrapez un type d'exception particulier dont vous savez qu'il ne sera déclenché que pour une raison particulière , et vous vous attendez à cette exception et vous n'avez vraiment rien à faire à ce sujet.

Mais même dans ce cas, un message de débogage pourrait être en ordre.

balpha
la source
6

Par Josh Bloch - Point 65: N'ignorez pas les exceptions de Java efficace :

  1. Un bloc catch vide va à l'encontre du but des exceptions
  2. À tout le moins, le bloc catch devrait contenir un commentaire expliquant pourquoi il est approprié d'ignorer l'exception.
KrishPrabakar
la source
3

Un bloc catch vide dit essentiellement "Je ne veux pas savoir quelles erreurs sont lancées, je vais juste les ignorer."

C'est similaire à VB6 On Error Resume Next, sauf que tout ce qui se trouve dans le bloc try après le lancement de l'exception sera ignoré.

Ce qui n'aide pas quand quelque chose se brise.

Powerlord
la source
En fait, je dirais que "On Error Resume Next" est pire - il va juste essayer la ligne suivante quand même. Une prise vide sautera pour passer l'accolade de fermeture de l'instruction try
MPritchard
Bon point. Je devrais probablement le noter dans ma réponse.
Powerlord
1
Ce n'est pas tout à fait vrai, si vous avez un essai vide ... catch avec un type d'exception spécifique, alors il ignorera juste ce type d'exception, et cela pourrait être légitime ...
Meta-Knight
Mais si vous savez qu'un type particulier d'exception est levé, il semble que vous ayez suffisamment d'informations pour écrire votre logique d'une manière qui évite l'utilisation de try-catch pour gérer la situation.
Scott Lawrence
@Scott: Certains langages (ie Java) ont des exceptions vérifiées ... des exceptions pour lesquelles vous êtes obligé d'écrire des blocs catch.
Powerlord
3

Cela va de pair avec «N'utilisez pas d'exceptions pour contrôler le déroulement du programme» et «N'utilisez les exceptions que dans des circonstances exceptionnelles». Si cela est fait, les exceptions ne devraient se produire qu'en cas de problème. Et s'il y a un problème, vous ne voulez pas échouer en silence. Dans les rares anomalies où il n'est pas nécessaire de gérer le problème, vous devez au moins enregistrer l'exception, juste au cas où l'anomalie ne deviendrait plus une anomalie. La seule chose pire que l'échec est l'échec en silence.

Imagiste
la source
2

Je pense qu'un bloc catch complètement vide est une mauvaise idée car il n'y a aucun moyen de déduire qu'ignorer l'exception était le comportement prévu du code. Il n'est pas forcément mal d'avaler une exception et de renvoyer false ou null ou une autre valeur dans certains cas. Le framework .net a de nombreuses méthodes "try" qui se comportent de cette façon. En règle générale, si vous avalez une exception, ajoutez un commentaire et une instruction de journal si l'application prend en charge la journalisation.

code complexe
la source
1

Parce que si une exception est lancée, vous ne la verrez jamais - échouer silencieusement est la pire option possible - vous obtiendrez un comportement erroné et aucune idée de regarder où cela se passe. Mettez au moins un message de journal là-bas! Même si c'est quelque chose qui «ne peut jamais arriver»!

Nate
la source
1

Les blocs catch vides indiquent qu'un programmeur ne sait pas quoi faire avec une exception. Ils suppriment l'exception qui risque de bouillonner et d'être gérée correctement par un autre bloc try. Essayez toujours de faire quelque chose, sauf que vous attrapez.

Dan
la source
1

Je trouve le plus ennuyeux avec les déclarations catch vides, c'est quand un autre programmeur l'a fait. Ce que je veux dire, c'est que lorsque vous avez besoin de déboguer le code de quelqu'un d'autre, toute instruction catch vide rend une telle entreprise plus difficile qu'elle ne doit l'être. Les déclarations de capture IMHO doivent toujours afficher une sorte de message d'erreur - même si l'erreur n'est pas gérée, elle doit au moins la détecter (alt. Activée uniquement en mode débogage)

AndersK
la source
1

Ce n'est probablement jamais la bonne chose car vous passez silencieusement toutes les exceptions possibles. S'il y a une exception spécifique que vous attendez, vous devez la tester, la renvoyer si ce n'est pas votre exception.

try
{
    // Do some processing.
}
catch (FileNotFound fnf)
{
    HandleFileNotFound(fnf);
}
catch (Exception e)
{
    if (!IsGenericButExpected(e))
        throw;
}

public bool IsGenericButExpected(Exception exception)
{
    var expected = false;
    if (exception.Message == "some expected message")
    {
        // Handle gracefully ... ie. log or something.
        expected = true;
    }

    return expected;
}
xanadont
la source
1
Ce n'est pas vraiment un modèle standard que vous conseillez aux gens d'utiliser là-bas. La plupart des gens attraperaient simplement les exceptions de tous les types auxquels ils s'attendent, et non ceux qu'ils ne sont pas. Je peux voir certaines circonstances dans lesquelles votre approche serait valable, faites juste attention de publier dans un forum comme celui-ci où les gens ont tendance à lire des choses comme celle-ci parce qu'ils ne comprennent pas en premier lieu;)
MPritchard
Mon intention n'était pas qu'IsKnownException () vérifie le type de l'exception (oui, vous devriez le faire par différents blocs catch), mais plutôt qu'il vérifie s'il s'agit d'une exception attendue. Je vais modifier pour que ce soit plus clair.
xanadont
Je pensais à l'origine à COMExceptions. Vous pouvez tester un ErrorCode spécifique et gérer les codes que vous attendez. Je fais cela souvent lors de la programmation avec ArcOjbects.
xanadont
2
-1 Prendre des décisions dans le code en fonction du message d'exception est une très mauvaise idée. Le message exact est rarement documenté et peut changer à tout moment; c'est un détail de mise en œuvre. Ou le message pourrait être basé sur une chaîne de ressource localisable. Dans tous les cas, il est uniquement destiné à être lu par un humain.
Wim Coenen
Eek. L'exception: le message pourrait être localisé et donc pas ce que vous attendez. C'est tout simplement faux.
frankhommers
1

En règle générale, vous ne devez détecter que les exceptions que vous pouvez réellement gérer. Cela signifie être aussi précis que possible lors de la détection des exceptions. Attraper toutes les exceptions est rarement une bonne idée et ignorer toutes les exceptions est presque toujours une très mauvaise idée.

Je ne peux penser qu'à quelques exemples où un bloc catch vide a un but significatif. Si une exception spécifique que vous attrapez est "gérée" en réessayant simplement l'action, il n'y aurait pas besoin de faire quoi que ce soit dans le bloc catch. Cependant, il serait toujours judicieux de consigner le fait que l'exception s'est produite.

Autre exemple: CLR 2.0 a changé la façon dont les exceptions non gérées sur le thread du finaliseur sont traitées. Avant la version 2.0, le processus était autorisé à survivre à ce scénario. Dans le CLR actuel, le processus est arrêté en cas d'exception non gérée sur le thread du finaliseur.

Gardez à l'esprit que vous ne devez implémenter un finaliseur que si vous en avez vraiment besoin et même dans ce cas, vous devez en faire le moins possible dans le finaliseur. Mais si le travail que votre finaliseur doit faire peut générer une exception, vous devez choisir entre le moindre de deux maux. Voulez-vous arrêter l'application en raison de l'exception non gérée? Ou souhaitez-vous procéder dans un état plus ou moins indéfini? Au moins en théorie, ce dernier peut être le moindre de deux maux dans certains cas. Dans ce cas, le bloc catch vide empêcherait le processus de se terminer.

Brian Rasmussen
la source
1
Je veux dire, par exemple, parfois, vous voulez obtenir des informations supplémentaires de quelque part (service Web, base de données) et vous ne vous souciez vraiment pas de savoir si vous obtiendrez ces informations ou non. Donc vous essayez de l'obtenir, et si quelque chose arrive, c'est ok, je vais juste ajouter un "catch (Exception ignored) {}" et c'est tout

Donc, pour reprendre votre exemple, c'est une mauvaise idée dans ce cas parce que vous attrapez et ignorez toutes les exceptions. Si vous attrapiez seulement EInfoFromIrrelevantSourceNotAvailableet l'ignoriez, ce serait bien, mais vous ne l'êtes pas. Vous ignorez également ENetworkIsDown, ce qui peut être important ou non. Vous ignorez ENetworkCardHasMeltedet EFPUHasDecidedThatOnePlusOneIsSeventeen, qui sont presque certainement importants.

Un bloc catch vide n'est pas un problème s'il est configuré pour attraper (et ignorer) uniquement les exceptions de certains types que vous savez être sans importance. Les situations dans lesquelles c'est une bonne idée de supprimer et d'ignorer silencieusement toutes les exceptions, sans s'arrêter pour les examiner d'abord pour voir si elles sont attendues / normales / non pertinentes ou non, sont extrêmement rares.

Dave Sherohman
la source
1

Il y a des situations où vous pourriez les utiliser, mais elles devraient être très rares. Les situations dans lesquelles je pourrais en utiliser un incluent:

  • journalisation des exceptions; selon le contexte, vous souhaiterez peut-être qu'une exception non gérée ou un message soit publié à la place.

  • des situations techniques en boucle, comme le rendu ou le traitement du son ou un rappel de listbox, où le comportement lui-même montrera le problème, le fait de lancer une exception ne fera que gêner et la journalisation de l'exception entraînera probablement des milliers de messages "échoué à XXX" .

  • programmes qui ne peuvent pas échouer, même s'ils devraient au moins enregistrer quelque chose.

pour la plupart des applications winforms, j'ai trouvé qu'il suffit d'avoir une seule instruction try pour chaque entrée utilisateur. J'utilise les méthodes suivantes: (AlertBox est juste un wrapper MessageBox.Show rapide)

  public static bool TryAction(Action pAction)
  {
     try { pAction(); return true; }
     catch (Exception exception)
     {
        LogException(exception);
        return false;
     }
  }

  public static bool TryActionQuietly(Action pAction)
  {
     try { pAction(); return true; }
     catch(Exception exception)
     {
        LogExceptionQuietly(exception);
        return false;
     }
  }

  public static void LogException(Exception pException)
  {
     try
     {
        AlertBox(pException, true);
        LogExceptionQuietly(pException);
     }
     catch { }
  }

  public static void LogExceptionQuietly(Exception pException)
  {
     try { Debug.WriteLine("Exception: {0}", pException.Message); } catch { }
  }

Ensuite, chaque gestionnaire d'événements peut faire quelque chose comme:

  private void mCloseToolStripMenuItem_Click(object pSender, EventArgs pEventArgs)
  {
     EditorDefines.TryAction(Dispose);
  }

ou

  private void MainForm_Paint(object pSender, PaintEventArgs pEventArgs)
  {
     EditorDefines.TryActionQuietly(() => Render(pEventArgs));
  }

Théoriquement, vous pourriez avoir TryActionSilently, ce qui pourrait être mieux pour le rendu des appels afin qu'une exception ne génère pas une quantité infinie de messages.

Dave Cousineau
la source
1

Si vous ne savez pas quoi faire dans catch block, vous pouvez simplement enregistrer cette exception, mais ne la laissez pas vide.

        try
        {
            string a = "125";
            int b = int.Parse(a);
        }
        catch (Exception ex)
        {
            Log.LogError(ex);
        }
Andzej Maciusovic
la source
Pourquoi ce vote a-t-il été rejeté?
Vino du
0

Vous ne devriez jamais avoir un bloc catch vide. C'est comme cacher une erreur que vous connaissez. À tout le moins, vous devriez écrire une exception dans un fichier journal pour la revoir plus tard si vous êtes pressé par le temps.

Chadit
la source