instrukcja switch - obsługa domyślnego przypadku, gdy nie można go osiągnąć

15

Jeśli używam instrukcji switch do obsługi wartości z wyliczenia (który jest własnością mojej klasy) i mam wielkość liter dla każdej możliwej wartości - czy warto dodać kod do obsługi wielkości „domyślnej”?

enum MyEnum
{
    MyFoo,
    MyBar,
    MyBat
}

MyEnum myEnum = GetMyEnum();
switch (myEnum)
{
    case MyFoo:
        DoFoo();
        break;
    case MyBar:
        DoBar();
        break;
    case MyBat:
        DoBat();
        break;
    default:
        Log("Unexpected value");
        throw new ArgumentException() 
}

Nie sądzę, że dzieje się tak, ponieważ tego kodu nigdy nie można osiągnąć (nawet przy testach jednostkowych). Mój współpracownik nie zgadza się i uważa, że ​​to chroni nas przed nieoczekiwanym zachowaniem spowodowanym dodaniem nowych wartości do MyEnum.

Co mówisz, społeczność?

sd
źródło
Powiedzmy, że MyEnum jest typem nie dopuszczającym wartości zerowej.
sd
3
„Dzisiaj” nie ma wartości zerowej. Co powiesz na jutro, kiedy nie będziesz już utrzymywał kodu? A może po dodaniu „MyBiz” do wyliczenia, ale nie w tym przypadku? Komentarze Caleba na temat konserwacji są bardzo niemądre.
1
Naucz kompilator, że jest to błąd krytyczny, jeśli przełącznik nie obejmuje wszystkich przypadków.
Co się stanie, jeśli ktoś rzuci nieprawidłową wartość, MyEnuma następnie przekaże ją za pomocą przełącznika?
Mawg mówi, że przywróć Monikę
1
Jaki język? Jeśli Java, powinieneś umieścić metodę wewnątrz Enum i po prostu ją nazwać (polimorfizm), switchcałkowicie eliminując tę instrukcję.
user949300,

Odpowiedzi:

35

Dołączenie domyślnego przypadku nie zmienia sposobu działania kodu, ale sprawia, że ​​jest on łatwiejszy w utrzymaniu. Robiąc kod w sposób oczywisty (loguj wiadomość i zgłaszaj wyjątek), dołączasz dużą czerwoną strzałkę dla stażysty, którego firma zatrudni w lecie, aby dodać kilka funkcji. Strzałka mówi: „Hej, ty! Tak, rozmawiam z WAMI! Jeśli zamierzasz dodać kolejną wartość do wyliczenia, lepiej tu też wstaw skrzynkę”. Ten dodatkowy wysiłek może dodać kilka bajtów do skompilowanego programu, co należy rozważyć. Ale uratuje to także kogoś (może nawet ciebie w przyszłości) gdzieś pomiędzy godziną a dniem bezproduktywnego drapania głowy.


Aktualizacja: Sytuacja opisana powyżej, tj. Ochrona przed wartościami dodanymi do wyliczenia w późniejszym czasie, może zostać przechwycona przez kompilator. Clang (i gcc, jak sądzę) domyślnie wyświetli ostrzeżenie, jeśli włączysz typ wyliczeniowy, ale nie masz przypadku obejmującego każdą możliwą wartość w wyliczeniu. Na przykład, jeśli usuniesz defaultskrzynkę z przełącznika i dodasz nową wartość MyBazdo wyliczenia, otrzymasz ostrzeżenie:

Enumeration value 'MyBaz' not handled in switch

Pozwolenie kompilatorowi na wykrycie odkrytych przypadków polega na tym, że w dużej mierze eliminuje potrzebę tej nieosiągalnej defaultsprawy, która przede wszystkim zainspirowała twoje pytanie.

