it-swarm.com.de

Gibt es einen signifikanten Unterschied zwischen if / else und switch-case in C #?

Was ist der Vorteil/Nachteil bei 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, abgesehen vom Aussehen Ihres Codes.

Gibt es einen Grund, warum die resultierende IL oder die damit verbundene Laufzeitleistung sich grundlegend unterscheiden würde?

Siehe auch: Was ist schneller, String einschalten oder elseif eintippen?

203

Die SWITCH-Anweisung erzeugt nur dieselbe Assembly wie IFs im Debug- oder Kompatibilitätsmodus. In Release wird es in eine Sprungtabelle kompiliert (über die MSIL-Anweisung 'switch') - dies ist O (1).

Mit C # lassen sich (im Gegensatz zu vielen anderen Sprachen) auch String-Konstanten einschalten - und das funktioniert etwas anders. Es ist offensichtlich nicht praktikabel, Sprungtabellen für Zeichenfolgen beliebiger Länge zu erstellen. In den meisten Fällen wird ein solcher Schalter in einen Stapel von IFs kompiliert.

Wenn die Anzahl der Bedingungen jedoch groß genug ist, um den Overhead 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 Hashtable-Suche ist nicht unbedingt O(1) und verursacht spürbare konstante Kosten. Wenn jedoch die Anzahl der Fallbeschriftungen 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, bevorzugen Sie SWITCH gegenüber IF, andernfalls verwenden Sie, was besser aussieht.

314
ima

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 ebenfalls, jedoch aus anderen Gründen.

Bei einer großen Anzahl von Zeichenfolgen hat 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 bei Jitting eine Sprungtabelle verwendet, um eine switch-Anweisung zu implementieren. Es funktioniert jedoch nur mit Integer-Typen (diese Frage fragt nach Strings).

Bei einer geringen 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 die gleiche 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 nett, mich darauf hinzuweisen (na ja ... er war nicht so nett, aber er hatte recht, und ich habe mich geirrt, was der wichtige Teil ist)

Ich machte auch eine knochige Vermutung über das Fehlen einer "switch" -Anweisung in MSIL (ich dachte, wenn es ein switch-Primitiv gab, warum benutzten sie es nicht mit einer Hash-Tabelle, so dass es kein switch-Primitiv geben darf). ...). Das war falsch und unglaublich dumm von meiner Seite. Wieder wies mich IMA darauf hin.

Ich habe die Updates hier vorgenommen, da es sich um den am höchsten bewerteten Beitrag und die akzeptierte Antwort handelt.

Ich habe es jedoch zu einem Community-Wiki gemacht, weil ich glaube, dass ich den REP nicht verdiene, weil ich falsch liege. Wenn Sie eine Chance bekommen, stimmen Sie bitte über den Beitrag von 'ima' ab.

49

Drei Gründe, die switch bevorzugen:

  • Ein Compiler, der auf systemeigenen Code abzielt, kompiliert häufig eine switch-Anweisung in einen bedingten Zweig und einen indirekten Sprung , während eine Folge von ifs ein Folge von bedingten Zweigen . Abhängig von der Dichte der Fälle wurden sehr viele gelernte Artikel darüber verfasst, wie Fallaussagen effizient zusammengestellt werden können. einige sind von der lcc compiler page verlinkt. (Lcc hatte einen der innovativsten Compiler für Switches.)

  • Eine switch-Anweisung ist eine Wahl unter sich gegenseitig ausschließenden Alternativen , und die switch-Syntax macht diesen Steuerfluss für den Programmierer transparenter dann ein Nest von if-then-else-Anweisungen.

  • In einigen Sprachen, einschließlich definitiv ML und Haskell , prüft der Compiler, ob Sie Fälle ausgelassen haben . Ich betrachte dieses Feature 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 allen Dingen, die er in seiner Karriere getan hatte, drei gab, auf die er am stolzesten war:

  • Quicksort erfinden
  • Die switch-Anweisung erfinden (die Tony die case-Anweisung nannte)
  • Beginn und Ende seiner Karriere in der Industrie

Ich kann mir nicht vorstellen, ohne switch zu leben.

18
Norman Ramsey

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:

