Finally Начинает безоговорочный раздел кода инструкции Try
On Определяет обработку особых ситуаций в предложении Try Except
Try Начинает код, который перехватывает ошибки
Источник
Raising an exception delphi
С целью поддержки структурной обработки исключительных ситуаций (exception) в Delphi введены новые расширения языка Pascal. В данной статье будет дано описание того, что из себя представляет такая обработка, почему она полезна, будут приведены соответствующий синтаксис Object Pascal и примеры использования исключительных ситуаций в Delphi.
Структурная обработка исключительных ситуаций
Структурная обработка исключительных ситуаций — это система, позволяющая программисту при возникновении ошибки (исключительной ситуации) связаться с кодом программы, подготовленным для обработки такой ошибки. Это выполняется с помощью языковых конструкций, которые как бы «охраняют» фрагмент кода программы и определяют обработчики ошибок, которые будут вызываться, если что-то пойдет не так в «охраняемом» участке кода. В данном случае понятие исключительной ситуации относится к языку и не нужно его путать с системными исключительными ситуациями (hardware exceptions), такими как General Protection Fault. Эти исключительные ситуации обычно используют прерывания и особые состояния «железа» для обработки критичной системной ошибки; исключительные ситуации в Delphi же независимы от «железа», не используют прерываний и используются для обработки ошибочных состояний, с которыми подпрограмма не готова иметь дело. Системные исключительные ситуации, конечно, могут быть перехвачены и преобразованы в языковые исключительные ситуации, но это только одно из применений языковых исключительных ситуаций.
При традиционной обработке ошибок, ошибки, обнаруженные в процедуре обычно передаются наружу (в вызывавшую процедуру) в виде возвращаемого значения функции, параметров или глобальных переменных (флажков). Каждая вызывающая процедура должна проверять результат вызова на наличие ошибки и выполнять соответствующие действия. Часто, это просто выход еще выше, в более верхнюю вызывающую процедуру и т.д. : функция A вызывает B, B вызывает C, C обнаруживает ошибку и возвращает код ошибки в B, B проверяет возвращаемый код, видит, что возникла ошибка и возвращает код ошибки в A, A проверяет возвращаемый код и выдает сообщение об ошибке либо решает сделать что-нибудь еще, раз первая попытка не удалась.
Такая «пожарная бригада» для обработки ошибок трудоемка, требует написания большого количества кода в котором можно легко ошибиться и который трудно отлаживать.
Одна ошибка в коде программы или переприсвоение в цепочке возвращаемых значений может привести к тому, что нельзя будет связать ошибочное состояние с положением дел во внешнем мире. Результатом будет ненормальное поведение программы, потеря данных или ресурсов, или крах системы.
Структурная обработка исключительной ситуации замещает ручную обработку ошибок автоматической, сгенерированной компилятором системой уведомления. В приведенном выше примере, процедура A установила бы «охрану» со связанным обработчиком ошибки на фрагмент кода, в котором вызывается B. B просто вызывает C. Когда C обнаруживает ошибку, то создает (raise) исключительную ситуацию. Специальный код, сгенерированный компилятором и встроенный в Run-Time Library (RTL) начинает поиск обработчика данной исключительной ситуации. При поиске «защищенного» участка кода используется информация, сохраненная в стеке. В процедурах C и B нет такого участка, а в A — есть. Если один из обработчиков ошибок, которые используются в A, подходит по типу для возникшей в C исключительной ситуации, то программа переходит на его выполнение. При этом, область стека, используемая в B и C, очищается; выполнение этих процедур прекращается.
Если в A нет подходящего обработчика, то поиск продолжается в более верхнем уровне, и так может идти, пока поиск не достигнет подходящего обработчика ошибок среди используемых по умолчанию обработчиков в RTL. Обработчики ошибок из RTL только показывают сообщение об ошибке и форсированно прекращают выполнение программы. Любая исключительная ситуация, которая осталась необработанной, приведет к прекращению выполнения приложения.
Без проверки возвращаемого кода после каждого вызова подпрограммы, код программы должен быть более простым, а скомпилированный код — более быстрым. При наличии исключительных ситуаций подпрограмма B не должна содержать дополнительный код для проверки возвращаемого результата и передачи его в A. B ничего не должна делать для передачи исключительной ситуации, возникшей в C, в процедуру A — встроенная система обработки исключительных ситуаций делает всю работу.
Данная система называется структурной, поскольку обработка ошибок определяется областью «защищенного» кода; такие области могут быть вложенными. Выполнение программы не может перейти на произвольный участок кода; выполнение программы может перейти только на обработчик исключительной ситуации активной программы.
Модель исключительных ситуаций в Delphi
Модель исключительных ситуаций в Object Pascal является невозобновляемой(non-resumable). При возникновении исключительной ситуации Вы уже не сможете вернуться в точку, где она возникла, для продолжения выполнения программы (это позволяет сделать возобновляемая(resumable) модель). Невозобновляемые исключительные ситуации разрушают стек, поскольку они сканируют его в поисках обработчика; в возобновляемой модели необходимо сохранять стек, состояние регистров процессора в точке возникновения ошибки и выполнять поиск обработчика и его выполнение в отдельном стеке. Возобновляемую систему обработки исключительных ситуаций гораздо труднее создать и применять, нежели невозобновляемую.
Синтаксис обработки исключительных ситуаций
Теперь, когда мы рассмотрели, что такое исключительные ситуации, давайте дадим ясную картину, как они применяются. Новое ключевое слово, добавленное в язык Object Pascal — try. Оно используется для обозначения первой части защищенного участка кода. Существует два типа защищенных участков:
- try..except
- try..finally
Первый тип используется для обработки исключительных ситуаций. Его синтаксис:
Для уверенности в том, что ресурсы, занятые вашим приложением, освободятся в любом случае, Вы можете использовать конструкцию второго типа. Код, расположенный в части finally, выполняется в любом случае, даже если возникает исключительная ситуация. Соответствующий синтаксис:
Примеры обработки исключительных ситуаций
Ниже приведены процедуры A,B и C, обсуждавшиеся ранее, воплощенные в новом синтаксисе Object Pascal:
При ErrorCondition = True программа выдаст:
Возможно вас удивила декларация типа ‘ESampleError =class’ вместо ‘=object’; это еще одно новое расширение языка. Delphi вводит новую модель объектов, доступную через декларацию типа ‘=class’. Описание новой объектной модели дается в других уроках. Здесь же достаточно сказать, что исключительные ситуации (exceptions) являются классами, частью новой объектной модели.
Процедура C проверяет наличие ошибки (в нашем случае это значение глобальной переменной) и, если она есть (а это так), C вызывает(raise) исключительную ситуацию класса ESampleError.
Процедура A помещает часть кода в блок try..except. Первая часть этого блока содержит часть кода, аналогично конструкции begin..end. Эта часть кода завершается ключевым словом except, далее следует один или более обработчиков исключительных ситуаций on xxxx do yyyy, далее может быть включен необязательный блок else, вся конструкция заканчивается end;. В конструкции, назначающей определенную обработку для конкретной исключительной ситуации (on xxxx do yyyy), после резервного слова on указывается класс исключительной ситуации, а после do следует собственно код обработки данной ошибки. Если возникшая исключительная ситуация подходит по типу к указанному после on, то выполнение программы переходит сюда (на код после do). Исключительная ситуация подходит в том случае, если она того же класса, что указан в on, либо является его потомком. Например, в случае on EFileNotFound обрабатываться будет ситуация, когда файл не найден. А в случае on EFileIO — все ошибки при работе с файлами, в том числе и предыдущая ситуация. В блоке else обрабатываются все ошибки, не обработанные до этого.
Приведенные в примере процедуры содержат код (строка с writeln), который отображает путь выполнения программы. Когда C вызывает exception, программа сразу переходит на обработчик ошибок в процедуре A, игнорируя оставшуюся часть кода в процедурах B и C.
После того, как найден подходящий обработчик ошибки, поиск оканчивается. После выполнения кода обработчика, программа продолжает выполняться с оператора, стоящего после слова end блока try..except (в примере — writeln(‘Exit A’)).
Конструкция try..except подходит, если известно, какой тип ошибок нужно обрабатывать в конкретной ситуации. Но что делать, если требуется выполнить некоторые действия в любом случае, произошла ошибка или нет? Это тот случай, когда понадобится конструкция try..finally.
Рассмотрим модифицированную процедуру B:
Если C вызывает исключительную ситуацию, то программа уже не возвращается в процедуру B. А что же с теми 1000 байтами памяти, захваченными в B? Строка FreeMem(P,1000) не выполнится и Вы потеряете кусок памяти. Как это исправить? Нужно ненавязчиво включить процедуру B в процесс, например:
Если в A поместить вызов NewB вместо B, то программа выведет сообщения следующим образом:
Код в блоке finally выполнится при любой ошибке, возникшей в соответствующем блоке try. Он же выполнится и в том случае, если ошибки не возникло. В любом случае память будет освобождена. Если возникла ошибка, то сначала выполняется блок finally, затем начинается поиск подходящего обработчика. В штатной ситуации, после блока finally программа переходит на следующее предложение после блока.
Почему вызов GetMem не помещен внутрь блока try? Этот вызов может окончиться неудачно и вызвать exception EOutOfMemory. Если это произошло, то FreeMem попытается освободить память, которая не была распределена. Когда мы размещаем GetMem вне защищаемого участка, то предполагаем, что B сможет получить нужное количество памяти, а если нет, то более верхняя процедура получит уведомление EOutOfMemory.
А что, если требуется в B распределить 4 области памяти по схеме все-или-ничего? Если первые две попытки удались, а третья провалилась, то как освободить захваченную область память? Можно так:
Установив сперва указатели в NIL, далее можно определить, успешно ли прошел вызов GetMem.
Оба типа конструкции try можно использовать в любом месте, допускается вложенность любой глубины. Исключительную ситуацию можно вызывать внутри обработчика ошибки, конструкцию try можно использовать внутри обработчика исключительной ситуации.
Вызов исключительной ситуации
В процедуре C из примера мы уже могли видеть, как должна поступать программа при обнаружении состояния ошибки — она вызывает исключительную ситуацию:
После ключевого слова raise следует код, аналогичный тому, что используется для создания нового экземпляра класса. Действительно, в момент вызова исключительной ситуации создается экземпляр указанного класса; данный экземпляр существует до момента окончания обработки исключительной ситуации и затем автоматически уничтожается. Вся информация, которую нужно сообщить в обработчик ошибки передается в объект через его конструктор в момент создания.
Почти все существующие классы исключительных ситуаций являются наследниками базового класса Exception и не содержат новых свойств или методов. Класс Exception имеет несколько конструкторов, какой из них конкретно использовать — зависит от задачи. Описание класса Exception можно найти в on-line Help.
Доступ к экземпляру объекта exception
До сих пор мы рассматривали механизмы защиты кода и ресурсов, логику работы программы в исключительной ситуации. Теперь нужно немного разобраться с тем, как же обрабатывать возникшую ошибку. А точнее, как получить дополнительную информацию о коде ошибки, текст сообщения и т.п.
Как уже говорилось, при вызове исключительной ситуации (raise) автоматически создается экземпляр соответствующего класса, который и содержит информацию об ошибке. Весь вопрос в том, как в обработчике данной ситуации получить доступ к этому объекту.
Рассмотрим модифицированную процедуру A в нашем примере:
Здесь все изменения внесены в строку
Пример демонстрирует еще одно новшество в языке Object Pascal — создание локальной переменной. В нашем примере локальной переменной является ESE — это тот самый экземпляр класса ESampleError, который был создан в процедуре C в момент вызова исключительного состояния. Переменная ESE доступна только внутри блока do. Свойство Message объекта ESE содержит сообщение, которое было передано в конструктор Create в процедуре C.
Есть еще один способ доступа к экземпляру exception — использовать функцию ExceptionObject:
Предопределенные обработчики исключительных ситуаций
Ниже Вы найдете справочную информацию по предопределенным исключениям, необходимую для профессионального программирования в Delphi.
- Exception — базовый класс-предок всех обработчиков исключительных ситуаций.
- EAbort — «скрытое» исключение. Используйте его тогда, когда хотите прервать тот или иной процесс с условием, что пользователь программы не должен видеть сообщения об ошибке. Для повышения удобства использования в модуле SysUtils предусмотрена процедура Abort, определенная, как:
- EComponentError — вызывается в двух ситуациях:
- при попытке регистрации компоненты за пределами процедуры Register;
- когда имя компоненты не уникально или не допустимо.
- EConvertError — происходит в случае возникновения ошибки при выполнении функций StrToInt и StrToFloat, когда конвертация строки в соответствующий числовой тип невозможна.
- EInOutError — происходит при ошибках ввода/вывода при включенной директиве <$I+>.
- EIntError — предок исключений, случающихся при выполнении целочисленных операций.
- EDivByZero — вызывается в случае деления на ноль, как результат RunTime Error 200.
- EIntOverflow — вызывается при попытке выполнения операций, приводящих к переполнению целых переменных, как результат RunTime Error 215 при включенной директиве <$Q+>.
- ERangeError — вызывается при попытке обращения к элементам массива по индексу, выходящему за пределы массива, как результат RunTime Error 201 при включенной директиве <$R+>.
- EInvalidCast — происходит при попытке приведения переменных одного класса к другому классу, несовместимому с первым (например, приведение переменной типа TListBox к TMemo).
- EInvalidGraphic — вызывается при попытке передачи в LoadFromFile файла, несовместимого графического формата.
- EInvalidGraphicOperation — вызывается при попытке выполнения операций, неприменимых для данного графического формата (например, Resize для TIcon).
- EInvalidObject — реально нигде не используется, объявлен в Controls.pas.
- EInvalidOperation — вызывается при попытке отображения или обращения по Windows-обработчику (handle) контрольного элемента, не имеющего владельца (например, сразу после вызова MyControl:=TListBox.Create(. ) происходит обращение к методу Refresh).
- EInvalidPointer — происходит при попытке освобождения уже освобожденного или еще неинициализированного указателя, при вызове Dispose(), FreeMem() или деструктора класса.
- EListError — вызывается при обращении к элементу наследника TList по индексу, выходящему за пределы допустимых значений (например, объект TStringList содержит только 10 строк, а происходит обращение к одиннадцатому).
- EMathError — предок исключений, случающихся при выполнении операций с плавающей точкой.
- EInvalidOp — происходит, когда математическому сопроцессору передается ошибочная инструкция. Такое исключение не будет до конца обработано, пока Вы контролируете сопроцессор напрямую из ассемблерного кода.
- EOverflow — происходит как результат переполнения операций с плавающей точкой при слишком больших величинах. Соответствует RunTime Error 205.
- Underflow — происходит как результат переполнения операций с плавающей точкой при слишком малых величинах. Соответствует RunTime Error 206.
- EZeroDivide — вызывается в результате деления на ноль.
- EMenuError — вызывается в случае любых ошибок при работе с пунктами меню для компонент TMenu, TMenuItem, TPopupMenu и их наследников.
- EOutlineError — вызывается в случае любых ошибок при работе с TOutLine и любыми его наследниками.
- EOutOfMemory — происходит в случае вызовов New(), GetMem() или конструкторов классов при невозможности распределения памяти. Соответствует RunTime Error 203.
- EOutOfResources — происходит в том случае, когда невозможно выполнение запроса на выделение или заполнение тех или иных Windows ресурсов (например таких, как обработчики — handles).
- EParserError — вызывается когда Delphi не может произвести разбор и перевод текста описания формы в двоичный вид (часто происходит в случае исправления текста описания формы вручную в IDE Delphi).
- EPrinter — вызывается в случае любых ошибок при работе с принтером.
- EProcessorException — предок исключений, вызываемых в случае прерывания процессора- hardware breakpoint. Никогда не включается в DLL, может обрабатываться только в «цельном» приложении.
- EBreakpoint — вызывается в случае останова на точке прерывания при отладке в IDE Delphi. Среда Delphi обрабатывает это исключение самостоятельно.
- EFault — предок исключений, вызываемых в случае невозможности обработки процессором тех или иных операций.
- EGPFault — вызывается, когда происходит «общее нарушение защиты» — General Protection Fault. Соответствует RunTime Error 216.
- EInvalidOpCode — вызывается, когда процессор пытается выполнить недопустимые инструкции.
- EPageFault — обычно происходит как результат ошибки менеджера памяти Windows, вследствие некоторых ошибок в коде Вашего приложения. После такого исключения рекомендуется перезапустить Windows.
- EStackFault — происходит при ошибках работы со стеком, часто вследствие некорректных попыток доступа к стеку из фрагментов кода на ассемблере. Компиляция Ваших программ со включенной проверкой работы со стеком <$S+>помогает отследить такого рода ошибки.
- ESingleStep — аналогично EBreakpoint, это исключение происходит при пошаговом выполнении приложения в IDE Delphi, которая сама его и обрабатывает.
- EPropertyError — вызывается в случае ошибок в редакторах свойств, встраиваемых в IDE Delphi. Имеет большое значение для написания надежных property editors. Определен в модуле DsgnIntf.pas.
- EResNotFound — происходит в случае тех или иных проблем, имеющих место при попытке загрузки ресурсов форм — файлов .DFM в режиме дизайнера. Часто причиной таких исключений бывает нарушение соответствия между определением класса формы и ее описанием на уровне ресурса (например,вследствие изменения порядка следования полей-ссылок на компоненты, вставленные в форму в режиме дизайнера).
- EStreamError — предок исключений, вызываемых при работе с потоками.
- EFCreateError — происходит в случае ошибок создания потока (например, при некорректном задании файла потока).
- EFilerError — вызывается при попытке вторичной регистрации уже зарегистрированного класса (компоненты). Является, также, предком специализированных обработчиков исключений, возникающих при работе с классами компонент.
- EClassNotFound — обычно происходит, когда в описании класса формы удалено поле-ссылка на компоненту, вставленную в форму в режиме дизайнера. Вызывается, в отличие от EResNotFound, в RunTime.
- EInvalidImage — вызывается при попытке чтения файла, не являющегося ресурсом, или разрушенного файла ресурса, специализированными функциями чтения ресурсов (например, функцией ReadComponent).
- EMethodNotFound — аналогично EClassNotFound, только при несоответствии методов, связанных с теми или иными обработчиками событий.
- EReadError — происходит в том случае, когда невозможно прочитать значение свойства или другого набора байт из потока (в том числе ресурса).
- EFOpenError — вызывается когда тот или иной специфированный поток не может быть открыт (например, когда поток не существует).
- EStringListError — происходит при ошибках работы с объектом TStringList (кроме ошибок, обрабатываемых TListError).
Исключения, возникающие при работе с базами данных
Delphi, обладая прекрасными средствами доступа к данным, основывающимися на интерфейсе IDAPI, реализованной в виде библиотеки Borland Database Engine (BDE), включает ряд обработчиков исключительных ситуаций для регистрации ошибок в компонентах VCL работающим с БД. Дадим краткую характеристику основным из них:
- EDatabaseError — наследник Exception ; происходит при ошибках доступа к данным в компонентах-наследниках TDataSet. Объявлено в модуле DB. Ниже приведен пример из Delphi On-line Help, посвященный этому исключению:
- EDBEngineError — наследник EDatabaseError ; вызывается, когда происходят ошибки BDE или на сервере БД. Объявлено в модуле DB:
Особенно важны два свойства класса EDBEngineError : Errors — список всех ошибок, находящихся в стеке ошибок BDE. Индекс первой ошибки 0;
ErrorCount — количество ошибок в стеке.
Объекты, содержащиеся в Errors, имеют тип TDBError. Доступные свойства класса TDBError:
ErrorCode — код ошибки, возвращаемый Borland Database Engine;
Category — категория ошибки, описанной в ErrorCode;
SubCode — ‘субкод’ ошибки из ErrorCode;
NativeError — ошибка, возвращаемая сервером БД. Если NativeError 0, то ошибка в ErrorCode не от сервера;
Message — сообщение, переданное сервером, если NativeError не равно 0; сообщение BDE — в противном случае.
Заключение
Данный урок должен был дать вам достаточно информации для того, чтобы начать исследование того, как Вы можете использовать систему обработки исключительных ситуаций в вашей программе. Вы, конечно, можете обрабатывать ошибки и без привлечения этой системы; но с ней Вы получите лучшие результаты с меньшими усилиями.
Источник
Adblock
detector Начинает предложение заманивающее в ловушку ошибки инструкции
I’m asking for Delphi native, not Prism(net).
This is my code:
raise Exception.Create('some test');
Undeclared identifier «Exception».
Where’s the problem, how do I throw/raise exceptions?
w5m
2,2763 gold badges33 silver badges46 bronze badges
asked Jul 13, 2009 at 10:08
Ivan ProdanovIvan Prodanov
34.2k76 gold badges173 silver badges246 bronze badges
The exception class «Exception» is declared in the unit SysUtils. So you must add «SysUtils» to your uses-clause.
uses
SysUtils;
procedure RaiseMyException;
begin
raise Exception.Create('Hallo World!');
end;
answered Jul 13, 2009 at 10:12
3
Remember to add SysUtils
to your uses
units.
I also suggest below a nice way to keep track of categories, formats of messages and meaning of exception:
Type TMyException=class
public
class procedure RaiseError1(param:integer);
class procedure RaiseError2(param1,param2:integer);
class procedure RaiseError3(param:string);
end;
implementation
class procedure TMyException.RaiseError1(param:integer);
begin
raise Exception.create(format('This is an exception with param %d',[param]));
end;
//declare here other RaiseErrorX
A simple way of using this is:
TMyException.RaiseError1(123);
w5m
2,2763 gold badges33 silver badges46 bronze badges
answered Jul 14, 2009 at 15:27
3
You may need to add sysutils to the uses clause, it is not built in and is optional according to Delphi in a nutshell.
answered Jul 13, 2009 at 10:11
RobSRobS
3,78727 silver badges34 bronze badges
You are using SysUtils aren’t you? Exception is declared in there IIRC.
answered Jul 13, 2009 at 10:11
WillWill
73k38 gold badges168 silver badges240 bronze badges
Обработка исключительных ситуаций в DelphiСодержание
Структурная обработка исключительных ситуаций Модель исключительных ситуаций в Delphi Синтаксис обработки исключительных ситуаций Примеры обработки исключительных ситуаций Вызов исключительной ситуации Доступ к экземпляру объекта exception Предопределенные обработчики исключительных ситуаций Исключения, возникающие при работе с базами данных Заключение ОбзорС целью поддержки структурной обработки исключительных ситуаций (exception) в Delphi введены новые расширения языка Pascal. В данной статье будет дано описание того, что из себя представляет такая обработка, почему она полезна, будут приведены соответствующий синтаксис Object Pascal и примеры использования исключительных ситуаций в Delphi. Структурная обработка исключительных ситуацийСтруктурная обработка исключительных ситуаций — это система, позволяющая программисту при возникновении ошибки (исключительной ситуации) связаться с кодом программы, подготовленным для обработки такой ошибки. Это выполняется с помощью языковых конструкций, которые как бы «охраняют» фрагмент кода программы и определяют обработчики ошибок, которые будут вызываться, если что-то пойдет не так в «охраняемом» участке кода. В данном случае понятие исключительной ситуации относится к языку и не нужно его путать с системными исключительными ситуациями (hardware exceptions), такими как General Protection Fault. Эти исключительные ситуации обычно используют прерывания и особые состояния «железа» для обработки критичной системной ошибки; исключительные ситуации в Delphi же независимы от «железа», не используют прерываний и используются для обработки ошибочных состояний, с которыми подпрограмма не готова иметь дело. Системные исключительные ситуации, конечно, могут быть перехвачены и преобразованы в языковые исключительные ситуации, но это только одно из применений языковых исключительных ситуаций. При традиционной обработке ошибок, ошибки, обнаруженные в процедуре обычно передаются наружу (в вызывавшую процедуру) в виде возвращаемого значения функции, параметров или глобальных переменных (флажков). Каждая вызывающая процедура должна проверять результат вызова на наличие ошибки и выполнять соответствующие действия. Часто, это просто выход еще выше, в более верхнюю вызывающую процедуру и т.д. : функция A вызывает B, B вызывает C, C обнаруживает ошибку и возвращает код ошибки в B, B проверяет возвращаемый код, видит, что возникла ошибка и возвращает код ошибки в A, A проверяет возвращаемый код и выдает сообщение об ошибке либо решает сделать что-нибудь еще, раз первая попытка не удалась. Такая «пожарная бригада» для обработки ошибок трудоемка, требует написания большого количества кода в котором можно легко ошибиться и который трудно отлаживать. Одна ошибка в коде программы или переприсвоение в цепочке возвращаемых значений может привести к тому, что нельзя будет связать ошибочное состояние с положением дел во внешнем мире. Результатом будет ненормальное поведение программы, потеря данных или ресурсов, или крах системы. Структурная обработка исключительной ситуации замещает ручную обработку ошибок автоматической, сгенерированной компилятором системой уведомления. В приведенном выше примере, процедура A установила бы «охрану» со связанным обработчиком ошибки на фрагмент кода, в котором вызывается B. B просто вызывает C. Когда C обнаруживает ошибку, то создает (raise) исключительную ситуацию. Специальный код, сгенерированный компилятором и встроенный в Run-Time Library (RTL) начинает поиск обработчика данной исключительной ситуации. При поиске «защищенного» участка кода используется информация, сохраненная в стеке. В процедурах C и B нет такого участка, а в A — есть. Если один из обработчиков ошибок, которые используются в A, подходит по типу для возникшей в C исключительной ситуации, то программа переходит на его выполнение. При этом, область стека, используемая в B и C, очищается; выполнение этих процедур прекращается. Если в A нет подходящего обработчика, то поиск продолжается в более верхнем уровне, и так может идти, пока поиск не достигнет подходящего обработчика ошибок среди используемых по умолчанию обработчиков в RTL. Обработчики ошибок из RTL только показывают сообщение об ошибке и форсированно прекращают выполнение программы. Любая исключительная ситуация, которая осталась необработанной, приведет к прекращению выполнения приложения. Без проверки возвращаемого кода после каждого вызова подпрограммы, код программы должен быть более простым, а скомпилированный код — более быстрым. При наличии исключительных ситуаций подпрограмма B не должна содержать дополнительный код для проверки возвращаемого результата и передачи его в A. B ничего не должна делать для передачи исключительной ситуации, возникшей в C, в процедуру A — встроенная система обработки исключительных ситуаций делает всю работу. Данная система называется структурной, поскольку обработка ошибок определяется областью «защищенного» кода; такие области могут быть вложенными. Выполнение программы не может перейти на произвольный участок кода; выполнение программы может перейти только на обработчик исключительной ситуации активной программы. Модель исключительных ситуаций в DelphiМодель исключительных ситуаций в Object Pascal является невозобновляемой(non-resumable). При возникновении исключительной ситуации Вы уже не сможете вернуться в точку, где она возникла, для продолжения выполнения программы (это позволяет сделать возобновляемая(resumable) модель). Невозобновляемые исключительные ситуации разрушают стек, поскольку они сканируют его в поисках обработчика; в возобновляемой модели необходимо сохранять стек, состояние регистров процессора в точке возникновения ошибки и выполнять поиск обработчика и его выполнение в отдельном стеке. Возобновляемую систему обработки исключительных ситуаций гораздо труднее создать и применять, нежели невозобновляемую. Синтаксис обработки исключительных ситуацийТеперь, когда мы рассмотрели, что такое исключительные ситуации, давайте дадим ясную картину, как они применяются. Новое ключевое слово, добавленное в язык Object Pascal — try. Оно используется для обозначения первой части защищенного участка кода. Существует два типа защищенных участков:
Первый тип используется для обработки исключительных ситуаций. Его синтаксис: try Statement 1; Statement 2; ... except on Exception1 do Statement; on Exception2 do Statement; ... else Statements; {default exception-handler} end; Для уверенности в том, что ресурсы, занятые вашим приложением, освободятся в любом случае, Вы можете использовать конструкцию второго типа. Код, расположенный в части finally, выполняется в любом случае, даже если возникает исключительная ситуация. Соответствующий синтаксис: try Statement1; Statement2; ... finally Statements; { These statements always execute } end; Примеры обработки исключительных ситуацийНиже приведены процедуры A,B и C, обсуждавшиеся ранее, воплощенные в новом синтаксисе Object Pascal: type ESampleError = class(Exception); var ErrorCondition: Boolean; procedure C; begin writeln('Enter C'); if (ErrorCondition) then begin writeln('Raising exception in C'); raise ESampleError.Create('Error!'); end; writeln('Exit C'); end; procedure B; begin writeln('enter B'); C; writeln('exit B'); end; procedure A; begin writeln('Enter A'); try writeln('Enter A''s try block'); B; writeln('After B call'); except on ESampleError do writeln('Inside A''s ESampleError handler'); on ESomethingElse do writeln('Inside A''s ESomethingElse handler'); end; writeln('Exit A'); end; begin writeln('begin main'); ErrorCondition := True; A; writeln('end main'); end. При ErrorCondition = True программа выдаст: begin main Enter A Enter A's try block enter B Enter C Raising exception in C Inside A's ESampleError handler Exit A end main Возможно вас удивила декларация типа ‘ESampleError =class’ вместо ‘=object’; это еще одно новое расширение языка. Delphi вводит новую модель объектов, доступную через декларацию типа ‘=class’. Описание новой объектной модели дается в других уроках. Здесь же достаточно сказать, что исключительные ситуации (exceptions) являются классами, частью новой объектной модели. Процедура C проверяет наличие ошибки (в нашем случае это значение глобальной переменной) и, если она есть (а это так), C вызывает(raise) исключительную ситуацию класса ESampleError. Процедура A помещает часть кода в блок try..except. Первая часть этого блока содержит часть кода, аналогично конструкции begin..end. Эта часть кода завершается ключевым словом except, далее следует один или более обработчиков исключительных ситуаций on xxxx do yyyy, далее может быть включен необязательный блок else, вся конструкция заканчивается end;. В конструкции, назначающей определенную обработку для конкретной исключительной ситуации (on xxxx do yyyy), после резервного слова on указывается класс исключительной ситуации, а после do следует собственно код обработки данной ошибки. Если возникшая исключительная ситуация подходит по типу к указанному после on, то выполнение программы переходит сюда (на код после do). Исключительная ситуация подходит в том случае, если она того же класса, что указан в on, либо является его потомком. Например, в случае on EFileNotFound обрабатываться будет ситуация, когда файл не найден. А в случае on EFileIO — все ошибки при работе с файлами, в том числе и предыдущая ситуация. В блоке else обрабатываются все ошибки, не обработанные до этого. Приведенные в примере процедуры содержат код (строка с writeln), который отображает путь выполнения программы. Когда C вызывает exception, программа сразу переходит на обработчик ошибок в процедуре A, игнорируя оставшуюся часть кода в процедурах B и C. После того, как найден подходящий обработчик ошибки, поиск оканчивается. После выполнения кода обработчика, программа продолжает выполняться с оператора, стоящего после слова end блока try..except (в примере — writeln(‘Exit A’)). Конструкция try..except подходит, если известно, какой тип ошибок нужно обрабатывать в конкретной ситуации. Но что делать, если требуется выполнить некоторые действия в любом случае, произошла ошибка или нет? Это тот случай, когда понадобится конструкция try..finally. Рассмотрим модифицированную процедуру B: procedure NewB; var P: Pointer; begin writeln('enter B'); GetMem(P, 1000); C; FreeMem(P, 1000); writeln('exit B'); end; Если C вызывает исключительную ситуацию, то программа уже не возвращается в процедуру B. А что же с теми 1000 байтами памяти, захваченными в B? Строка FreeMem(P,1000) не выполнится и Вы потеряете кусок памяти. Как это исправить? Нужно ненавязчиво включить процедуру B в процесс, например: procedure NewB; var P: Pointer; begin writeln('enter NewB'); GetMem(P, 1000); try writeln('enter NewB''s try block'); C; writeln('end of NewB''s try block'); finally writeln('inside NewB''s finally block'); FreeMem(P, 1000); end; writeln('exit NewB'); end; Если в A поместить вызов NewB вместо B, то программа выведет сообщения следующим образом: begin main Enter A Enter A's try block enter NewB enter NewB's try block Enter C Raising exception in C inside NewB's finally block Inside A's ESampleError handler Exit A end main Код в блоке finally выполнится при любой ошибке, возникшей в соответствующем блоке try. Он же выполнится и в том случае, если ошибки не возникло. В любом случае память будет освобождена. Если возникла ошибка, то сначала выполняется блок finally, затем начинается поиск подходящего обработчика. В штатной ситуации, после блока finally программа переходит на следующее предложение после блока. Почему вызов GetMem не помещен внутрь блока try? Этот вызов может окончиться неудачно и вызвать exception EOutOfMemory. Если это произошло, то FreeMem попытается освободить память, которая не была распределена. Когда мы размещаем GetMem вне защищаемого участка, то предполагаем, что B сможет получить нужное количество памяти, а если нет, то более верхняя процедура получит уведомление EOutOfMemory. А что, если требуется в B распределить 4 области памяти по схеме все-или-ничего? Если первые две попытки удались, а третья провалилась, то как освободить захваченную область память? Можно так: procedure NewB; var p,q,r,s: Pointer; begin writeln('enter B'); P := nil; Q := nil; R := nil; S := nil; try writeln('enter B''s try block'); GetMem(P, 1000); GetMem(Q, 1000); GetMem(R, 1000); GetMem(S, 1000); C; writeln('end of B''s try block'); finally writeln('inside B''s finally block'); if P <> nil then FreeMem(P, 1000); if Q <> nil then FreeMem(Q, 1000); if R <> nil then FreeMem(R, 1000); if S <> nil then FreeMem(S, 1000); end; writeln('exit B'); end; Установив сперва указатели в NIL, далее можно определить, успешно ли прошел вызов GetMem. Оба типа конструкции try можно использовать в любом месте, допускается вложенность любой глубины. Исключительную ситуацию можно вызывать внутри обработчика ошибки, конструкцию try можно использовать внутри обработчика исключительной ситуации. Вызов исключительной ситуацииВ процедуре C из примера мы уже могли видеть, как должна поступать программа при обнаружении состояния ошибки — она вызывает исключительную ситуацию: raise ESampleError.Create('Error!'); После ключевого слова raise следует код, аналогичный тому, что используется для создания нового экземпляра класса. Действительно, в момент вызова исключительной ситуации создается экземпляр указанного класса; данный экземпляр существует до момента окончания обработки исключительной ситуации и затем автоматически уничтожается. Вся информация, которую нужно сообщить в обработчик ошибки передается в объект через его конструктор в момент создания. Почти все существующие классы исключительных ситуаций являются наследниками базового класса Exception и не содержат новых свойств или методов. Класс Exception имеет несколько конструкторов, какой из них конкретно использовать — зависит от задачи. Описание класса Exception можно найти в on-line Help. Доступ к экземпляру объекта exceptionДо сих пор мы рассматривали механизмы защиты кода и ресурсов, логику работы программы в исключительной ситуации. Теперь нужно немного разобраться с тем, как же обрабатывать возникшую ошибку. А точнее, как получить дополнительную информацию о коде ошибки, текст сообщения и т.п. Как уже говорилось, при вызове исключительной ситуации (raise) автоматически создается экземпляр соответствующего класса, который и содержит информацию об ошибке. Весь вопрос в том, как в обработчике данной ситуации получить доступ к этому объекту. Рассмотрим модифицированную процедуру A в нашем примере: procedure NewA; begin writeln('Enter A'); try writeln('Enter A''s try block'); B; writeln('After B call'); except on E: ESampleError do writeln(E.Message); on ESomethingElse do writeln('Inside A''s ESomethingElse handler'); end; writeln('Exit A'); end; Здесь все изменения внесены в строку on ESE: ESampleError do writeln(ESE.Message); Пример демонстрирует еще одно новшество в языке Object Pascal — создание локальной переменной. В нашем примере локальной переменной является ESE — это тот самый экземпляр класса ESampleError, который был создан в процедуре C в момент вызова исключительного состояния. Переменная ESE доступна только внутри блока do. Свойство Message объекта ESE содержит сообщение, которое было передано в конструктор Create в процедуре C. Есть еще один способ доступа к экземпляру exception — использовать функцию ExceptionObject: on ESampleError do writeln(ESampleError(ExceptionObject).Message); Предопределенные обработчики исключительных ситуацийНиже Вы найдете справочную информацию по предопределенным исключениям, необходимую для профессионального программирования в Delphi.
procedure Abort; begin raise EAbort.CreateRes(SOperationAborted) at ReturnAddr; end;
Исключения, возникающие при работе с базами данныхDelphi, обладая прекрасными средствами доступа к данным, основывающимися на интерфейсе IDAPI, реализованной в виде библиотеки Borland Database Engine (BDE), включает ряд обработчиков исключительных ситуаций для регистрации ошибок в компонентах VCL работающим с БД. Дадим краткую характеристику основным из них:
repeat {пока не откроем таблицу или не нажмем кнопку Cancel} try Table1.Active := True; {Пытаемся открыть таблицу} Break; { Если нет ошибки - прерваем цикл} except on EDatabaseError do {Если нажата OK - повторяем попытку открытия Table1} if MessageDlg('Не могу открыть Table1', mtError, [mbOK, mbCancel], 0) <> mrOK then raise; end; until False;
EDBEngineError = class(EDatabaseError) private FErrors: TList; function GetError(Index: Integer): TDBError; function GetErrorCount: Integer; public constructor Create(ErrorCode: DBIResult); destructor Destroy; property ErrorCount: Integer; property Errors[Index: Integer]: TDBError; end; Особенно важны два свойства класса EDBEngineError : Errors ЗаключениеДанный урок должен был дать вам достаточно информации для того, чтобы начать исследование того, как Вы можете использовать систему обработки исключительных ситуаций в вашей программе. Вы, конечно, можете обрабатывать ошибки и без привлечения этой системы; но с ней Вы получите лучшие результаты с меньшими усилиями. Назад | Содержание |
Description | ||||||||||||
The Raise keyword creates an exception object that is passed to the Delphi exception handler. You would only raise an exception in literally exceptional circumstances. This is partly because of the resource and performance overheads incurred, but also because there are neater ways for application error handling, such as return codes from functions. Version 1 On its own, Raise is used inside the Except clause of a Try statement. It simply re-raises the current exception for handling at a higher level in the application. Version 2 Uses an new exception object to report an exception. Normally, you would use an Exception object, or an inherited Exception object, but you are not restricted to do so. The exception address is that of the raise statement. You can create the object at the time of the raise: Raise Exception.Create(‘Error happened’); Version 3 In all cases, when the Raise call is made, code execution jumps to the Delphi exception handler — it either terminates the program, or uses the current Try statement to handle it. |
||||||||||||
Notes | ||||||||||||
Warning use only when appropriate. | ||||||||||||
Related commands | ||||||||||||
|
||||||||||||
Example code : Use of Raise in a function | |||||
var fred, jim : string; begin // Set up some sample names fred := ‘Good name’; jim := ‘Badname ‘; // Try to swap these names try ShowMessage(fred+’ swapped = ‘+SwapNames(fred)); ShowMessage(jim+’ swapped = ‘+SwapNames(jim)); except On E : Exception do ShowMessage(E.Message); end; end; // Swaps first and second names in the passed name string // Raises an exception if the name is invalid function TForm1.SwapNames(name: string): string; var blankPos : Integer; i : Integer; nameLen : Integer; begin // Clear the result string to indicate no success yet Result := »; // Find the position of the last name blankPos := Pos(‘ ‘, name); // If found, and position is short of the name end // then we are OK so far nameLen := Length(name); if (blankPos > 0) and (blankPos < nameLen) then begin // Find the start of the second name i := blankPos + 1; repeat // If last name start found, swap first and last names if name[i] <> ‘ ‘ then Result := Copy(name, i, nameLen-i+1) + ‘ ‘ + Copy(name, 1, blankPos-1) else Inc(i); until (i > nameLen) or (Length(Result) > 0); end; // Couldn’t swap first and second names ? if Length(Result) = 0 then Raise Exception.CreateFmt(‘Invalid name : »%s»’, [name]); end; |
|||||
Show full unit code | |||||
Good name swapped = name Good Invalid name : ‘Badname ‘ |
|||||
|
Delphi Programming © Neil Moffatt . All rights reserved. | Home Page
Возбуждение исключений
При создании приложений часто приходится проектировать блоки кода, которые могли бы возбуждать исключения. В Delphi для этих целей предусмотрены оператор Raise и несколько специальных процедур.
Оператор Raise
Для возбуждения исключений обычно используется оператор Raise. Синтаксис его использования следующий:
Raise [<объект>] [At <адрес>]
Возможны следующие варианты использования оператора Raise:
а) Для повторного возбуждения (reraising) текущего исключения.
Внутри нескольких вложенных обработчиков Raise захватывает конкретный экземпляр исключения и передает его вверх первой встречной конструкции Try. Если такой конструкцией будет Try..Finally, то выполняться операторы раздела Finally. Если это конструкция Try..Except, то исключение будет помечено, как обрабатываемое и начнется выполнение операторов раздела Except. Чаще всего оператор Raise без параметров включают внутрь раздела Try..Except.
Если других уровней обработки исключения нет, то оно будет перехвачено имеющимся в Delphi обработчиком по умолчанию, который выведет соответствующее сообщение и уничтожит объект исключения.
б) Для возбуждения исключения, включая собственные. Если создаются собственные компоненты, то может возникнуть необходимость в создании специальных исключений, которые могут возбуждаться, например, при присвоении недопустимого значению свойству компонента. Для этого необходимо, как минимум, объявить класс исключения. Когда код в компоненте должен возбудить исключение необходимо создать представителя этого класса.
Raise возбуждает исключительную ситуацию, создавая экземпляр исключения. Обычно эту операцию выполняют по условию If..Then Raise … В этом заключается второе назначение Raise — инициировать различные ситуации, включая пользовательские. Класс исключения может быть и уже известным. Очистка ошибки также выполняется.
If Length(Editl.Text)=O Then Raise Exception.Create(‘He указана длина строки!’);
Процедуры, возбуждающие исключения
а) Для возбуждения исключений, связанных с отсутствием оперативной памяти предусмотрена следующая процедура:
OutOfMemoryError; — Возбуждает исключение класса EOutOfMemory.
Использовать процедуру OutOfMemoryError для возбуждения исключения гораздо проще, чем базовое исключение Exception. Так стандартное возбуждение исключения с помощью базового исключения должно выглядеть примерно так:
Raise Exception.Create(‘Out of memory.’);
Для возбуждения исключения с помощью процедуры OutOfMemoryError и вывода аналогичного сообщения достаточно записать:
OutOfMemoryError;
б) Для повторного возбуждения исключений, связанных с ошибками функций Win32 API можно использовать следующую процедуру.
RaiseLastWin32Error — Возбуждает заново исключение EWin32Error.
Возбуждение исключений в базах данных
Для возбуждения исключительных ситуаций класса EDatabaseError может использоваться несколько следующих процедур:
DatabaseError(Const Message: String; Component: TComponent=Nil);
Возбуждает исключение класса EDatabaseError, передавая сообщение с помощью параметра Message. Второй параметр позволяет уточнить источник ошибки.
Использовать процедуру DatabaseError для возбуждения исключения гораздо проще, чем базовое исключение Exception. Так стандартное возбуждение исключения с помощью базового исключения должно выглядеть примерно так:
Raise Exception.Create(‘ошибка!’);
Для возбуждения исключения с помощью процедуры DatabaseError достаточно записать:
DatabaseError(‘ ошибка!’);
DBError(Ident: Word);
Возбуждает исключение класса EDatabaseError, передавая сообщение из строкового ресурса с номером Ident.
DBErrorFmt(Ident: Word; Const Args: Array Of Const);
Возбуждает исключение класса EDatabaseError, передавая сообщение из строкового ресурса с номером Ident, которое может быть отформатировано с помощью констант Args.
Для возбуждения исключительных ситуаций класса EDBEngineError может использоваться несколько следующих процедур:
DBIError(ErrorCode: DBIResult);
Возбуждает исключение класса EDBEngineError для BDE, с передаваемым номером кода ошибки ErrorCode.
Воспользоваться вышеприведенными процедурами для возбуждения исключительных ситуаций можно при прямых вызовах BDE, при попытках динамического создания объектов базы данных с помощью соответствующих команд SQL и т.п.
Разрабатывая какое-нибудь приложение, вы должны написать код. который будет решать поставленную задачу, а также код, который будет выполнять проверку на наличие ошибок. Как правило, код для обработки ошибок строится на основе оператора if.
Оператор if часто используется для проверки данных, вводимых пользователем, а также результатов выполнения функций. В простых алгоритмах можно ограничиться применением оператора if, однако в приложениях с графическим интерфейсом пользователя, где пользователи имеют полную свободу действий, ошибки могут возникать когда угодно и где угодно. Использование одного только оператора if для защиты приложения — не самая лучшая идея.
С задачей перехвата ошибок и реагирования на них лучше всего справляется механизм обработки исключений. Если в приложении, написанном с помощью Delphi, возникает ошибка, то приложение автоматически генерирует исключение. Исключением представляет собой объект, который описывает возникающую ошибку.
Генерация исключения означает всего лишь то. что приложение создало объект исключения и максимально подробно описало ошибку.
Если мы не обрабатываем исключение (то есть не приготовлен специальный код для перехвата исключения), приложение само сделает это автоматически. Обычно приложение обрабатывает исключение, выводя на экран монитора окно с сообщением о возникшей ошибке. Например, если вы передадите функции StrToInt строку, содержащую символы, которые не могут быть преобразованы в числовое значение, или вообще пустую строку, то функция сгенерирует исключение (рис. 13.1).
Рис. 13.1. Исключение, обработанное приложением
Чтобы обработать исключение, сгенерированное функцией StrToInt, мы должны поместить вызов функции StrToInt в защищенный блок кода. Защищенным является блок кода, который может реагировать на некоторое исключение. В Delphiзащищенный блок выглядит следующим образом:
Операторы, которые могут сгенерировать исключение, записываются в блоке try, а в обработчике исключений пишется код, который занимается обработкой исключений. Обработчик исключения является частью защищенного блока, начинающегося с зарезервированного слова except в Delphi.
Если вы передадите функции StrToInt допустимую строку, и при этом исключение не возникнет, будет выполнен только тот код, который находится в блоке try. Код в блоке исключения выполняется только в том случае, если оператор, находящийся Внутри этого блока, сгенерирует исключение.
В следующих двух примерах показано, как осуществляется вызов функции StrToInt и перехват исключения, которое может быть сгенерировано этой функцией (рис. 13.2). В листинге 13.1А показан пример перехвата исключений в приложениях, написанных с помощью Delphi.
Листинг 13.1А. Перехват исключения в Delphi
Теперь давайте попытаемся создать простой калькулятор, с помощью которого можно будет делить числа. Интерфейс пользователя этой небольшой программы показан на рис. 13.3.
Чтобы разделить значения, введенные в компонентах TEdit, мы должны написать код, который сначала преобразует их в целые числа, а затем разделит одно на другое. Этот код может легко сгенерировать два исключения.
Одно из них, EConvertError, может быть сгенерировано в том случае, если значение одного из компонентов TEdit невозможно преобразовать к целому типу, а другое. EDivByZero, может быть сгенерировано тогда, когда предпринимается попытка разделить первое число на 0.
Листинг 13.2. Деление двух чисел
Несмотря на то что вы можете написать обработчики для перехвата всех исключений, вы должны постараться обрабатывать только специфические исключения. Обработать специфическое исключение можно с помощью зарезервированного слова on. с которым связан следующий синтаксис:
on
Некоторое_Исключение
do
Обработка_Исключения;
Конструкцию on-do можно использовать только в рамках обработчика исключений:
По мере возможности, для обработки различных исключений лучше использовать конструкцию on-do. Например, вы можете обработать исключение EConvertError, выводя сообщение об ошибке, а исключение EDivByZero — уведомляя пользователя о том. что второе число не может быть равно нулю, и автоматически заменяя его единицей. В листинге 13.ЗА показан пример обработки специфических исключений в Delphi.
Листинг 13.3А. Обработка специфических исключений
Если конструкцию on-do использовать для обработки специфических исключений, вы должны также написать код для обработки ошибок, о которых вам ничего не будет известно. Чтобы обработать исключения, которые вам не удастся обработать специфическим образом, можно добавить к обработчику исключения часть else.
В ответ на ошибку, возникшую во время работы приложения, создается экземпляр объекта исключения. Когда это исключение будет обработано, его объект будет автоматически освобожден. Если вы не хотите обрабатывать специфическое исключение, или же не знаете, как это сделать, вы должны разрешить Delphi самостоятельно разобраться с ним. Для этого вы должны повторно сгенерировать исключение, то есть повторно создать экземпляр объекта исключения. Для этой цели в Delphiиспользуется зарезервированное слово raise.
Например, следующий обработчик исключения обрабатывает только исключение EConvertError. Как только возникнет какое-то другое исключение, обработчик исключения сгенерирует его повторно. В итоге исключение останется «в силе» после завершения работы обработчика, и будет передано на обработку уже другому обработчику, который обычно используется по умолчанию. В листинге 13.4А показан пример повторного вызова исключения в Delphi.
Листинг 13.4А. Повторная генерация исключения в Delphi
Итак, если будет сгенерировано исключение EConvertError. то обработчик справится с ним самостоятельно, а если возникнет любое другое исключение, скажем. EDivByZero или EAccessViolation. то обработчик сгенерирует его повторно и направит его другому обработчику (рис. 13.4).
Зарезервированное слово raise используется также и для генерации исключения. Чтобы сгенерировать исключение в Delphi, используйте зарезервированное слово raise, указывая вслед за ним экземпляр объекта исключения. Экземпляром объекта исключения обычно является вызов конструктора исключения.
Синтаксис генерации исключения обычно выглядит следующим образом:
Вы можете, например, создать специальный вариант функции StrToInt, которая будет генерировать исключение EConvertError с помощью специальных сообщений об ошибке, если строку нельзя будет преобразовать в целое число. В листинге 13.5А представлена версия этой функции в Delphi.
Листинг 13.5А. Генерация исключений в Delphi
Конструкция оп-с!о позволяет получать на время объект исключения с помощью следующего синтаксиса:
В качестве идентификатора обычно применяется заглавная буква Е. Когда вы получаете объект исключения, вы можете использовать его подобно любому другому объекту и даже обращаться к его свойствам и методам. Единственное, что не рекомендуется делать, это уничтожать объект исключения, поскольку объекты исключения автоматически управляются обработчиком исключения. На рис. 13.6 показан результат использования объекта исключения.
В качестве идентификатора обычно применяется заглавная буква Е. Когда вы получаете объект исключения, вы можете использовать его подобно любому другому объекту и даже обращаться к его свойствам и методам. Единственное, что не рекомендуется делать, это уничтожать объект исключения, поскольку объекты исключения автоматически управляются обработчиком исключения. На рис. 13.6 показан результат использования объекта исключения.
Листинг 13.6. Использование объекта исключения
Создать специальное исключение несложно, и этот процесс ничем не отличается от создания специального класса. Специальные исключения должны порождаться от класса Exception или другого потомка этого класса. Имена классов исключений должны начинаться с заглавной буквы Е.
В листинге 13.7А показана генерация и перехват специального исключения в Delphi. На рис. 13.7 можно видеть результат работы со специальным исключением.
Листинг 13.7А. Работа со специальным исключением
Зарезервированное слово try позволяет построить два различных блока: блок обработчика исключений и блок защиты ресурсов. Блок обработчика исключений создается с помощью зарезервированного слова except, а блок защиты ресурсов— с помощью зарезервированного слова finally. Синтаксическая структура блока защиты ресурсов в Delphi выглядит следующим образом:
Блоки обработки исключений и защиты ресурсов используются по-разному и работают тоже по-разному. Операторы обработчика исключений выполняются только в том случае, если операторы в блоке try сгенерировали исключение, а операторы в блоке finally выполняются всегда, даже если операторы в блоке try не сгенерировали никакого исключения. Если в блоке try возникнет исключение, управление будет передано блоку finally, после чего будет выполнен код очистки. Если в блоке try исключения не возникнут, операторы в блоке finally будут выполняться после операторов в блоке try.
Подходящим способом использования блока защиты ресурсов является распределение или, с другой стороны, затребование ресурса перед блоком try. После того как вы затребуете ресурс, поместите операторы, использующие ресурс, внутрь блока try. Когда работа с ресурсом будет завершена, вы должны будете освободить его. Операторы, освобождающие ресурс, должны быть написаны в блоке finally.
Блок защиты ресурса часто используется для того, чтобы обеспечить надлежащее освобождение динамически созданных объектов. Например, динамическое создание модальной формы необходимо всегда защищать с помощью блока try-finally (см. листинг 13.8).
Листинг 13.8. Динамическое создание формы с защитой ресурса, версий Delphi
В листинге 13.9 представлен более короткий способ динамического создания формы, защищенной блоком try-finally:
Другое отличие между блоками обработки исключений и блоками обработки ресурсов заключается в том, что блок обработки ресурсов не обрабатывает исключения. Таким образом, если исключение возникнет, оно будет передано первому доступному обработчику исключений. Например, если вы выполните следующий код. то исключение EDivByZero приведет к тому, что обработчик исключений, используемый по умолчанию, выведет на экран монитора сообщение об ошибке, информирующее пользователя о возникшем исключении.
Если вы хотите обработать исключение EDivByZero (или любое другое исключение) внутри блока защиты ресурсов, вы должны написать вложенный блок обработчика исключений. О вложенных блоках читайте в этой статье: Вложенные блоки