Was ist der Vorteil / Nachteil der Verwendung einer switch
Anweisung gegenüber einer if/else
in C # ? Ich kann mir nicht vorstellen, dass es einen so großen Unterschied gibt, außer vielleicht dem Aussehen Ihres Codes.
Gibt es einen Grund, warum die resultierende IL oder die damit verbundene Laufzeitleistung radikal anders wäre?
Verwandte: Was ist schneller, String einschalten oder sonst Typ?
c#
.net
switch-statement
Matthew M. Osborn
quelle
quelle
Antworten:
Die SWITCH-Anweisung erzeugt nur dieselbe Assembly wie IFs im Debug- oder Kompatibilitätsmodus. In der Version wird es in die Sprungtabelle kompiliert (über die MSIL-Anweisung 'switch') - das ist O (1).
C # ermöglicht (im Gegensatz zu vielen anderen Sprachen) auch das Einschalten von String-Konstanten - und das funktioniert etwas anders. Es ist offensichtlich nicht praktikabel, Sprungtabellen für Zeichenfolgen beliebiger Länge zu erstellen, daher wird ein solcher Schalter meistens zu einem Stapel von IFs kompiliert.
Wenn die Anzahl der Bedingungen jedoch groß genug ist, um Overheads abzudecken, erstellt der C # -Compiler ein HashTable-Objekt, füllt es mit Zeichenfolgenkonstanten und führt eine Suche in dieser Tabelle durch, gefolgt von einem Sprung. Die Suche nach Hashtabellen ist nicht unbedingt O (1) und verursacht spürbare konstante Kosten. Wenn jedoch die Anzahl der Fallbezeichnungen groß ist, ist sie erheblich schneller als der Vergleich mit jeder Zeichenfolgenkonstante in IFs.
Um es zusammenzufassen: Wenn die Anzahl der Bedingungen mehr als 5 beträgt, ziehen Sie SWITCH gegenüber IF vor, andernfalls verwenden Sie das, was besser aussieht.
quelle
Im Allgemeinen (unter Berücksichtigung aller Sprachen und aller Compiler) kann eine switch-Anweisung manchmal effizienter sein als eine if / else-Anweisung, da es für einen Compiler einfach ist, Sprungtabellen aus switch-Anweisungen zu generieren. Es ist möglich, dasselbe für if / else-Anweisungen zu tun, wenn entsprechende Einschränkungen gegeben sind, aber das ist viel schwieriger.
Im Fall von C # gilt dies auch, jedoch aus anderen Gründen.
Bei einer großen Anzahl von Zeichenfolgen bietet die Verwendung einer switch-Anweisung einen erheblichen Leistungsvorteil, da der Compiler eine Hash-Tabelle verwendet, um den Sprung zu implementieren.
Mit einer kleinen Anzahl von Zeichenfolgen ist die Leistung zwischen den beiden gleich.
Dies liegt daran, dass der C # -Compiler in diesem Fall keine Sprungtabelle generiert. Stattdessen wird MSIL generiert, das IF / ELSE-Blöcken entspricht.
Es gibt eine MSIL-Anweisung "switch statement", die beim Jitting eine Sprungtabelle verwendet, um eine switch-Anweisung zu implementieren. Es funktioniert jedoch nur mit ganzzahligen Typen (diese Frage fragt nach Zeichenfolgen).
Für eine kleine Anzahl von Zeichenfolgen ist es für den Compiler effizienter, IF / ELSE-Blöcke zu generieren, als eine Hash-Tabelle zu verwenden.
Als ich dies ursprünglich bemerkte, ging ich davon aus, dass der Compiler dieselbe Transformation für eine große Anzahl von Zeichenfolgen durchführte, da IF / ELSE-Blöcke mit einer kleinen Anzahl von Zeichenfolgen verwendet wurden.
Das war falsch. 'IMA' war so freundlich, mich darauf aufmerksam zu machen (nun ... er war nicht freundlich, aber er hatte Recht und ich habe mich geirrt, was der wichtige Teil ist)
Ich habe auch eine knochenköpfige Annahme über das Fehlen einer "switch" -Anweisung in MSIL gemacht (ich dachte, wenn es ein switch-Grundelement gibt, warum verwenden sie es nicht mit einer Hash-Tabelle, also darf es kein switch-Grundelement geben. ...). Das war sowohl falsch als auch unglaublich dumm von meiner Seite. Wieder machte mich 'IMA' darauf aufmerksam.
Ich habe die Aktualisierungen hier vorgenommen, da dies der am höchsten bewertete Beitrag und die akzeptierte Antwort ist.
Ich habe es jedoch zum Community-Wiki gemacht, weil ich glaube, ich verdiene die REP nicht, weil ich falsch liege. Wenn Sie eine Chance bekommen, stimmen Sie bitte den Beitrag von 'ima' ab.
quelle
Drei Gründe, das zu bevorzugen
switch
:Ein Compiler, der auf nativen Code abzielt, kann häufig eine switch-Anweisung in einen bedingten Zweig plus einen indirekten Sprung kompilieren, während eine Folge von
if
s eine Folge von bedingten Zweigen erfordert . Abhängig von der Dichte der Fälle wurden sehr viele gelernte Artikel darüber geschrieben, wie Fallaussagen effizient zusammengestellt werden können. Einige sind von der lcc-Compilerseite verlinkt . (Lcc hatte einen der innovativeren Compiler für Switches.)Eine switch-Anweisung ist eine Auswahl unter sich gegenseitig ausschließenden Alternativen und die switch-Syntax macht diesen Steuerungsfluss für den Programmierer transparenter als ein Nest von if-then-else-Anweisungen.
In einigen Sprachen, darunter definitiv ML und Haskell, prüft der Compiler, ob Sie Fälle ausgelassen haben . Ich betrachte diese Funktion als einen der Hauptvorteile von ML und Haskell. Ich weiß nicht, ob C # das kann.
Eine Anekdote: Bei einem Vortrag, den er über die Auszeichnung für sein Lebenswerk hielt, hörte ich Tony Hoare sagen, dass es von all den Dingen, die er in seiner Karriere getan hat, drei gab, auf die er am stolzesten war:
case
Anweisung nannte)Ich kann mir nicht vorstellen, ohne zu leben
switch
.quelle
Der Compiler wird so ziemlich alles mit geringfügigen Unterschieden in denselben Code optimieren (Knuth, irgendjemand?).
Der Unterschied besteht darin, dass eine switch-Anweisung sauberer als fünfzehn ist, wenn sonst Anweisungen aneinandergereiht sind.
Freunde lassen Freunde keine if-else-Anweisungen stapeln.
quelle
Tatsächlich ist eine switch-Anweisung effizienter. Der Compiler optimiert es in eine Nachschlagetabelle, in der es mit if / else-Anweisungen nicht möglich ist. Der Nachteil ist, dass eine switch-Anweisung nicht mit variablen Werten verwendet werden kann.
Sie können nicht tun:
Es muss sein
quelle
Ich habe niemanden gesehen, der den (offensichtlichen?) Punkt angesprochen hat, dass der vermeintliche Effizienzvorteil der switch-Anweisung davon abhängt, dass die verschiedenen Fälle ungefähr gleich wahrscheinlich sind. In Fällen, in denen einer (oder einige) der Werte viel wahrscheinlicher ist, kann die Wenn-Dann-Sonst-Leiter viel schneller sein, indem sichergestellt wird, dass die häufigsten Fälle zuerst überprüft werden:
Also zum Beispiel:
vs.
Wenn x in 90% der Fälle Null ist, kann der "if-else" -Code doppelt so schnell sein wie der switchbasierte Code. Selbst wenn der Compiler den "Schalter" in eine Art cleveres tabellengesteuertes Goto verwandelt, ist er nicht so schnell wie das einfache Überprüfen auf Null.
quelle
switch
kompatibel sind, ist dieswitch
Aussage im Allgemeinen besser (besser lesbar, manchmal schneller). Wenn Sie wissen, dass ein Fall viel wahrscheinlicher ist, können Sie ihn herausziehen, um einif
-else
-switch
Konstrukt zu bilden, und wenn er messbar schneller ist , lassen Sie ihn in. (Wiederholen Sie ihn, falls erforderlich.) IMO, die immer noch einigermaßen lesbar ist. Wenn dasswitch
degeneriert und zu klein wird, erledigt ein Regex-Ersatz den größten Teil der Arbeit, um es in eineelse if
Kette umzuwandeln.oft sieht es besser aus - dh es ist leichter zu verstehen, was los ist. Angesichts der Tatsache, dass der Leistungsvorteil bestenfalls äußerst gering ist, ist die Ansicht des Codes der wichtigste Unterschied.
Wenn das if / else besser aussieht, verwenden Sie es, andernfalls verwenden Sie eine switch-Anweisung.
quelle
Nebenthema, aber ich mache mir oft Sorgen (und sehe öfter)
if
/else
undswitch
Aussage wird bei zu vielen Fällen viel zu groß. Diese beeinträchtigen häufig die Wartbarkeit.Häufige Schuldige sind:
Reparieren:
quelle
Gemäß diesem Link entspricht der Vergleich von IF und Switch des Iterationstests mit switch und if-Anweisung 1.000.000.000 Iterationen. Die Zeit, die Switch- Anweisung = 43,0 s und If-Anweisung = 48,0 s benötigt
Das sind buchstäblich 20833333 Iterationen pro Sekunde. Sollten wir uns wirklich mehr konzentrieren müssen ?
PS: Nur um den Leistungsunterschied für eine kleine Liste von Bedingungen zu kennen.
quelle
Wenn Sie nur die if- oder else-Anweisung verwenden, verwendet die Basislösung den Vergleich? Operator
Sie können die Routine oder in einem Schalter ausführen
quelle
Dies beantwortet Ihre Frage nicht wirklich, aber da es kaum Unterschiede zwischen den kompilierten Versionen gibt, möchte ich Sie dringend bitten, Ihren Code so zu schreiben, dass Ihre Absichten am besten beschrieben werden. Es besteht nicht nur eine bessere Chance, dass der Compiler das tut, was Sie erwarten, sondern es erleichtert auch anderen, Ihren Code zu pflegen.
Wenn Sie beabsichtigen, Ihr Programm basierend auf dem Wert einer Variablen / eines Attributs zu verzweigen, repräsentiert eine switch-Anweisung diese Absicht am besten.
Wenn Sie beabsichtigen, Ihr Programm basierend auf verschiedenen Variablen / Attributen / Bedingungen zu verzweigen, repräsentiert eine if / else if-Kette diese Absicht am besten.
Ich gebe zu, dass Cody Recht hat, wenn Leute den Befehl break vergessen, aber fast genauso häufig sehe ich Leute, die komplizierte Aktionen ausführen, wenn Blöcke, bei denen sie das {} falsch verstehen, Zeilen, die in der bedingten Anweisung enthalten sein sollten, nicht. Dies ist einer der Gründe, warum ich immer {} in meine if-Anweisungen einbinde, auch wenn eine Zeile darin enthalten ist. Es ist nicht nur einfacher zu lesen, sondern wenn ich eine weitere Zeile in die Bedingung einfügen muss, kann ich nicht vergessen, sie hinzuzufügen.
quelle
Zinsfrage. Dies kam vor ein paar Wochen bei der Arbeit und wir fanden eine Antwort, indem wir ein Beispiel-Snippet schrieben und es in .NET Reflector betrachteten (Reflektor ist fantastisch !! ich liebe es).
Folgendes haben wir entdeckt: Eine gültige switch-Anweisung für etwas anderes als eine Zeichenfolge wird als switch-Anweisung in IL kompiliert. Wenn es sich jedoch um eine Zeichenfolge handelt, wird sie in IL als if / else if / else umgeschrieben. In unserem Fall wollten wir wissen, wie switch-Anweisungen Zeichenfolgen vergleichen, z. B. zwischen Groß- und Kleinschreibung unterscheiden usw. Der Reflektor gab uns schnell eine Antwort. Das war nützlich zu wissen.
Wenn Sie auf Zeichenfolgen vergleichen , um zu tun case-sensitive wollen , dann Sie könnte eine switch - Anweisung verwenden , da es schneller ist als ein String.Compare in einer if / else durchführen. (Bearbeiten: Lesen Was ist schneller, Zeichenfolge einschalten oder Typ einschalten? Für einige tatsächliche Leistungstests.) Wenn Sie jedoch die Groß- und Kleinschreibung nicht berücksichtigen möchten , ist es besser, ein if / else zu verwenden, da der resultierende Code nicht hübsch ist.
Die beste Faustregel ist, switch-Anweisungen zu verwenden, wenn dies (ernsthaft) sinnvoll ist, z.
Wenn Sie den Wert bearbeiten müssen, um ihn in die switch-Anweisung einzuspeisen (erstellen Sie eine temporäre Variable, gegen die umgeschaltet werden soll), sollten Sie wahrscheinlich eine if / else-Steueranweisung verwenden.
Ein Update:
Es ist tatsächlich besser, den String in Großbuchstaben umzuwandeln (z. B.
ToUpper()
), da es anscheinend weitere Optimierungen gibt, die der Just-in-Time-Compiler im Vergleich zum ausführen kannToLower()
. Es ist eine Mikrooptimierung, aber in einer engen Schleife könnte es nützlich sein.Eine kleine Randnotiz:
Versuchen Sie Folgendes, um die Lesbarkeit von switch-Anweisungen zu verbessern:
quelle
Die switch-Anweisung ist definitiv die schnellere als eine if else if. Es gibt Speedtests, die von BlackWasp bereitgestellt wurden
http://www.blackwasp.co.uk/SpeedTestIfElseSwitch.aspx
--Hör zu
Hängt aber stark von den Möglichkeiten ab, die Sie zu berücksichtigen versuchen, aber ich versuche, wann immer möglich eine switch-Anweisung zu verwenden.
quelle
Ich denke, nicht nur C #, sondern alle C-basierten Sprachen: Da ein Schalter auf Konstanten beschränkt ist, ist es möglich, mithilfe einer "Sprungtabelle" sehr effizienten Code zu generieren. Der C-Fall ist wirklich ein gutes altes FORTRAN-berechnetes GOTO, aber der C # -Fall wird immer noch gegen eine Konstante getestet.
Es ist nicht der Fall, dass der Optimierer denselben Code erstellen kann. Betrachten Sie z.
Da es sich um zusammengesetzte Boolesche Werte handelt, muss der generierte Code einen Wert und einen Kurzschluss berechnen. Betrachten Sie nun das Äquivalent
Dies kann in kompiliert werden
weil Sie dem Compiler implizit mitteilen, dass er die OR- und Gleichheitstests nicht berechnen muss.
quelle
Mein CS-Professor hat Ihnen vorgeschlagen, die Anweisungen nicht zu wechseln, da die Leute so oft die Pause vergessen oder sie falsch verwenden. Ich kann mich nicht genau erinnern, was er gesagt hat, aber etwas in der Richtung, dass das Betrachten einer wegweisenden Codebasis, die Beispiele für die switch-Anweisung (vor Jahren) zeigte, auch eine Menge Fehler enthielt.
quelle
Mir ist gerade aufgefallen, dass Sie if / else- und switch-Anweisungen kombinieren können! Sehr nützlich, wenn Sie die Voraussetzungen überprüfen müssen.
quelle
Ich denke, Switch ist schneller als wenn Bedingungen wie sehen, ob es ein Programm gibt wie:
Schreiben Sie ein Programm, um eine beliebige Zahl (zwischen 1 - 99) einzugeben, und überprüfen Sie, in welchem Steckplatz a) 1 - 9, dann Steckplatz eins b) 11 - 19, dann Steckplatz zwei c) 21-29, dann Steckplatz drei und so weiter bis 89- 99
Dann ein, wenn Sie viele Bedingungen erfüllen müssen, aber Son Switch Case müssen Sie nur eingeben
es wird so einfach sein
Es gibt noch viele weitere Beispiele!
quelle
Eine switch-Anweisung ist im Grunde ein Vergleich für Gleichheit. Tastaturereignisse haben einen großen Vorteil gegenüber switch-Anweisungen, wenn sie einfach zu schreibenden und zu lesenden Code haben, und eine if elseif-Anweisung, bei der eine {Klammer} fehlt, kann ebenfalls problematisch werden.
Eine if elseif-Anweisung eignet sich für mehr als eine Lösung, wenn (theAmountOfApples ist größer als 5 && theAmountOfApples ist kleiner als 10) Ihre Äpfel speichern, wenn if (theAmountOfApples größer als 10 || theAmountOfApples == 100) Ihre Äpfel verkauft. Ich schreibe nicht c # oder c ++, aber ich habe es gelernt, bevor ich Java gelernt habe, und sie sind enge Sprachen.
quelle
Ein möglicher Nachteil von switch-Anweisungen ist das Fehlen mehrerer Bedingungen. Sie können mehrere Bedingungen für die if (else), aber nicht mehrere case-Anweisungen mit unterschiedlichen Bedingungen in einem Switch haben.
Switch-Anweisungen eignen sich nicht für logische Operationen, die über den Rahmen einfacher boolescher Gleichungen / Ausdrücke hinausgehen. Für diese booleschen Gleichungen / Ausdrücke ist es hervorragend geeignet, jedoch nicht für andere logische Operationen.
Sie haben viel mehr Freiheit mit der in If-Anweisungen verfügbaren Logik, aber die Lesbarkeit kann leiden, wenn die If-Anweisung unhandlich wird oder schlecht behandelt wird.
Beide haben dort Platz, abhängig vom Kontext, mit dem Sie konfrontiert sind.
quelle