Instruction Switch fallthrough est l' une de mes principales raisons personnelles pour l' amour switch
contre les if/else if
constructions. Un exemple est en ordre ici:
static string NumberToWords(int number)
{
string[] numbers = new string[]
{ "", "one", "two", "three", "four", "five",
"six", "seven", "eight", "nine" };
string[] tens = new string[]
{ "", "", "twenty", "thirty", "forty", "fifty",
"sixty", "seventy", "eighty", "ninety" };
string[] teens = new string[]
{ "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen",
"sixteen", "seventeen", "eighteen", "nineteen" };
string ans = "";
switch (number.ToString().Length)
{
case 3:
ans += string.Format("{0} hundred and ", numbers[number / 100]);
case 2:
int t = (number / 10) % 10;
if (t == 1)
{
ans += teens[number % 10];
break;
}
else if (t > 1)
ans += string.Format("{0}-", tens[t]);
case 1:
int o = number % 10;
ans += numbers[o];
break;
default:
throw new ArgumentException("number");
}
return ans;
}
Les gens intelligents grincent des dents parce que les string[]
s doivent être déclarés en dehors de la fonction: eh bien, ils le sont, ce n'est qu'un exemple.
Le compilateur échoue avec l'erreur suivante:
Le contrôle ne peut pas passer d'une étiquette de cas («cas 3:») à une autre Le contrôle ne peut pas passer d'une étiquette de cas («cas 2:») à une autre
Pourquoi? Et existe-t-il un moyen d'obtenir ce genre de comportement sans avoir trois if
s?
c#
switch-statement
Matthew Scharley
la source
la source
Le "pourquoi" est d'éviter les chutes accidentelles, ce dont je suis reconnaissant. Il s'agit d'une source non rare de bogues en C et en Java.
La solution consiste à utiliser goto, par exemple
La conception générale de l'interrupteur / boîtier est un peu malheureuse à mon avis. Il est resté trop proche de C - il y a des changements utiles qui pourraient être apportés en termes de portée, etc. - à quel moment un nom différent serait peut-être demandé.
la source
if (result = true) { }
Le basculement des commutateurs est historiquement l'une des principales sources de bugs dans les logiciels modernes. Le concepteur de langage a décidé de rendre obligatoire le saut à la fin du cas, sauf si vous passez directement au cas suivant sans traitement.
la source
case 1, 2:
dans les langues qui le permettent. Ce que je ne comprendrai jamais, c'est pourquoi aucune langue moderne ne choisirait de permettre cela.case 1, 2:
s'agit d'une seule étiquette mais avec plusieurs noms. - FWIW, je pense que la plupart des langues qui interdisent la chute ne font pas cas particulier des "étiquettes de cas consécutives" indium mais traitent plutôt les étiquettes de cas comme une annotation sur la déclaration suivante et nécessitent la dernière déclaration avant une déclaration étiquetée avec (un ou plus) les étiquettes de cas seront un saut.Pour ajouter aux réponses ici, je pense qu'il vaut la peine de considérer la question opposée conjointement avec cela, à savoir. pourquoi C a-t-il permis la chute en premier lieu?
Tout langage de programmation sert bien entendu deux objectifs:
La création de tout langage de programmation est donc un équilibre entre la meilleure façon de servir ces deux objectifs. D'une part, plus il est facile de se transformer en instructions informatiques (qu'il s'agisse de code machine, de bytecode comme IL ou que les instructions soient interprétées lors de l'exécution), plus le processus de compilation ou d'interprétation sera efficace, fiable et compact en sortie. Poussé à son extrême, cet objectif se traduit par notre simple écriture en assembleur, en IL ou même en op-codes bruts, car la compilation la plus simple est celle où il n'y a pas du tout de compilation.
Inversement, plus le langage exprime l'intention du programmeur, plutôt que les moyens utilisés à cette fin, plus le programme est compréhensible à la fois lors de l'écriture et pendant la maintenance.
Maintenant,
switch
aurait toujours pu être compilé en le convertissant en une chaîne équivalente deif-else
blocs ou similaire, mais il a été conçu comme permettant la compilation dans un modèle d'assemblage commun particulier où l'on prend une valeur, en calcule un décalage (que ce soit en recherchant une table indexé par un hachage parfait de la valeur, ou par une arithmétique réelle sur la valeur *). Il convient de noter à ce stade qu'aujourd'hui, la compilation C # se transformera parfoisswitch
en équivalentif-else
et utilisera parfois une approche de saut basée sur le hachage (et de même avec C, C ++ et d'autres langages avec une syntaxe comparable).Dans ce cas, il y a deux bonnes raisons pour autoriser les retards:
Cela se produit naturellement de toute façon: si vous créez une table de sauts dans un ensemble d'instructions et que l'un des premiers lots d'instructions ne contient pas de saut ou de retour, l'exécution progressera naturellement dans le lot suivant. Permettre les retombées était ce qui "arriverait" si vous tourniez la
switch
C en utilisant une table de saut à l'aide du code machine.Les codeurs qui écrivaient dans l'assembly étaient déjà habitués à l'équivalent: lors de l'écriture manuelle d'une table de sauts dans l'assembly, ils devaient se demander si un bloc de code donné se terminerait par un retour, un saut en dehors de la table ou simplement continuer au bloc suivant. En tant que tel, avoir le codeur ajouter un explicite
break
lorsque cela était nécessaire était "naturel" pour le codeur aussi.Il s'agissait donc à l'époque d'une tentative raisonnable d'équilibrer les deux objectifs d'un langage informatique en ce qui concerne à la fois le code machine produit et l'expressivité du code source.
Cependant, quatre décennies plus tard, les choses ne sont pas tout à fait les mêmes, pour plusieurs raisons:
switch
devenir soitif-else
parce qu'elle a été considérée comme l'approche susceptible d'être la plus efficace, soit de devenir une variante particulièrement ésotérique de l'approche saut-table est plus élevée. La correspondance entre les approches de niveau supérieur et inférieur n'est pas aussi solide qu'auparavant.switch
blocs utilisaient une jonction autre que plusieurs étiquettes sur le même bloc, et on pensait que l'utilisation - cas ici signifiait que ces 3% étaient en fait beaucoup plus élevés que la normale). Ainsi, la langue étudiée rend l'insolite plus facile à approvisionner que le commun.Relativement à ces deux derniers points, considérez la citation suivante de l'édition actuelle de K&R:
Donc, de la bouche du cheval, la chute en C est problématique. Il est considéré comme une bonne pratique de toujours documenter les retombées avec des commentaires, ce qui est une application du principe général selon lequel on doit documenter où l'on fait quelque chose d'inhabituel, car c'est ce qui déclenchera l'examen ultérieur du code et / ou fera ressembler votre code contient un bug de novice alors qu'il est correct.
Et quand on y pense, un code comme celui-ci:
Est d' ajouter quelque chose à faire tomber par-explicite dans le code, il est tout simplement pas quelque chose qui peut être détectée (ou dont l' absence peut être détectée) par le compilateur.
En tant que tel, le fait que l'on doive être explicite avec les retombées en C # n'ajoute aucune pénalité aux personnes qui ont bien écrit dans d'autres langages de style C, car elles seraient déjà explicites dans leurs retombées. †
Enfin, l'utilisation d'
goto
ici est déjà une norme de C et d'autres langages de ce type:Dans ce genre de cas où nous voulons qu'un bloc soit inclus dans le code exécuté pour une valeur autre que celle qui amène un au bloc précédent, alors nous devons déjà utiliser
goto
. (Bien sûr, il existe des moyens et des façons d'éviter cela avec différentes conditions, mais c'est vrai pour à peu près tout ce qui concerne cette question). En tant que tel, C # s'est construit sur la manière déjà normale de traiter une situation où nous voulons frapper plus d'un bloc de code dans aswitch
, et l'a simplement généralisé pour couvrir également les retombées. Cela a également rendu les deux cas plus pratiques et plus documentés, car nous devons ajouter une nouvelle étiquette en C, mais nous pouvons utiliser lacase
comme étiquette en C #. En C #, nous pouvons nous débarrasser de l'below_six
étiquette et l'utiliser pourgoto case 5
qui est plus claire quant à ce que nous faisons. (Il faudrait également ajouterbreak
default
, que j'ai omis juste pour que le code C ci-dessus ne soit clairement pas un code C #).En résumé donc:
break
, pour un apprentissage plus facile de la langue par ceux qui connaissent des langages similaires, et un portage plus facile.goto
approche pour frapper le même bloc à partir d'case
étiquettes différentes que celui utilisé en C. Il le généralise simplement à d'autres cas.goto
approche basée sur plus pratique et plus claire qu'elle ne l'est en C, en permettant auxcase
instructions d'agir comme des étiquettes.Dans l'ensemble, une décision de conception assez raisonnable
* Certaines formes de BASIC permettraient de faire ce
GOTO (x AND 7) * 50 + 240
qui, tout en étant fragile et donc un argument particulièrement convaincant pour l'interdictiongoto
, sert à montrer un équivalent en langage plus élevé du type de manière dont le code de niveau inférieur peut faire un saut basé sur arithmétique sur une valeur, ce qui est beaucoup plus raisonnable quand elle est le résultat de la compilation plutôt que quelque chose qui doit être maintenu manuellement. Les implémentations de Duff's Device en particulier se prêtent bien au code machine équivalent ou IL car chaque bloc d'instructions aura souvent la même longueur sans avoir besoin d'ajouter desnop
charges.† L'appareil de Duff revient ici, à titre d'exception raisonnable. Le fait qu'avec cela et des modèles similaires il y ait une répétition des opérations sert à rendre l'utilisation de la traversée relativement claire même sans commentaire explicite à cet effet.
la source
Vous pouvez 'goto case label' http://www.blackwasp.co.uk/CSharpGoto.aspx
la source
Ils ont omis ce comportement par conception pour éviter qu'il ne soit pas utilisé par testament mais qu'il cause des problèmes.
Il ne peut être utilisé que s'il n'y a pas de déclaration dans la partie de cas, comme:
la source
Ils ont changé le comportement de l'instruction switch (de C / Java / C ++) pour c #. Je suppose que le raisonnement était que les gens avaient oublié la chute et que des erreurs avaient été causées. Un livre que j'ai lu disait d'utiliser goto pour simuler, mais cela ne me semble pas être une bonne solution.
la source
goto case
sert. Son avantage par rapport à la retombée est qu'il est explicite. Le fait que certaines personnes s'ygoto case
opposent montre simplement qu'elles ont été endoctrinées contre le «goto» sans aucune compréhension du problème et sont incapables de penser par elles-mêmes. Lorsque Dijkstra a écrit "GOTO considéré comme nuisible", il s'adressait à des langues qui n'avaient aucun autre moyen de modifier le flux de contrôle.goto
optimisation du code?Vous pouvez effectuer une chute comme c ++ par le mot-clé goto.
EX:
la source
- Documentation C # switch ()
la source
Après chaque instruction de cas, il faut une instruction break ou goto , même s'il s'agit d'un cas par défaut.
la source
Juste une petite note pour ajouter que le compilateur pour Xamarin s'est vraiment trompé et qu'il permet une solution. Il aurait été corrigé, mais n'a pas été rendu public. J'ai découvert cela dans un code qui était en train de tomber et le compilateur ne s'est pas plaint.
la source
commutateur (référence C #) dit
Vous devez donc également ajouter un
break;
à votredefault
section, sinon il y aura toujours une erreur de compilation.la source
C # ne prend pas en charge les instructions Fall / Switch. Je ne sais pas pourquoi, mais il n'y a vraiment aucun support pour cela. Lien
la source
goto case
. C'était un choix de conception intentionnel et judicieux par les concepteurs C #.Vous avez oublié d'ajouter la "pause"; dans le cas 3. Dans le cas 2, vous l'avez écrit dans le bloc if. Essayez donc ceci:
la source