Frage

Switch-Anweisung ist fallthrough einer meiner persönlichen Hauptgründe für switch vs. if/else if Konstrukte zu lieben. Ein Beispiel dafür ist, um sich hier:

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 intelligenten Menschen kriechend, weil die string[]s sollten außerhalb der Funktion deklariert werden: gut, sie sind, ist dies nur ein Beispiel

.

Der Compiler schlägt mit dem folgenden Fehler:

Control cannot fall through from one case label ('case 3:') to another
Control cannot fall through from one case label ('case 2:') to another

Warum? Und gibt es eine Möglichkeit, diese Art von Verhalten zu bekommen, ohne drei ifs mit?

War es hilfreich?

Lösung

(Kopieren / Einfügen einer Antwort, die ich an anderer Stelle zur Verfügung gestellt)

Fallen durch switch-cases können, indem er keinen Code in einem case (siehe case 0) oder mit dem speziellen goto case (siehe case 1) oder goto default (siehe case 2) Formen erreicht werden:

switch (/*...*/) {
    case 0: // shares the exact same code as case 1
    case 1:
        // do something
        goto case 2;
    case 2:
        // do something else
        goto default;
    default:
        // do something entirely different
        break;
}

Andere Tipps

Die „Warum“ ist ein versehentlicher Durchfall zu vermeiden, für die ich dankbar bin. Dies ist eine nicht seltene Quelle für Fehler in C und Java.

Die Abhilfe ist goto zu verwenden, z.

switch (number.ToString().Length)
{
    case 3:
        ans += string.Format("{0} hundred and ", numbers[number / 100]);
        goto case 2;
    case 2:
    // Etc
}

Der allgemeine Aufbau des Schalters / Fall ist ein wenig aus meiner Sicht unglücklich. Es stecken zu nahe an C - es einige nützliche Änderungen sind, die in Bezug auf die Scoping usw. Diskutierbar ein intelligenter Schalter hergestellt werden könnte, das Muster usw. nützlich wäre tun könnte passender, aber das ist wirklich von Schalter auf „überprüfen eine Reihe von Bedingungen“ zu ändern -., an welcher Stelle ein anderer Name vielleicht nennen würde

Schalten fallthrough ist historisch eine der wichtigsten Fehlerquellen in der modernen Software. Die Sprache Designer entschieden, es zwingend notwendig zu machen am Ende des Falles zu springen, es sei denn, Sie zum nächsten Fall werden säumige direkt ohne zu verarbeiten.

switch(value)
{
    case 1:// this is still legal
    case 2:
}

zu den Antworten hier hinzuzufügen, ich denke, es lohnt sich, die entgegengesetzte Frage in Verbindung mit dieser Berücksichtigung, nämlich. warum C erlaubt Durchfall in erster Linie?

Jede Programmiersprache natürlich dient zwei Zielen:

  1. Geben Sie Anweisungen an den Computer.
  2. Lassen Sie eine Aufzeichnung der Absichten des Programmierers.

Die Schaffung einer beliebigen Programmiersprache ist somit ein Gleichgewicht zwischen dem, wie diese beiden Ziele am besten dienen. Auf der einen Seite, desto leichter ist es in Computerbefehle drehen (ob das ist Maschinencode, Bytecode wie IL, oder die Anweisungen bei der Ausführung interpretiert) dann mehr in der Lage, dass Prozess der Zusammenstellung oder Interpretation wird effizient, zuverlässig zu sein und kompakt in ausgegeben. Genommen seine extremen, um dieses Ziel Ergebnisse in unserer nur schriftlich in der Montage, IL, oder sogar roh op-Codes, da die einfachste Zusammenstellung ist, wo es keine Kompilation überhaupt ist.

Im Gegensatz dazu, je mehr die Sprache drückt die Absicht des Programmierers, anstatt die Mittel zu diesem Zweck ergreifen, desto verständlich das Programm sowohl beim Schreiben und bei der Wartung.

Jetzt switch immer durch sie in die entsprechende Kette von if-else Blöcken oder ähnlicher Umwandlung kompiliert worden sein könnte, aber es wurde als erlaubt compilation in ein bestimmten gemeinsamen Montagemuster entwickelt, bei denen man einen Wert annimmt, berechnet eine dazu versetzt (wäre indem Sie eine Tabelle mit einer perfekten Hash-Wert des Wertes oder durch tatsächliche Arithmetik auf den Wert * indiziert nach oben). Es lohnt mich an dieser Stelle darauf hingewiesen, dass heute, # compilation C wird manchmal switch in die äquivalente if-else drehen, und manchmal einen Hash-basierter Sprung Ansatz verwenden (und ebenfalls mit C, C ++ und anderen Sprachen mit ähnlicher Syntax).