switch(variable)
{
   case someVariable
   break;
   default:
   break;
}

es muss sein

switch(variable)
{
  case CONSTANT_VALUE;
  break;
  default:
  break;
}
14
kemiller2002

Der Compiler wird mit geringfügigen Unterschieden so ziemlich alles im selben Code optimieren (Knuth, irgendjemand?).

Der Unterschied besteht darin, dass eine switch-Anweisung sauberer als fünfzehn ist, wenn sonst Anweisungen aneinandergereiht werden.

Freunde lassen Freunde keine if-else-Anweisungen stapeln.

14
Will

Ich habe niemanden gesehen, der den (offensichtlichen?) Punkt ansprach, 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 sehr viel wahrscheinlicher sind, kann die Wenn-Dann-Sonst-Rangliste sehr viel schneller sein, indem sichergestellt wird, dass die häufigsten Fälle zuerst überprüft werden:

Also zum Beispiel:

if (x==0) then {
  // do one thing
} else if (x==1) {
  // do the other thing
} else if (x==2) {
  // do the third thing
}

vs

switch(x) {
  case 0: 
         // do one thing
         break;
  case 1: 
         // do the other thing
         break;
  case 2: 
         // do the third thing
         break;
}

Wenn x in 90% der Fälle null ist, kann der "if-else" -Code doppelt so schnell sein wie der switch-basierte Code. Selbst wenn der Compiler den "Schalter" in eine Art kluges, tabellengesteuertes Goto verwandelt, ist es immer noch nicht so schnell wie das bloße Überprüfen auf Null.

12
Mark Bessey

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 also besser aussieht, verwenden Sie es, andernfalls verwenden Sie eine switch-Anweisung.

7
gbjbaanb

Nebenthema, aber ich mache mir oft Sorgen darüber (und sehe es öfter), dass die Anweisungen if/else und switch in zu vielen Fällen viel zu groß werden. Diese beeinträchtigen oft die Wartbarkeit.

Häufige Schuldige sind:

  1. Zu viel innerhalb mehrerer if-Anweisungen tun
  2. Mehr Fallaussagen als menschlich möglich zu analysieren
  3. Zu viele Bedingungen in der if-Bewertung, um zu wissen, wonach gesucht wird

Reparieren:

  1. Auszug zur Methode Refactoring.
  2. Verwenden Sie ein Dictionary mit Methodenzeigern anstelle einer Groß-/Kleinschreibung oder eine IoC, um die Konfigurierbarkeit zu verbessern. Auch Methodenfabriken können hilfreich sein.
  3. Extrahieren Sie die Bedingungen nach ihrer eigenen Methode
4
Chris Brandsma

Wenn Sie nur if oder else-Anweisung verwenden, verwendet die Basislösung den Vergleich? Operator

(value == value1) ? (type1)do this : (type1)or do this;

Sie können die Routine oder in einem Schalter ausführen

switch(typeCode)
{
   case TypeCode:Int32:
   case TypeCode.Int64:
     //dosomething here
     break;
   default: return;
}
3
Robert W.

Die switch-Anweisung ist definitiv schneller als ein if else if. Es gibt Speedtest, die von BlackWasp darauf geliefert wurden

http://www.blackwasp.co.uk/SpeedTestIfElseSwitch.aspx

--Hör zu

Aber hängt stark von den Möglichkeiten ab, die Sie zu berücksichtigen versuchen, aber ich versuche, wann immer möglich, eine switch-Anweisung zu verwenden.

2
sleath

Dies beantwortet Ihre Frage nicht wirklich, aber da es kaum einen Unterschied zwischen den kompilierten Versionen gibt, möchte ich Sie dringend bitten, Ihren Code so zu schreiben, dass er Ihre Absichten am besten beschreibt. Es gibt nicht nur eine bessere Chance für den Compiler, das zu tun, was Sie erwarten, sondern es wird auch für andere einfacher, 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 break-Befehl vergessen, aber fast genauso häufig sehe ich Leute, die komplizierte Dinge tun, wenn Blöcke, in denen sie das {} falsch verstehen, nicht. Dies ist einer der Gründe, warum ich immer {} in meine if-Anweisungen einbeziehe, auch wenn eine Zeile darin enthalten ist. Es ist nicht nur einfacher zu lesen, sondern ich kann auch nicht vergessen, eine weitere Zeile in die Bedingung einzufügen.

