Wyjątki w C# Opracował dr Robert Fidytek Wyjątki Nie ma programu, który nie byłby pozbawiony błędów. Są jedynie programy, w których błędy się nie ujawniają, co jednak nie oznacza, iż program nie zawiera błędów. Występują one w sytuacjach, o których nie pomyślał programista. Sytuacje, które mogą spowodować błąd można podzielić na trzy kategorie:
Błędy programisty błędy popełniane najczęściej przez nieuwagę programisty np: wykroczenie indeksu tablicy poza jej zakres.
Błędy użytkownika - błędy spowodowane niewłaściwym użytkowaniem programu przez użytkownika np: wpisanie liter w miejsce kodu pocztowego. Jeśli w kodzie programu programista nie zadba o sprawdzanie poprawności wprowadzanych danych może nastąpić wygenerowanie błędu.
Wyjątki - anomalie uruchomieniowe, które trudno przewidzieć w czasie pisania programu np: próba łączenia się z bazą danych, która już nie istnieje. 2 Blok try i catch Do wyłapywania wyjątków służy blok try. W tym bloku umieszcza się instrukcje kodu, które mogą spowodować pojawienie się wyjątku. Natomiast do wyłapywania pojawiających się błędów służy blok catch. W bloku catch umieszcza się kod, który informuje użytkownika w przyjazny sposób o tym, że wystąpił jakiś błąd. Dzięki wyłapywaniu pojawiających się błędów program nie zawiesza nagle swojego działania. Oprócz kodu informującego w tym bloku umieszcza się instrukcje, które obsługują błąd np: wycofują wprowadzone zmiany. Schemat obsługi wyjątków: try { //"pilnowany" kod programu } catch (TypWyjątku ex) //ex uchwyt do zgłoszonego wyjątki { //kod obsługi wyjątku } 3 Obsługa wyjątków Jeżeli podczas wykonywania bloku try (kodu "pilnowanego") nie zostanie zgłoszony wyjątek, to blok catch (kod obsługi wyjątku) zostanie pominięty. W przypadku, gdy podczas wykonywania zostanie zgłoszony wyjątek typu podanego w instrukcji catch lub typu, dla którego typ podany w instrukcji catch jest typem ogólnym (bazowym), wtedy wykonanie kodu "pilnowanego zostanie przerwane i przechodzimy do wykonania kodu obsługi wyjątku. Po wykonaniu wszystkich instrukcji kodu obsługi wyjątku program przechodzi do wykonania instrukcji znajdujących się za blokiem catch, oczywiście pod warunkiem, że w kodzie obsługi wyjątku nie został zgłoszony wyjątek. Jeżeli nie zostanie znaleziony blok try, z którym skojarzona jest instrukcja catch (z odpowiednim typem wyjątku), program zostanie przerwany i zostanie wyświetlony komunikat o kłopotach z danym programem. 4 Obsługa wszystkich wyjątków Jeżeli chcemy przechwycić wszystkie zgłoszone wyjątki w danym bloku try, w instrukcji catch nie podajemy typu wyjątku: try { //"pilnowany" kod programu } catch { //kod obsługi wyjątku } Rozwiązanie to uniemożliwia jednak uzyskanie dostępu do obiektu wyjątku, który może zawierać ważne informacje na temat błędu. try { //"pilnowany" kod programu } catch(System.Exception ex) //uzyskujemy dostęp do informacji o błędzie { 5 //kod obsługi wyjątku } Przykład 1: dzielenie przez zero static void Main(string[] args) { int a = 0, b; try { b = 1 / a; //wyjątek (dzielimy przez zero) } catch(DivideByZeroException ex) { Console.WriteLine("Próbujesz dzielić przez zero!"); } Console.ReadKey(true); 6 } Przykład 2: dzielenie przez zero static void Main(string[] args) { int a = 0, b; try { b = 1 / a; //wyjątek (dzielimy przez zero) } catch(System.Exception ex) { Console.WriteLine("Komunikat wyjątku {0}",ex.Message); } Console.ReadKey(true); 7 } Użycie wielu bloków catch Z pojedynczym blokiem try może zostać skojarzonych wiele bloków catch. Zgłoszony wyjątek może być obsłużony tylko przez jeden blok catch. Wyjątek zostanie obsłużony przez pierwszy blok catch, którego typ jest zgodny z typem zgłoszonego wyjątku. Dlatego powinno się umieszczać wyjątki bardziej szczegółowe przed wyjątkami bardziej ogólnymi. W przeciwnym razie wyjątek bardziej szczegółowy nigdy nie zostałby wykonany. try { //"pilnowany" kod programu } catch(DivideByZeroException ex) { //kod obsługi wyjątku szczegółowego } catch(System.Exception ex) { //kod obsługi wyjątku ogólnego 8 } Blok finally W sytuacji pojawienia się wyjątku jest przerywane działanie instrukcji w bloku try, a sterowanie jest przenoszone do catch. Mechanizm wyjątków udostępnia jeszcze jeden blok - jest to finally. Jest on opcjonalny, a instrukcje w nim zawarte są realizowane zawsze na końcu, niezależnie czy wystąpił wyjątek czy nie. Blok ten może zapobiegać duplikowaniu kodu, który musiałby znajdować się blokach try i catch. Przykładowe sytuacje, w jakich powinno się używać się tego bloku to między innymi: zamykanie plików, rozłączanie z bazą danych. try { //"pilnowany" kod programu } catch(TypWyjątku ex) { //kod obsługi wyjątku } finally { 9 //kod realizowany na końcu, niezależnie od wystąpienia wyjątku } Zgłaszanie wyjątków Wyjątek można zgłosić samodzielnie, służy do tego instrukacja throw: throw new TypWyjątku(); Z wyjątkiem możemy skojarzyć pewien komunikat informujący o błędzie: throw new TypWyjątku("Treść komunikatu"); W razie konieczności możemy też ponownie zgłosić wyjątek: try { //"pilnowany" kod programu } catch(Exception ex) { //kod obsługi wyjątku throw; //ponowne zgłoszenie wyjątku } 10 double a = 1, b; Przetestuj program dla Przykład try wartości: 12, 0, ALA. { Console.Write("Podaj liczbę: "); b = Convert.ToDouble(Console.ReadLine()); if (b == 0) throw new DivideByZeroException("Dzielenie przez zero"); else a = a / b; } catch (DivideByZeroException ex) { Console.WriteLine("Komunikat: {0}", ex.Message); } catch (FormatException ex) { Console.WriteLine("Nie udało się przekonwertować napisu na liczbę"); } finally { Console.WriteLine("Blok finally"); } 11 Console.ReadKey(true); Typy wyjątków 12