Interruption de l'instruction Switch… devrait-elle être autorisée? [fermé]

120

D'aussi loin que je me souvienne, j'ai évité d'utiliser une instruction switch fall-through. En fait, je ne me souviens pas que cela soit entré dans ma conscience comme un moyen possible de faire les choses car il m'a été percé très tôt dans la tête que ce n'était rien de plus qu'un bug dans la déclaration de commutation. Cependant, aujourd'hui, j'ai rencontré du code qui l'utilise par conception, ce qui m'a immédiatement amené à me demander ce que tout le monde dans la communauté pense de l'échec des instructions switch.

Est-ce quelque chose qu'un langage de programmation ne devrait explicitement pas autoriser (comme le fait C #, bien qu'il fournisse une solution de contournement) ou est-ce une fonctionnalité de tout langage suffisamment puissant pour être laissé entre les mains du programmeur?

Edit: Je n'étais pas assez précis sur ce que j'entendais par fall-through. J'utilise beaucoup ce type:

    switch(m_loadAnimSubCt){
        case 0:
        case 1:
            // Do something
            break;
        case 2:
        case 3:
        case 4:
            // Do something
            break;
   }

Cependant, je suis préoccupé par quelque chose comme ça.

   switch(m_loadAnimSubCt){
        case 0:
        case 1:
            // Do something, but fall through to the other cases
            // after doing it.

        case 2:
        case 3:
        case 4:
            // Do something else.
            break;
   }

De cette façon, chaque fois que le cas est 0, 1, il fera tout dans l'instruction switch. J'ai vu cela par conception et je ne sais tout simplement pas si je suis d'accord que les instructions switch devraient être utilisées de cette façon. Je pense que le premier exemple de code est très utile et sûr. Le second semble assez dangereux.

Fostah
la source
51
Pas d'accord sur le fait de ne pas être constructif. 27 votes positifs sur la question indiquent qu'il y en a d'autres qui ressentent cela aussi.
Wes Modes
2
«pas constructif» est une raison ancienne et quelque peu vague. De nos jours, de telles questions peuvent et doivent être signalées pour clôture comme étant purement basées sur l'opinion. SO est pour les questions auxquelles on peut répondre sur le code ou la conception, pas «quelle est l'opinion de« la communauté »[quoi que ce soit] sur mon opinion sur la fonctionnalité X». Programmers.SE conviendrait mieux, mais même dans ce cas, la valeur d'une question aussi large et motivée par l'opinion est encore très douteuse.
underscore_d
1
Il y a des cas d'utilisation très clairs où le fallthrough est utilisé, donc je ne sais pas pourquoi cela serait basé sur une opinion. Je conviens que cette question est un peu floue, mais elle n'aurait pas dû être fermée car non constructive. Peut-être juste édité pour être un cas plus clair de: "Quand le fallthrough est-il utilisé?" Ce qui a une réponse claire. De bons textes C ++ passent en revue cela, c'est déroutant pour les nouveaux codeurs (ou même pour les codeurs plus expérimentés d'autres langages), cela semble donc une bonne question pour SO. Il semble en effet comme une prototypiquement bonne question SO. Même si la façon dont il a été demandé n'était pas parfaite.
eric

Réponses:

89

Cela peut dépendre de ce que vous considérez comme un échec. Je suis d'accord avec ce genre de chose:

switch (value)
{
  case 0:
    result = ZERO_DIGIT;
    break;

  case 1:
  case 3:
  case 5:
  case 7:
  case 9:
     result = ODD_DIGIT;
     break;

  case 2:
  case 4:
  case 6:
  case 8:
     result = EVEN_DIGIT;
     break;
}

Mais si vous avez une étiquette de cas suivie d'un code qui passe par une autre étiquette de cas, je considérerais presque toujours cela comme un mal. Peut-être que déplacer le code commun vers une fonction et appeler des deux endroits serait une meilleure idée.

Et veuillez noter que j'utilise la définition de la FAQ C ++ de «mal»

