[ Pobierz całość w formacie PDF ]
W przypadku typu wyliczeniowego oznaczonego atrybutem Flags występują problemy z zasto-
sowaniem metody Enum.IsDefined. Rozważmy typ wyliczeniowy Language o następującej
deklaracji:
[Flags]
enum Language
{
CSharp = 1, VBNET = 2, VB6 = 4
}
Prawidłowe wartości typu Language to liczby {1, 2, 3, 4, 5, 6, 7}. Wartości 3, 5, 6 i 7 nie
występują jednak jawnie w definicji typu. Wartość 3 uzyskujemy poprzez wykonanie operacji
OR dla elementów CSharp i VBNET, z kolei wartość 7 uzyskamy, jeśli wszystkie elementy typu
poddamy operacji OR. W przypadku wartości 3, 5, 6 i 7 wywołanie metody Enum.IsDefined
zwróci false, co wskazuje na to, że nie są to prawidłowe wartości, podczas gdy w rzeczywistości
są prawidłowe. Potrzebujemy sposobu sprawdzenia, czy do metody przekazano prawidłowy
zbiór flag.
Aby rozwiązać problem, można wprowadzić nowy element typu wyliczeniowego Language,
który definiuje wszystkie wartości prawidłowe. W naszym przypadku deklarację typu wyli-
czeniowego Language należy zapisać w następujący sposób:
[Flags]
enum Language
{
CSharp = 1, VBNET = 2, VB6 = 4,
All = (CSharp | VBNET | VB6)
}
Nowy element typu wyliczeniowego All jest równy wszystkim pozostałym elementom typu
poddanym operacji OR. Aby teraz sprawdzić poprawność flag typu Language, można wyko-
rzystać metodę w następującej postaci:
public bool HandleFlagsEnum(Language language)
{
if ((language != 0) && ((language & Language.All) == language))
{
return (true);
}
else
{
return (false);
}
}
Analiza
Aby skorzystać z metody HandleFlagsEnum w przypadku definicji typu Language z atrybu-
tem Flags, wystarczy dodać element All. Powinien on być równy wszystkim elementom typu
poddanym operacji OR.
1.19. Sprawdzanie poprawności typu wyliczeniowego z atrybutem Flags | 51
Metoda HandleFlagsEnum wykorzystuje element All do sprawdzenia, czy element typu
wyliczeniowego jest prawidłowy. Aby to zrobić, wykonuje operację AND wartości parametru
language z elementem Language.All, a następnie sprawdza, czy wynik odpowiada oryginalnej
wartości parametru language.
W celu obsługi typu wyliczeniowego w pokazanej postaci można również przeciążyć metodę
HandleFlagsEnum (w tym przypadku zdefiniowano metodę dla typu integer zamiast typu
wyliczeniowego Language). Poniższy kod kontroluje, czy zmienna typu integer zawiera
poprawną wartość elementu typu wyliczeniowego Language:
public static bool HandleFlagsEnum(int language)
{
if ((language!=0) && ((language & (int)Language.All) == language))
{
return (true);
}
else
{
return (false);
}
}
Przeciążona wersja metody HandleFlagsEnum zwraca true w przypadku, gdy parametr
language jest prawidłowy, i false w przeciwnym razie.
Zobacz również
Sposób testowania prawidłowej wartości typu wyliczeniowego bez atrybutu Flags opisano
w recepturze 1.18.
1.20. Zastosowanie elementów typu wyliczeniowego
w masce bitowej
Problem
Chcemy, aby elementy typu wyliczeniowego były interpretowane jako flagi bitowe, które można
wykorzystać do tworzenia kombinacji wartości (flag).
Rozwiązanie
Należy oznaczyć typ wyliczeniowy za pomocą atrybutu Flags:
[Flags]
enum Language
{
CSharp = 0x0001, VBNET = 0x0002, VB6 = 0x0004, Cpp = 0x0008
}
Aby skorzystać z kombinacji elementów tego typu wyliczeniowego, wystarczy zastosować dla
nich bitową operację OR (|), na przykład:
Language lang = Language.CSharp | Language.VBNET;
52 | Rozdział 1. Liczby i typy wyliczeniowe
Analiza
Dodanie atrybutu Flags do deklaracji typu wyliczeniowego powoduje, że określone elementy
typu są traktowane jako indywidualne flagi bitowe, dla których można wykonywać bitową
operację OR. Zastosowanie typu wyliczeniowego oznaczonego atrybutem Flags niczym nie
różni się od stosowania standardowego typu wyliczeniowego. Należy zwrócić uwagę, że brak
oznaczenia typu wyliczeniowego atrybutem Flags nie spowoduje zgłoszenia wyjątku ani
powstania błędu kompilacji nawet wtedy, gdy elementy typu wyliczeniowego będą wyko-
rzystane jako flagi bitowe.
Dodanie atrybutu Flags daje dwie korzyści. Po pierwsze, w przypadku oznaczenia typu
wyliczeniowego atrybutem Flags metody ToString oraz ToString("G") zwracają ciąg zna-
ków składający się z nazw stałych rozdzielonych przecinkami. W innym razie te dwie metody
zwracają liczbową reprezentację elementu typu wyliczeniowego. Należy zwrócić uwagę, że
metoda ToString("F") zwraca ciąg znaków składający się z nazw stałych rozdzielonych prze-
cinkami, niezależnie od tego, czy określony typ wyliczeniowy oznaczono atrybutem Flags
czy nie. Aby uzyskać wskazówkę dotyczącą sposobu działania tej metody, można zapoznać
się z opisem typu formatowania "F" w tabeli 1.2 zamieszczonej w recepturze 1.16.
Druga korzyść polega na tym, że w przypadku natknięcia się na typ wyliczeniowy w analizo-
wanym kodzie, można łatwiej ocenić, do czego programista chciał go wykorzystać. Jeśli jawnie
zdefiniował go z atrybutem Flags, możemy stosować jego wartości jako maski bitowe.
Typ wyliczeniowy oznaczony atrybutem Flags można interpretować jako wartość pojedynczą
lub jako kilka wartości połączonych w jedną. Jeśli chcemy jednocześnie przekazać kilka języków,
możemy wykorzystać następujący kod:
Language lang = Language.CSharp | Language.VBNET;
Zmienna lang jest teraz równa bitowym wartościom dwóch elementów typu wyliczeniowego
po poddaniu ich operacji OR. W wyniku wykonania tej operacji uzyskamy wartość 3, tak jak
pokazano poniżej:
Language.CSharp 0001
Language.VBNET.....0010
Po wykonaniu OR 0011
Elementy typu wyliczeniowego przekształcono na liczby dwójkowe, a następnie wykonano
operację OR. W jej wyniku uzyskano wartość 0011, czyli 3 w systemie dziesiętnym. Dla kom-
pilatora ta wartość to zarówno dwa elementy typu wyliczeniowego po wykonaniu operacji OR
(Language.CSharp | Language.VBNET), jak pojedyncza wartość 3.
Aby sprawdzić, czy w zmiennej typu wyliczeniowego włączono określoną flagę, można sko-
rzystać z bitowego operatora AND (&) w następujący sposób:
Language lang = Language.CSharp | Language.VBNET;
if((lang & Language.CSharp) == Language.CSharp)
Console.WriteLine("Zmienna zawiera flagę odpowiadającą językowi C#");
else
Console.WriteLine("Zmienna nie zawiera flagi odpowiadającej językowi C#");
Wykonanie powyższego kodu spowoduje wyświetlenie tekstu Zmienna zawiera flagę odpowia-
dającą językowi C#. Wykonanie operacji AND dla zmiennej lang i elementu Language.CSharp
zwróci zero, jeśli w zmiennej lang nie włączono flagi odpowiadającej językowi C#, lub wartość
1.20. Zastosowanie elementów typu wyliczeniowego w masce bitowej | 53
Language.CSharp w przypadku, gdy w zmiennej lang włączono tę flagę. Wykonanie operacji
AND dla dwójkowych reprezentacji obu wartości można zapisać w sposób następujący:
Language.CSharp | Language.VBNET 0011
Language.CSharp 0001
[ Pobierz całość w formacie PDF ]