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


Использование исключений (Руководство по программированию на C#)

Обновлен: Ноябрь 2007

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

Исключения представляются посредством классов, производных от Exception. Такой класс устанавливает тип исключения и содержит свойства с подробными сведениями об исключении. При генерации исключения создается экземпляр класса, производного от исключения, настраиваются свойства исключения (необязательно), а затем при помощи ключевого слова throw создается объект. Пример.

class CustomException : Exception
{
    public CustomException(string message)
    {

    }

}
private static void TestThrow()
{
    CustomException ex =
        new CustomException("Custom exception in TestThrow()");

    throw ex;
}

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

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

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

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

static void TestCatch2()
{
    System.IO.StreamWriter sw = null;
    try
    {
        sw = new System.IO.StreamWriter(@"C:\test\test.txt");
        sw.WriteLine("Hello");
    }

    catch (System.IO.FileNotFoundException ex)
    {
        System.Console.WriteLine(ex.ToString());  // put the more specific exception first
    }

    catch (System.IO.IOException ex)
    {
        System.Console.WriteLine(ex.ToString());  // put the less specific exceptions last
    }
    finally 
    {
        sw.Close();
    }

    System.Console.WriteLine("Done");  // this statement is executed after the catch block
}

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

static void TestFinally()
{
    System.IO.FileStream file = null;
    //Change the path to something that works on your machine
    System.IO.FileInfo fileInfo = new System.IO.FileInfo(@"C:\file.txt");

    try
    {
        file = fileInfo.OpenWrite();
        file.WriteByte(0xF);
    }
    finally
    {
        // Closing the file allows you to reopen it immediately - otherwise IOException is thrown.
        if (file != null)
        {
            file.Close();
        }
    }

    try
    {
        file = fileInfo.OpenWrite();
        System.Console.WriteLine("OpenWrite() succeeded");
    }
    catch (System.IO.IOException)
    {
        System.Console.WriteLine("OpenWrite() failed");
    }
}

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

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

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

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

  • По достижении начала потока он прерывается.

См. также

Основные понятия

Руководство по программированию в C#

Ссылки

Исключения и обработка исключений (руководство по программированию в C#)