Fred Larson
la source
Je suis d'accord avec vous: je ne considère pas votre exemple comme un échec, et s'il y a un cas où je veux exécuter plusieurs blocs de code via un échec, il y a probablement une meilleure façon de le faire.
Dave DuPlantis
10
+1 simplement pour cette définition du «mal». Je vais l'emprunter, merci ;-)
Joachim Sauer
Je comprends votre point de vue et vous avez raison, mais votre exemple est vraiment mauvais. Personne ne devrait tester des chiffres de test comme celui-ci. Scnr, votre réponse est juste de toute façon.
Tim Büthe
Par exemple, c'est précisément ce que C # interdit. Probablement pour une raison :-)
Joey
Malheureusement, la définition du mal semble être hors ligne
Espérons que
57

C'est une épée à double tranchant. C'est parfois très utile, mais souvent dangereux.

Quand est-ce bon? Lorsque vous voulez que 10 cas soient tous traités de la même manière ...

switch (c) {
  case 1:
  case 2:
            ... Do some of the work ...
            /* FALLTHROUGH */
  case 17:
            ... Do something ...
            break;
  case 5:
  case 43:
            ... Do something else ...
            break;
}

La seule règle que j'aime est que si jamais vous faites quelque chose d'extraordinaire où vous excluez la pause, vous avez besoin d'un commentaire clair / * FALLTHROUGH * / pour indiquer que c'était votre intention.

