Udostępnij przez


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 się on w górę stosu wywołań, aż zostanie znalezione catch instrukcja 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 pochodne Exception. 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 . Przykład:

class CustomException : Exception
{
    public CustomException(string message)
    {
    }
}
private static void TestThrow()
{
    throw new CustomException("Custom exception in TestThrow()");
}

Po zgłoszeniu wyjątku środowisko uruchomieniowe sprawdza bieżące instrukcje, aby ustalić, czy znajdują się one w bloku try. Jeśli tak jest, wszystkie bloki catch skojarzone z blokiem try są sprawdzane, 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ę. Przykład:

try
{
    TestThrow();
}
catch (CustomException ex)
{
    System.Console.WriteLine(ex.ToString());
}

Jeśli instrukcja, która zgłasza wyjątek, nie znajduje się w bloku try lub jeśli blok try, który go otacza, nie ma pasującego bloku catch, środowisko uruchomieniowe sprawdza metodę wywołania dla instrukcji try i bloków catch. Środowisko uruchomieniowe przeszukuje stos wywołań, szukając zgodnego bloku catch. Po znalezieniu i wykonaniu bloku catch sterowanie zostanie przekazane do następnej instrukcji po tym bloku catch.

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. 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 oczyszczenie wszelkich niejednoznacznych stanów, które mogą pozostać po przerwanym try bloku, lub zwolnienie jakichkolwiek zasobów zewnętrznych (takich jak uchwyty grafiki, połączenia bazy danych lub strumienie plików) bez czekania na to, aż moduł odśmiecania pamięci w środowisku uruchomieniowym zakończy pracę. 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() zgłosi wyjątek, kod w drugim bloku try, który próbuje ponownie otworzyć plik, zakończy się niepowodzeniem, jeżeli file.Close() nie zostanie wywołany, i 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 wywołany zostanie domyślny finalizator, jeśli istnieje.
  • Jeśli stos wywołań zawiera konstruktor statyczny lub inicjator pola statycznego, zostanie zgłoszony wyjątek TypeInitializationException, a oryginalny wyjątek zostanie przypisany do właściwości InnerException nowego wyjątku.
  • Jeśli początek wątku zostanie osiągnięty, wątek zostanie zakończony.