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 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 catchfinally 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 falseklauzulę , 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#.

Zobacz też