Механизм обработки исключений
Основные понятия
Примерами неправильных действий в программе могут служить попытки деления на нуль или, допустим, выход индекса за пределы объявленного диапазона. При обнаружении такого рода попытки сиcтема, в которой отсутствуют средства обработки исключений, реагирует сообщением о ней и прерыванием работы программы. Это очень нежелательное аварийное завершение программы. Программист должен отреагировать на него определенным образом – внести такие изменения в программу, которые позволят программе завершиться естественным образом, и снова запустить ее на выполнение. Такой подход утомителен и может привести к новым ошибкам.
Работа С#-подсистемы вносит существенные коррективы в этот порядок. Она организована таким образом, что в программе может быть определен блок кода, называемый «обработчиком исключений», который автоматически выполняется при выявлении недопустимых действий в программе, и ее работоспособность не теряется. Достигается такой эффект с помощью определенных операторов языка и грамотного их применения программистом. Таким образом, механизм обработки исключительных ситуаций позволяет программе, содержащей некоторые ошибки, продолжить свое выполнение. Достигается это за счет логического разделения вычислительного процесса на две части: обнаружение аварийной ситуации и ее обработки. Это важно не только для лучшей структуризации программы. Главное то, что обе эти части работают совместно, но разделяя действия между собой: одна часть обнаруживает ошибку, а другая реагирует на нее.
Еще одним преимуществом обработки исключительных ситуаций в С# является определение стандартных исключений для таких распространенных программных ошибок, как деление на нуль, или попадание вне диапазона определения индекса, или ряда других. Чтобы отреагировать на возникновение таких ошибок, программа должна отслеживать и обрабатывать эти исключения. Без знания возможностей С#-подсистемы обработки исключений успешное программирование на С# попросту невозможно.
Механизм обработки исключений
В С# исключения представляются классами. Все классы исключений должны быть выведены из встроенного класса исключений Exception, который является частью пространства имен System. Таким образом, все исключения – подклассы класса Exception. Класс Exception имеет два производных класса SystemException и ApplicationException, поддерживающие две общие категории исключений:
- исключения, которые генерируются общей системой поддержки времени выполнения С#;
- исключения, которые генерируются прикладными программами С#.
В обработке исключений используются несколько ключевых слов языка: try, саtch, finallу, throw, использование которых взаимосвязано. Так например ключевые слова саtch, finallу нельзя использовать без ключевого слова try.
Программные инструкции, которые нужно проконтролировать на предмет исключений, помещаются в try-блок. Оператор try содержит три части:
- контролируемый блок, предваряемый ключевым словом trу. В контролируемый блок включаются потенциально опасные операторы программы. Все функции, прямо или косвенно вызываемые из блока, также считаются ему принадлежащими;
- один или несколько обработчиков исключений – блоков саtch, в которых описывается, как обрабатываются ошибки различных типов;
- блок завершения finallу выполняется независимо от того, возникла ошибка в контролируемом блоке или нет.
Рассмотрим, каким образом реализуется обработка исключительных ситуаций.
- Обработка исключения начинается с появления ошибки. Функция или операция, в которой возникла ошибка, генерирует исключение. Часто оно генерируется в функциях, вложенных в блок try.
- Выброшенное исключение может быть перехвачено программным путем с помощью catch-блока и обработано соответствующим образом.
- Если обработчик не найден, вызывается стандартный обработчик исключения. Его действия зависят от конфигурации среды. Обычно он выводит на экран окно с информацией об исключении и завершает текущий процесс. Системные исключения автоматически генерируются С#-системой динамического управления.
- Чтобы сгенерировать исключение вручную, используется ключевое слово throw.
- Любой код, который должен быть обязательно выполнен при выходе из try-блока, помещается в блок finally.
Отсутствовать могут либо блоки саtch, либо блок finallу, но не оба одновременно.
Итак, ядром обработки исключений являются блоки try и catch. Эти ключевые слова работают «в одной связке»; формат записи try/catch-блоков обработки исключений имеет следующий вид:
try {
// Блок кода, подлежащий проверке на наличие ошибок.
}
catch (Тип искл1 объект искл) {
// Обработчик для исключения Тип искл1
}
catch (Тип искл21 объект искл) {
// Обработчик для исключения Тип искл2
}
Здесь Тип искл – это тип сгенерированного исключения. После «выброса» исключение перехватывается соответствующей инструкцией catch, которая его обрабатывает. Как видно из формата записи try/catch-блоков, с try-блоком может быть связана не одна, а несколько catch-инструкций. Их последовательность должна располагаться непосредственно за блоком try. Блоки саtch просматриваются в том порядке, в котором они записаны. Какой именно из них будет выполнен, определит тип исключения. Другими словами, будет выполнена та catch-инструкция, тип исключения которой совпадает с типом сгенерированного исключения (а все остальные будут проигнорированы). После перехвата исключения параметр объект искл, если он присутствует,примет значение Тип искл.
Если обработчику исключения не нужен доступ к объекту исключения, то параметр объект искл можно опустить. Если исключение не генерируется, то try-блок завершается нормально, и все его catch-инструкции игнорируются. В табл. 1 представлены наиболее часто используемые исключения, определенные в пространстве имен System.
Таблица 1 – Наиболее часто используемые исключения
Исключение | Значение |
ArrayTypeMismatchException | Тип сохраняемого значения несовместим с типом массива |
DivideByZeroException | Попытка деления на нуль |
IndexOutOfRangeException | Индекс массива оказался вне диапазона |
InvalidCastException | Неверно выполнено динамическое приведение типов |
OutofMemoryException | Недостаточный объем свободной памяти |
OverflowException | Имеет место арифметическое переполнение |
NullReferenceException | Попытка использовать нулевую ссылку |
StackoverflowException | Переполнение стека |
Иногда требуется перехватывать все исключения, независимо от их типа. Для этого используется catch-инструкция без параметров. В этом случае создается обработчик, который используется, чтобы программа гарантированно обработала все исключения.
В любом случае, произошло исключение или нет, управление передается в блок завершения finally (если он существует), а затем – первому оператору, находящемуся непосредственно за оператором try. В завершающем блоке обычно записываются операторы, которые необходимо выполнить независимо от того, возникло исключение или нет, например, закрытие файлов, с которыми выполнялась работа в контролируемом блоке, или вывод информации.