1. Wprowadzenie
W programowaniu często trzeba operować listami z góry znanych, ograniczonych opcji. Na przykład: dni tygodnia, kolory świateł drogowych, poziomy trudności gry, statusy zamówienia. Każda z tych opcji to zwykle stała wartość. I tu właśnie dochodzimy do naszego dzisiejszego tematu — enumów (enum).
Enum — to specjalny typ danych, który pozwala zadeklarować zestaw nazwanych stałych.
enum — jak pudełko z przegródkami i naklejkami, gdzie każda naklejka to unikalna nazwa powiązana z liczbą.
- Twój kod staje się czytelny i samoopisujący się;
- Zamiast tajemniczych „magicznych liczb” typu 0, 1, 2 pojawiają się wyraziste nazwy: DayOfWeek.Monday albo TrafficLight.Red;
- Kompilator się obrazi, jeśli spróbujesz przypisać niepoprawną wartość;
- Za każdym elementem kryje się... zwykła liczba całkowita! (Ale do tego jeszcze wrócimy.)
2. Składnia deklaracji enumów
Zróbmy pierwszy enum — typy pogody do naszej przyszłej apki. Tak to wygląda:
// Enum warunków pogodowych
public enum WeatherCondition
{
Sunny, // 0
Cloudy, // 1
Rainy, // 2
Stormy, // 3
Snowy // 4
}
- Słowo kluczowe enum definiuje nowy typ.
- W klamrach — nazwy opcji. Domyślnie każda opcja dostaje liczbę całkowitą, zaczynając od zera (Sunny == 0, Cloudy == 1 itd.).
- Możesz jawnie ustawić wartości:
public enum WeatherCondition
{
Sunny = 1,
Cloudy = 2,
Rainy = 4,
Stormy = 8,
Snowy = 16
}
To wygodne, jeśli wartości mają odpowiadać jakimś zewnętrznym standardom albo protokołom.
3. Użycie enumów
Wyobraź sobie, że napisaliśmy apkę — mały program konsolowy, który reaguje na pogodę.
// Klasa aplikacji
class Program
{
static void Main()
{
// Używamy naszego enum do przechowywania stanu pogody
WeatherCondition todayWeather = WeatherCondition.Sunny;
// Wyświetlamy na ekranie
Console.WriteLine($"Dzisiaj pogoda: {todayWeather} ({(int)todayWeather})");
// A teraz zasymulujemy zmianę pogody
todayWeather = WeatherCondition.Rainy;
Console.WriteLine($"Oj! Pogoda się zmieniła: {todayWeather} ({(int)todayWeather})");
}
}
Co się stanie?
Dzisiaj pogoda: Sunny (0)
Oj! Pogoda się zmieniła: Rainy (2)
- todayWeather — to nie string, nie liczba, tylko element enum.
- Rzutowanie na (int) pozwala zobaczyć „liczbowy” kod tej wartości.
Enumy świetnie współpracują z warunkami:
if (todayWeather == WeatherCondition.Rainy)
Console.WriteLine("Weź parasol!");
4. Praktyczne scenariusze użycia enum
Gdzie spotkasz enumy?
- W aplikacjach biznesowych: statusy zamówień (New, Processing, Shipped, Cancelled).
- W systemach zarządzania użytkownikami: poziomy dostępu (User, Moderator, Admin).
- W grach: stany postaci (Idle, Moving, Jumping, Falling).
- W GUI: przyciski, komunikaty, kolory ikonek.
A jeśli wejdziesz głębiej w .NET — znajdziesz je praktycznie w co drugim klasie standardowej biblioteki! Na przykład operacje na plikach mają enumy do określania trybu otwarcia pliku (czytanie, zapis, tworzenie).
5. Jawne wartości i zakres wartości
Czasem chcesz przekonwertować zestaw int używanych w kodzie na enum. To bardzo proste:
public enum CompassDirection
{
North = 10,
East = 20,
South = 30,
West = 40
}
Teraz CompassDirection.North = 10, East = 20 itd. Jeśli jakiś element nie ma jawnie podanej wartości, dostaje automatycznie +1 względem poprzedniego.
Typ przechowywania
Domyślnie enum jest przechowywany jako int (4-bajtowa liczba całkowita). Ale możesz podać swój typ bazowy, jeśli chcesz oszczędzić pamięć:
public enum TinyEnum : byte
{
First, // 0
Second, // 1
Third // 2
}
Teraz każda wartość enum to tylko jeden bajt!
Pełna lista możliwych typów: byte, sbyte, short, ushort, int, uint, long, ulong.
6. Konwersja między enum a liczbami/stringami
Konwersja enum ↔ int:
Żeby przekonwertować enum na int i odwrotnie, po prostu użyj rzutowania:
WeatherCondition current = WeatherCondition.Cloudy;
int number = (int)current; // Dostaniemy 1
WeatherCondition fromNumber = (WeatherCondition)2; // Dostaniemy Rainy
Konwersja enum ↔ string:
Enum na string? Wystarczy wywołać metodę ToString().
// Konwertujemy enum na string
string name = WeatherCondition.Stormy.ToString(); // "Stormy"
// Konwertujemy string na enum przez Enum.Parse
WeatherCondition parsed = (WeatherCondition)Enum.Parse(typeof(WeatherCondition), "Stormy");
// Można też użyć Enum.TryParse
WeatherCondition parsedOk;
if (Enum.TryParse("Snowy", out parsedOk))
{
Console.WriteLine(parsedOk); // Snowy
}
Typowy błąd:
Jeśli zrzutujesz liczbę na enum, dla której nie ma takiego członka, kompilator się nie obrazi, ale dostaniesz „wirtualny” stan.
Na przykład:
WeatherCondition weird = (WeatherCondition)999;
Console.WriteLine(weird); // 999 (nie zamieni się w "Unknown"!)
Czyli programista musi uważać przy takich konwersjach.
7. switch i enum są dla siebie stworzone
Enumy (enum) świetnie współpracują z operatorem switch. To wygodny i czytelny sposób na „reagowanie” na różne wartości enum — bez długich „if-else”.
Przykład: reakcja na pogodę
public enum WeatherCondition
{
Sunny,
Cloudy,
Rainy,
Stormy,
Snowy
}
WeatherCondition today = WeatherCondition.Rainy;
switch (today)
{
case WeatherCondition.Sunny:
Console.WriteLine("Słonecznie! Można iść na spacer.");
break;
case WeatherCondition.Cloudy:
Console.WriteLine("Pochmurno. Możesz wziąć bluzę.");
break;
case WeatherCondition.Rainy:
Console.WriteLine("Pada deszcz. Nie zapomnij parasola!");
break;
case WeatherCondition.Stormy:
Console.WriteLine("Burza! Lepiej zostań w domu.");
break;
case WeatherCondition.Snowy:
Console.WriteLine("Pada śnieg — załóż czapkę!");
break;
default:
Console.WriteLine("Nieznana pogoda...");
break;
}
- Każdy przypadek (case) odpowiada jednej wartości enum.
- Jeśli wartość nie pasuje do żadnej znanej opcji, wykona się default (przydatne, jeśli do zmiennej trafi coś niespodziewanego).
Dlaczego połączenie enum + switch jest super?
- Kod jest czytelny: wszystkie możliwe opcje widać od razu po liście case.
- Łatwo rozbudować: dodajesz nowy typ pogody — po prostu dodaj kolejny case.
- Bezpieczeństwo: kompilator ostrzeże, jeśli zapomnisz obsłużyć wszystkie opcje (od C# 8.0 można nawet włączyć sprawdzanie kompletności).
Przykład: enum z logiką do gry
public enum GameState
{
Start,
Playing,
Paused,
GameOver
}
void PrintState(GameState state)
{
switch (state)
{
case GameState.Start:
Console.WriteLine("Witamy w grze!");
break;
case GameState.Playing:
Console.WriteLine("Gra trwa. Powodzenia!");
break;
case GameState.Paused:
Console.WriteLine("Pauza. Możesz odpocząć.");
break;
case GameState.GameOver:
Console.WriteLine("Koniec gry. Spróbuj ponownie!");
break;
default:
Console.WriteLine("Nieznany stan gry.");
break;
}
}
Tipy:
- Jeśli masz dużo podobnych akcji dla kilku opcji, możesz je połączyć:
switch (today)
{
case WeatherCondition.Rainy:
case WeatherCondition.Stormy:
Console.WriteLine("Nie zapomnij parasola i cieplejszych ciuchów!");
break;
}
- Nie zapominaj o default, żeby obsłużyć niespodziewane wartości (np. jeśli ktoś zrzutuje liczbę na enum bezpośrednio).
- Ze switch łatwo budować „maszyny stanów”, menu i wszelkie systemy z ograniczonym zestawem opcji.
8. Enumy i podstawowe metody/usługi .NET
.NET daje masę przydatnych metod do enumów:
- Enum.GetNames(typeof(WeatherCondition)) — pobierz wszystkie nazwy.
- Enum.GetValues(typeof(WeatherCondition)) — wszystkie wartości.
- Enum.IsDefined(typeof(WeatherCondition), "Rainy") — sprawdź, czy taki członek istnieje w enumie.
- Dokumentacja: System.Enum
Przykład wypisania wszystkich opcji pogody:
foreach (var name in Enum.GetNames(typeof(WeatherCondition)))
{
Console.WriteLine(name);
}
9. Częste błędy i „pułapki”
- Używanie „magicznych liczb” zamiast enumów, przez co kod traci czytelność.
- Brak obsługi wartości, które nie są zdefiniowane w enumie (patrz przypadek z rzutowaniem liczby).
- Nazywanie elementów enumów bez sensu (Value1, Value2... — i już nikt nie pamięta, o co chodziło).
- Łamanie zasady pojedynczej odpowiedzialności — używanie jednego enum do różnych rzeczy naraz.
Najlepsze rozwiązanie: zawsze używaj enumów, gdy masz skończony zestaw opcji, i dawaj elementom sensowne, samoopisujące się nazwy.
GO TO FULL VERSION