Udostępnij za pośrednictwem


Obsługa wyjątków (Przewodnik programowania w języku C#)

Blok try jest używany przez programistów języka C# do wydzielenia kodu, który może być dotknięty przez wyjątek. Powiązane bloki catch służą do obsługi wszelkich wynikowych wyjątków. Blok finally zawiera kod, który jest uruchamiany niezależnie od tego, czy w bloku try zostanie zgłoszony wyjątek, na przykład zwalniając zasoby przydzielone w bloku try. Blok try wymaga co najmniej jednego skojarzonego bloku catch, lub bloku finally, 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 bloku catch lub bez bloku finally powoduje błąd kompilatora.

Bloki przechwytywania błędów

Blok catch może określać typ wyjątku do przechwycenia. Specyfikacja typu jest nazywana filtrem wyjątku. Typ wyjątku powinien pochodzić z Exception klasy. 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ą być rzucane w try bloku, lub zawierasz 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 jest wykonywany. Jeśli żaden blok catch nie określa pasującej klasy wyjątków, zostanie wybrany blok catch, nieokreślony typowo, jeśli taki istnieje w danej instrukcji. Ważne jest, aby najpierw umieścić catch bloki z najbardziej specyficznymi (czyli najbardziej pochodnymi) klasami wyjątków.

Przechwyć wyjątki, kiedy spełnione są następujące warunki.

  • Dobrze rozumiesz, dlaczego wyjątek może zostać zgłoszony, i możesz zaimplementować określone odzyskiwanie, takie jak poproszenie użytkownika o wprowadzenie nowej nazwy pliku, kiedy złapiesz obiekt FileNotFoundException.
  • 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 zająć się wyjątkiem przed przekazaniem go do dalszej 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ą klasy tego samego wyjątku, ale sprawdzany jest 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; żadna klauzula catch korzystająca z tego filtru wyjątku nie pasuje. Klauzula catch może być ogólna, używając System.Exception, a późniejsze klauzule mogą obsługiwać bardziej szczegółowe klasy wyjątków.

Bloki na koniec

Blok finally umożliwia czyszczenie wykonanych akcji w bloku try. Jeśli blok finally jest obecny, jest wykonywany jako ostatni, po bloku try i bloku dopasowanego w dowolny sposób catch. finally Blok zawsze jest uruchamiany, bez względu na to, czy został zgłoszony wyjątek, czy znaleziono blok zgodny z typem wyjątku catch.

Blok finally może być użyty do zwalniania zasobów takich jak strumienie plików, połączenia z bazą danych i uchwyty graficzne, bez oczekiwania na zakończenie finalizacji obiektów przez garbage collector w środowisku uruchomieniowym.

W poniższym przykładzie blok finally służy do zamknięcia pliku, który został otwarty w bloku try. Zwróć uwagę, że stan uchwytu pliku jest sprawdzany przed zamknięciem pliku. Jeśli blok try nie może otworzyć pliku, uchwyt pliku nadal ma wartość null, a blok finally 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#.

Zobacz także