Switch-Anweisung fällt durch ... sollte es erlaubt sein? [geschlossen]

120

So lange ich mich erinnern kann, habe ich es vermieden, die switch-Anweisung fall-through zu verwenden. Eigentlich kann ich mich nicht erinnern, dass es jemals in mein Bewusstsein eingedrungen ist, um Dinge zu tun, da mir schon früh in den Kopf gebohrt wurde, dass es nichts weiter als ein Fehler in der switch-Anweisung war. Heute bin ich jedoch auf Code gestoßen, der ihn von Natur aus verwendet, und habe mich sofort gefragt, was alle in der Community über das Durchfallen von switch-Anweisungen denken.

Ist es etwas, das eine Programmiersprache explizit nicht zulassen sollte (wie C #, obwohl es eine Problemumgehung bietet), oder ist es eine Funktion einer Sprache, die leistungsfähig genug ist, um sie in den Händen des Programmierers zu belassen?

Bearbeiten: Ich war nicht spezifisch genug für das, was ich mit Durchfall meinte. Ich benutze diesen Typ oft:

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

Ich mache mir jedoch Sorgen um so etwas.

   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;
   }

Auf diese Weise wird immer dann, wenn der Fall 0, 1 ist, alles in der switch-Anweisung ausgeführt. Ich habe dies beabsichtigt und weiß nur nicht, ob ich damit einverstanden bin, dass switch-Anweisungen auf diese Weise verwendet werden. Ich denke, das erste Codebeispiel ist sehr nützlich und sicher. Der zweite scheint irgendwie gefährlich zu sein.

Fostah
quelle
51
Nicht einverstanden über nicht konstruktiv. 27 positive Stimmen zu dieser Frage besagen, dass es auch andere gibt, die so denken.
Wes Modes
2
"nicht konstruktiv" ist ein alter und etwas vager enger Grund. Heutzutage können und sollten solche Fragen zum Abschluss als rein meinungsbasiert gekennzeichnet werden. SO ist für beantwortbare Fragen zu Code oder Design, nicht 'was ist die Meinung der "Community" [was auch immer das ist] zu meiner Meinung zu Feature X'. Programmers.SE wäre besser geeignet, aber selbst dann ist der Wert einer so breiten und meinungsorientierten Frage immer noch höchst zweifelhaft.
underscore_d
1
Es gibt sehr klare Anwendungsfälle, in denen Fallthrough verwendet wird, daher nicht sicher, warum es meinungsbasiert wäre. Ich bin damit einverstanden, dass diese Frage etwas unklar ist, aber sie hätte nicht als nicht konstruktiv abgeschlossen werden dürfen. Vielleicht nur bearbeitet, um einen klareren Fall von: "Wann wird Fallthrough verwendet?" Welches hat eine klare Antwort. Gute C ++ - Texte gehen darüber hinweg, es ist verwirrend für neue Codierer (oder noch erfahrenere Codierer aus anderen Sprachen), daher scheint dies eine gute Frage für SO zu sein. In der Tat scheint es eine prototypisch gute SO-Frage zu sein. Auch wenn die Art und Weise, wie es gefragt wurde, nicht perfekt war.
Eric

Antworten:

89

Es kann davon abhängen, was Sie als Durchfall betrachten. Ich bin mit so etwas einverstanden:

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;
}

Aber wenn Sie eine Fallbezeichnung haben, gefolgt von Code, der zu einer anderen Fallbezeichnung durchfällt, würde ich das so ziemlich immer für böse halten. Vielleicht wäre es besser, den gemeinsamen Code in eine Funktion zu verschieben und von beiden Stellen aus aufzurufen.

Und bitte beachten Sie, dass ich die C ++ - FAQ- Definition von "böse" verwende.

Fred Larson
quelle
Ich stimme Ihnen zu: Ich betrachte Ihr Beispiel nicht als Durchfall, und wenn ich in einem Fall mehrere Codeblöcke über Durchfall ausführen möchte, gibt es wahrscheinlich einen besseren Weg, dies zu tun.
Dave DuPlantis
10
+1 einfach für diese Definition von "böse". Ich werde das ausleihen, danke ;-)
Joachim Sauer
Ich verstehe Ihren Standpunkt und Sie haben Recht, aber Ihr Beispiel ist wirklich schlecht. Niemand sollte solche Testziffern testen. Scnr, Ihre Antwort ist sowieso richtig.
Tim Büthe
In diesem Fall ist dies genau das, was C # nicht zulässt. Wahrscheinlich aus einem Grund :-)
Joey
Leider scheint die Definition des Bösen offline gegangen zu sein
hoffentlich
57

