Używanie wyjątków
W języku C#błędy w programie w czasie wykonywania są propagowane za pośrednictwem programu przy użyciu mechanizmu nazywanego wyjątkami. Wyjątki są zgłaszane przez kod, który napotyka błąd i przechwycony przez kod, który może poprawić błąd. Wyjątki mogą być zgłaszane przez środowisko uruchomieniowe platformy .NET lub kod w programie. Po wyrzuceniu wyjątku propaguje stos wywołań do momentu catch
znalezienia instrukcji dla wyjątku. Wyjątki nieuchwycone są obsługiwane przez ogólną procedurę obsługi wyjątków dostarczaną przez system, który wyświetla okno dialogowe.
Wyjątki są reprezentowane przez klasy pochodzące z Exceptionklasy . Ta klasa identyfikuje typ wyjątku i zawiera właściwości, które zawierają szczegółowe informacje o wyjątku. Zgłaszanie wyjątku polega na utworzeniu wystąpienia klasy pochodnej wyjątku, opcjonalnie skonfigurowaniu właściwości wyjątku, a następnie wyrzuceniu obiektu przy użyciu słowa kluczowego throw
. Na przykład:
class CustomException : Exception
{
public CustomException(string message)
{
}
}
private static void TestThrow()
{
throw new CustomException("Custom exception in TestThrow()");
}
Po wystąpieniu wyjątku środowisko uruchomieniowe sprawdza bieżącą try
instrukcję, aby sprawdzić, czy znajduje się ona w bloku. Jeśli tak jest, wszystkie catch
bloki skojarzone z blokiem try
są sprawdzane, aby sprawdzić, czy mogą przechwycić wyjątek. Catch
bloki zazwyczaj określają typy wyjątków; Jeśli typ catch
bloku jest taki sam jak wyjątek lub klasa bazowa wyjątku, catch
blok może obsłużyć metodę. Na przykład:
try
{
TestThrow();
}
catch (CustomException ex)
{
System.Console.WriteLine(ex.ToString());
}
Jeśli instrukcja zgłasza wyjątek nie znajduje się w try
bloku lub jeśli try
blok, który go otacza, nie ma pasującego catch
bloku, środowisko uruchomieniowe sprawdza metodę wywołującą try
dla instrukcji i catch
bloków. Środowisko uruchomieniowe kontynuuje stos wywołujący, wyszukując zgodny catch
blok. Po znalezieniu i wykonaniu bloku kontrolka catch
zostanie przekazana do następnej instrukcji po tym catch
bloku.
Instrukcja try
może zawierać więcej niż jeden catch
blok. Pierwsza catch
instrukcja, która może obsłużyć wyjątek, jest wykonywana; wszystkie następujące catch
instrukcje, nawet jeśli są zgodne, są ignorowane. Kolejność bloków połowu z najbardziej specyficznych (lub najbardziej pochodnych) do najmniej określonych. Na przykład:
using System;
using System.IO;
namespace Exceptions
{
public class CatchOrder
{
public static void Main()
{
try
{
using (var sw = new StreamWriter("./test.txt"))
{
sw.WriteLine("Hello");
}
}
// Put the more specific exceptions first.
catch (DirectoryNotFoundException ex)
{
Console.WriteLine(ex);
}
catch (FileNotFoundException ex)
{
Console.WriteLine(ex);
}
// Put the least specific exception last.
catch (IOException ex)
{
Console.WriteLine(ex);
}
Console.WriteLine("Done");
}
}
}
Przed wykonaniem catch
bloku środowisko uruchomieniowe sprawdza finally
bloki. Finally
bloki umożliwiają programistom czyszczenie wszelkich niejednoznacznych stanów, które mogą zostać pominięte z przerwanego try
bloku lub zwolnienie jakichkolwiek zasobów zewnętrznych (takich jak uchwyty grafiki, połączenia bazy danych lub strumienie plików) bez oczekiwania na zakończenie modułu odśmiecanie pamięci w środowisku uruchomieniowym. Na przykład:
static void TestFinally()
{
FileStream? file = null;
//Change the path to something that works on your machine.
FileInfo fileInfo = new System.IO.FileInfo("./file.txt");
try
{
file = fileInfo.OpenWrite();
file.WriteByte(0xF);
}
finally
{
// Closing the file allows you to reopen it immediately - otherwise IOException is thrown.
file?.Close();
}
try
{
file = fileInfo.OpenWrite();
Console.WriteLine("OpenWrite() succeeded");
}
catch (IOException)
{
Console.WriteLine("OpenWrite() failed");
}
}
Jeśli WriteByte()
zostanie zgłoszony wyjątek, kod w drugim try
bloku, który próbuje ponownie otworzyć plik, zakończy się niepowodzeniem, jeśli file.Close()
nie zostanie wywołany, a plik pozostanie zablokowany. Ponieważ finally
bloki są wykonywane nawet w przypadku zgłoszenia wyjątku, finally
blok w poprzednim przykładzie umożliwia poprawne zamknięcie pliku i pomaga uniknąć błędu.
Jeśli nie zostanie znaleziony żaden zgodny catch
blok w stosie wywołań po wystąpieniu wyjątku, wystąpi jedna z trzech rzeczy:
- Jeśli wyjątek znajduje się w finalizatorze, finalizator zostanie przerwany, a finalizator podstawowy, jeśli istnieje, jest wywoływany.
- Jeśli stos wywołań zawiera konstruktor statyczny lub inicjator pola statycznego, TypeInitializationException zgłaszany jest wyjątek z oryginalnym wyjątkiem przypisanym do InnerException właściwości nowego wyjątku.
- Jeśli początek wątku zostanie osiągnięty, wątek zostanie zakończony.