Das Durchfallen der Switch-Anweisung ist einer meiner persönlichen Hauptgründe für das Lieben switch
gegenüber if/else if
Konstrukten. Ein Beispiel ist hier angebracht:
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;
}
Die klugen Leute kriechen, weil das string[]
s außerhalb der Funktion deklariert werden sollte: Nun , das ist nur ein Beispiel.
Der Compiler schlägt mit folgendem Fehler fehl:
Die Kontrolle kann nicht von einem Falletikett ('Fall 3:') zum anderen durchfallen Die Kontrolle kann nicht von einem Falletikett ('Fall 2:') zum anderen durchfallen
Warum? Und gibt es eine Möglichkeit, diese Art von Verhalten zu erreichen, ohne drei if
Sekunden zu haben?
c#
switch-statement
Matthew Scharley
quelle
quelle
Das "Warum" besteht darin, ein versehentliches Durchfallen zu vermeiden, wofür ich dankbar bin. Dies ist eine nicht seltene Fehlerquelle in C und Java.
Die Problemumgehung besteht darin, goto zu verwenden, z
Das allgemeine Design des Schalters / Gehäuses ist meiner Ansicht nach etwas unglücklich. Es blieb zu nahe an C - es gibt einige nützliche Änderungen, die in Bezug auf den Umfang usw. vorgenommen werden könnten. Ein intelligenterer Schalter, der Musterabgleich usw. durchführen könnte, wäre wahrscheinlich hilfreich, aber das ändert sich wirklich von Schalter zu "Überprüfen einer Folge von Bedingungen". - an diesem Punkt würde vielleicht ein anderer Name verlangt.
quelle
if (result = true) { }
Switch Fallthrough ist historisch gesehen eine der Hauptursachen für Fehler in moderner Software. Der Sprachdesigner hat beschlossen, das Springen am Ende des Falls obligatorisch zu machen, es sei denn, Sie gehen direkt ohne Verarbeitung direkt zum nächsten Fall über.
quelle
case 1, 2:
in Sprachen, die das erlauben. Was ich nie verstehen werde, ist, warum eine moderne Sprache dies nicht zulassen würde.case 1, 2:
es sich um ein einzelnes Etikett mit mehreren Namen handelt. - FWIW, ich denke, die meisten Sprachen, die das Durchfallen verbieten, sind keine Sonderfälle für das Indium "aufeinanderfolgende Fallbezeichnungen", sondern behandeln die Fallbezeichnungen als Anmerkung zur nächsten Anweisung und erfordern die letzte Anweisung vor einer mit (eins oder) gekennzeichneten Anweisung mehr) Fallbezeichnungen als Sprung.Um die Antworten hier zu ergänzen, denke ich, dass es sich lohnt, die entgegengesetzte Frage in Verbindung damit zu betrachten, nämlich. Warum hat C überhaupt einen Durchfall zugelassen?
Jede Programmiersprache dient natürlich zwei Zielen:
Die Schaffung einer Programmiersprache ist daher ein Gleichgewicht zwischen der Frage, wie diese beiden Ziele am besten erreicht werden können. Einerseits ist der Kompilierungs- oder Interpretationsprozess umso effizienter, zuverlässiger und zuverlässiger, je einfacher es ist, sich in Computeranweisungen umzuwandeln (unabhängig davon, ob es sich um Maschinencode, Bytecode wie IL handelt oder ob die Anweisungen bei der Ausführung interpretiert werden) kompakte Ausgabe. Im Extremfall führt dieses Ziel dazu, dass wir nur in Assembly, IL oder sogar in rohen Op-Codes schreiben, da die einfachste Kompilierung darin besteht, dass überhaupt keine Kompilierung erfolgt.
Umgekehrt ist das Programm sowohl beim Schreiben als auch während der Wartung verständlicher, je mehr die Sprache die Absicht des Programmierers zum Ausdruck bringt und nicht die Mittel, die zu diesem Zweck eingesetzt werden.
Jetzt
switch
hätte man es immer kompilieren können, indem man es in die äquivalente Kette vonif-else
Blöcken oder ähnlichem konvertierte , aber es wurde so konzipiert, dass es die Kompilierung in ein bestimmtes gemeinsames Baugruppenmuster ermöglicht, in dem man einen Wert nimmt und einen Versatz daraus berechnet (sei es durch Nachschlagen einer Tabelle) indiziert durch einen perfekten Hash des Wertes oder durch tatsächliche Arithmetik des Wertes *). An dieser Stelle ist anzumerken, dass die C # -Kompilierung heute manchmalswitch
zum Äquivalentif-else
wird und manchmal einen Hash-basierten Sprungansatz verwendet (und ebenfalls mit C, C ++ und anderen Sprachen mit vergleichbarer Syntax).In diesem Fall gibt es zwei gute Gründe, einen Durchfall zuzulassen:
Es passiert sowieso nur natürlich: Wenn Sie eine Sprungtabelle in einen Befehlssatz einbauen und einer der früheren Befehlsstapel keine Art von Sprung oder Rückgabe enthält, wird die Ausführung natürlich auf den nächsten Stapel fortgesetzt. Fall-Through zuzulassen war das, was "einfach passieren" würde, wenn Sie das-
switch
using C in eine Sprungtabelle verwandeln würden - unter Verwendung von Maschinencode.Codierer, die in Assembly geschrieben haben, waren bereits an das Äquivalent gewöhnt: Beim Schreiben einer Sprungtabelle von Hand in Assembly mussten sie berücksichtigen, ob ein bestimmter Codeblock mit einer Rückgabe, einem Sprung außerhalb der Tabelle enden oder einfach fortfahren würde zum nächsten Block. Daher war es auch
break
für den Codierer "natürlich" , wenn der Codierer bei Bedarf eine explizite Option hinzufügte .Zu dieser Zeit war es daher ein vernünftiger Versuch, die beiden Ziele einer Computersprache in Einklang zu bringen, da sie sich sowohl auf den erzeugten Maschinencode als auch auf die Ausdruckskraft des Quellcodes bezieht.
Vier Jahrzehnte später sind die Dinge aus mehreren Gründen nicht ganz die gleichen:
switch
entweder umgesetzt werden,if-else
weil sie als der wahrscheinlich effizienteste Ansatz angesehen wurden, oder dass sie zu einer besonders esoterischen Variante des Sprungtabellenansatzes werden. Die Zuordnung zwischen den Ansätzen auf höherer und niedrigerer Ebene ist nicht mehr so stark wie früher.switch
Blöcke einen anderen Fall-Through als mehrere Labels auf demselben Block verwendeten, und es wurde angenommen, dass die Verwendung von Fall hier bedeutete, dass diese 3% tatsächlich viel höher als normal waren). Die Sprache, wie sie studiert wird, macht das Ungewöhnliche leichter zugänglich als das Übliche.Beachten Sie im Zusammenhang mit diesen beiden letzten Punkten das folgende Zitat aus der aktuellen Ausgabe von K & R:
Aus dem Maul des Pferdes ist ein Durchfall in C problematisch. Es wird als gute Praxis angesehen, Durchfälle immer mit Kommentaren zu dokumentieren. Dies ist eine Anwendung des allgemeinen Prinzips, dass man dokumentieren sollte, wo man etwas Ungewöhnliches tut, da dies die spätere Prüfung des Codes auslöst und / oder Ihren Code so aussehen lässt hat einen Anfängerfehler, wenn er tatsächlich korrekt ist.
Und wenn Sie darüber nachdenken, codieren Sie wie folgt:
Bin das Hinzufügen etwas den Durchfall explizit im Code zu machen, es ist einfach nicht etwas , das erkannt werden kann (oder deren Abwesenheit nachgewiesen werden kann) durch den Compiler.
Die Tatsache, dass on mit Fall-Through in C # explizit angegeben werden muss, bedeutet für Personen, die ohnehin gut in anderen C-Sprachen geschrieben haben, keine Strafe, da sie in ihren Fall-Throughs bereits explizit wären. †
Schließlich ist die Verwendung von
goto
hier bereits eine Norm aus C und anderen solchen Sprachen:In einem solchen Fall, in dem ein Block in den Code aufgenommen werden soll, der für einen anderen Wert als den ausgeführt wird, der einen zum vorhergehenden Block bringt, müssen wir ihn bereits verwenden
goto
. (Natürlich gibt es Mittel und Wege, dies mit unterschiedlichen Bedingungen zu vermeiden, aber das gilt für fast alles, was mit dieser Frage zu tun hat). Als solches baute C # auf der ohnehin normalen Art und Weise auf, mit einer Situation umzugehen, in der wir mehr als einen Codeblock in a treffen möchtenswitch
, und verallgemeinerte ihn nur, um auch den Durchfall abzudecken. Es machte auch beide Fälle bequemer und selbstdokumentierender, da wir in C ein neues Etikett hinzufügen müssen, dascase
aber in C # als Etikett verwenden können. In C # können wir dasbelow_six
Etikett entfernen und verwenden,goto case 5
was klarer ist, was wir tun. (Wir müssten auch hinzufügenbreak
für diedefault
, die ich weggelassen habe, nur um den obigen C-Code eindeutig nicht C # -Code zu machen).Zusammenfassend also:
break
, sondern auch das Erlernen der Sprache durch Personen erleichtert, die mit ähnlichen Sprachen vertraut sind, und das Portieren erleichtert.goto
Ansatz, um denselben Block von verschiedenencase
Bezeichnungen zu treffen , wie er in C verwendet wird. Es verallgemeinert ihn nur auf einige andere Fälle.goto
basierten Ansatz bequemer und klarer als in C, indemcase
Anweisungen als Beschriftungen fungieren.Alles in allem eine ziemlich vernünftige Designentscheidung
* Einige Formen von BASIC würden es einem ermöglichen, solche Dinge zu tun,
GOTO (x AND 7) * 50 + 240
die zwar spröde und daher ein besonders überzeugender Fall für ein Verbot sindgoto
, aber dazu dienen, ein höhersprachiges Äquivalent der Art und Weise zu zeigen, auf der Code auf niedrigerer Ebene einen Sprung basierend machen kann Arithmetik auf einen Wert, der viel vernünftiger ist, wenn er das Ergebnis einer Kompilierung ist, als etwas, das manuell gepflegt werden muss. Insbesondere Implementierungen von Duff's Device eignen sich gut für den entsprechenden Maschinencode oder IL, da jeder Anweisungsblock häufig dieselbe Länge hat, ohne dassnop
Füllstoffe hinzugefügt werden müssen.† Duffs Gerät wird hier als vernünftige Ausnahme wieder angezeigt. Die Tatsache, dass es mit diesem und ähnlichen Mustern zu einer Wiederholung von Operationen kommt, dient dazu, die Verwendung von Durchfall auch ohne einen expliziten Kommentar zu diesem Effekt relativ deutlich zu machen.
quelle
Sie können das Falletikett http://www.blackwasp.co.uk/CSharpGoto.aspx aufrufen
quelle
Sie haben dieses Verhalten absichtlich ausgelassen, um zu vermeiden, dass es nicht vom Willen verwendet wurde, sondern Probleme verursachte.
Es kann nur verwendet werden, wenn der Fallteil keine Aussage enthält, wie z.
quelle
Sie haben das Verhalten der switch-Anweisung (von C / Java / C ++) für c # geändert. Ich denke, die Begründung war, dass die Leute den Durchfall vergessen haben und Fehler verursacht wurden. Ein Buch, das ich gelesen habe, soll goto zum Simulieren verwenden, aber das klingt für mich nicht nach einer guten Lösung.
quelle
goto case
ist es gedacht. Sein Vorteil gegenüber Fallthrough ist, dass es explizit ist. Dass einige Leute hiergoto case
nur Einwände erheben, zeigt, dass sie gegen "goto" indoktriniert wurden, ohne das Problem zu verstehen, und nicht in der Lage sind, selbstständig zu denken. Als Dijkstra "GOTO als schädlich" schrieb, sprach er Sprachen an, die keine andere Möglichkeit hatten, den Kontrollfluss zu ändern.goto
kann, Code zu optimieren?Sie können Fall Through wie C ++ mit dem Schlüsselwort goto erreichen.
EX:
quelle
- C # switch () Dokumentation
quelle
Nach jeder case-Anweisung ist eine break- oder goto- Anweisung erforderlich , auch wenn es sich um einen Standardfall handelt.
quelle
Nur eine kurze Anmerkung, um hinzuzufügen, dass der Compiler für Xamarin dies tatsächlich falsch verstanden hat und ein Durchfallen ermöglicht. Es wurde angeblich behoben, aber nicht veröffentlicht. Entdeckte dies in einem Code, der tatsächlich durchfiel und der Compiler beschwerte sich nicht.
quelle
Schalter (C # Referenz) sagt
Sie müssen also auch ein
break;
zu Ihremdefault
Abschnitt hinzufügen , da sonst immer noch ein Compilerfehler auftritt.quelle
C # unterstützt kein Durchfallen mit switch / case-Anweisungen. Ich weiß nicht warum, aber es gibt wirklich keine Unterstützung dafür. Verknüpfung
quelle
goto case
. Dies war eine absichtliche, kluge Designentscheidung der C # -Designer.Sie haben vergessen, die "Pause" hinzuzufügen. Anweisung in Fall 3. In Fall 2 haben Sie sie in den if-Block geschrieben. Versuchen Sie deshalb Folgendes:
quelle