Flagi w Enum

Typy Enum są ładnym rozwiązaniem służącym do definiowania stałych. Zamiast przekazywać nic nie mówiącą liczbę do funkcji, możemy zdefiniować enum i przekazać np. TextAlignment.Left – z pewnością poprawi to czytelność kodu. Czasami jednak chcemy przekazać dwa enumy naraz lub dowolną ich kombinację. W tym celu musimy skorzystać z tzw. flag. Zacznijmy od razu od przykładu:

[Flags]
enum Days
{
    None = 0,
    Sunday = 1,
    Monday = 2,
    Tuesday = 4,
    Wednesday = 8,
    Thursday = 16,
    Friday = 32,
    Saturday = 64
}

Pierwszą kwestią jest atrybut Flags. Jak widać określiłem z góry wartości dla poszczególnych enum – jest to niezbędne, jednak  wyjaśnię to dopiero na końcu post’a. Najpierw zobaczmy co dzięki flagom możemy uzyskać (źródło MSDN):

// Initialize with two flags using bitwise OR.
Days meetingDays = Days.Tuesday | Days.Thursday;

// Set an additional flag using bitwise OR.
meetingDays = meetingDays | Days.Friday;

Console.WriteLine("Meeting days are {0}", meetingDays);
// Output: Meeting days are Tuesday, Thursday, Friday

// Remove a flag using bitwise XOR.
meetingDays = meetingDays ^ Days.Tuesday;
Console.WriteLine("Meeting days are {0}", meetingDays);

Operator OR (w C# |) służy do tworzenia kombinacji z poszczególnych typów. Z kolei XOR (W C# ^) odpowiedzialny jest za usuwanie enuma z sumy. Można operatory utożsamiać z + oraz –, jednak na końcu zobaczymy jak to wygląda dokładnie z punktu widzenia algebry Bool’a. Warto również, że dzięki atrybutowi Flags, kompilator rozpoznaje złączenia i wyświetla wszystkie elementy (a nie liczbę utworzoną przez OR).

W kodzie jednak dużo częściej będziemy musieli sprawdzić czy dana suma zawiera konkretny element (rzadko kiedy wyświetlamy enum’y jak pokazano w kodzie wyżej). Niezbędny jest zatem mechanizm umożliwiający sprawdzenie czy dany element znajduje się zbiorze. Służy do tego operator AND:

bool test = (meetingDays & Days.Thursday) == Days.Thursday;
Console.WriteLine("Thursday {0} a meeting day.", test == true ? "is" : "is not");

Przyjrzyjmy się teraz wartościom binarnym enuma:

00000000  0

00000001  1

00000010  2

00000100  4

00001000  16

00010000  32

00100000  64

Złączmy dwie ostatnie wartości tzn:

00010000  OR  00100000  = 00110000

Jak widać żaden bit nie został napisany (dzięki wykorzystanemu kodowaniu). Zobaczmy jeszcze operator AND służący do testowania czy dany element znajduje się w zbiorze:

00110000  AND 00100000  == 00100000

Gdybyśmy źle zakodowali wartości enum’a, niektóre bity mogłyby się napisać. Wykorzystując prostą algebrę można w sprytny sposób łączyć liczby.