Kiedy należy zastosować zastępczą (klasyczną) instrukcję switch? Czy takie użycie jest zalecane i zalecane, czy należy tego unikać za wszelką cenę?
Kiedy należy zastosować zastępczą (klasyczną) instrukcję switch? Czy takie użycie jest zalecane i zalecane, czy należy tego unikać za wszelką cenę?
Odpowiedzi:
Oto przykład, w którym byłoby to przydatne.
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;
}
Wydaje mi się, że tego rodzaju rzeczy (gdzie jeden przypadek obejmuje drugi), są raczej rzadkie i dlatego niektóre nowsze języki albo nie pozwalają na awarię, albo wymagają specjalnej składni.
// INTENTIONAL FALL THROUGH HERE
komentarza wielkimi literami.
GetItemsForLevel(Info)
wywołując wywołanie GetItemsForLevel(Warning)
i tak dalej.
Używam ich, gdy pewne funkcje muszą być zastosowane dla więcej niż jednej wartości. Załóżmy na przykład, że masz obiekt o właściwości o nazwie operationCode. Jeśli kod jest równy 1, 2, 3 lub 4, chcesz uruchomićOperationX (). Jeśli jest to 5 lub 6, chcesz uruchomićOperationY (), a 7 - startOperationZ (). Dlaczego masz 7 kompletnych skrzynek z funkcjonalnością i przerwami, kiedy możesz skorzystać z awariów?
Myślę, że jest całkowicie poprawny w pewnych sytuacjach, szczególnie jeśli unika 100 instrukcji if-else. =)
switch
wielokrotność case
powiązana z tym samym kodem. Różni się to od upadku, w którym wykonanie jednego case
trwa do następnego z powodu braku break
między nimi.
Przypadki wypadania są w porządku. Często stwierdzam, że wyliczenie jest używane w wielu miejscach i że gdy nie trzeba rozróżniać niektórych przypadków, łatwiej jest zastosować logikę awaryjną.
Na przykład (zwróć uwagę na komentarze wyjaśniające):
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;
}
}
Uważam, że tego rodzaju użycie jest całkowicie akceptowalne.
case X: case Y: doSomething()
i case X: doSomething(); case Y: doAnotherThing()
. W pierwszym przypadku intencja jest dość wyraźna, w drugim zaś upadek może być celowy lub nie. Z mojego doświadczenia wynika, że pierwszy przykład nigdy nie uruchamia żadnych ostrzeżeń w kompilatorach / analizatorach kodów, podczas gdy drugi tak robi. Osobiście nazwałbym drugi przykład tylko „wpadnięciem” - nie jestem jednak pewien co do „oficjalnej” definicji.
To zależy od:
Dwa główne problemy związane z przejściem jednej sprawy do drugiej to:
To uzależnia Twój kod od kolejności instrukcji case. Nie dzieje się tak, jeśli nigdy się nie przewrócisz, a to powoduje, że stopień złożoności jest często niepożądany.
Nie jest oczywiste, że kod dla jednej sprawy zawiera kod dla jednej lub więcej kolejnych spraw.
Niektóre miejsca wyraźnie zabraniają wpadania. Jeśli nie pracujesz w takim miejscu i jeśli czujesz się swobodnie z praktyką, i jeśli złamanie tego kodu nie spowoduje żadnego prawdziwego cierpienia, może to nie być najgorsze na świecie. Jeśli to zrobisz, pamiętaj o umieszczeniu przyciągającego uwagę komentarza w pobliżu, aby ostrzec tych, którzy przyjdą później (w tym ciebie przyszłego).
Oto szybki (co prawda niekompletny (bez specjalnego obchodzenia roku przestępnego)) przykład upadku, który upraszcza moje życie:
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;
}
Jeśli czuję potrzebę przechodzenia od jednej sprawy do drugiej (co rzadkie, co prawda), wolę być bardzo wyraźny i goto case
, oczywiście, zakładając, że twój język to obsługuje.
Ponieważ wpadanie jest tak rzadkie i bardzo łatwe do przeoczenia podczas czytania kodu, uważam, że właściwe jest, aby być wyraźnym - a goto, nawet jeśli jest to przypadek, powinno wyróżniać się jak obolały kciuk.
Pomaga to również uniknąć błędów, które mogą wystąpić podczas zmiany kolejności instrukcji case.