«Машины должны работать. Люди должны думать»
33. Дополнительные возможности обработки ИС
Вызов исключительной ситуации.
В процедуре C из примера мы уже могли видеть, как должна поступать программа при обнаружении состояния ошибки - она вызывает (генерирует) исключительную ситуацию:
raise ESampleError.Create('Error!');
После ключевого слова raise следует код, аналогичный тому, что используется для создания нового экземпляра класса. Действительно, в момент вызова исключительной ситуации создается экземпляр указанного класса. Данный экземпляр существует до момента окончания обработки исключительной ситуации и затем автоматически уничтожается. Вся информация, которую нужно сообщить в обработчик ошибки, передается в объект через его конструктор в момент создания.
Почти все существующие классы исключительных ситуаций являются наследниками базового класса Exception и не содержат новых свойств или методов. Класс Exception имеет несколько конструкторов (а именно 8), какой из них конкретно использовать - зависит от задачи. Конструкторы, в имени которых присутствует Fmt, например, CreateFmtHelp, позволяют включать в текст сообщения значения переменных. Впрочем, сделать это можно и другими способами.
Приведем пример использования конструктора с форматированием. Функция StrToIntRange преобразует число из символьного формата во внутренний с контролем диапазона:
Function StrToIntRange(const S: string; Min, Max: Longint): Longint;
Begin
Result := StrToInt(S);
if (Result < Min) or (Result > Max) then
raise ERangeError.CreateFmt('%d вне диапазона %d..%d',
[Result, Min, Max]);
End;
Объекты ИС создаются с помощью конструкторов, а уничтожаются автоматически после обработки ИС и для них не нужно вызывать деструкторов.
Исключительные ситуации не могут обрабатываться в секции инициализации, так как для их обработки необходимо выполнение секции инициализации модуля SysUtils. Если ИС возникает в секции инициализации, то это всегда приводит к завершению приложения.
Доступ к экземпляру объекта exception.
До сих пор мы рассматривали механизмы защиты кода и ресурсов, логику работы программы в исключительной ситуации. Теперь нужно немного разобраться с тем, как же обрабатывать возникшую ошибку. А точнее, как получить дополнительную информацию о коде ошибки, текст сообщения и т.п.
Как уже говорилось, при вызове (raise) ИС автоматически создается экземпляр соответствующего класса, который и содержит информацию об ошибке. Весь вопрос в том, как в обработчике данной ситуации получить доступ к этому объекту.
Рассмотрим модифицированную процедуру A в нашем примере:
procedure NewA;
begin
writeln('Enter A');
try
writeln('Enter A''s try block');
B;
writeln('After B call');
except
on ExLocal : ESampleError do writeln(ExLocal.Message);
on ESomethingElse do
writeln('Inside A''s ESomethingElse handler');
end;
writeln('Exit A');
end;
Здесь все изменения внесены в строку
on ExLocal: ESampleError do writeln(ExLocal.Message);
Пример демонстрирует еще одно новшество в языке Object Pascal - создание локальной переменной. В нашем примере локальной переменной является ExLocal - это тот самый экземпляр класса ESampleError, который был создан в процедуре C в момент вызова ИС. Переменная ExLocal доступна только внутри блока do. Свойство Message объекта ExLocal содержит сообщение, которое было передано в конструктор Create в процедуре C.
Экземпляр класса ИС существует только внутри оператора on и автоматически уничтожается по завершению этого оператора. В Delphi 6, тем не менее, его жизнь можно продлить с помощью процедуры AcquireExceptionObject, но в этом случае программист должен сам позаботиться об уничтожении ИС с помощью процедуры ReleaseExceptionObject.
В модуле SysUtils есть также процедура
Procedure ShowException(ExceptObject: TObject; ExceptAddr: Pointer);
Она выводит сообщение об ИС и адрес точки программы, в которой случилась ИС. Этот адрес можно использовать в меню Search/Find Error для поиска соответствующей строки программы. В качестве фактических параметров при вызове ShowException удобно использовать функции ExceptObject и ExceptAddr. Функция ExceptObject возвращает имя класса последней ИС (например, EMyException), а ExceptAddr – адрес точки программы, в которой возникла ИС. Процедуру ShowException можно использовать в блоке else оператора try…except, где класс ИС не известен.
Другая возможность для определения места возникновения ИС – использование процедуры
Procedure Assert(expression:boolean; [ const msg:string ] );
Если expression=true, то эта процедура ничего не делает. В противном случае создается ИС EAssertionFailed. Фокус в том, что обработчик этой ИС возвращает точные координаты места ИС, а именно вызова Assert: полное имя файла и номер строки программы, что весьма полезно для целей отладки.
Пример ее использования:
Procedure C;
Begin
…
if ErrorCondition then Assert(false,'Error in C');
…
End;
Procedure A;
Begin
try
writeln('Enter A''s try block');
B;
writeln('After B call');
except
on EAssertionFailed do writeln('Inside A''s EAssertion handler');
end;
End;
Ценно также то, что с помощью директивы компилятора {$ASSERTIONS ON/OFF} возникновение обсуждаемой ИС можно цетрализованно разрешить или запретить.
Еще одно исключение – EAbort – является “скрытым”. Используйте его тогда, когда хотите прервать тот или иной процесс с условием, что пользователь программы не должен видеть сообщения об ошибке. Отличие EAbort от других ИС состоит в том, что в этом случае не выводится окно с сообщением о возникшей ИС. Пример использования:
. . .
if ErrorCondition then
begin {СоздаемсамостоятельноИС}
writeln('Raising exception in C');
raise EAbort.Create('Condition 1');
end;
. . .
except
on LocalAbort : EAbort do
begin
writeln('Inside A''s EAbort handler' + LocalAbort.Message);
end;
. . .
end;
Еще один способ вызова этой ИС – просто вызов процедуры Abort.
«32. Примеры обработки исключительных ситуаций»
34. Предопределенные обработчики исключительных ситуаций