Typy wyliczeniowe to nic innego, jak zbiór wartości reprezentowanych przez słowa. Aby zadeklarować taką grupę, należy użyć słowa kluczowego enum
, po którym następuje nazwa, a następnie wartości oddzielone przecinkami. Oto przykład.
public enum Alignment
{
Left,
Center,
Right,
}
Wyobraźmy sobie sytuację, w której jesteśmy odpowiedzialni za przygotowanie metody wyświetlającej tekst w konsoli. Metoda, oprócz tekstu, ma przyjmować parametr typu int
, którego wartość będzie sterować wyrównaniem tekstu: 0 - do lewej, 1 - wyśrodkowany, oraz 2 - do prawej, identycznie jak w edytorach tekstu. Aby uzyskać wyrównanie, użyjemy właściwości CursorLeft
klasy Console
, która odpowiada za ustawienie pozycji kursora względem lewej krawędzi. Użycie metody WriteLine
spowoduje wstawienie tekstu w miejscu kursora. Aby określić liczbę znaków mieszczących się w wierszu konsoli, skorzystamy z właściwości WindowWidth
klasy Console
. W głównej metodzie aplikacji dodajmy trzykrotne uruchomienie przygotowanej metody, za każdym razem z innym wyrównaniem tekstu.
using System;
namespace TypyWyliczeniowe
{
class Program
{
static void Main(string[] args)
{
Write("Hello, I''m Robert Troska", 0);
Write("Programming is my passion ", 1);
Write("See you soon", 2);
Console.ReadLine();
}
private static void Write(string text, int alignment)
{
switch(alignment)
{
case 0:
Console.CursorLeft = 0;
break;
case 1:
Console.CursorLeft = (Console.WindowWidth - text.Length) / 2;
break;
case 2:
Console.CursorLeft = Console.WindowWidth - text.Length;
break;
}
Console.WriteLine(text);
}
}
}
Prawdopodobnie wasza metoda wygląda podobnie. Pisząc ten fragment kodu, wiemy, co oznaczają poszczególne wartości odpowiedzialne za wyrównanie tekstu. Jednak zmuszanie siebie, a co gorsza – innych, do zapamiętywania znaczenia tych wartości nie jest dobrym pomysłem. Korzystanie z dokumentacji, chociaż pomocne, wymaga poświęcenia dodatkowego czasu. Jakże wygodnie byłoby, gdyby zamiast zmiennej typu int
można było użyć typu wyliczeniowego. Spróbujmy zatem zrefaktoryzować naszą metodę, by wykorzystać wcześniej zaprezentowany enum Alignment. Takie podejście pozwala w naturalny sposób zrozumieć działanie poszczególnych wartości. Poniżej zamieszczam poprawioną metodę.
private static void Write(string text, Alignment alignment)
{
switch(alignment)
{
case Alignment.Left:
Console.CursorLeft = 0;
break;
case Alignment.Center:
Console.CursorLeft = (System.Console.WindowWidth - text.Length) / 2;
break;
case Alignment.Right:
Console.CursorLeft = System.Console.WindowWidth - text.Length;
break;
}
Console.WriteLine(text);
}
Typ wyliczeniowy w rzeczywistości jest ukrytym typem int
. Możliwa jest zmiana domyślnego typu poprzez jego określenie po znaku dwukropka. Domyślnie, numeracja zaczyna się od zera i zwiększa się o jeden. Nic jednak nie stoi na przeszkodzie, by pominąć kolejne wartości. Ponadto, typy wyliczeniowe możemy rzutować zarówno z typu int
na enum
, jak i odwrotnie.
namespace TypyWyliczeniowe
{
public enum Anchor : short
{
None = 0,
Left = 1,
Top = 2,
Right = 10,
Bottom = 11
}
class Program
{
static void Main(string[] args)
{
System.Console.WriteLine("Unboxing (int)Anchor.Right = {0}", (int)Anchor.Right);
System.Console.WriteLine("Unboxing (Anchor)2 = {0}", (Anchor)2);
System.Console.ReadLine();
}
}
}
Inny sposób deklaracji typu wyliczeniowego umożliwia niewykluczanie się wzajemnie wartości, co pozwala na ich sumowanie. W sytuacji, gdy potrzebujemy jednocześnie ustawić kilka wartości, możemy użyć operatora |
. Do sprawdzenia, czy zmienna zawiera daną wartość, służy operator &
. Tworząc zbiór, którego wartości będą łączone, należy dodać atrybut Flags
. Pominięcie tej flagi nie spowoduje błędu, gdyż kompilator C# ignoruje ten atrybut. Czy zatem jest sens dodawania tej flagi? Mimo iż flaga jest ignorowana przez kompilator, programista może stwierdzić, czy podczas tworzenia typu zakładano jego łączenie. Dodatkowo są miejsca, które z tego korzystają, np. metoda ToString
, która w przypadku ustawienia atrybutu potrafi wyświetlić wartość. Sprawdźcie sami, jak to działa.
namespace TypyWyliczeniowe
{
[System.Flags]
public enum Anchor
{
None = 0,
Left = 1,
Top = 2,
Right = 0x4,
Bottom = 0x8
}
class Program
{
static void Main(string[] args)
{
var anchor = Anchor.Left | Anchor.Right;
System.Console.WriteLine("Anchor.Left | Anchor.Right = {0}", anchor);
System.Console.WriteLine("anchor is Right = {0}", (anchor & Anchor.Right) > 0);
System.Console.WriteLine("anchor is Top = {0}", (anchor & Anchor.Top) > 0);
System.Console.ReadLine();
}
}
}
Statyczna klasa Enum
z przestrzeni nazw System
udostępnia kilka przydatnych funkcji. Metoda GetValues
zwraca wszystkie wartości dla przekazanego w parametrze typu. Natomiast GetName
zwraca nazwę związaną z przekazaną wartością.
static void Main(string[] args)
{
foreach (var e in System.Enum.GetValues(typeof(Anchor)))
System.Console.WriteLine(e);
System.Console.WriteLine("Anchor, 2 = {0}", System.Enum.GetName(typeof(Anchor), 2));
System.Console.ReadLine();
}
Troska Robert