Perché scegliere Switch / Case e non If / Else If?

Questa domanda è principalmente rivolta a C / C ++, ma credo che anche altre lingue siano rilevanti.

Non riesco a capire perché sia ​​ancora in uso switch / case invece che if / else if. Mi sembra molto simile all’utilizzo di goto e risulta nello stesso tipo di codice disordinato, mentre gli stessi risultati potrebbero essere raggiunti con if / else se è in un modo molto più organizzato.

Tuttavia, vedo questi blocchi in giro abbastanza spesso. Un luogo comune per trovarli è vicino a un loop di messaggi (WndProc …), mentre questi sono tra i luoghi in cui provocano il caos più pesante: le variabili sono condivise lungo l’intero blocco, anche quando non sono appropriate (e non possono essere inizializzato al suo interno). È necessario prestare particolare attenzione a non lasciar cadere le pause, e così via …

Personalmente, evito di usarli e mi chiedo se mi manchi qualcosa?

Sono più efficienti di quelli di / else? Sono portati avanti dalla tradizione?

Riassumendo il mio post e i commenti iniziali – ci sono molti vantaggi switch su if / else :

  1. Codice Cleaner. Codice con più catene if / else if ... sembra disordinato ed è difficile da mantenere – switch fornisce una struttura più pulita.

  2. Prestazione. Per i valori dei case densi il compilatore genera una tabella di salto, per la ricerca binaria-sparsa o una serie di if / else , quindi nel peggiore dei casi l’ switch è veloce come if / else , ma in genere più veloce. Sebbene alcuni compilatori possano similmente ottimizzare if / else .

  3. L’ordine di prova non ha importanza. Per velocizzare le serie di test if / else , è necessario innanzitutto mettere i casi più probabili. Con programmatore switch / case non è necessario pensarci.

  4. Il valore predefinito può essere ovunque. Con if / else il caso predefinito deve essere alla fine – dopo l’ultimo. In switch : l’ default può essere ovunque, laddove il programmatore lo trova più appropriato.

  5. Codice comune Se è necessario eseguire codice comune per diversi casi, è ansible omettere l’ break e l’esecuzione “fallirà” – qualcosa che non è ansible ottenere con if / else . (Vi è una buona pratica per inserire un commento speciale /* FALLTHROUGH */ per tali casi – /* FALLTHROUGH */ lo riconosce e non si lamenta, senza questo commento si lamenta perché è un errore comune dimenticare l’ break ).

Grazie a tutti i commentatori.

Bene, una ragione è la chiarezza ….

se hai un interruttore / caso, quindi l’espressione non può cambiare …. vale a dire

 switch (foo[bar][baz]) { case 'a': ... break; case 'b': ... break; } 

mentre con if / else, se scrivi per errore (o intento):

 if (foo[bar][baz] == 'a') { .... } else if (foo[bar][baz+1] == 'b') { .... } 

le persone che leggono il tuo codice si chiederanno “le espressioni foo dovevano essere uguali” o “perché sono diverse”?

per favore ricorda che caso / select offre una flessibilità aggiuntiva:

  • la condizione viene valutata una volta
  • è abbastanza flessibile da build cose come il dispositivo di Duff
  • fallthrough (aka caso senza pausa)

così come viene eseguito molto più velocemente (tramite jump / lookup table) * storicamente

Ricorda inoltre che le istruzioni switch consentono al stream di controllo di continuare, il che ti consente di combinare piacevolmente le condizioni consentendo al contempo di aggiungere codice aggiuntivo per determinate condizioni, ad esempio nel seguente codice:

 switch (dayOfWeek) { case MONDAY: garfieldUnhappy = true; case TUESDAY: case WEDNESDAY: case THURSDAY: case FRIDAY: weekDay = true; break; case SATURDAY: weekendJustStarted = true; case SUNDAY: weekendDay = true; break; } 

Usare if/else qui le affermazioni non sarebbe comunque bello.

 if (dayOfWeek == MONDAY) { garfieldUnhappy = true; } if (dayOfWeek == SATURDAY) { weekendJustStarted = true; } if (dayOfWeek == MONDAY || dayOfWeek == TUESDAY || dayOfWeek == WEDNESDAY || dayOfWeek == THURSDAY || dayOfWeek == FRIDAY) { weekDay = true; } else if (dayOfWeek == SATURDAY || dayOfWeek == SUNDAY) { weekendDay = true; } 

Se ci sono molti casi, l’affermazione dell’interruttore sembra più pulita.

È anche bello quando si hanno più valori per i quali si desidera lo stesso comportamento: basta usare più istruzioni “case” che ricadono in un’unica implementazione è molto più facile da leggere rispetto a if (questo || che || someotherthing || .. .)

