Dans l'une de mes premières révisions de code (il y a quelque temps), on m'a dit que c'était une bonne pratique d'inclure une clause par défaut dans toutes les instructions switch. Je me suis récemment souvenu de ce conseil, mais je ne me souviens pas de la justification. Cela me semble assez étrange maintenant.
Y a-t-il une raison raisonnable de toujours inclure une déclaration par défaut?
Cette langue dépend-elle? Je ne me souviens pas de la langue que j'utilisais à l'époque - peut-être que cela s'applique à certaines langues et pas à d'autres?
default
switch-statement
tttppp
la source
la source
Réponses:
Les boîtiers de commutation devraient presque toujours avoir un
default
boîtier.Raisons d'utiliser un
default
1.Attraper une valeur inattendue
2. Pour gérer les actions «par défaut», où les cas concernent un comportement spécial.
Vous voyez cela beaucoup dans les programmes pilotés par menu et les scripts shell bash. Vous pouvez également voir cela lorsqu'une variable est déclarée en dehors du commutateur mais n'est pas initialisée, et chaque cas l'initialise à quelque chose de différent. Ici, la valeur par défaut doit également l'initialiser afin que le code de ligne qui accède à la variable ne génère pas d'erreur.
3. Pour montrer à quelqu'un lisant votre code que vous avez couvert ce cas.
C'était un exemple trop simplifié, mais le fait est que quelqu'un qui lit le code ne devrait pas se demander pourquoi
variable
ne peut pas être autre chose que 1 ou 2.Le seul cas auquel je peux penser de ne PAS utiliser,
default
c'est lorsque le commutateur vérifie quelque chose où son alternative plutôt évidente peut être joyeusement ignoréela source
enum MyEnum { FOO = 0, BAR = 1 }; MyEnum value = (MyEnum)2;
crée uneMyEnum
instance valide qui n'est pas égale àFOO
ouBAR
. Si l'un de ces problèmes entraîne une erreur dans votre projet, un cas par défaut vous aidera à trouver l'erreur très rapidement, souvent sans débogueur nécessaire!Non.
Et s'il n'y a pas d'action par défaut, le contexte compte. Que se passe-t-il si vous ne souhaitez agir que sur quelques valeurs?
Prenons l'exemple de la lecture des touches d'un jeu
Ajouter:
C'est juste une perte de temps et augmente la complexité du code sans raison.
la source
// do nothing
commentaire indique clairement que c'est «ok» si tous les cas ne sont pas couverts, contrairement à d'autres instructions de commutation où ce serait «pas ok».NE PAS avoir le cas par défaut peut en fait être bénéfique dans certaines situations.
Si vos cas de commutation sont des valeurs d'énumération, en n'ayant pas de cas par défaut, vous pouvez obtenir un avertissement du compilateur s'il vous manque des cas. De cette façon, si de nouvelles valeurs d'énumération sont ajoutées à l'avenir et que vous oubliez d'ajouter des cas pour ces valeurs dans le commutateur, vous pouvez découvrir le problème au moment de la compilation. Vous devez toujours vous assurer que le code prend les mesures appropriées pour les valeurs non gérées, dans le cas où une valeur non valide a été convertie en type enum. Donc, cela peut fonctionner mieux pour les cas simples où vous pouvez revenir dans le cas de l'énumération plutôt que d'interrompre.
la source
J'utiliserais toujours une clause par défaut, quelle que soit la langue dans laquelle vous travaillez.
Les choses peuvent et vont mal. Les valeurs ne seront pas celles que vous attendez, etc.
Ne pas vouloir inclure une clause par défaut implique que vous êtes sûr de connaître l'ensemble des valeurs possibles. Si vous croyez connaître l'ensemble des valeurs possibles, alors, si la valeur est en dehors de cet ensemble de valeurs possibles, vous voudriez en être informé - c'est certainement une erreur.
C'est la raison pour laquelle vous devez toujours utiliser une clause par défaut et renvoyer une erreur, par exemple en Java:
Il n'y a aucune raison d'inclure plus d'informations que la chaîne "inaccessible"; si cela se produit réellement, vous devrez de toute façon regarder la source et les valeurs des variables, et la trace de pile d'exceptions inclura ce numéro de ligne, donc pas besoin de perdre votre temps à écrire plus de texte dans le message d'exception.
la source
throw new RuntimeException("myVar invalid " + myVar);
car cela pourrait vous donner suffisamment d'informations pour comprendre et corriger le bogue sans avoir à utiliser un débogueur et à inspecter des variables, ce qui peut être difficile s'il s'agit d'un problème rare et difficile à reproduire.default:
. Mais le plus souvent, on laisse de côté un défaut car on pensemyVar
qu'il ne peut jamais avoir d'autre valeur que celles listées; cependant je ne peux pas compter le nombre de fois où j'ai été surpris dans la vraie vie quand la variable a eu une valeur autre que les valeurs qu'elle "pourrait éventuellement" avoir. Dans ces cas, j'ai été reconnaissant de l'exception qui m'a fait voir tout de suite, plutôt que de provoquer une autre erreur plus tard (plus difficile à déboguer) ou une mauvaise réponse (pourrait être négligée lors des tests).AssertionError
au lieu deRuntimeException
, car cette situation est très similaire à une assertion indiquant qu'ilmyVar
s'agit d'une des valeurs gérées. De plus, la chance que quelqu'un attrape et avale l'erreur est plus petite.Dans mon entreprise, nous écrivons des logiciels pour le marché de l'avionique et de la défense, et nous incluons toujours une instruction par défaut, car TOUS les cas dans une instruction switch doivent être explicitement traités (même s'il s'agit simplement d'un commentaire disant 'Ne rien faire'). Nous ne pouvons pas nous permettre que le logiciel se comporte mal ou se bloque simplement sur des valeurs inattendues (ou même ce que nous pensons impossibles).
On peut dire qu'un cas par défaut n'est pas toujours nécessaire, mais en l'exigeant toujours, il est facilement vérifié par nos analyseurs de code.
la source
Une instruction "switch" doit-elle toujours inclure une clause par défaut? Non. Il doit généralement inclure une valeur par défaut.
L'inclusion d'une clause par défaut n'a de sens que s'il y a quelque chose à faire, comme affirmer une condition d'erreur ou fournir un comportement par défaut. Y compris un «juste parce que» est une programmation culte du fret et ne fournit aucune valeur. C'est l'équivalent "switch" de dire que toutes les instructions "if" doivent inclure un "else".
Voici un exemple trivial où cela n'a aucun sens:
C'est l'équivalent de:
la source
default:
dans ces cas codifie vos hypothèses. Par exemple, est-ce OK quandsgn==0
imprimerinteger
(ni positif ni négatif), ou est-ce une erreur? Pour moi, lire ce code, c'est difficile à dire. Je suppose que vous ne voudriez pas écrirezero
dans ce casinteger
, et que le programmeur a fait l'hypothèse quisgn
ne peut être que -1 ou +1. Si tel était le cas, avoir ledefault:
permettrait au programmeur de détecter l'erreur d'hypothèse tôt et de changer le code.Pour autant que je le vois, la réponse est «par défaut» est facultative, dire qu'un commutateur doit toujours contenir un défaut, c'est comme dire que chaque «if-elseif» doit contenir un «autre». S'il y a une logique à faire par défaut, alors l'instruction 'default' devrait être là, mais sinon le code pourrait continuer à s'exécuter sans rien faire.
la source
Avoir une clause par défaut quand elle n'est pas vraiment nécessaire est une programmation défensive Cela conduit généralement à un code trop complexe en raison de trop de code de gestion des erreurs. Ce code de gestion et de détection des erreurs nuit à la lisibilité du code, rend la maintenance plus difficile et conduit finalement à plus de bogues qu'il n'en résout.
Je pense donc que si la valeur par défaut ne doit pas être atteinte - vous n'avez pas à l'ajouter.
Notez que "ne devrait pas être atteint" signifie que s'il atteint, c'est un bogue dans le logiciel - vous devez tester les valeurs qui peuvent contenir des valeurs indésirables en raison de l'entrée de l'utilisateur, etc.
la source
Je dirais que cela dépend de la langue, mais en C si vous activez un type d'énumération et que vous gérez toutes les valeurs possibles, vous feriez probablement mieux de ne PAS inclure de cas par défaut. De cette façon, si vous ajoutez une balise enum supplémentaire plus tard et oubliez de l'ajouter au commutateur, un compilateur compétent vous donnera un avertissement concernant le cas manquant.
la source
gcc -Wall
(sorte de plus petit dénominateur commun des compilateurs compétents) donne des avertissements sur les énumérations non gérées dans les instructions switch.Si vous savez que l'instruction switch n'aura jamais qu'un ensemble strictement défini d'étiquettes ou de valeurs, faites-le simplement pour couvrir les bases, de cette façon, vous obtiendrez toujours un résultat valide. être le meilleur gestionnaire pour d'autres valeurs.
la source
default
toujours nécessaire ici? Ou est-ce que cela permet une syntaxe spéciale qui vous a permis de l'omettre?Au moins, ce n'est pas obligatoire en Java. Selon JLS, il indique qu'au moins un cas par défaut peut être présent. Ce qui signifie qu'aucun cas par défaut n'est acceptable. Cela dépend parfois du contexte que vous utilisez l'instruction switch. Par exemple, en Java, le bloc de commutation suivant ne nécessite pas de casse par défaut
Mais dans la méthode suivante qui s'attend à renvoyer une chaîne, le cas par défaut est pratique pour éviter les erreurs de compilation
bien que vous puissiez éviter les erreurs de compilation pour la méthode ci-dessus sans avoir de casse par défaut en ayant simplement une instruction de retour à la fin, mais fournir une casse par défaut la rend plus lisible.
la source
Certaines directives (obsolètes) le disent, comme MISRA C :
Ce conseil est dépassé car il n'est pas basé sur des critères actuellement pertinents. L'omission flagrante est ce que Harlan Kassler a dit:
Le fait de laisser le cas par défaut permet au compilateur d'avertir ou d'échouer éventuellement lorsqu'il voit un cas non géré. La vérifiabilité statique est après tout meilleure que n'importe quelle vérification dynamique, et n'est donc pas un sacrifice digne lorsque vous avez également besoin de la vérification dynamique.
Comme Harlan l'a également démontré, l'équivalent fonctionnel d'un boîtier par défaut peut être recréé après le basculement. Ce qui est trivial lorsque chaque cas est un retour anticipé.
Le besoin typique d'une vérification dynamique est la gestion des entrées, au sens large. Si une valeur vient de l'extérieur du contrôle du programme, elle ne peut pas être approuvée.
C'est également là que Misra adopte le point de vue d'une programmation défensive extrême, où tant qu'une valeur invalide est physiquement représentable, elle doit être vérifiée, peu importe si le programme est prouvablement correct. Ce qui est logique si le logiciel doit être aussi fiable que possible en présence d'erreurs matérielles. Mais comme l'a dit Ophir Yoktan, la plupart des logiciels feraient mieux de ne pas "gérer" les bogues. Cette dernière pratique est parfois appelée programmation offensive .
la source
Vous devriez avoir une valeur par défaut pour intercepter les valeurs non attendues.
Cependant, je ne suis pas d'accord avec Adrian Smith selon lequel votre message d'erreur par défaut devrait être quelque chose de totalement dénué de sens. Il peut y avoir un cas non traité que vous n'avez pas prévu (ce qui est un peu le point) que votre utilisateur finira par voir et un message comme "inaccessible" est entièrement inutile et n'aide personne dans cette situation.
À titre d'exemple, combien de fois avez-vous eu un BSOD totalement dénué de sens? Ou une exception fatale @ 0x352FBB3C32342?
la source
Si la valeur du commutateur ( commutateur (variable )) ne peut pas atteindre le cas par défaut, alors le cas par défaut n'est pas du tout nécessaire. Même si nous conservons le cas par défaut, il n'est pas du tout exécuté. C'est du code mort.
la source
Il s'agit d'une «convention» de codage facultative. Selon l'utilisation, il est nécessaire ou non. Je crois personnellement que si vous n'en avez pas besoin, cela ne devrait pas être là. Pourquoi inclure quelque chose qui ne sera pas utilisé ou atteint par l'utilisateur?
Si les possibilités de cas sont limitées (c'est-à-dire un booléen) alors la clause par défaut est redondante !
la source
S'il n'y a pas de cas par défaut dans une
switch
instruction, le comportement peut être imprévisible si ce cas survient à un moment donné, ce qui n'était pas prévisible au stade du développement. Il est recommandé d'inclure undefault
cas.Une telle pratique peut entraîner un bogue comme une déréférence NULL , une fuite de mémoire ainsi que d'autres types de bogues graves .
Par exemple, nous supposons que chaque condition initialise un pointeur. Mais si un
default
cas est censé se produire et si nous ne l'initialisons pas dans ce cas, il y a toutes les possibilités de se retrouver avec une exception de pointeur nul. Par conséquent, il est suggéré d'utiliser unedefault
déclaration de cas, même si elle peut être triviale.la source
Le cas par défaut peut ne pas être nécessaire dans le commutateur utilisé par enum. lorsque switch contient toute la valeur, le cas par défaut ne s'exécutera jamais. Donc dans ce cas, ce n'est pas nécessaire.
la source
Dépend de la façon dont le commutateur dans une langue particulière fonctionne, mais dans la plupart des langues quand aucun cas n'est trouvé, l'exécution passe par l'instruction switch sans avertissement. Imaginez que vous vous attendiez à un ensemble de valeurs et que vous les ayez traitées dans le commutateur, mais vous obtenez une autre valeur en entrée. Il ne se passe rien et vous ne savez rien. Si vous preniez l'affaire par défaut, vous sauriez qu'il y avait quelque chose de mal.
la source
Je ne suis pas d'accord avec la réponse la plus votée de Vanwaril ci-dessus.
Tout code ajoute de la complexité. Des tests et de la documentation doivent également être effectués pour cela. Il est donc toujours bon de programmer avec moins de code. Mon opinion est que j'utilise une clause par défaut pour les instructions de commutation non exhaustives tandis que je n'utilise aucune clause par défaut pour les instructions de commutation exhaustives. Pour être sûr d'avoir bien fait, j'utilise un outil d'analyse de code statique. Entrons donc dans les détails:
Instructions de commutation non exhaustives: celles-ci doivent toujours avoir une valeur par défaut. Comme son nom l'indique, ce sont des déclarations qui ne couvrent pas toutes les valeurs possibles. Cela peut également ne pas être possible, par exemple une instruction switch sur une valeur entière ou sur une chaîne. Ici, je voudrais utiliser l'exemple de Vanwaril (Il convient de mentionner que je pense qu'il a utilisé cet exemple pour faire une mauvaise suggestion. Je l'utilise ici pour indiquer le contraire -> Utiliser une déclaration par défaut):
Le joueur peut appuyer sur n'importe quelle autre touche. Ensuite, nous ne pouvions rien faire (cela peut être montré dans le code simplement en ajoutant un commentaire au cas par défaut) ou il devrait par exemple imprimer quelque chose à l'écran. Ce cas est pertinent car il peut arriver.
Déclarations de commutateur exhaustives: ces instructions de commutateur couvrent toutes les valeurs possibles, par exemple une instruction de commutateur sur une énumération des types de système de notes. Lors du développement de code la première fois, il est facile de couvrir toutes les valeurs. Cependant, comme nous sommes humains, il y a une petite chance d'en oublier. De plus, si vous ajoutez une valeur enum plus tard, de sorte que toutes les instructions de commutation doivent être adaptées pour les rendre à nouveau exhaustives, vous ouvrez la voie à l'enfer des erreurs. La solution simple est un outil d'analyse de code statique. L'outil doit vérifier toutes les instructions switch et vérifier si elles sont exhaustives ou si elles ont une valeur par défaut. Voici un exemple pour une déclaration de commutateur exhaustive. Nous avons d'abord besoin d'une énumération:
Ensuite, nous avons besoin d'une variable de cette énumération comme
GradeSystemType type = ...
. Une déclaration de commutateur exhaustive ressemblerait alors à ceci:Donc, si nous étendons le
GradeSystemType
par exempleSystem1To3
l'outil d'analyse de code statique devrait détecter qu'il n'y a pas de clause par défaut et que l'instruction switch n'est pas exhaustive, nous sommes donc sauvegardés.Juste une chose supplémentaire. Si nous utilisons toujours une
default
clause, il peut arriver que l'outil d'analyse de code statique ne soit pas capable de détecter des instructions de commutation exhaustives ou non exhaustives car il détecte toujours ladefault
clause. C'est super mauvais car nous ne serons pas informés si nous étendons l'énumération par une autre valeur et oublions de l'ajouter à une instruction switch.la source
Les instructions switch doivent-elles toujours contenir une clause par défaut? Aucun cas de commutation ne peut exister sans cas par défaut, dans le cas par défaut, le cas déclenchera la valeur de commutation
switch(x)
dans ce cas x lorsqu'il ne correspondra à aucune autre valeur de cas.la source
Je crois que c'est tout à fait spécifique au langage et pour le cas C ++ un point mineur pour le type de classe enum . Ce qui semble plus sûr que l'énumération C traditionnelle. MAIS
Si vous regardez l'implémentation de std :: byte, c'est quelque chose comme:
Source: https://en.cppreference.com/w/cpp/language/enum
Et considérez également ceci:
Source: https://en.cppreference.com/w/cpp/language/list_initialization
Ceci est un exemple de classe enum représentant des valeurs qui ne sont pas des énumérateurs définis. Pour cette raison, vous ne pouvez pas faire entièrement confiance aux énumérations. Selon l'application, cela peut être important.
Cependant, j'aime vraiment ce que @Harlan Kassler a dit dans son article et je commencerai moi-même à utiliser cette stratégie dans certaines situations.
Juste un exemple de classe d'énumération non sécurisée:
la source