Obsługa wyjątków (Przewodnik programowania w języku C#)
Blok try jest używany przez programistów języka C# do partycjonowania kodu, który może mieć wpływ na wyjątek. Skojarzone bloki catch służą do obsługi wszelkich wynikowych wyjątków. Na koniec blok zawiera kod, który jest uruchamiany bez względu na to, czy w try
bloku jest zgłaszany wyjątek, taki jak zwalnianie zasobów przydzielonych w try
bloku. Blok try
wymaga co najmniej jednego skojarzonego catch
finally
bloku lub bloku albo obu tych bloków.
W poniższych przykładach pokazano instrukcję try-catch
, instrukcję try-finally
i instrukcję try-catch-finally
.
try
{
// Code to try goes here.
}
catch (SomeSpecificException ex)
{
// Code to handle the exception goes here.
// Only catch exceptions that you know how to handle.
// Never catch base class System.Exception without
// rethrowing it at the end of the catch block.
}
try
{
// Code to try goes here.
}
finally
{
// Code to execute after the try block goes here.
}
try
{
// Code to try goes here.
}
catch (SomeSpecificException ex)
{
// Code to handle the exception goes here.
}
finally
{
// Code to execute after the try (and possibly catch) blocks
// goes here.
}
Blok try
bez catch
bloku lub finally
powoduje błąd kompilatora.
Bloki catch
Blok catch
może określać typ wyjątku do przechwycenia. Specyfikacja typu jest nazywana filtrem wyjątku. Typ wyjątku powinien pochodzić z Exceptionklasy . Ogólnie rzecz biorąc, nie określaj Exception jako filtru wyjątków, chyba że wiesz, jak obsługiwać wszystkie wyjątki, które mogą zostać zgłoszone w try
bloku, lub dołączono instrukcjęthrow
na końcu catch
bloku.
Wiele catch
bloków z różnymi klasami wyjątków można połączyć ze sobą. Bloki catch
są oceniane od góry do dołu w kodzie, ale tylko jeden catch
blok jest wykonywany dla każdego zgłaszanego wyjątku. Pierwszy catch
blok określający dokładny typ lub klasę bazową zgłaszanego wyjątku. Jeśli żaden blok nie catch
określa pasującej klasy wyjątków, zostanie wybrany blok, catch
który nie ma żadnego typu, jeśli istnieje w instrukcji . Ważne jest, aby najpierw umieścić catch
bloki z najbardziej specyficznymi (czyli najbardziej pochodnymi) klasami wyjątków.
Przechwyć wyjątki, gdy spełnione są następujące warunki:
- Dobrze rozumiesz, dlaczego wyjątek może zostać zgłoszony i można zaimplementować określone odzyskiwanie, takie jak monitowanie użytkownika o wprowadzenie nowej nazwy pliku podczas przechwytywania FileNotFoundException obiektu.
- Możesz utworzyć i zgłosić nowy, bardziej szczegółowy wyjątek.
int GetInt(int[] array, int index) { try { return array[index]; } catch (IndexOutOfRangeException e) { throw new ArgumentOutOfRangeException( "Parameter index is out of range.", e); } }
- Chcesz częściowo obsłużyć wyjątek przed przekazaniem go w celu uzyskania większej obsługi. W poniższym przykładzie
catch
blok służy do dodawania wpisu do dziennika błędów przed ponownym wprowadzeniem wyjątku.try { // Try to access a resource. } catch (UnauthorizedAccessException e) { // Call a custom error logging procedure. LogError(e); // Re-throw the error. throw; }
Można również określić filtry wyjątków , aby dodać wyrażenie logiczne do klauzuli catch. Filtry wyjątków wskazują, że określona klauzula catch jest zgodna tylko wtedy, gdy ten warunek ma wartość true. W poniższym przykładzie oba klauzule catch używają tej samej klasy wyjątku, ale jest sprawdzany dodatkowy warunek, aby utworzyć inny komunikat o błędzie:
int GetInt(int[] array, int index)
{
try
{
return array[index];
}
catch (IndexOutOfRangeException e) when (index < 0)
{
throw new ArgumentOutOfRangeException(
"Parameter index cannot be negative.", e);
}
catch (IndexOutOfRangeException e)
{
throw new ArgumentOutOfRangeException(
"Parameter index cannot be greater than the array size.", e);
}
}
Filtr wyjątku, który zawsze zwraca false
, może służyć do badania wszystkich wyjątków, ale nie przetwarzania ich. Typowym zastosowaniem jest rejestrowanie wyjątków:
public class ExceptionFilter
{
public static void Main()
{
try
{
string? s = null;
Console.WriteLine(s.Length);
}
catch (Exception e) when (LogException(e))
{
}
Console.WriteLine("Exception must have been handled");
}
private static bool LogException(Exception e)
{
Console.WriteLine($"\tIn the log routine. Caught {e.GetType()}");
Console.WriteLine($"\tMessage: {e.Message}");
return false;
}
}
Metoda LogException
zawsze zwraca false
klauzulę , bez catch
klauzuli używającej tego filtru wyjątku. Klauzula catch może być ogólna, przy użyciu System.Exceptionklauzul , a późniejsze klauzule mogą przetwarzać bardziej szczegółowe klasy wyjątków.
Bloki na koniec
Blok finally
umożliwia czyszczenie akcji wykonywanych w try
bloku. Jeśli jest finally
obecny, blok jest wykonywany ostatnio, po try
bloku i dowolnym dopasowanym catch
bloku. finally
Blok zawsze jest uruchamiany, niezależnie od tego, czy wyjątek jest zgłaszany, czy blok zgodny z typem catch
wyjątku jest znaleziony.
Blok finally
może służyć do zwalniania zasobów, takich jak strumienie plików, połączenia bazy danych i uchwyty graficzne bez oczekiwania na moduł odśmiecanie pamięci w środowisku uruchomieniowym w celu sfinalizowania obiektów.
W poniższym przykładzie finally
blok jest używany do zamykania pliku otwartego w try
bloku. Zwróć uwagę, że stan uchwytu pliku jest sprawdzany przed zamknięciem pliku. try
Jeśli blok nie może otworzyć pliku, dojście do pliku nadal ma wartość null
, a finally
blok nie próbuje go zamknąć. Zamiast tego, jeśli plik został pomyślnie otwarty w try
bloku, finally
blok zamyka otwarty plik.
FileStream? file = null;
FileInfo fileinfo = new System.IO.FileInfo("./file.txt");
try
{
file = fileinfo.OpenWrite();
file.WriteByte(0xF);
}
finally
{
// Check for null because OpenWrite might have failed.
file?.Close();
}
Specyfikacja języka C#
Aby uzyskać więcej informacji, zobacz Wyjątki i instrukcja try w specyfikacji języka C#. Specyfikacja języka jest ostatecznym źródłem informacji o składni i użyciu języka C#.