In diesem Fall gibt es zwei gute Gründe dafür, dass Durchfall:

  1. Es passiert einfach natürlich trotzdem: wenn Sie eine Sprungtabelle in eine Reihe von Anweisungen bauen, und einer der früherer Chargen von Anweisungen nicht irgendeine Art von Sprung oder Rückkehr enthält, dann wird die Ausführung nur natürlich Fortschritte in die nächste Partie. Zulassen von Durchfall war, was wäre „einfach passieren“, wenn Sie die switch betrieben C in drehte Maschinencode Sprung-Tabellen verwenden.

  2. Coders, die in der Montage schrieben bereits auf die äquivalent verwendet: wenn eine Sprungtabelle mit der Hand in der Montage zu schreiben, müßten sie prüfen, ob ein bestimmte Codeblock mit einer Rückkehr enden würde, einen Sprung außerhalb der Tabelle oder weiter auf nur auf den nächsten Block. Als solcher hat der Codierer eine explizite break hinzufügen, wenn notwendig, „natürlich“ für den Codierer auch war.

Zu der Zeit war es also ein vernünftiger Versuch, die beiden Ziele einer Computersprache zu balancieren, da sie sowohl auf dem erzeugten Maschinencode beziehen, und die Ausdruckskraft des Quellcodes.

Vier Jahrzehnte später aber die Dinge sind nicht ganz das gleiche, für ein paar Gründe:

  1. Coders in C können heute wenig oder gar keine Montage Erfahrung. Coders in vielen anderen C-Stil Sprachen ist auch weniger wahrscheinlich (vor allem Javascript!). Jeder Begriff „was die Menschen verwendet werden, um von der Montage“ ist nicht mehr relevant.
  2. Verbesserungen in Optimierungen bedeuten, dass die Wahrscheinlichkeit von switch entweder in if-else gedreht werden, weil sie den Ansatz erachtet wurde, wahrscheinlich am effizientesten sein, oder aber in einer besonders esoterische Variante des Sprungtabellen Ansatz gedreht sind höher. Die Zuordnung zwischen dem Hoch- und Nieder Ebene Ansätzen ist nicht so stark, wie es einmal war.
  3. Die Erfahrung hat gezeigt, dass Durchfall neigt die Minderheit Fall sein eher als die Norm (eine Studie von Sun Compiler Ergebnis 3% switch Blöcke verwendete einen Durchfall andere als mehr Etiketten auf dem gleichen Block, und es wurde angenommen, hier gemeint, dass der anwendungs~~POS=TRUNC, dass dieses 3% in der Tat war viel höher als normal). So die languAlter als studierte die ungewöhnliche leichter gesorgt zu als die gemeinsame machen.
  4. Die Erfahrung hat gezeigt, dass Durchfall die Quelle der Probleme sowohl in den Fällen neigt, wo es versehentlich geschehen ist, und auch in Fällen, in denen richtigen Durchfall von jemandem verpaßt wird, um den Code zu halten. Letzteres ist ein subtiles zusätzlich zu den damit verbundenen Fehlern mit Durchfall, denn selbst wenn Ihr Code vollkommen fehlerfrei ist, Ihr Durchfall noch Probleme verursachen kann.

zu diesen letzten beiden Punkten Verwandte, betrachten Sie das folgende Zitat aus der aktuellen Ausgabe von K & R:

  

durch von einem zum anderen Fall Fallen ist nicht robust, Zerfall zu sein anfällig, wenn das Programm geändert wird. Mit Ausnahme von mehreren Etiketten für eine einzige Berechnung, fallen Durch sollten sparsam verwendet werden, und kommentiert.

     

Als eine Frage der guten Form, legt eine Pause nach dem letzten Fall (der Standard hier), obwohl es logisch nicht notwendig ist. Einen Tag, wenn ein anderer Fall wird am Ende hinzugefügt, dieses Stück defensiver Programmierung werden Ihnen speichern.