Es ist ein zweischneidiges Schwert. Es ist manchmal sehr nützlich, aber oft gefährlich.

Wann ist es gut Wenn Sie möchten, dass 10 Fälle alle gleich verarbeitet werden ...

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;
}

Die einzige Regel, die ich mag, ist, dass Sie, wenn Sie jemals etwas Besonderes tun, bei dem Sie die Pause ausschließen, einen klaren Kommentar / * FALLTHROUGH * / benötigen, um anzuzeigen, dass dies Ihre Absicht war.

John M.
quelle
3
+1! Ich stimme zu, dass es gelegentlich die richtige Antwort ist, und ich stimme DOPPELT zu, dass es kommentiert werden sollte, wenn es beabsichtigt ist. (In einer Codeüberprüfung würde ich den anderen Entwickler dazu bringen, einen nicht leeren Fall-by-Entwurf zu kommentieren. Nicht, dass es passiert; wir sind ein C # -Laden, in dem dies nicht unterstützt wird :)
John Rudy
4
Ich selbst verwende gegebenenfalls einen / * FALL THROUGH * / -Kommentar in meinem Code. =]
Strager
2
Wenn Sie PC-Lint, müssen Sie / * - fallthrough * / einfügen, sonst beschwert es sich.
Steve Melnikoff
1
Wenn Sie jedoch klarstellen möchten, dass das Verhalten beabsichtigt ist, können if(condition > 1 && condition <10) {condition = 1}; switch(...Sie auch Fälle speichern (für ein klares Aussehen), und jeder kann sehen, dass Sie wirklich wollten, dass diese Fälle dieselbe Aktion auslösen.
Mark
1
Dies ist immer noch eine schreckliche Codierung. Der erste Fall sollte eine Funktion aufrufen, der zweite Fall sollte dieselbe Funktion aufrufen und dann eine zweite Funktion aufrufen.
BlueRaja - Danny Pflughoeft
21

Hast du von Duffs Gerät gehört ? Dies ist ein großartiges Beispiel für die Verwendung von Switch Fallthrough.

Es ist eine Funktion, die verwendet und missbraucht werden kann, wie fast alle Sprachfunktionen.

tzot
quelle
Lerne jeden Tag etwas Neues - danke! Ich fühle für die armen Entwickler, die solche Verrenkungen durchmachen.
Justin R.
Wow, das ist ziemlich viel, um dein Gehirn herumzuwickeln.
Fostah
6
Beachten Sie Duffs zitierten Kommentar in diesem Artikel: "Dieser Code bildet eine Art Argument in dieser Debatte, aber ich bin nicht sicher, ob es dafür oder dagegen ist."
John Carter
Duffs Gerät ist eindeutig böse
Marjeta
21

Fall-through ist wirklich eine praktische Sache, je nachdem, was Sie tun. Betrachten Sie diese übersichtliche und verständliche Art, Optionen zu arrangieren:

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

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

Stellen Sie sich vor, Sie tun dies mit if / else. Es wäre ein Chaos.

Lucas Oman
quelle
1
Ich finde diese Art von Durchfall sehr hilfreich
Mitchel Sellers
Wenn 'etwas tun' mehr als nur ein bisschen ist als der Schalter, wird es eher das Chaos sein als wenn / sonst. Mit if / else ist überall klar, welche Variable getestet wird. Selbst dieses kleine Beispiel würde nicht so schlecht aussehen, wenn / sonst in meinen Augen
Grenix
10

Es kann einige Male sehr nützlich sein, aber im Allgemeinen ist kein Durchfall das gewünschte Verhalten. Durchfallen sollte erlaubt sein, aber nicht implizit.

Ein Beispiel zum Aktualisieren alter Versionen einiger Daten:

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
quelle
6

Ich würde eine andere Syntax für Fallbacks in Schaltern lieben, so etwas wie äh.

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

Hinweis: Dies wäre bereits mit Aufzählungen möglich, wenn Sie alle Fälle in Ihrer Aufzählung mit Flags deklarieren, oder? Es klingt auch nicht so schlecht; Die Fälle könnten (sollten?) bereits Teil Ihrer Aufzählung sein.

Vielleicht wäre dies ein guter Fall (kein Wortspiel beabsichtigt) für eine fließende Schnittstelle mit Erweiterungsmethoden? So etwas wie, ähm ...

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

Obwohl das wahrscheinlich noch weniger lesbar ist: P.

Erik van Brakel
quelle
1
Ich mag den ersten Vorschlag sehr. Das liest sich sehr gut und spart ein paar Codezeilen. Die Sekunde hat eine Minute gedauert, um mein Gehirn herumzuwickeln, macht aber Sinn, aber es sieht so aus, als wäre es ein Chaos.
Fostah
1
Ja, das habe ich auch gedacht, es war nur ein zufälliger Gedankenfurz, den ich niedergelegt habe, um einen anderen Weg zu finden, um mit vorhandenen Sprachkonstrukten zu wechseln.
Erik van Brakel
Ich bin diese Frage durchgegangen und das gefällt mir wirklich! Hatte eine Idee, wie man die Lesbarkeit verbessern kann, aber SO lässt mich keine mehrzeiligen Kommentare posten :( Jemand sollte dies genauso wie POC implementieren, es macht Spaß, es zu implementieren und zu verwenden (:
Victor Elias
5

Wie bei allem: Bei sorgfältiger Anwendung kann es ein elegantes Werkzeug sein.

Ich denke jedoch, dass die Nachteile mehr als rechtfertigen, es nicht zu verwenden und es schließlich nicht mehr zuzulassen (C #). Zu den Problemen gehören:

  • Es ist leicht, eine Pause zu "vergessen"
  • Für Code-Betreuer ist es nicht immer offensichtlich, dass eine ausgelassene Unterbrechung beabsichtigt war

Gute Verwendung eines Schalters / Gehäuses:

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

Baaaaad Verwendung eines Switch / Case Fall-Through:

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

Dies kann meiner Meinung nach mit if / else-Konstrukten ohne Verlust umgeschrieben werden.

Mein letztes Wort: Halten Sie sich wie im schlechten Beispiel von Fall-Through-Fallbezeichnungen fern , es sei denn, Sie pflegen Legacy-Code, in dem dieser Stil verwendet und gut verstanden wird.

steffenj
quelle
3
Sie können die meisten Sprachkonstrukte mit if () und goto ... neu schreiben. Dies rechtfertigt jedoch nicht das Aufgeben eines expliziten Konstrukts.
Shog9
2
Ich weiß, aber switch / case-Konstrukte, die explizit den "Fall 1: einige Code-Fall 2: einige weitere Code-Fall 3: endgültige Code-Unterbrechung" verwenden; kann und sollte mit if / else if (meiner Meinung nach) umgeschrieben werden.
steffenj
1
Beachten Sie, dass der Wechsel um ein Vielfaches schneller sein kann als eine Liste von if-elses. Switch verwendet eine Sprungtabelle, oder wenn dies nicht möglich ist, werden seine bedingten Sprünge in einem Entscheidungsbaum anstelle einer linearen Liste von bedingten Sprüngen strukturiert. Gut strukturierte verschachtelte If-else-Anweisungen sind bestenfalls gleich schnell.
Jochem Kuijpers
4

Es ist mächtig und gefährlich. Das größte Problem beim Durchfallen ist, dass es nicht explizit ist. Wenn Sie beispielsweise auf häufig bearbeiteten Code stoßen, der einen Schalter mit Fall-Throughs hat, woher wissen Sie, dass dies beabsichtigt und kein Fehler ist?

Überall, wo ich es benutze, stelle ich sicher, dass es richtig kommentiert ist:

switch($var) {
    case 'first':
        // Fall-through
    case 'second':
        i++;
        break;
 }
Dan Hulton
quelle
2
Die Absicht ist offensichtlich, wenn es keinen Code für den Fall 'first' gibt. Wenn Sie dort Code eingeben und den Code für den zweiten Fall ausführen möchten, ist der Kommentar wichtig. Aber ich würde dieses Szenario trotzdem vermeiden.
Joel Coehoorn
1
@ Joel - ich stimme vollkommen zu. In meinem Geschäft haben wir in solchen Fällen Leute dazu gebracht, Kommentare abzugeben, und ich denke, dies verringert die Lesbarkeit. Wenn dort jedoch Code vorhanden ist, geben Sie den Fallthrough-Kommentar ein.
Fred Larson
Was meinen wir mit "nicht explizit"? Deshalb brechen wir; Andere Sprachen tun dies. Es ist nur ein Problem für Menschen, die ihre Sprachen nicht in der richtigen Reihenfolge gelernt haben. C # fordert es für den Standardfall ffs an, ohne Grund zu imho.
McKenzm
3

Die Verwendung von Fall-Through wie in Ihrem ersten Beispiel ist eindeutig in Ordnung, und ich würde es nicht als echten Fall-Through betrachten.

Das zweite Beispiel ist gefährlich und (wenn nicht ausführlich kommentiert) nicht offensichtlich. Ich lehre meine Schüler nicht solche Konstrukte zu verwenden , es sei denn , sie halten es für die Mühe wert, einen Kommentarblock dafür zu verwenden, der beschreibt, dass dies ein absichtlicher Durchfall ist und warum diese Lösung besser ist als die Alternativen. Dies entmutigt die schlampige Verwendung, macht es aber dennoch in den Fällen zulässig, in denen es zu einem Vorteil verwendet wird.

Dies entspricht mehr oder weniger dem, was wir in Weltraumprojekten getan haben, als jemand gegen den Kodierungsstandard verstoßen wollte: Er musste eine Ausnahmegenehmigung beantragen (und ich wurde aufgefordert, über das Urteil zu beraten).

Wouter van Ooijen
quelle
2

Ich mag es nicht, wenn meine switchAussagen durchfallen - es ist viel zu fehleranfällig und schwer zu lesen. Die einzige Ausnahme ist, wenn mehrere caseAnweisungen genau ausgeführt werden dasselbe tun.

Wenn es einen gemeinsamen Code gibt, den mehrere Zweige einer switch-Anweisung verwenden möchten, extrahiere ich diesen in eine separate gemeinsame Funktion, die in jedem Zweig aufgerufen werden kann.

Matt Dillard
quelle
1

In einigen Fällen ist die Verwendung von Fall-throughs für den Programmierer ein Akt der Faulheit - sie könnten eine Reihe von || verwenden Anweisungen zum Beispiel, verwenden jedoch stattdessen eine Reihe von "catch-all" -Schalterfällen.

Davon abgesehen habe ich festgestellt, dass sie besonders hilfreich sind, wenn ich das irgendwann weiß werde ich die Optionen sowieso (zum Beispiel in einem Menü - Antwort) benötigen, aber noch nicht alle Entscheidungen umgesetzt. Wenn Sie sowohl für 'a' als auch für 'A' einen Fall-Through durchführen, finde ich es wesentlich sauberer, den Switch-Fall-Through als eine zusammengesetzte if-Anweisung zu verwenden.

Es ist wahrscheinlich eine Frage des Stils und der Denkweise der Programmierer, aber ich mag es im Allgemeinen nicht, Komponenten einer Sprache im Namen der "Sicherheit" zu entfernen - weshalb ich mich mehr für C und seine Varianten / Nachkommen interessiere als beispielsweise Java. Ich mag es, mit Zeigern und dergleichen herumzuspielen, auch wenn ich keinen "Grund" dazu habe.

Labyrinth
quelle
Genau genommen. Die switch-Anweisungen springen über einen festgelegten Wert zu einer Reihe bestimmter Codepositionen. Während der Compiler solche Dinge wahrscheinlich abfangen würde, gibt es einen Geschwindigkeits- und Korrektheitswert für eine switch-Anweisung über eine Reihe von oder Anweisungen. Wenn p 0 ist oder p 1 ist oder p 2 ist, musste ich tatsächlich bewerten, ob p 0 und p 1 war, anstatt zur Stelle p = 2 im Code zu springen. Was zufällig mit p0 und p1 identisch war. Aber ich musste sie nie überprüfen.
Tatarize
1

Fall-through sollte nur verwendet werden, wenn es als Sprungtabelle in einen Codeblock verwendet wird. Wenn es einen Teil des Codes gibt, der vor weiteren Fällen eine bedingungslose Unterbrechung aufweist, sollten alle Fallgruppen auf diese Weise enden.

Alles andere ist "böse".

BCS
quelle