web-dev-qa-db-fra.com

Switch Statement Fallthrough ... devrait-il être autorisé?

Aussi longtemps que je me souvienne, j'ai évité d'utiliser l'instruction de commutateur fallthrough. En fait, je ne me souviens pas que cela soit entré dans ma conscience comme un moyen possible de faire les choses, car cela m’a pénétré très tôt dans la tête et qu’il n’était rien de plus qu’un bug dans l’instruction switch. Cependant, aujourd’hui, j’ai rencontré un code qui l’utilise de par sa conception, ce qui m’a immédiatement amené à me demander ce que tout le monde dans la communauté pensait au sujet de l’instruction switch.

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

Edit: Je n'étais pas assez spécifique à ce que je voulais dire par chute. 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 de par ma conception et je ne sais tout simplement pas si je conviens que les instructions switch doivent être utilisées de cette façon. Je pense que le premier exemple de code est très utile et sûr. La seconde semble un peu dangereuse.

107
Fostah

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 tombe dans une autre étiquette de cas, je considérerais presque toujours ce mal. Peut-être que déplacer le code commun vers une fonction et appeler des deux endroits serait une meilleure idée.

Et s'il vous plaît noter que j'utilise la C++ FAQ définition de "mal"

79
Fred Larson

C'est une épée à deux tranchants. Parfois très utile, souvent dangereux.

Quand est-ce bon? Quand vous voulez que 10 cas soient traités de la même façon ...

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 règle que j’aime bien est que si vous faites quelque chose d’extraordinaire où vous excluez la pause, vous avez besoin d’un commentaire clair/* FALLTHROUGH */pour indiquer que telle était votre intention.

50
John M

La chute est vraiment une chose pratique, selon ce que vous faites. Considérez ce moyen simple 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 cela avec if/else. Ce serait un gâchis.

20
Lucas Oman

Avez-vous entendu parler de appareil de Duff ? C’est un excellent exemple d’utilisation de l’interrupteur en cascade.

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

20
tzot

Cela peut être très utile à quelques reprises, mais en général, le comportement souhaité est sans faille. Fallthrough devrait être autorisé, mais pas implicite.

Un exemple, pour mettre à jour les 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
}
10
Mister

Je voudrais une syntaxe différente pour les replis 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 enums, si vous déclarez tous les cas sur votre enum en utilisant des drapeaux, n'est-ce pas? Ne semble pas si grave non plus, les cas pourraient (devraient?) Faire très bien partie de votre enum.

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

6
Erik van Brakel

Comme avec n'importe quoi: utilisé avec précaution, il peut être un outil élégant.

Cependant, je pense que les inconvénients plus que justifient de NE PAS l'utiliser, et finalement de ne plus le permettre (C #). Parmi les problèmes sont:

  • il est facile d'oublier une pause
  • il n'est pas toujours évident pour les mainteneurs de code QU'une interruption omise soit intentionnelle

bon usage d'un interrupteur/boîtier casse:

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

Utilisation BAAAAAD d'un interrupteur/boîtier caser:

switch (x)
{
case 1:
    some code
case 2:
    some more code
case 3:
    even more code
    break;
}

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

Mon dernier mot: restez à l’écart des étiquettes de casse perdues, comme dans l’exemple BAD, à moins que vous ne conserviez le code hérité où ce style est utilisé et bien compris.

5
steffenj

Puissant et dangereux. Le plus gros problème avec les retombées est que ce n'est pas explicite. Par exemple, si vous rencontrez du code fréquemment édité comportant un commutateur avec des interruptions automatiques, comment savez-vous que c'est intentionnel et non un bogue?

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

switch($var) {
    case 'first':
        // fall-through
    case 'second':
        i++;
        break;
 }
4
Dan Hulton

Utiliser des retombées comme dans votre premier exemple est clairement correct, je ne considérerais pas cela comme une véritable retombée.

Le deuxième exemple est dangereux et (s'il n'est pas commenté de manière exhaustive) non évident. J'enseigne à mes étudiants à ne pas utiliser ce type de construction SAUF s'ils estiment qu'il vaut la peine d'y consacrer un bloc de commentaires, décrivant qu'il s'agit d'une erreur intentionnelle et pourquoi cette solution est meilleure que les solutions de rechange. Cela décourage une utilisation négligente, mais le permet quand même dans les cas où il est utilisé de manière avantageuse.

Cela équivaut plus ou moins à ce que nous faisions dans les projets spatiaux lorsque quelqu'un voulait violer la norme de codage: il devait demander une dérogation (et on m'a demandé de donner son avis sur la décision).

3
Wouter van Ooijen

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

S'il existe un code commun que plusieurs branches d'une instruction switch souhaitent utiliser, je l'extrais dans une fonction commune distincte pouvant être appelée dans n'importe quelle branche.

2
Matt Dillard

fall pens devrait être utilisé uniquement lorsqu'il est utilisé comme table de saut dans un bloc de code. S'il existe une partie du code avec une rupture inconditionnelle avant plusieurs observations, tous les groupes d'observations doivent se terminer de cette manière. Tout le reste est "diabolique".

1
BCS

Dans certains cas, l’utilisation de retombées est un acte de paresse de la part du programmeur - ils pourraient utiliser une série de || déclarations, par exemple, mais utilisez plutôt une série de casses de commutation "fourre-tout".

Cela étant dit, je les ai trouvées particulièrement utiles quand je sais que éventuellement je vais quand même avoir besoin des options (par exemple, dans une réponse au menu), mais je n'ai pas encore implémenté toutes les options. les choix. De même, si vous faites une chute directe entre "un" et "un", je trouve qu'il est nettement plus propre d'utiliser l'interrupteur qui passe à travers qu'une déclaration composée si.

C'est probablement une question de style et de pensée des programmeurs, mais je n'aime généralement pas retirer des composants d'un langage au nom de 'sécurité' - c'est pourquoi je préfère le C et ses variantes/descendants plutôt que, disons, Java. J'aime pouvoir singer avec des pointeurs et autres, même quand je n'ai aucune "raison" de le faire.

1
warren