Les instructions switch doivent-elles toujours contenir une clause par défaut?

255

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.

  1. Y a-t-il une raison raisonnable de toujours inclure une déclaration par défaut?

  2. 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?

tttppp
la source
9
Cela va dépendre de la langue dans une large mesure
skaffman

Réponses:

277

Les boîtiers de commutation devraient presque toujours avoir un defaultboîtier.

Raisons d'utiliser un default

1.Attraper une valeur inattendue

switch(type)
{
    case 1:
        //something
    case 2:
        //something else
    default:
        // unknown type! based on the language,
        // there should probably be some error-handling
        // here, maybe an exception
}

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.

variable = (variable == "value") ? 1 : 2;
switch(variable)
{
    case 1:
        // something
    case 2:
        // something else
    default:
        // will NOT execute because of the line preceding the switch.
}

C'était un exemple trop simplifié, mais le fait est que quelqu'un qui lit le code ne devrait pas se demander pourquoi variablene peut pas être autre chose que 1 ou 2.


Le seul cas auquel je peux penser de ne PAS utiliser, defaultc'est lorsque le commutateur vérifie quelque chose où son alternative plutôt évidente peut être joyeusement ignorée

switch(keystroke)
{
    case 'w':
        // move up
    case 'a':
        // move left
    case 's':
        // move down
    case 'd':
        // move right
    // no default really required here
}
Vanwaril
la source
26
Votre exemple concernant les frappes en quelque sorte contredit le reste de ce que vous venez de dire. : | Je pense qu'un peu plus d'explications auraient un sens, par exemple: vous ne vous souciez pas d'un défaut dans ce cas parce que si une autre touche est enfoncée, vous ne vous souciez pas, mais si une variable est passée est différente de celle attendue, nous nous en soucions .
Andrew
1
En même temps, si vous vérifiez un paramètre GET, vous ne voudrez peut-être pas qu'une exception soit levée à chaque fois qu'un utilisateur modifie l'URL get en quelque chose qui n'est pas valide: [
Andrew
4
@Andrew WASD est courant pour les mouvements de personnages de jeux informatiques.Vous n'aurez pas besoin d'action par défaut car d'autres touches pourraient être affectées à d'autres interactions. Dans ce type de commutateur, il est bon d'appeler des fonctions de mouvement.
jemiloii
13
Certains compilateurs avertissent lors de l'activation d'une énumération si un cas est manqué, et l'ajout d'un cas par défaut supprimera cet avertissement. Étant donné que c'est, selon mon expérience, une source d'erreur très courante (oublier de mettre à jour un commutateur quelque part après l'ajout d'une valeur d'énumération), cela semble être une très bonne raison d'omettre un cas par défaut.
rdb
3
@virusrocks Si votre condition est une énumération, vous pouvez toujours avoir des valeurs inattendues. Par exemple, si vous ajoutez une nouvelle valeur à l'énumération et oubliez de mettre à jour un commutateur. De plus, dans certaines langues, vous pouvez attribuer des valeurs entières aux énumérations. En C ++ enum MyEnum { FOO = 0, BAR = 1 }; MyEnum value = (MyEnum)2;crée une MyEnuminstance valide qui n'est pas égale à FOOou BAR. 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!
cdgraham
54

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

switch(a)
{
   case 'w':
     // Move Up
     break;
   case 's':
     // Move Down
     break;
   case 'a':
     // Move Left
     break;
   case 'd':
     // Move Right
     break;
}

Ajouter:

default: // Do nothing

C'est juste une perte de temps et augmente la complexité du code sans raison.

Jared Kells
la source
14
Bon exemple. Mais en fait, l'ajout d'une clause par défaut avec un simple // do nothingcommentaire 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».
The Nail
8
Non. Le codage ne concerne pas seulement la gestion de certains cas. Il sert également de document. En écrivant default: et en commentant comme // Ne rien faire ou autre chose, la lisibilité du code devient meilleure.
JongAm Park
22
En désaccord avec les autres commentaires. Ajouter par défaut // Ne rien faire ajoute très peu sauf si vous ne savez pas comment fonctionne le code. Je peux regarder une instruction switch sans défaut et savoir que la valeur par défaut est de ne rien faire. L'ajout d'encombrement ne rend pas cela plus évident.
Robert Noack
Exactement. C'est dans le même concept de remettre al param param. Vous vous occupez de ceux dont vous vous souciez pas des autres.
Jimmy Kane
2
@jybrd Si vous ne croyez pas que le développeur précédent a intentionnellement omis la valeur par défaut, pourquoi croiriez-vous qu'il était correct de la mettre? Une valeur par défaut peut être accidentellement vide aussi facilement que complètement absente. Un commentaire n'est pas nécessaire pour quelque chose d'aussi banal. C'est l'encombrement du code. Écrivez des tests unitaires de couverture complète pour montrer vos intentions à la place. (Juste mon avis)
Robert Noack
45

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.

enum SomeEnum
{
    ENUM_1,
    ENUM_2,
    // More ENUM values may be added in future
};

int foo(SomeEnum value)
{
    switch (value)
    {
    case ENUM_1:
        return 1;
    case ENUM_2:
        return 2;
    }
    // handle invalid values here
    return 0;
 }
Harlan Kassler
la source
Ceci est une observation importante! Il s'applique encore plus en Java car il ne vous permet pas de transcrire des ents en énumérations.
Lii
2
Dans votre exemple, vous pouvez lever une exception au lieu de retourner après l'instruction switch. De cette façon, vous pourriez avoir à la fois une vérification statique par le compilateur et obtenir une erreur claire si quelque chose d'inattendu se produit.
Lii
1
Je suis d'accord. Dans Swift par exemple, le switch doit être exhaustif sinon vous obtenez une erreur de compilation! Fournir une valeur par défaut réduit au silence cette erreur utile. D'un autre côté, fournir une valeur par défaut si tous les cas sont traités déclenche en fait un avertissement du compilateur (instruction inaccessible).
Andy
1
Je viens de corriger un bogue, qui aurait provoqué un avertissement du compilateur s'il n'y avait pas de valeur par défaut: donc, généralement, les variables Switch over enum ne devraient pas avoir de valeur par défaut.
notan
42

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:

switch (myVar) {
   case 1: ......; break;
   case 2: ......; break;
   default: throw new RuntimeException("unreachable");
}

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.

Adrian Smith
la source
39
Je préférerais 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.
Chris Dodd
1
Que faire si ce n'est pas une condition d'erreur pour que la valeur ne corresponde pas à l'un des cas?
Gabe
4
Si ce n'est pas une condition d'erreur, alors il n'y aurait pas besoin d'un default:. Mais le plus souvent, on laisse de côté un défaut car on pense myVarqu'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).
Adrian Smith
2
Je suis d'accord avec Adrian. Échouez dur et échouez tôt.
Slappy
Je préfère lancer un AssertionErrorau lieu de RuntimeException, car cette situation est très similaire à une assertion indiquant qu'il myVars'agit d'une des valeurs gérées. De plus, la chance que quelqu'un attrape et avale l'erreur est plus petite.
Philipp Wendler
15

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.

Kurt Pattyn
la source
1
J'ai la même exigence là où je travaille; nous écrivons du code pour les µControllers embarqués qui doivent passer des contrôles de sécurité stricts et sont souvent soumis à EMI. Il serait irresponsable de supposer qu'une variable énumérée n'aura jamais une valeur qui ne figure pas dans la liste d'énumération.
oosterwal
12

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:

void PrintSign(int i)
{
    switch (Math.Sign(i))
    {
    case 1:
        Console.Write("positive ");
        break;
    case -1:
        Console.Write("negative ");
        break;
    default: // useless
    }
    Console.Write("integer");
}

C'est l'équivalent de:

void PrintSign(int i)
{
    int sgn = Math.Sign(i);
    if (sgn == 1)
        Console.Write("positive ");
    else if (sgn == -1)
        Console.Write("negative ");
    else // also useless
    {
    }
    Console.Write("integer");
}
Gabe
la source
Je ne suis pas d'accord. À mon humble avis, la seule fois où un défaut ne devrait pas exister est s'il n'y a aucun moyen, pour l'instant et pour toujours, que l'ensemble des entrées puisse changer, et que vous avez couvert toutes les valeurs possibles. Le cas le plus simple auquel je puisse penser est une valeur booléenne d'une base de données, où les seules réponses (jusqu'à ce que SQL change!) Sont true, false et NULL. Dans tous les autres cas, avoir une valeur par défaut avec une assertion ou une exception "ne devrait pas arriver!" est logique. Si votre code change et que vous avez une ou plusieurs nouvelles valeurs, vous pouvez vous assurer que vous les codez et vos tests exploseront si vous ne le faites pas.
Harper Shelby
4
@Harper: votre exemple relève de la catégorie "affirmer une condition d'erreur".
Gabe
Je dirais que mon exemple est la norme et que le petit nombre de cas où l'on peut être sûr à 100% que tous les cas possibles sont couverts et qu'un défaut n'est pas nécessaire sont l'exception. Votre réponse est formulée d'une manière qui donne l'impression (au moins pour moi) que si le défaut de ne rien faire se produira plus souvent qu'autrement.
Harper Shelby
@Harper: OK, j'ai changé le libellé pour indiquer que la situation de ne rien faire est moins courante.
Gabe
4
Je pense que default:dans ces cas codifie vos hypothèses. Par exemple, est-ce OK quand sgn==0imprimer integer(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 écrire zerodans ce cas integer, et que le programmeur a fait l'hypothèse qui sgnne peut être que -1 ou +1. Si tel était le cas, avoir le default:permettrait au programmeur de détecter l'erreur d'hypothèse tôt et de changer le code.
Adrian Smith
7

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.

Gershon Herczeg
la source
6

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.

Ophir Yoktan
la source
3
Une autre raison courante pour obtenir des cas inattendus: D'autres parties du code sont modifiées, peut-être des années plus tard.
Hendrik Brummermann
En effet, c'est un comportement très dangereux. Au moins, j'ajouterais une clause assert (). Ensuite, il est facilement détecté chaque fois qu'il y a un bug.
Kurt Pattyn
5

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.

Chris Dodd
la source
5
Il est peu probable que "chaque valeur possible" soit gérée avec des énumérations. Vous pouvez convertir un entier en une énumération même si cette valeur particulière n'est pas explicitement définie. Et quel compilateur met en garde contre le cas manquant?
TrueWill
1
@ TrueWill: Oui, vous pouvez utiliser des transtypages explicites pour écrire du code obscurci qui est impossible à comprendre; pour écrire du code compréhensible, vous devez éviter cela. 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.
Chris Dodd
4

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.

switch(ResponseValue)
{
    default:
    case No:
        return false;
    case Yes;
        return true;
}
deegee
la source
Le côlon est-il defaulttoujours nécessaire ici? Ou est-ce que cela permet une syntaxe spéciale qui vous a permis de l'omettre?
Ponkadoodle
Le deux-points est requis, c'était une faute de frappe, merci de l'avoir porté à mon attention.
Deegee
3

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

private static void switch1(String name) {
    switch (name) {
    case "Monday":
        System.out.println("Monday");
        break;
    case "Tuesday":
        System.out.println("Tuesday");
        break;
    }
}

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

    private static String switch2(String name) {
    switch (name) {
    case "Monday":
        System.out.println("Monday");
        return name;

    case "Tuesday":
        System.out.println("Tuesday");
        return name;

    default:
        return name;
    }
}

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.

Shiva Kumar
la source
3

Certaines directives (obsolètes) le disent, comme MISRA C :

L'exigence d'une clause finale par défaut est une programmation défensive. Cette clause doit soit prendre les mesures appropriées, soit contenir un commentaire approprié expliquant pourquoi aucune mesure n'est prise.

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 .

user2394284
la source
2

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?

John Hunt
la source
par exemple - rappelez-vous que ce n'est pas seulement le développeur qui voit toujours les messages d'erreur. Nous vivons dans le monde réel, les gens font des erreurs.
John Hunt
2

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.

shiju
la source
2

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 !

Danny Mahoney
la source
2

S'il n'y a pas de cas par défaut dans une switchinstruction, 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 un defaultcas.

switch ( x ){
  case 0 : { - - - -}
  case 1 : { - - - -}
}

/* What happens if case 2 arises and there is a pointer
* initialization to be made in the cases . In such a case ,
* we can end up with a NULL dereference */

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 defaultcas 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 une defaultdéclaration de cas, même si elle peut être triviale.

Trevor
la source
2

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.

iOkay
la source
1

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.

Gabriel Ščerbák
la source
1

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:

  1. 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):

    switch(keystroke)
    {
      case 'w':
        // move up
      case 'a':
        // move left
      case 's':
        // move down
      case 'd':
        // move right
      default:          
        // cover all other values of the non-exhaustive switch statement
    }
    

    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.

  2. 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:

    public enum GradeSystemType {System1To6, SystemAToD, System0To100}
    

    Ensuite, nous avons besoin d'une variable de cette énumération comme GradeSystemType type = ... . Une déclaration de commutateur exhaustive ressemblerait alors à ceci:

    switch(type)
    {
      case GradeSystemType.System1To6:
        // do something
      case GradeSystemType.SystemAToD:
        // do something
      case GradeSystemType.System0To100:
        // do something
    }
    

    Donc, si nous étendons le GradeSystemTypepar 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 defaultclause, 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 la defaultclause. 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.

Cendre
la source
0

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.

Nisal Edu
la source
0

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:

enum class byte : unsigned char {} ;

Source: https://en.cppreference.com/w/cpp/language/enum

Et considérez également ceci:

Sinon, si T est un type d'énumération qui est délimité ou non avec un type sous-jacent fixe, et si la liste initiée-contreventée n'a qu'un seul initialiseur, et si la conversion de l'initialiseur au type sous-jacent n'est pas restrictive, et si l'initialisation est l'initialisation directe par liste, puis l'énumération est initialisée avec le résultat de la conversion de l'initialiseur en son type sous-jacent.

(depuis C ++ 17)

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:

enum class Numbers : unsigned
{
    One = 1u,
    Two = 2u
};

int main()
{
    Numbers zero{ 0u };
    return 0;
}
David Ledger
la source