2
dj_segfault

Gemäß diesem Link entspricht IF vs Switch der Vergleich des Iterationstests mit switch und if-Anweisung für 1.000.000.000 Iterationen der Zeit, die von Switch benötigt wird Statement = 43.0s & by If-Anweisung = 48.0s

Das ist 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.

2
Bretfort

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 ansahen (Reflector 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., und Reflector gab uns schnell eine Antwort. Dies war nützlich zu wissen.

Wenn Sie zwischen Groß- und Kleinschreibung unterscheiden möchten, können Sie could eine switch-Anweisung verwenden, da diese schneller ist als Ausführen eines String.Compare in einem if/else. (Bearbeiten: Lesen Was ist schneller, String einschalten oder elseif eintippen? für einige aktuelle Leistungstests) Wenn Sie jedoch die Groß- und Kleinschreibung nicht berücksichtigen möchten, ist es besser, ein if/else als das zu verwenden Der resultierende Code ist nicht schön.

switch (myString.ToLower())
{
  // not a good solution
}

Die beste Faustregel ist, switch-Anweisungen zu verwenden, wenn dies (ernsthaft) sinnvoll ist, z.

  • es verbessert die Lesbarkeit Ihres Codes
  • sie vergleichen einen Wertebereich (float, int) oder eine Aufzählung

Wenn Sie den Wert ändern müssen, um ihn in die switch-Anweisung einzufügen (erstellen Sie eine temporäre Variable, gegen die gewechselt werden soll), sollten Sie wahrscheinlich eine if/else-Steueranweisung verwenden.

Ein Update:

Es ist eigentlich besser, den String in Großbuchstaben umzuwandeln (z. B. ToUpper()), da es anscheinend weitere Optimierungen gibt, die der Just-in-Time-Compiler im Vergleich zu ToLower() durchführen kann. Es ist eine Mikrooptimierung, die jedoch in einer engen Schleife nützlich sein kann.


Eine kleine Randnotiz:

Versuchen Sie Folgendes, um die Lesbarkeit von switch-Anweisungen zu verbessern:

  • lege den wahrscheinlichsten Zweig an die erste Stelle, d. h. den Zweig, auf den am häufigsten zugegriffen wird
  • wenn es wahrscheinlich ist, dass sie alle auftreten, listen Sie sie in alphabetischer Reihenfolge auf, damit Sie sie leichter finden.
  • verwenden Sie niemals den Standard-Catch-All für die letzte verbleibende Bedingung. Dies ist faul und führt später zu Problemen im Code-Leben.
  • verwenden Sie das Standard-Catch-All, um eine unbekannte Bedingung geltend zu machen, auch wenn es sehr unwahrscheinlich ist, dass sie jemals auftreten wird. Dafür sind Behauptungen gut.
1
Dennis

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 zB

if(a == 3){ //...
} else if (a == 5 || a == 7){ //...
} else {//...
}

Da es sich um zusammengesetzte Boolesche Werte handelt, muss der generierte Code einen Wert und einen Kurzschluss berechnen. Betrachten Sie nun das Äquivalent

switch(a){
   case 3: // ...
    break;
   case 5:
   case 7: //...
    break;
   default: //...
}

Dies kann in kompiliert werden

BTABL: *
B3:   addr of 3 code
B5:
B7:   addr of 5,7 code
      load 0,1 ino reg X based on value
      jump indirect through BTABL+x

weil Sie dem Compiler implizit mitteilen, dass er die OR und Gleichheitstests nicht berechnen muss.

1
Charlie Martin

Ich denke, Switch ist schneller als wenn Bedingungen wie sehen, ob es ein Programm wie:

Schreiben Sie ein Programm, um eine beliebige Zahl (zwischen 1 und 99) einzugeben, und überprüfen Sie, in welchem ​​Steckplatz a) 1 - 9 sich dann Steckplatz eins befindet. B) 11 - 19 sich dann Steckplatz zwei. C) 21 - 29 sich dann Steckplatz drei und so weiter bis 89 - 99

