Ta nazwa brzmi niezwykle. Jeśli ktoś kiedyś spyta was, czego się dziś nauczyliście, to powiedzcie — „Dziś nauczyliśmy się używać typów wyliczeniowych”, a na pewno mu lub jej zaimponujecie. Chyba że ta osoba zna się na programowaniu. To wtedy tylko odpowie — „Aaa, czyli ponumerowałeś parę stanów”.
11.1. Wyliczenia i stany
Słowo wyliczeniowy brzmi efektownie. Wystarczy jednak zamienić je na „ponumerowany” i od razu traci swój wyjątkowy urok. Aby zrozumieć, o co chodzi z tymi wyliczeniami, musimy znać jakiś problem, który za ich pomocą można rozwiązać.
Wiemy już, że do przechowywania wartości całkowitoliczbowych służy typ int
. Do przechowywania wartości typu prawda i fałsz służą zmienne typu bool
. Czasami jednak potrzebujemy miejsca do zapisania pewnego zakresu wartości lub stanów.
Przykładowe stany
Typy wyliczeniowe bardzo ułatwiają przechowywanie informacji o stanie. Stany to coś innego niż takie informacje, jak nazwisko klienta czy saldo rachunku.
Jeśli na przykład postanowię zaprogramować grę w statki (w której rysuje się prostokątne okręty różnego typu, a potem stara się zniszczyć jednostki rywala), to mogę zdecydować, że wybrany kwadrat może zawierać jeden z następujących obiektów:
- Puste morze
- Zaatakowany
- Okręt wojenny
- Krążownik
- Łódź podwodna
- Łódka wiosłowa
Można powiedzieć, że w ten sposób zbieram dodatkowe metadane — postanowiłem monitorować morze, a następnie dokładnie określiłem, co w danym miejscu mogę umieścić. Gdybym chciał, mógłbym ponumerować poszczególne typy obiektów:
- Puste morze = 1
- Zaatakowany = 2
- Okręt wojenny = 3
- Krążownik = 4
- Łódź podwodna = 5
- Łódka wiosłowa = 6
Wtedy musiałbym jednak samodzielnie monitorować te wartości i pamiętać, że jeśli gdziekolwiek na morzu użyję wartości 7, to popełnię błąd.
W języku C# istnieje specjalna konstrukcja umożliwiająca utworzenie typu zawierającego konkretny zbiór dopuszczalnych wartości. Typy takie nazywają się typami wyliczeniowymi.
enum SeaState
{
EmptySea, Attacked, Battleship, Cruiser, Submarine, RowingBoat
};
Utworzyłem typ o nazwie SeaState
, przy użyciu którego mogę przechowywać stan określonej części morza. Może on przyjmować tylko wartości określone powyżej i może być używany wyłącznie w odniesieniu do tych nazwanych wartości wyliczenia. Na przykład muszę napisać tak:
SeaState openSea;
openSea = SeaState.EmptySea;
Moja zmienna openSea
może przechowywać wyłącznie wartości reprezentujące stan zawartości kawałka morza. Oczywiście C# będzie reprezentować te stany za pomocą wartości liczbowych, ale sposób, w jaki to robi, nie jest moim problemem.
Zwróć uwagę, że typy tworzone przeze mnie (np. SeaState
) na listingach są oznaczone innym kolorem niż prawdziwe słowa kluczowe.
W ten sposób edytor podpowiada, że są to dodatkowe typy utworzone przez programistę i można przy ich użyciu tworzyć zmienne, ale w odróżnieniu od słów kluczowych nie należą one do języka C#. Skup się, aby na pewno dobrze zrozumieć to, o czym tu mowa. Wcześniej używaliśmy typów należących do C#, takich jak int
i double
. Teraz stworzyliśmy własny typ danych, przy użyciu którego możemy przechowywać dane potrzebne nam w naszej aplikacji.
11.2. Typy wyliczeniowe — tworzenie
Słowo kluczowe enum
umożliwia utworzenie nowego typu, nie znajdującego się wewnątrz klasy, którego będę mógł używać we wszystkich swoich programach:
using System;
enum TrafficLight
{
Red, RedAmber, Green, Amber
};
class EnumDemonstration
{
public static void Main ()
{
TrafficLight light; light = TrafficLight.Red;
}
}
Przykład kodu. Wyliczenie reprezentujące sygnalizację świetlną
Jeśli kiedykolwiek będziesz szukać sposobu na przechowanie czegoś, co może przyjąć ograniczoną liczbę wartości lub stanów (np. OnSale
, UnderOffer
, Sold
, OffTheMarket
itd.), pierwszą myśl skieruj ku typom wyliczeniowym.
Złota myśl programisty: używaj typów wyliczeniowych
Typy wyliczeniowe mają to do siebie, że na ich użyciu wszyscy korzystają. Dzięki nim pisanie programu staje się łatwiejsze, a kod jest bardziej zrozumiały i bezpieczniejszy. Dlatego często z nich korzystaj.
Weźmy na przykład system bankowy. Chcemy zapisać stan elementu wraz z innymi informacjami na temat klienta. Powiedzmy, że w banku tym używany jest następujący zestaw możliwych stanów konta: Zawieszone, Nowe, Aktywne, Zamknięte i W trakcie kontroli. Do przechowywania takich wartości idealnie nadaje się właśnie typ wyliczeniowy.
enum AccountState
{
Nowe, Aktywne, WTrakcieKontroli, Zawieszone, Zamknięte
};
W ten sposób uzyskaliśmy zmienną do przechowywania informacji na temat stanu konta bankowego. Każde konto będzie zawierało zmienną typu AccountState
reprezentującą jego stan.