Potrebbe anche dipendere dalla tua lingua – Ad esempio, alcune lingue cambiano solo con tipi numerici, quindi ti risparmia un po ‘di digitazione quando lavori con un valore enumerato, costanti numeriche … ecc …

 If (day == DAYOFWEEK_MONDAY) { //... } else if (day == DAYOFWEEK_TUESDAY) { //... } //etc... 

O leggermente più facile da leggere …

 switch (day) { case DAYOFWEEK_MONDAY : //... case DAYOFWEEK_TUESDAY : //... //etc... } 

Switch / case viene generalmente ottimizzato in modo più efficiente rispetto a if / else se / else, ma occasionalmente (a seconda della lingua e del compilatore) viene tradotto in semplici istruzioni if ​​/ else if / else.

Personalmente ritengo che le dichiarazioni switch rendano il codice più leggibile rispetto a una serie di dichiarazioni if; purché tu segua alcune semplici regole. Regole che dovresti probabilmente seguire anche per le tue situazioni if ​​/ else if / else, ma questa è ancora una mia opinione.

Quelle regole:

  • Mai e poi mai più di una linea sul tuo switch block. Chiama un metodo o una funzione e fai il tuo lavoro lì.
  • Verificare sempre la presenza di rottura / caso.
  • Eccezionali bolle.

Chiarezza. Come ho detto qui , un indizio che else if è problematico è

la frequenza con cui ELSE IF viene utilizzato in un modo molto più limitato di quanto consentito dalla syntax. È un maglio di flessibilità, che consente di testare condizioni completamente indipendenti. Ma viene abitualmente utilizzato per swatare le mosche di CASE, confrontando la stessa espressione con valori alterni …

Questo riduce la leggibilità del codice. Dal momento che la struttura consente un universo di complessità condizionale, il lettore deve tenere a mente più possibilità quando analizza ELSE IF piuttosto che quando analizza CASE.

In realtà, un’istruzione switch implica che stai lavorando su qualcosa che è più o meno un enum che ti dà un indizio istantaneo su cosa sta succedendo.

Detto questo, un passaggio a un enum in qualsiasi linguaggio OO potrebbe probabilmente essere codificato meglio – e una serie di if / else con lo stesso valore “enum” sarebbe altrettanto ctriggers e persino peggiore nel trasmettere significato.

affrontando la preoccupazione che tutto all’interno dello switch abbia un ambito equivalente, puoi sempre gettare la tua logica caso in un altro {} blocco, in questo modo ..

 switch( thing ) { case ONETHING: { int x; // local to the case! ... } break; case ANOTHERTHING: { int x; // a different x than the other one } break; } 

.. ora non sto dicendo che è carina Basta metterlo fuori come qualcosa che è ansible se devi assolutamente isolare qualcosa in un caso da un altro.

un altro pensiero sul problema dell’ottica: sembra una buona pratica mettere solo un interruttore in una funzione e non molto altro. In tali circostanze, l’ambito della variabile non è più di una preoccupazione, dal momento che in questo modo si tratta in genere solo di un caso di esecuzione su una determinata chiamata della funzione.

ok, un ultimo pensiero sugli switch: se una funzione contiene più di un paio di opzioni, è probabilmente il momento di ridefinire il codice. Se una funzione contiene interruttori annidati , è probabilmente un indizio per ripensare il tuo design un po ‘=)

l’interruttore è usato principalmente per avere la scelta di fare in programmazione. Questo non è correlato alla dichiarazione condizionale come:

se il tuo programma richiede solo la scelta da fare, allora perché usi il blocco if / else e aumenti lo sforzo di programmazione, più riduci la velocità di esecuzione del programma.

Le istruzioni switch possono essere ottimizzate per la velocità, ma possono richiedere più memoria se i valori del caso sono distribuiti su un numero elevato di valori.

se / else sono generalmente lenti, poiché ogni valore deve essere controllato.

Un Smalltalker potrebbe rifiutare sia switch che if-then-else e potrebbe scrivere qualcosa come: –

 shortToLongDaysMap := Dictionary new. shortToLongDaysMap at: 'Mon' put: 'Monday'; at: 'Tue' put: 'Tuesday'; at: 'Wed' put: 'Wednesday' etc etc. longForm := shortToLongDaysMap at: shortForm ifAbsent: [shortForm] 

Questo è un esempio banale ma spero che tu possa vedere come questa tecnica scala per un numero elevato di casi.

Notare il secondo argomento at:IfAbsent: è simile alla clausola predefinita di un’istruzione case.

La ragione principale alla base di questo è la manutenibilità e la leggibilità. È facile rendere il codice più leggibile e mantenibile con l’istruzione Switch / case, quindi se / else. Perché ne hai molti se / e poi il codice diventa così disordinato come il nido ed è molto difficile mantenerlo.

E alcuni come il tempo di esecuzione è un’altra ragione.

Abbastanza sicuro che si compili per le stesse cose come if / else if , ma trovo che l’ switch / case più facile da leggere quando ci sono più di 2 o 3 s.