Also, aus dem Maul des Pferdes, Durchfall in C ist problematisch. Es ist gute Praxis betrachtet immer Falldurch mit Kommentaren zu dokumentieren, die eine Anwendung des allgemeinen Prinzips ist, dass man dokumentieren sollte, wo tut man etwas ungewöhnlich, denn das ist, was die spätere Untersuchung des Codes auslöst und / oder Code aussehen lassen, wie es ein Anfänger Bug drin hat, wenn es in der Tat korrekt ist.

Und wenn man darüber nachdenkt, Code wie folgt:

switch(x)
{
  case 1:
   foo();
   /* FALLTHRU */
  case 2:
    bar();
    break;
}

Ist etwas Zugabe des Durchfall explizit im Code zu machen, es ist einfach nicht etwas, das erkannt werden kann (oder deren Abwesenheit nachgewiesen werden kann) durch den Compiler.

Als solches ist die Tatsache, dass auf hat mit Durchfall in C explizit seine # keine Strafe für Menschen hinzufügt, die gut geschrieben in anderen C-Stil Sprachen sowieso, da sie bereits in ihrem Herbstdurch explizit sein würden . †

Schließlich hier die Verwendung von goto ist bereits eine Norm von C und anderen Sprachen:

switch(x)
{
  case 0:
  case 1:
  case 2:
    foo();
    goto below_six;
  case 3:
    bar();
    goto below_six;
  case 4:
    baz();
    /* FALLTHRU */
  case 5:
  below_six:
    qux();
    break;
  default:
    quux();
}

In dieser Art von Fall, wo wir wollen, ein Block, in dem für einen anderen Wert als nur das, was man ausgeführten Code aufgenommen werden zu dem vorhergehenden Block bringt, dann sind wir mit bereits goto verwenden. (Natürlich gibt es Mittel und Wege zur Vermeidung dieses mit verschiedenen conditionals aber das stimmt so ziemlich alles auf diese Frage in Bezug). Als solche C # auf die bereits normale Art und Weise gebaut mit einer Situation umgehen, wo wir mehr als einen Block von Code in einem switch treffen wollen, und es einfach verallgemeinerten als auch zur Deckung des Durchfall. Es machte auch in beiden Fällen bequeme und selbsterklärend, da wir ein neues Label in C hinzufügen, aber die case als Label in C # verwenden können. In C # können wir loswerden der below_six Etikett bekommen und goto case 5 verwenden, die klarer als auf das, was wir tun. (Wir würden auch break für die default hinzufügen, die ich gerade verlassen, um den oben C-Code eindeutig nicht C # -Code zu machen).

Zusammengefasst also:

  1. C # sich nicht mehr auf nicht optimierten Compiler Ausgabe vor so direkt wie C-Code 40 Jahre hat (noch tut C in diesen Tagen), die eine der Inspirationen von Durchfall irrelevant macht.
  2. C # bleibt kompatibel mit C in nicht nur implizit break mit zum leichteren Erlernen der Sprache durch denjenigen, die mit ähnlichen Sprachen und einfacher zu portieren.
  3. C # entfernt eine mögliche Quelle für Fehler oder falsch verstandenen Code, der als Ursache für Probleme in den letzten vier Jahrzehnten gut dokumentiert.
  4. C # macht bestehende Best-Practice mit C (Dokument fällt durch) durchsetzbar durch den Compiler.
  5. C # macht den ungewöhnliche Fall den mit explizitem Code, den üblichen Fall des mit dem Code einer nur automatisch schreibt.
  6. C # verwendet die gleiche goto basierten Ansatz für den gleichen Block aus verschiedenen case Etiketten treffen wie in C verwendet Es verallgemeinert es nur zu einigen anderen Fällen.
  7. macht C #, dass goto basierten Ansatz bequeme und klarer, als es in C, indem case Aussagen als Etikett zu handeln.

Alles in allem ein ziemlich vernünftig Design-Entscheidung


* Einige Formen von BASIC einem erlauben würde, die gerne von GOTO (x AND 7) * 50 + 240 zu tun, die während spröde und daher ein besonders überzeugendes Argument goto für ein Verbot, kann eine höhere sprachige Äquivalent der Art und Weise, dass niedrigerer Ebene Code zu erhalten ausgeschenkt wird macht einen Sprung auf Basis von Arithmetik auf einen Wert, der viel vernünftiger ist, wenn es das Ergebnis der Zusammenstellung ist nicht etwas, das manuell vorgehalten werden muss. Implementationen von Duff Einrichtung insbesondere eignet sich gut für den äquivalenten Maschinencode oder IL, da jeder Block von Befehlen oft die gleiche Länge sein wird ohne Zusatz von Füllstoffen nop zu benötigen.

