Cette question concerne principalement C/C++, mais je suppose que d'autres langages sont également pertinents.
Je ne comprends pas pourquoi le commutateur/boîtier est toujours utilisé au lieu de si/sinon si. Cela me ressemble beaucoup à l'utilisation de goto et donne le même type de code désordonné, tandis que les mêmes résultats pourraient être obtenus avec if/else if de manière beaucoup plus organisée.
Pourtant, je vois ces blocs assez souvent. Un endroit commun pour les trouver est près d'une boucle de message (WndProc ...), alors que ceux-ci sont parmi les endroits où ils soulèvent le plus de ravages: les variables sont partagées tout au long du bloc, même lorsqu'elles ne sont pas appropriées (et ne peuvent pas être initialisé à l'intérieur). Une attention particulière doit être accordée à la non-interruption des pauses, etc.
Personnellement, j'évite de les utiliser et je me demande si je manque quelque chose?
Sont-ils plus efficaces que ceux de if/else? Sont-ils portés par la tradition?
Résumant mon message initial et mes commentaires - il y a plusieurs avantages de la déclaration switch
par rapport à la déclaration if
/else
:
Code plus propre. Code avec plusieurs chaînes if
/else if ...
semble désordonné et difficile à entretenir - switch
donne une structure plus propre.
Performance. Pour les valeurs denses case
, le compilateur génère une table de sauts, pour une recherche binaire clairsemée ou une série de if
/else
, donc dans le pire des cas switch
est aussi rapide que if
/else
, mais généralement plus rapide. Bien que certains compilateurs puissent également optimiser if
/else
.
L'ordre de test n'a pas d'importance. Pour accélérer les séries de tests if
/else
, il faut d'abord mettre les cas les plus probables. Avec switch
/case
, le programmeur n'a pas besoin d'y penser.
La valeur par défaut peut être n'importe où. Avec if
/else
, le cas par défaut doit être à la toute fin - après le dernier else
. Dans switch
- default
peut être n'importe où, partout où le programmeur le trouve plus approprié.
Code commun. Si vous devez exécuter du code commun pour plusieurs cas, vous pouvez omettre break
et l'exécution "échouera" - quelque chose que vous ne pouvez pas réaliser avec if
/else
. (Il est recommandé de placer un commentaire spécial /* FALLTHROUGH */
pour de tels cas - lint le reconnaît et ne se plaint pas, sans ce commentaire, il se plaint car c'est une erreur courante d'oublier break
).
Merci à tous les commentateurs.
n'oubliez pas que case/select offre une flexibilité supplémentaire:
ainsi que il s'exécute beaucoup plus rapidement (via la table de saut/recherche) * historiquement
Souvenez-vous également que les instructions switch permettent au flux de contrôle de continuer, ce qui vous permet de combiner parfaitement les conditions tout en vous permettant d'ajouter du code supplémentaire pour certaines conditions, comme dans le code suivant:
switch (dayOfWeek)
{
case MONDAY:
garfieldUnhappy = true;
case TUESDAY:
case WEDNESDAY:
case THURSDAY:
case FRIDAY:
weekDay = true;
break;
case SATURDAY:
weekendJustStarted = true;
case SUNDAY:
weekendDay = true;
break;
}
En utilisant if/else
ici, les déclarations ne seraient nulle part comme Nice.
if (dayOfWeek == MONDAY)
{
garfieldUnhappy = true;
}
if (dayOfWeek == SATURDAY)
{
weekendJustStarted = true;
}
if (dayOfWeek == MONDAY || dayOfWeek == TUESDAY || dayOfWeek == WEDNESDAY
|| dayOfWeek == THURSDAY || dayOfWeek == FRIDAY)
{
weekDay = true;
}
else if (dayOfWeek == SATURDAY || dayOfWeek == SUNDAY)
{
weekendDay = true;
}
S'il y a beaucoup de cas, l'instruction switch semble plus propre.
C'est aussi bien quand vous avez plusieurs valeurs pour lesquelles vous voulez le même comportement - il suffit de lire plusieurs instructions "case" qui tombent dans une seule implémentation qu'un if (this || that || someotherthing || .. .)
Cela peut également dépendre de votre langue - Par exemple, certaines langues ne fonctionnent qu'avec des types numériques, donc cela vous évite de taper lorsque vous travaillez avec une valeur énumérée, des constantes numériques ... etc ...
If (day == DAYOFWEEK_MONDAY) {
//...
}
else if (day == DAYOFWEEK_TUESDAY) {
//...
}
//etc...
Ou un peu plus facile à lire ...
switch (day) {
case DAYOFWEEK_MONDAY :
//...
case DAYOFWEEK_TUESDAY :
//...
//etc...
}
Switch/case est généralement optimisé plus efficacement que if/else if/else, mais est occasionnellement (selon la langue et le compilateur) traduit en simples instructions if/else if/else.
Je pense personnellement que les instructions switch rendent le code plus lisible qu'un tas d'instructions if; à condition que vous suiviez quelques règles simples. Des règles que vous devriez probablement suivre même pour vos situations if/else if/else, mais c'est encore mon avis.
Ces règles:
Clarté. Comme je l'ai dit ici , un indice que else if
est problématique est
la fréquence avec laquelle ELSE IF est utilisé d'une manière beaucoup plus restreinte que ne le permet la syntaxe. C'est un marteau de flexibilité, permettant de tester des conditions totalement indépendantes. Mais il est couramment utilisé pour écraser les mouches de CASE, en comparant la même expression avec des valeurs alternatives ...
Cela réduit la lisibilité du code. Étant donné que la structure permet un univers de complexité conditionnelle, le lecteur doit garder à l'esprit plus de possibilités lors de l'analyse ELSE IF que lors de l'analyse CASE.
En fait, une instruction switch implique que vous travaillez sur quelque chose qui est plus ou moins une énumération qui vous donne un indice instantané de ce qui se passe.
Cela dit, un commutateur sur une énumération dans n'importe quelle langue OO pourrait probablement être mieux codé - et une série de if/else sur la même valeur de style "enum" serait au moins aussi mauvaise et encore pire pour transmettre du sens.
pour répondre au souci que tout ce qui se trouve à l'intérieur du commutateur ait une portée équivalente, vous pouvez toujours jeter votre logique de cas dans un autre bloc {}, comme ça.
switch( thing ) {
case ONETHING: {
int x; // local to the case!
...
}
break;
case ANOTHERTHING: {
int x; // a different x than the other one
}
break;
}
.. maintenant je ne dis pas que c'est joli. Il suffit de le présenter comme quelque chose qui est possible si vous devez absolument isoler quelque chose dans un cas d'un autre.
une autre réflexion sur la question de la portée - cela semble être une bonne pratique de ne mettre qu'un seul commutateur à l'intérieur d'une fonction, et pas beaucoup d'autre. Dans ces circonstances, la portée variable n'est pas aussi préoccupante, car de cette façon, vous ne gérez généralement qu'un seul cas d'exécution sur une invocation donnée de la fonction.
ok, une dernière réflexion sur les commutateurs: si une fonction contient plus de quelques commutateurs, il est probablement temps de refactoriser votre code. Si une fonction contient des commutateurs imbriqués, c'est probablement un indice pour repenser un peu votre conception =)
le cas de commutateur est principalement utilisé pour avoir le choix de faire dans la programmation. Ceci n'est pas lié à la conditionnelle comme:
si votre programme nécessite seulement le choix de faire alors pourquoi vous utilisez le bloc if/else et augmentez l'effort de programmation et réduisez la vitesse d'exécution du programme.
Un Smalltalker peut rejeter à la fois le commutateur et if-then-else et peut écrire quelque chose comme: -
shortToLongDaysMap := Dictionary new.
shortToLongDaysMap
at: 'Mon' put: 'Monday';
at: 'Tue' put: 'Tuesday';
at: 'Wed' put: 'Wednesday'
etc etc.
longForm := shortToLongDaysMap at: shortForm ifAbsent: [shortForm]
Ceci est un exemple trivial mais j'espère que vous pouvez voir comment cette technique évolue pour un grand nombre de cas.
Notez le deuxième argument de at:IfAbsent:
est similaire à la clause par défaut d'une instruction case.
La principale raison derrière cela est la maintenabilité et la lisibilité. Il est facile de rendre le code plus lisible et plus facile à entretenir avec l'instruction Switch/case puis if/else. Parce que vous avez beaucoup de code if/else, cela devient tellement désordonné comme nest et il est très difficile de le maintenir.
Et certains comment le temps d'exécution est une autre raison.
À peu près sûr qu'ils compilent les mêmes choses que if
/else if
, mais je trouve le switch
/case
plus facile à lire lorsqu'il y a plus de 2 ou 3 else
s.
Les instructions de commutation peuvent être optimisées pour la vitesse, mais peuvent occuper plus de mémoire si les valeurs de cas sont réparties sur un grand nombre de valeurs.
si/sinon sont généralement lents, car chaque valeur doit être vérifiée.