Jak wygląda poprawna implementacja enuma

Tak IMHO powinien wyglądać dobrze napisany enum:

public enum DocumentStatus
{
    Draft = 1,
    Approved = 2,
    UnderRevision = 3,
    Released = 4
}

Nie enumerujemy od zera

„0” jest domyślną wartością dla enuma (bo pod spodem musi być typ całkowity). Dlatego chcemy aby nasze znaczące wartości zaczynały się od jedynki. Wtedy prościej wyśledzić w której ścieżce wykonania jest błąd, spowodowany nieustawieniem wartości. W naszych switchach zawsze wtedy wpadniemy w default gdzie najlepiej rzucić wyjątkiem.

Pozbyć się wartości Undefined

Ten enum mógłby wyglądać tak:

public enum DocumentStatus
{
    Undefined = 0,
    Draft = 1,
    Approved = 2,
    UnderRevision = 3,
    Released = 4
}

Inne spotykane nazwy to Unknown, NotExists, NoValue, itp.

Niby podobnie do wzorcowego przykładu, ale jednak ma wady. Gdy generejemy instrukcję switch to z pomocą R# dostaniemy wypełnione wszystkie wartości enumów. I niestety dostaniemy też case dla Undefined, a lepiej żeby te przypadki wpadły do default i spowodowały wyjątek.

switch with undefined

Dziedziczyć po byte?

Wystarczy nie dziedziczyć po niczym wtedy domyślnie jest to int i wszystko jest w porządku. Trzeba mieć dobry powód żeby dziedziczyć np. po byte, ponieważ w procesorze nie ma typu byte więc i tak musi zachodzić konwersja byteint.

Zasada: Domyślnie po niczym nie dziedziczymy

W przykładzie poniżej dziedziczę po byte ponieważ enum ląduje w bazie (korzystam z EF) i tam staram się używać typów, które mniej miejsca zajmują.

public enum DocumentStatus : byte
{
    Draft = 1,
    Approved,
    UnderRevision,
    Released
}

Czy nadawać wartości liczbowe wszystkim?

Kiedyś przeczytałem rozróżnienie, żeby nadawać wszystkie wartości tylko tym enumom, które lądują w bazie. To jest na pewno ważne, bo zdarza się często, że usuwamy jakąś wartość ze „środka”. Było tam też napisane, że dla enumów, które nie lądują w bazie nie powinno się tego robić, tylko zostawić to frameworkowi.

public enum DocumentStatus
{
    Draft = 1,
    Approved,
    UnderRevision,
    Released
}

W praktyce wydaje mi się, że lepiej zdecydować się na jedno i zawsze nadawać wartości.

Czy macie sytuację gdzie lepiej nadać tylko pierwszą wartość równą 1 (żeby domyślnie nie numerowało od 0), a numerowanie pozostałych zostawić frameworkowi?

Reklamy
Ten wpis został opublikowany w kategorii Programowanie i oznaczony tagami , . Dodaj zakładkę do bezpośredniego odnośnika.

4 odpowiedzi na „Jak wygląda poprawna implementacja enuma

  1. Pingback: dotnetomaniak.pl

  2. Maciek pisze:

    Wydaje mi się, ze nie warto jest się pozbywać wartości Undefined. Dzięki temu możemy rozdzielić przypadki, gdy wartość jest niezdefiniowana, a np poprostu błędna. dostaję 0, aha jest undefined, bo obiekt dopiero został stworzony. Dostaję wartość np -5 wiedz, że coś się dzieje…

    • @Maciek a czy Twoja domena rozróżnia między wartością „niezdefiniowaną” a „błędną”? Chciałem uwypuklić, że lepiej traktować bardzo ostro wszystkie „niezdefiniowane/błędne” dane niż pozwalać im żyć w systemie.

  3. krepiarz pisze:

    Jawne specyfikowanie wszystkich wartości ma tę zaletę, że programista zastanowi się dwa razy, zanim potem wstawi nową wartość enuma w środek, co łamie kompatybilność binarną. Gdy wartości nie są jawnie podane, mało kto o tym pamięta i często niechcący zmienia semantykę programu.

Możliwość komentowania jest wyłączona.