† Duff Gerät kommt hier wieder auf, als eine vernünftige Ausnahme. Die Tatsache, dass mit diesen und ähnlichen Mustern gibt es eine Wiederholung von Operationen dient die Verwendung von Durchfall relativ klar, auch ohne expliziten Kommentar zu diesem Zweck zu machen.

Sie können 'goto case label' http://www.blackwasp.co.uk/CSharpGoto.aspx

  

Die goto-Anweisung ist ein einfacher Befehl, der die Steuerung des Programms auf eine andere Aussage vorbehaltlos überträgt. Der Befehl wird oft kritisiert, mit einigen Entwicklern ihre Entfernung aus allen High-Level-Programmiersprachen befürworten, weil es an Spaghetti-Code . Dies geschieht, wenn es so viele GOTO-Anweisungen oder ähnliche Sprunganweisungen sind, dass der Code schwierig wird, zu lesen und zu pflegen. Allerdings gibt es Programmierer, die darauf hinweisen, dass die goto-Anweisung, wenn sie sorgfältig verwendet, eine elegante Lösung für einige Probleme bietet ...

Sie ließen dieses Verhalten von Entwurf zu vermeiden, wenn es nicht durch den Willen verwendet wurde, aber verursachte Probleme.

Es kann nur verwendet werden, wenn es keine Aussage im Fall Teil ist, wie:

switch (whatever)
{
    case 1:
    case 2:
    case 3: boo; break;
}

Sie änderten die switch-Anweisung (von C / Java / C ++) Verhalten für c #. Ich denke, die Begründung war, dass die Menschen über den Fall vergessen haben, durch und Fehler verursacht wurden. Ein Buch, das ich die Lese ginge zu verwenden, um zu simulieren, aber das klingt nicht wie eine gute Lösung für mich.

  

Eine Sprunganweisung wie eine Pause   erforderlich, nachdem jeweils Block,   einschließlich des letzten Blocks, ob es   eine case-Anweisung oder eine Standard   Aussage. Mit einer Ausnahme (im Gegensatz zu   die C ++ switch-Anweisung), tut C # nicht   unterstützen einen impliziten Fall durch von   ein Fall Etikett zum anderem. Der Eine   Ausnahme ist, wenn ein Fall Anweisung hat   kein Code.

- C # Schalter () Dokumentation

Nach jeder case-Anweisung erfordert Pause oder goto Aussage, auch wenn es sich um einen Standardfall.

Sie erreichen können, wie c fallen durch ++ durch die goto Schlüsselwort.

EX:

switch(num)
{
   case 1:
      goto case 3;
   case 2:
      goto case 3;
   case 3:
      //do something
      break;
   case 4:
      //do something else
      break;
   case default:
      break;
}

Nur eine kurze Notiz hinzuzufügen, dass der Compiler für Xamarin dieses Unrecht tatsächlich bekam und es erlaubt fallthrough. Es wird angeblich behoben worden, aber noch nicht freigegeben worden. Entdeckt dies in einigen Code, der tatsächlich fiel durch und der Compiler beschwerte sich nicht.

C # unterstützt nicht mit Switch / Case-Anweisungen durchfallen. Nicht sicher, warum, aber es gibt wirklich keine Unterstützung für sie. Linkage

Schalter (C # -Referenz) sagt

  

C # erfordert das Ende der Schaltabschnitte, einschließlich der letzten,

So können Sie auch einen break; zu Ihrem default Abschnitt hinzufügen müssen, sonst wird es noch ein Compiler-Fehler sein.

Sie haben vergessen, die hinzuzufügen „brechen“; Anweisung in Fall 3. Im Fall 2 Sie schreibt es in den Block, wenn. Deshalb versuchen Sie dies:

case 3:            
{
    ans += string.Format("{0} hundred and ", numbers[number / 100]);
    break;
}


case 2:            
{
    int t = (number / 10) % 10;            
    if (t == 1)            
    {                
        ans += teens[number % 10];                
    }            
    else if (t > 1)                
    {
        ans += string.Format("{0}-", tens[t]);        
    }
    break;
}

case 1:            
{
    int o = number % 10;            
    ans += numbers[o];            
    break;        
}

default:            
{
    throw new ArgumentException("number");
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top