Udostępnij za pośrednictwem


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.