class ColorPick{ static void Main(){Color favorite = SelectFavoriteColor();RespondToFavoriteColor(favorite);}static Color...
Serwis znalezionych hasełOdnośniki
- Smutek to uczucie, jak gdyby się tonęło, jak gdyby grzebano cię w ziemi.
- In England, Wales and Northern Ireland the General Certificate of Secondary Education General Certificate of Education Advanced (GCE (GCSE) is the main...
- <menubar name="Main Window" id="DWMainWindow"> <menu name="_File" id="DWMenu_File"> <menuitem name="_New" key="Cmd+N"...
- To define the Dreamweaver remote folder: 1 In the Advanced tab of the Site Definition dialog box, select Remote Info from the Category list...
- Tuż przed obiadem Shannon otrzymał wiadomość z Pithead, że Carizan znajduje się w drodze na Jowisza Pięć przez bazę Main na Ganimedesie...
- string ob_get_length ( void) ob_implicit_flush Włącza lub wyłącza ukryte opróżnianie bufora wyjściowego (jeżeli nie podany został znacznik...
- INTERSECT INTERSECT INTERSECT returns all the common elements of two SELECT statements...
- INPUT/OUTPUT: SQL> SELECT DISTINCT COMPANY...
- The Arrays class has another sort( ) method that takes a single argument: an array of Object, but with no Comparator...
- LCC–00114 illegal boolean response character Cause A value other than TRUE or FALSE was specified...
- EXT REF1 SELECT lub 1106 EXT REF2 SELECT – na COMM, COMM+AI1 lub COMM*AI1...
Smutek to uczucie, jak gdyby się tonęło, jak gdyby grzebano cię w ziemi.
Write("Wpisz swój ulubiony kolor (");
foreach (string name in Enum.GetNames(typeof(Color)))
Console.Write(" {0} ", name);
Console.Write("): ");
string input = Console.In.ReadLine();
try
{
favorite = (Color)Enum.Parse(typeof(Color), input, true);
}
catch (ArgumentException)
{
// Dane wpisane przez użytkownika nie pasują do nazw w wyliczeniu
Console.WriteLine("Błędny kolor, wybierz jeszcze raz!");
Console.WriteLine();
92
Część I n Podstawowe informacje o CLR
}
}
while (favorite == (Color)(0xFF));
return favorite;
}
static void RespondToFavoriteColor(Color c)
{
// Zauważmy, że w C# można stosować instrukcję switch na wyliczeniu
switch (c)
{
case Color.Czerwony:
// Robimy coś dla osób, które lubią kolor czerwony
// …
break;
case Color.Zielony:
// Robimy coś dla osób, które lubią kolor zielony
// …
break;
case Color.Niebieski:
// Robimy coś dla osób, które lubią kolor niebieski
// …
break;
default:
// Wykryto nieznany kolor.
// Jest to prawdopodobnie błąd, ale musimy go obsłużyć!
break;
}
}
}
Zauważmy pomocnicze metody statyczne takie jak GetNames i Sarse należące do samego typu Enum. Klasę Enum omówimy dokładniej w dalszej części rozdziału.
Wyliczenia znacznikowe
Najczęściej wartości wyliczenia wzajemnie się wykluczają, co oznacza, że dana instancja wyliczenia może mieć tylko jedną z możliwych wartości. Czasem jednak pojedyncza instancja musi reprezentować kombinację wartości. Typ wyliczeniowy może być oznaczony atrybutem niestandardowym System.FlagsAttribute, który sprawia, że języki dopuszczają łączenie i wyodrębnianie poszczególnych wartości z pojedynczej instancji. Rozważmy na przykład zbiór uprawnień do operacji na pliku:
[Flags]
enum FileAccess
{ Read = 1,
Write = 2,
ReadWrite = 3;
}
Pliki często otwiera się jednocześnie do odczytu (Read) i zapisu (Write). Użycie wyliczenia znacznikowego pozwala wyrazić tę ideę. Zauważmy, że liczbowe wartości Read i Write są potęgami 2, począwszy od 1. Dlaczego? Otóż łączenie lub wyodrębnianie wartości
Rozdział 2. n Wspólny system typów
93
z pojedynczej instancji odbywa się z wykorzystaniem bitowych operacji AND lub OR. Kolejne elementy wyliczenia zajmowałyby wartości 4, 8, 16, 32 itd. Kompilator C# nie numeruje ich automatycznie — trzeba zrobić to ręcznie, bo w przeciwnym razie zostanie zastosowany sekwencyjny sposób numerowania, który nie działałby prawidłowo z operacjami bitowymi.
Zauważmy, że w celu zasygnalizowania możliwości zapisu i odczytu dwie niezależne wartości są logicznie sumowane: 1 | 2 to 3, stąd pomocnicza wartość ReadWrite. Dzięki niej w po-niższym przykładzie druga instrukcja jest równoważna pierwszej, ale bardziej czytelna: FileAccess rw1 = FileAccess.Read | FileAccess.Write; // wartość 3
FileAccess rw2 = FileAccess.ReadWrite; // wartość 3
Wyliczeń znacznikowych oczywiście nie można testować pod kątem równości, aby sprawdzić, czy instancja (z kombinacją wartości) zawiera konkretną wartość. Jeśli na przykład sprawdzamy, czy instancja FileAccess zezwala na odczyt, moglibyśmy napisać:
FileAccess fa = /* … */;
if (fa == FileAccess.Read)
// mamy zezwolenie na odczyt
else
// nie mamy dostępu
Niestety, gdyby wyliczenie fa miało wartość FileAccess.ReadWrite, test zawiódłby i nie uzyskalibyśmy dostępu do pliku. W większości przypadków byłoby to niewłaściwe. Musimy zatem użyć bitowej operacji AND i wyodrębnić poszczególne wartości z instancji wyliczenia: FileAccess fa = /* … */;
if ((fa & FileAccess.Read) != 0)
// mamy zezwolenie na odczyt
else
// nie mamy dostępu
Ten test daje prawidłowy wynik, tzn. zezwala na dostęp, kiedy wartością wyliczenia jest FileAccess.ReadWrite.
Ograniczenia bezpieczeństwa typologicznego
Wspomniałem już kilkakrotnie, że wyliczenia zapewniają bezpieczeństwo typologiczne tam, gdzie dawniej istniało ryzyko, że użytkownik poda wartość spoza dopuszczalnego zakresu.
Jest to jednak nieco mylące. Wyliczenia opierają się na prostych, podstawowych typach danych, więc można sfabrykować instancję, która zawiera wartość nieodpowiadającą żadnej nazwie.
W przykładzie zamieszczonym na początku niniejszego punktu — z wyliczeniem Color o wartościach Czerwony (0), Zielony (1) i Niebieski (2) — możemy utworzyć instancję, która ma wartość 3!
Color MakeFakeColor()
{ return (Color)3;
}
Nie ma sposobu, aby temu zapobiec. Jedyne, co można zrobić, to zachować ostrożność w razie przyjmowania instancji wyliczenia z niezaufanego źródła. Jeśli na przykład napisaliśmy publiczne wywołanie API, które przyjmuje wyliczenie jako parametr wejściowy, musimy
94
Część I n Podstawowe informacje o CLR
zawsze sprawdzać, czy jego wartość mieści się w dozwolonym zakresie. Aby to ułatwić, typ System.Enum definiuje statyczną metodę pomocniczą IsDefined. Zwraca ona wartość logiczną, która wskazuje, czy dana wartość należy do zdefiniowanego zakresu określonego wyliczenia: void AcceptColorInput(Color c)
{ // Sprawdzamy, czy wejściowa wartość wyliczenia jest prawidłowa
if (!Enum.IsDefined(typeof(Color), c))
throw new ArgumentOutOfRangeException("c");
// Zweryfikowaliśmy dane wejściowe, możemy z nimi pracować
// …
}
Jeśli ktoś celowo wywoła metodę AcceStColorInSut z nieprawidłową instancją wyliczenia Color, metoda zgłosi wyjątek, zamiast zawieść w nieoczekiwany sposób:
AcceptColorInput(MakeFakeColor());
Metoda ta nie sprawdza się zbyt dobrze w przypadku wyliczeń znacznikowych. Jeśli instancja takiego wyliczenia reprezentuje kombinację wartości, to sama nie jest prawidłowym wyborem spośród zakresu wartości wyliczenia. Zwykle jest to jednak do przyjęcia. Prawdopodobnie kod nie będzie wykonywał instrukcji switch ani sprawdzał dokładnej równości. Zamiast tego zawiodą wszystkie testy bitów, prowadząc do gałęzi programu, która zapoczątkuje kontro-lowane zgłoszenie błędu.
Inne metody pomocnicze