John M
la source
3
+1! Je conviens que c'est parfois la bonne réponse, et je suis DOUBLEMENT d'accord que lorsque c'est intentionnel, elle devrait être commentée. (Dans une revue de code, je FAIS l'autre développeur commentaire a pas que par la conception non vide chute à travers elle arrive, nous sommes un magasin C # où ce n'est pas pris en charge :).
John Rudy
4
J'utilise moi-même un / * FALL THROUGH * / comment dans mon code le cas échéant. =]
strager
2
Si vous PC-Lint, vous devez insérer / * - fallthrough * /, sinon il se plaint.
Steve Melnikoff
1
Néanmoins, si vous vouliez indiquer clairement que le comportement est intentionnel, vous pourriez aussi bien vous if(condition > 1 && condition <10) {condition = 1}; switch(...sauver des cas (pour une apparence claire) et tout le monde peut voir que vous voulez vraiment que ces cas déclenchent la même action.
Mark
1
C'est encore un codage terrible. Le premier cas doit appeler une fonction, le second cas doit appeler la même fonction puis appeler une seconde fonction.
BlueRaja - Danny Pflughoeft
21

Avez-vous entendu parler de l'appareil de Duff ? Ceci est un excellent exemple d'utilisation de basculement de commutateur.

C'est une fonctionnalité qui peut être utilisée et qui peut être abusée, comme presque toutes les fonctionnalités du langage.

tzot
la source
Apprenez quelque chose de nouveau chaque jour - merci! Je ressens cependant pour les pauvres développeurs qui traversent de telles contorsions.
Justin R.
Wow, c'est quelque chose pour envelopper votre cerveau.
Fostah
6
Remarquez le commentaire cité par Duff dans cet article: "Ce code forme une sorte d'argument dans ce débat, mais je ne sais pas si c'est pour ou contre."
John Carter
L'appareil de Duff est clairement diabolique
Marjeta
21

La transition est vraiment pratique, selon ce que vous faites. Considérez cette façon claire et compréhensible d'organiser les options:

switch ($someoption) {
  case 'a':
  case 'b':
  case 'c':
    // Do something
    break;

  case 'd':
  case 'e':
    // Do something else
    break;
}

Imaginez faire cela avec if / else. Ce serait un gâchis.

Lucas Oman
la source
1
Je trouve que ce type de solution est très utile
Mitchel Sellers
si «faire quelque chose» est plus que juste un peu que le commutateur sera plutôt le désordre que if / else. Avec if / else, la variable testée est partout claire. Même ce petit exemple n'aurait pas l'air si mal avec if / else dans mes yeux
grenix
10

Cela peut être très utile à quelques reprises, mais en général, aucune erreur n'est le comportement souhaité. L'interruption doit être autorisée, mais pas implicite.

Un exemple, pour mettre à jour d'anciennes versions de certaines données:

switch (version) {
    case 1:
        // Update some stuff
    case 2:
        // Update more stuff
    case 3:
        // Update even more stuff
    case 4:
        // And so on
}
Peter Mortensen
la source
6

J'aimerais une syntaxe différente pour les solutions de secours dans les commutateurs, quelque chose comme, errr ..

switch(myParam)
{
  case 0 or 1 or 2:
    // Do something;
    break;
  case 3 or 4:
    // Do something else;
    break;
}

Remarque: Cela serait déjà possible avec les énumérations, si vous déclarez tous les cas sur votre énumération en utilisant des indicateurs, non? Cela ne sonne pas si mal non plus; les cas pourraient (devraient?) très bien faire déjà partie de votre énumération.

Peut-être que ce serait un bon cas (sans jeu de mots) pour une interface fluide utilisant des méthodes d'extension? Quelque chose comme, euh ...

int value = 10;
value.Switch()
  .Case(() => { /* Do something; */ }, new {0, 1, 2})
  .Case(() => { /* Do something else */ } new {3, 4})
  .Default(() => { /* Do the default case; */ });

Bien que ce soit probablement encore moins lisible: P

Erik van Brakel
la source
1
J'aime beaucoup la première suggestion. Cela se lit très bien et enregistre quelques lignes de code. La seconde a pris une minute pour envelopper mon cerveau, mais elle a du sens, mais elle semble prête à être un gâchis.
Fostah
1
Ouais, c'est ce que je pensais aussi, c'était juste un pet mental aléatoire que j'ai posé, dans le but de trouver une manière différente de changer en utilisant les constructions de langage existantes.
Erik van Brakel
J'ai parcouru cette question et j'aime vraiment ça! J'ai eu une idée sur la façon d'améliorer la lisibilité de ceci, mais SO ne me laisse pas poster de commentaires sur plusieurs lignes: (Quelqu'un devrait l'implémenter tout comme POC, semble amusant à implémenter et à utiliser (:
Victor Elias
5

Comme pour tout: s'il est utilisé avec précaution, il peut être un outil élégant.

Cependant, je pense que les inconvénients justifient largement de ne pas l'utiliser, et finalement de ne plus l'autoriser (C #). Parmi les problèmes figurent:

  • c'est facile d '"oublier" une pause
  • il n'est pas toujours évident pour les responsables du code qu'une pause omise était intentionnelle

Bonne utilisation d'un interrupteur / boîtier:

switch (x)
{
case 1:
case 2:
case 3:
 Do something
 break;
}

Utilisation Baaaaad d'un interrupteur / boîtier:

switch (x)
{
case 1:
    Some code
case 2:
    Some more code
case 3:
    Even more code
    break;
}

Cela peut être réécrit en utilisant des constructions if / else sans aucune perte à mon avis.

Mon dernier mot: évitez les étiquettes de cas fall-through comme dans le mauvais exemple, à moins que vous ne mainteniez un code hérité où ce style est utilisé et bien compris.

Steffenj
la source
3
Vous pouvez réécrire la plupart des constructions de langage en utilisant if () et goto ... cela ne justifie cependant pas d'abandonner une construction explicite.
Shog9
2
Je sais, mais les constructions switch / case qui utilisent explicitement le "cas 1: un cas de code 2: un autre cas de code 3: rupture de code finale;" peut et doit être réécrit en utilisant if / else if (mon avis).
steffenj
1
Notez que le changement peut être plusieurs fois plus rapide qu'une liste de if-elses. Switch utilise une table de sauts, ou lorsque cela n'est pas possible, ses sauts conditionnels seront structurés dans un arbre de décision au lieu d'une liste linéaire de sauts conditionnels. Les instructions If-else imbriquées bien structurées seront, au mieux, tout aussi rapides.
Jochem Kuijpers
4

C'est puissant et dangereux. Le plus gros problème avec le fall-through est qu'il n'est pas explicite. Par exemple, si vous rencontrez du code fréquemment modifié qui a un commutateur avec des erreurs, comment savez-vous que c'est intentionnel et non un bogue?

Partout où je l'utilise, je m'assure qu'il est correctement commenté:

switch($var) {
    case 'first':
        // Fall-through
    case 'second':
        i++;
        break;
 }
Dan Hulton
la source
2
L'intention est évidente s'il n'y a pas de code pour le cas «premier». Si vous codez là-dedans et que vous souhaitez également exécuter le code pour le cas «second», le commentaire est important. Mais j'éviterais ce scénario de toute façon.
Joel Coehoorn
1
@Joel - Je suis entièrement d'accord. Dans ma boutique, nous avons eu des gens qui ont mis des commentaires à tort dans de tels cas, et je pense que cela réduit la lisibilité. S'il y a du code, insérez le commentaire fallthrough.
Fred Larson
Que voulons-nous dire "non explicite". C'est pourquoi nous nous cassons; D'autres langues font cela. Ce n'est un problème que pour les personnes qui n'ont pas appris leur langue dans l'ordre. C # le demande pour le cas par défaut ffs, sans raison à mon humble avis.
mckenzm
3

Utiliser le fall-through comme dans votre premier exemple est clairement acceptable, et je ne le considérerais pas comme un véritable fall-through.

Le deuxième exemple est dangereux et (s'il n'est pas commenté en détail) non évident. J'apprends à mes étudiants à ne pas utiliser de telles constructions à moins qu'ils ne considèrent que cela vaut la peine de lui consacrer un bloc de commentaires, qui décrit qu'il s'agit d'une erreur intentionnelle et pourquoi cette solution est meilleure que les alternatives. Cela décourage une utilisation bâclée, mais cela le rend toujours autorisé dans les cas où il est utilisé à un avantage.

C'est plus ou moins équivalent à ce que nous faisions dans les projets spatiaux lorsque quelqu'un voulait violer la norme de codage: il fallait demander une dispense (et j'ai été appelé à donner des avis sur la décision).

Wouter van Ooijen
la source
2

Je n'aime pas que mes switchdéclarations échouent - elles sont beaucoup trop sujettes aux erreurs et difficiles à lire. La seule exception est lorsque plusieurs caseinstructions font toutes exactement la même chose.

S'il y a du code commun que plusieurs branches d'une instruction switch veulent utiliser, je l'extraye dans une fonction commune distincte qui peut être appelée dans n'importe quelle branche.

Matt Dillard
la source
1

Dans certains cas, l'utilisation de fall-through est un acte de paresse de la part du programmeur - il pourrait utiliser une série de || par exemple, mais utilisez plutôt une série de cas de commutation «fourre-tout».

Cela étant dit, je les ai trouvés particulièrement utiles quand je sais que finalement je vais quand même avoir besoin des options (par exemple dans une réponse de menu), mais que je n'ai pas encore implémenté tous les choix. De même, si vous faites une chute à la fois pour «a» et «A», je trouve qu'il est nettement plus propre d'utiliser le commutateur de chute qu'une instruction if composée.

C'est probablement une question de style et de la façon dont les programmeurs pensent, mais je n'aime généralement pas supprimer les composants d'un langage au nom de la `` sécurité '' - c'est pourquoi je tend davantage vers le C et ses variantes / descendants que, disons, Java. J'aime pouvoir faire des singes avec des pointeurs et autres, même si je n'ai aucune «raison» de le faire.

garenne
la source
À proprement parler. Les instructions switch sautent via une valeur définie vers un tas d'emplacements de code spécifiques. Bien que le compilateur détecte probablement de telles choses, il existe une valeur de vitesse et d'exactitude pour une instruction switch sur une série d'instructions ou. Si p est 0 ou p est 1 ou p est 2. En fait, j'ai dû évaluer si p était 0 et p était 1, plutôt que de sauter à l'emplacement p = 2 dans le code. Ce qui s'est avéré être identique à p0 et p1. Mais je n'ai jamais eu à les vérifier.
Tatarize
1

Fall-through ne doit être utilisé que lorsqu'il est utilisé comme table de saut dans un bloc de code. S'il y a une partie du code avec une interruption inconditionnelle avant plus de cas, tous les groupes de cas doivent se terminer de cette façon.

Tout le reste est "mal".

BCS
la source