Распознавание класса исключительной ситуации выполняется с помощью конструкций
on <класс исключительной ситуации> do <оператор>;
которые записываются в секции обработки исключительной ситуации, например:
try // вычисления с вещественными числамиexcept on EZeroDivide do ... ; // обработка ошибки деления на нуль on EMathError do ... ; // обработка других ошибок вещественной математикиend;
Поиск соответствующего обработчика выполняется последовательно до тех пор, пока класс исключительной ситуации не окажется совместимым с классом, указанным в операторе on. Как только обработчик найден, выпоняется оператор, стоящий за словом do и управление передается за секцию except...end. Если исключительная ситуация не относится ни к одному из указанных классов, то управление передается во внешний блок try...except...end и обработчик ищется в нем.
Обратите внимание, что порядок операторов on имеет значение, поскольку распознавание исключительных ситуаций должно происходить от частных классов к общим классам, иначе говоря, от потомков к предкам. С чем это связано? Сейчас поймете. Представьте, к чему приведет изменение порядка операторов on в примере выше, если принять во внимание, что класс EMathError является базовым для EZeroDivide. Ответ простой: обработчик EMathError будет поглощать все ошибки вещественной математики, в том числе EZeroDivide, в результате обработчик EZeroDivide никогда не выполнится.
На самом высоком уровне программы бывает необходимо перехватывать все исключительные ситуации, чтобы в случае какой-нибудь неучтенной ошибки корректно завершить приложение. Для этого применяется так называемый обработчик по умолчанию (default exception handler). Он записывается в секции except после всех операторов on и начинается ключевым словом else:
try { вычисления с вещественными числами }except on EZeroDivide do { обработка ошибки деления на нуль }; on EMathError do { обработка других ошибок вещественной математики }; else { обработка всех остальных ошибок (обработчик по умолчанию) };end;
Отсутствие части else соответствует записи else raise, которое нет смысла использовать явно. На мой взгляд нет необходимости пользоваться обработкой исключительных ситуаций по умолчанию, поскольку все ваши приложения будут строиться, как правило, на основе библиотеки VCL, в которой обработка по умолчанию уже предусмотрена.
Пример обработки исключительной ситуации
В качестве примера обработки исключительной ситуации рассмотрим две функции: StringToCardinal и StringToCardinalDef.
Функция StringToCardinal выполняет преобразование строки в число с типом Cardinal. Если преобразование невозможно, функция создает исключительную ситуацию класса EConvertError.
function StringToCardinal(const S: string): Cardinal;var I: Integer; B: Cardinal;begin Result := 0; B := 1; for I := Length(S) downto 1 do begin if not (S[I] in ['0'..'9']) then raise EConvertError.Create(S + ' is not a valid cardinal value'); Result := Result + B * (Ord(S[I]) - Ord('0')); B := B * 10; end;end;
Функция StringToCardinalDef также выполняет преобразование строки в число с типом Cardinal, но в отличие от функции StringToCardinal она не создает исключительную ситуацию. Вместо этого она позволяет задать значение, которое возвращается в случае неудачной попытки преобразования:
function StringToCardinalDef(const S: string; Default: Cardinal = 0): Cardinal;begin try Result := StringToCardinal(S); except on EConvertError do Result := Default; end;end;
Для преобразования исходной строки в число используется определенная выше функция StringToCardinal. Если при преобразовании возникает исключительная ситуация, то она «поглощается» функцией StringToCardinalDef, которая в этом случае возвращает значение параметра Default. Если происходит какая-нибудь другая ошибка (не EConvertError), то управление передается внешнему блоку обработки исключительных ситуаций, из которого была вызвана функция StringToCardinalDef.
Пример очень прост, но хорошо демонстрирует преимущества исключительных ситуаций перед традиционной обработкой ошибок. Представьте более сложные вычисления, состоящие из множества операторов, в каждом из которых может произойти ошибка. Насколько сложной окажется обработка ошибок многочисленными операторами if и насколько простой оператором try.