Quando è appropriato utilizzare un'istruzione switch fall-through (classica)? Tale utilizzo è raccomandato e incoraggiato o dovrebbe essere evitato a tutti i costi?
Quando è appropriato utilizzare un'istruzione switch fall-through (classica)? Tale utilizzo è raccomandato e incoraggiato o dovrebbe essere evitato a tutti i costi?
Risposte:
Ecco un esempio in cui sarebbe utile.
public Collection<LogItems> GetAllLogItems(Level level) {
Collection<LogItems> result = new Collection<LogItems>();
switch (level) {
// Note: fall through here is INTENTIONAL
case All:
case Info:
result.Add(GetItemsForLevel(Info));
case Warning:
result.Add(GetItemsForLevel(Warning));
case Error:
result.Add(GetItemsForLevel(Error));
case Critical:
result.Add(GetItemsForLevel(Critical));
case None:
}
return result;
}
Questo genere di cose (in cui un caso include l'altro) è piuttosto raro, penso, ed è per questo che alcuni linguaggi più recenti non consentono il fallover o richiedono una sintassi speciale per questo.
// INTENTIONAL FALL THROUGH HERE
commento in maiuscolo richiesto .
GetItemsForLevel(Info)
invocare la chiamata GetItemsForLevel(Warning)
e così via.
Li uso quando è necessario applicare determinate funzionalità per più di un valore. Ad esempio, supponiamo che tu avessi un oggetto con una proprietà chiamata operationCode. Se il codice è uguale a 1, 2, 3 o 4, si desidera avviare OperazioneX (). Se è 5 o 6, desideri startOperationY () e 7 startOperationZ (). Perché 7 casi completi con funzionalità e interruzioni quando è possibile utilizzare i fall-through?
Penso che sia completamente valido in determinate situazioni, specialmente se evita 100 dichiarazioni if-else. =)
switch
con più case
s legate allo stesso codice. È diverso da un fall-through, in cui l'esecuzione di uno case
continua nel successivo a causa della mancanza di una break
tra di loro.
I casi di caduta sono perfettamente a posto. Trovo spesso che un'enumerazione sia utilizzata in molti luoghi e che quando non è necessario differenziare alcuni casi è più facile usare la logica fall-through.
Ad esempio (notare i commenti esplicativi):
public boolean isAvailable(Server server, HealthStatus health) {
switch(health) {
// Equivalent positive cases
case HEALTHY:
case UNDER_LOAD:
return true;
// Equivalent negative cases
case FAULT_REPORTED:
case UNKNOWN:
case CANNOT_COMMUNICATE:
return false;
// Unknown enumeration!
default:
LOG.warn("Unknown enumeration " + health);
return false;
}
}
Trovo questo tipo di utilizzo perfettamente accettabile.
case X: case Y: doSomething()
e case X: doSomething(); case Y: doAnotherThing()
. Nel primo, l'intenzione è abbastanza esplicita, mentre nel secondo, la caduta può essere intenzionale o no. Nella mia esperienza, il primo esempio non attiva mai alcun avviso nei compilatori / analizzatori di codice, mentre il secondo lo fa. Personalmente, chiamerei solo il secondo esempio "fall through" - non sono sicuro di una definizione "ufficiale", però.
Dipende da:
I due problemi principali associati a far passare un caso al successivo sono:
Rende il tuo codice dipendente dall'ordine delle dichiarazioni del caso. Questo non è il caso se non si fallisce mai, e aggiunge un grado di complessità che spesso non è gradito.
Non è ovvio che il codice per un caso includa il codice per uno o più casi successivi.
Alcuni luoghi proibiscono esplicitamente di cadere. Se non lavori in un posto del genere, e se ti senti a tuo agio con la pratica, e se infrangere il codice in questione non causerà alcuna vera sofferenza, allora potrebbe non essere la cosa peggiore del mondo. Se lo fai, però, assicurati di mettere un commento che catturi l'attenzione nelle vicinanze per avvertire coloro che verranno dopo (incluso il futuro tu).
Ecco un esempio rapido (certamente incompleto (nessuna gestione speciale dell'anno bisestile)) di fallimenti che mi semplificano la vita:
function get_julian_day (date) {
int utc_date = date.getUTCDate();
int utc_month = date.getUTCMonth();
int julian_day = 0;
switch (utc_month) {
case 11: julian_day += 30;
case 10: julian_day += 31;
case 9: julian_day += 30;
case 8: julian_day += 31;
case 7: julian_day += 31;
case 6: julian_day += 30;
case 5: julian_day += 31;
case 4: julian_day += 30;
case 3: julian_day += 31;
case 2: julian_day += 28;
case 1: julian_day += 31;
default: break;
}
return julian_day + utc_date;
}
Se sento la necessità di passare da un caso all'altro (raro, devo ammettere), preferisco essere molto esplicito e goto case
, naturalmente, ciò presuppone che la tua lingua lo supporti.
Poiché cadere attraverso è così insolito e molto facile da trascurare durante la lettura del codice, ritengo sia opportuno essere espliciti - e un goto, anche se è un caso, dovrebbe emergere come un pollice dolente.
Aiuta anche a evitare bug che possono verificarsi quando le istruzioni del caso vengono riordinate.