Caleb
źródło
2
Ok, przekonałeś mnie :) Muszę zaakceptować wgniecenie w numerach mojego kodu.
sd
@st Nie ma powodu, dla którego nie można przetestować tego kodu. Po prostu wykonaj kompilację testową, która warunkowo kompiluje dodatkową wartość w twoim wyliczeniu, a następnie napisz test jednostkowy, który z niej korzysta. Być może nie jest to idealne, ale prawdopodobnie nie jest to jedyny przypadek, w którym trzeba przetestować kod, który normalnie nigdy nie zostanie osiągnięty.
Caleb
Lub możesz rzucić wartość inną niż wyliczenie na typ wyliczenia i użyć tego.
Mawg mówi, że przywróć Monikę
5

Rozmawiałem dziś rano również ze współpracownikiem - to naprawdę niefortunne, ale myślę, że obchodzenie się z ustawieniem domyślnym jest wymagane ze względów bezpieczeństwa z dwóch powodów:

Po pierwsze, jak wspomina współpracownik, kod zabezpiecza kod przed dodaniem nowych wartości do wyliczenia. Może się to wydawać lub nie, ale zawsze istnieje.

Co ważniejsze, w zależności od języka / kompilatora może istnieć możliwość posiadania wartości, które nie są członkami wyliczenia w zmiennej przełączanej. Na przykład w języku C #:

MyEnum myEnum = (MyEnum) 3; // This could come from anywhere, maybe parsed from text?
// ... code goes on for a while

switch ( myEnum )
{
    case MyEnum.A:
        // ... handle A case
        break;
    case MyEnum.B:
        // ... handle B case
        break;
}

// ... code that expects either A or B to have happened

Dodając prosty case default:i rzucając wyjątek, uchroniłeś się przed tym dziwnym przypadkiem, w którym „nic” się nie dzieje, ale „coś” powinno się zdarzyć.

Niestety, w zasadzie za każdym razem, gdy piszę instrukcję switch, to dlatego, że sprawdzam przypadki wyliczenia. Naprawdę chciałbym, aby zachowanie „domyślnie wyrzucać” mogło być wymuszone przez sam język (przynajmniej przez dodanie słowa kluczowego).

Chris Phillips
źródło
3

Dodanie skrzynki domyślnej, nawet jeśli nie spodziewasz się jej osiągnąć, może być dobrą rzeczą. Ułatwi to debugowanie, jeśli Twój kod od razu zgłasza wyjątek „To nie powinno się zdarzyć”, a nie później w programie, wywołując tajemniczy wyjątek lub zwracając nieoczekiwane wyniki bez błędów.

Ryathal
źródło
2

Mówię:

Spróbuj dodać inny typ do MyEnum. Następnie zmień ten wiersz:

MyEnum myEnum = GetMyEnum();

do

MyEnum myEnum = SomethingElse;

Następnie uruchom kod z domyślną wielkością liter i bez domyślnej wielkości liter. Które zachowanie preferujesz?

Posiadanie domyślnej wielkości liter może być również przydatne do wychwytywania NULLwartości i zapobiegania NullPointerExceptions.

FrustratedWithFormsDesigner
źródło
-1

Jeśli masz pomysł, jak zaoszczędzić czas mach, nalegając na domyślny przypadek, nie musisz zadawać pytania. Ciche nie robienie niczego w przypadku błędu jest nie do przyjęcia - czy po cichu wychwytujesz wyjątki, które nigdy nie powinny się zdarzyć? Pozostawienie „kopalni” dla programistów, którzy podążają za tobą, jest również niedopuszczalne.

Jeśli Twój kod nigdy nie będzie zmieniany ani modyfikowany i jest w 100% wolny od błędów, pominięcie domyślnej wielkości może być OK.

Ada (dziadek solidnych języków programowania) nie skompiluje przełącznika na wyliczeniu, chyba że wszystkie wyliczenia są pokryte lub istnieje domyślny moduł obsługi - ta funkcja jest na mojej liście życzeń dla każdego języka. Nasz standard kodowania Ada stwierdza, że ​​przy włączonych wyliczeniach jawna obsługa wszystkich wartości bez domyślnego ustawienia jest preferowanym sposobem radzenia sobie z tą sytuacją.

mattnz
źródło
Dlaczego wszystkie -1?
mattnz
2
Nie zrobiłem ci -1, ale sądzę, że tak będzie z powodu twojego nastawienia;)
Friek