Dann ein, wenn Sie viele Bedingungen erfüllen müssen, aber den Son Switch-Fall müssen Sie nur eingeben

Schalter (Nr./10)

und im Fall 0 = 1-9, im Fall 1 = 11-19 und so weiter

es wird so einfach sein

Es gibt noch viele weitere solcher Beispiele!

0
user3957230

ich weiß, dass dies nicht genau die Frage ist, aber ich muss wirklich darauf hinweisen, dass, wenn Sie über Effizienz auf dieser Ebene nachdenken, Sie möglicherweise mehr Abstraktion in Ihrem Code benötigen. Sie brauchen keine Schaltergehäuse mehr, insbesondere wenn diese Logik enthalten. (mein beispiel in php).

    $feeMapping = [
        1000 => 1,
        2000 => 2,
        3000 => 3,
        4000 => 4,
        5000 => 5,
        6000 => 6,
        7000 => 7
    ];

    function findFee($feeMapping, $amount) {
        foreach ($feeMapping as $fee => $value) {
            if ($value >= $amount) {
                return $fee;
            }
        }

        return 7;
    }

    $feeValue = findFee($feeMapping, 200);

Schauen Sie sich nun die Redundanz des ähnlichen Codes an!

    if ($amount >= 1000) {
        return 1;
    } elseif ($amount >= 2000) {
        return 2;
    } elseif ($amount >= 3000) {
        return 3;
    } elseif ($amount >= 4000) {
        return 4;
    } elseif ($amount >= 5000) {
        return 5;
    } elseif ($amount >= 6000) {
        return 6;
    } else {
        return 7;
    }
0
SinisterGlitch

Mein cs-Professor schlug Ihnen vor, die Aussagen nicht zu wechseln, da so oft die Leute die Pause vergessen oder sie falsch verwenden. Ich kann mich nicht genau erinnern, was er gesagt hat, aber etwas in der Art, wie es bei der Betrachtung einer wegweisenden Codebasis, die Beispiele für die switch-Anweisung (vor Jahren) enthielt, auch eine Menge Fehler gemacht hat.

0
cody

Ein möglicher Nachteil von switch-Anweisungen ist das Fehlen mehrerer Bedingungen. Sie können mehrere Bedingungen für die if (else) -Anweisungen haben, jedoch nicht mehrere cases mit unterschiedlichen Bedingungen in einem Schalter.

Switch-Anweisungen sind nicht für logische Verknüpfungen geeignet, die über einfache boolesche Gleichungen/Ausdrücke hinausgehen. Für diese Booleschen Gleichungen/Ausdrücke ist es hervorragend geeignet, aber nicht für andere logische Operationen.

Sie haben viel mehr Freiheit mit der in If-Anweisungen verfügbaren Logik, aber die Lesbarkeit kann darunter leiden, wenn die If-Anweisung unhandlich wird oder schlecht gehandhabt wird.

Beide haben dort Platz, je nachdem, mit welchem ​​Kontext Sie konfrontiert sind.

0
Neil Meyer

Mir ist gerade aufgefallen, dass Sie if/else- und switch-Anweisungen kombinieren können! Sehr nützlich, wenn Vorbedingungen überprüft werden müssen.

if (string.IsNullOrEmpty(line))
{
    //skip empty lines
}
else switch (line.Substring(0,1))
{
    case "1":
        Console.WriteLine(line);
        break;
    case "9":
        Console.WriteLine(line);
        break;
    default:
        break;
}
0
Even Mien

eine switch-Anweisung ist im Grunde genommen ein Vergleich für Gleichheit. Tastaturereignisse haben einen großen Vorteil gegenüber switch-Anweisungen, wenn sie einfach zu schreiben und zu lesen sind, dann könnte eine if elseif-Anweisung, bei der eine {Klammer} fehlt, ebenfalls problematisch werden.

char abc;
switch(abc)
{
case a: break;
case b: break;
case c: break;
case d: break;
}

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 gespeichert werden. Ich schreibe kein C # oder C++, aber ich habe es gelernt, bevor ich Java und sie sind enge Sprachen.

0
Geen