Поделиться через


Использование исключений

В C#ошибки в программе во время выполнения распространяются через программу с помощью механизма, называемого исключениями. Исключения вызываются кодом, который встречает ошибку, и перехватываются кодом, который может ее исправить. Исключения могут вызываться средой выполнения .NET или кодом в программе. После генерации исключения он распространяется по стеку вызовов до тех пор, пока catch не будет найдена инструкция для исключения. Необработанные исключения обрабатываются универсальным обработчиком исключений, предоставляемым системой, в котором отображается диалоговое окно.

Исключения представлены классами, производными от Exception. Этот класс определяет тип исключения и содержит свойства, содержащие сведения об исключении. Создание исключения включает создание экземпляра класса, производного от класса исключения, при необходимости настраивая свойства исключения, а затем выбрасывание объекта с помощью ключевого слова throw. Рассмотрим пример.

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

После возникновения исключения среда выполнения проверяет текущую инструкцию, чтобы узнать, находится ли она в блоке try . Если это так, все catch блоки, связанные с блоком try, проверяются, чтобы определить, способны ли они перехватывать исключение. Catch блоки обычно указывают типы исключений; Если тип catch блока совпадает с типом исключения или базовым классом исключения, catch блок может обрабатывать метод. Рассмотрим пример.

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

Если инструкция, вызывающая исключение, не находится в пределах блока try или если блок try, заключающий её, не имеет соответствующего блока catch, среда выполнения проверяет вызывающий метод на наличие инструкции try и блоков catch. Среда выполнения продолжает двигаться вверх по стеку вызовов, выполняя поиск совместимого блока catch. После обнаружения и выполнения блока catch управление передается следующему оператору после этого блока catch.

Оператор try может содержать более одного блока catch. Первая catch инструкция, которая может обрабатывать исключение, выполняется. Все следующие catch инструкции, даже если они совместимы, игнорируются. Расположите блоки catch от самых конкретных (или более производных) до наименее конкретных. Рассмотрим пример.

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");
        }
    }
}

Перед выполнением catch блока среда выполнения проверяет наличие finally блоков. Finally блоки позволяют программисту устранить любое неоднозначное состояние, которое может быть оставлено из прекращённого try блока, или освободить внешние ресурсы (например, графические дескрипторы, подключения к базам данных или потоки файлов) без ожидания завершения объектов сборщиком мусора в среде выполнения. Рассмотрим пример.

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");
    }
}

Если WriteByte() выдало исключение, код в блоке try, который пытается повторно открыть файл, завершится ошибкой, если file.Close() не будет вызван, и файл останется заблокированным. Так как finally блоки выполняются даже при возникновении исключения, finally блок в предыдущем примере позволяет правильно закрыть файл и избежать ошибки.

Если в стеке вызовов не найден совместимый catch блок после исключения, происходит одно из трех событий.

  • Если исключение находится в методе завершения, метод завершения прерван, а базовый метод завершения, если таковой есть, вызывается.
  • Если стек вызовов содержит статический конструктор или инициализатор статических полей, происходит исключение TypeInitializationException, причем исходное исключение присваивается свойству InnerException нового исключения.
  • Если достигается начало потока, поток завершается.