КОМПЬЮТЕРНЫЕ КУРСЫ "ПОИСК"
[Главная страница] [Delphi] [Контакты]
Разрабатывая какое-нибудь приложение, вы должны написать код, который будет решать поставленную задачу, а также код, который будет выполнять проверку на наличие ошибок. Как правило, код для обработки ошибок строится на основе оператора if. Оператор if часто используется для проверки данных, вводимых пользователем, а также результатов выполнения функций. В простых алгоритмах можно ограничиться применением оператора if, однако в приложениях с графическим интерфейсом пользователя, где пользователи имеют полную свободу действий, ошибки могут возникать когда угодно и где угодно. Использование одного только оператора if для защиты приложения — не самая лучшая идея.
С задачей перехвата ошибок и реагирования на них лучше всего справляется механизм обработки исключений (Структурированная обработка исключений (Structured exception handling – SHE) представляет собой метод обработки ошибок, благодаря которому можно восстановить нормальную работу приложения после сбоя в работе программы, который в противном случае был бы фатальным). Если в приложении, написанном с помощью Delphi, возникает ошибка, то приложение автоматически генерирует исключение. Исключением представляет собой объект, который описывает возникающую ошибку.
Генерация исключения означает всего лишь то, что приложение создало объект исключения и максимально подробно описало ошибку. Если мы не обрабатываем исключение (то есть не приготовлен специальный код для перехвата исключения), приложение само сделает это автоматически. Обычно приложение обрабатывает исключение, выводя на экран монитора окно с сообщением о возникшей ошибке. Например, если вы передадите функции StrToInt строку, содержащую пустую строку, то функция сгенерирует исключение.
procedure TForm1.Button1Click(Sender: TObject); var x: Integer; begin x := StrToInt(Edit1.Text); end;
Чтобы обработать исключение, сгенерированное функцией StrToInt, мы должны поместить вызов функции StrToInt в защищенный блок кода. Защищенным является блок кода, который может реагировать на некоторое исключение. В Delphi защищенный блок выглядит следующим образом:
try
оператор(ы)
except
операторы обработки исключения
end;
Операторы, которые могут сгенерировать исключение, записываются в блоке try, а в обработчике исключений пишется код, который занимается обработкой исключений. Обработчик исключения является частью защищенного блока, начинающегося с зарезервированного слова except в Delphi.
Если вы передадите функции StrToInt допустимую строку, и при этом исключение не возникнет, будет выполнен только тот код, который находится в блоке try. Код в блоке исключения выполняется только в том случае, если оператор, находящийся внутри этого блока, сгенерирует исключение.
procedure TForm1.Button1Click(Sender: TObject); var x: Integer; begin try x := StrToInt(Edit1.Text); except ShowMessage('Ошибка преобразования'); end; end;
Обработка специфических исключений в Delphi
Теперь давайте попытаемся создать простой калькулятор, с помощью которого можно будет делить числа. Интерфейс пользователя этой небольшой программы показан на рисунке ниже.
Чтобы разделить значения, введенные в компонентах TEdit, мы должны написать код, который сначала преобразует их в целые числа, а затем разделит одно на другое. Этот код может легко сгенерировать два исключения.
Одно из них, EConvertError, может быть сгенерировано в том случае, если значение одного из компонентов TEdit невозможно преобразовать к целому типу, а другое, EDivByZero, может быть сгенерировано тогда, когда предпринимается попытка разделить первое число на 0.
procedure TForm1.Button1Click(Sender: TObject); var Num1,Num2: Integer; begin try Num1 := StrToInt(Edit1.Text); Num2 := StrToInt(Edit2.Text); ShowMessage('Результат: ' + IntToStr(Num1 div Num2)); except ShowMessage('Вы не можете делить эти числа'); end; end;
Несмотря на то, что вы можете написать обработчик для перехвата всех исключений, вы должны постараться обрабатывать только специфические исключения. Обработать специфическое исключение можно с помощью зарезервированного слова on, с которым связан следующий синтаксис:
on Некоторое-Исключение do Обработка_Исключения;
Конструкцию on-do можно использовать только в рамках обработчика исключений:
try
оператор (операторы);
except
on Исключение do
Обработка_Исключения;
on Другое_Исключение do Его_Обработка;
По мере возможности, для обработки различных исключений лучше использовать конструкцию on-do. Например, вы можете обработать исключение EConvertError, выводя сообщение об ошибке, а исключение EDivByZero — уведомляя пользователя о том, что второе число не может быть равно нулю, и автоматически заменяя его единицей.
procedure TForm1.Button1Click(Sender: TObject); var Num1,Num2: Integer; begin try Num1 := StrToInt(Edit1.Text); Num2 := StrToInt(Edit2.Text); ShowMessage('Результат: ' + IntToStr(Num1 div Num2)); except on EConvertError do ShowMessage('Одно из чисел неправильно'); on EDivByZero do begin ShowMessage('Делитель не может быть равным 0'); Edit2.Text := '1'; Edit2.SetFocus; end; // Завершение конструкции on EDivByZero do end; end;
Если конструкцию on-do использовать для обработки специфических исключений, вы должны также написать код для обработки ошибок, о которых вам ничего не будет известно. Чтобы обработать исключения, которые вам не удастся обработать специфическим образом, можно добавить к обработчику исключения часть else.
try
оператор (операторы);
except
on Исключение do
Его_Обра6отка;
on Другое_Исключение
do Его_Обработка;
else
Обработка_Всех_Остальных_Исключений;
end;
Генерация исключений
Зарезервированное слово raise используется для генерации исключения. Чтобы сгенерировать исключение в Delphi, используйте зарезервированное слово raise, указывая вслед за ним экземпляр объекта исключения. Экземпляром объекта исключения обычно является вызов конструктора исключения.
Синтаксис генерации исключения обычно выглядит следующим образом:
raise Класс_Исключения.Create('Сообщение_Об_Ошибке');
Вы можете, например, создать специальный вариант функции StrToInt, которая будет генерировать исключение EConvertError с помощью специальных сообщений об ошибке, если строку нельзя будет преобразовать в целое число.
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Edit1: TEdit; Edit2: TEdit; Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } function CustomStrToInt(const s:string): Integer; end; var Form1: TForm1; implementation {$R *.dfm} { TForm1 } procedure TForm1.Button1Click(Sender: TObject); var Num1, Num2: Integer; begin Num1 := CustomStrToInt(Edit1.Text); Num2 := CustomStrToInt(Edit2.Text); ShowMessage(IntToStr(Num1 div Num2)); end; function TForm1.CustomStrToInt(const s: string): Integer; var ErrorCode: Integer; begin Val(s, Result, ErrorCode); if ErrorCode <> 0 then begin if s = '' then raise EConvertError.Create('Пустая строка не может использоваться') else raise EConvertError.Create('Привет. Вы не можете конвертировать "' + s + '" в целое'); end; end; end.
Использование объекта исключения
Конструкция on-do позволяет получать на время объект исключения с помощью следующего синтаксиса
on Идентификатор: Исключение do Его_Обработка;
В качестве идентификатора обычно применяется заглавная буква Е. Когда вы получаете объект исключения, вы можете использовать его подобно любому другому объекту и даже обращаться к его свойствам и методам. Единственное, что не рекомендуется делать, это уничтожать объект исключения, поскольку объекты исключения автоматически управляются обработчиком исключения.
procedure TForm1.Button1Click(Sender: TObject); var x,y: Integer; begin x := 20; y := 0; try Caption := IntToStr(x div y); except on E: EDivByZero do ShowMessage('Exception: ' + E.ClassType.ClassName + #13 + 'Exception.Message: ' + E.Message); end; end;
Создание специальных исключений в Delphi
Создать специальное исключение несложно, и этот процесс ничем не отличается от создания специального класса. Специальные исключения должны порождаться от класса Exception или другого потомка этого класса. Имена классов исключений должны начинаться с заглавной буквы Е.
type
EMyException = class(Exception);
В листинге ниже показана генерация и перехват специального исключения в Delphi.
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type ENoUpperCaseLetters = class(Exception); TForm1 = class(TForm) Label1: TLabel; Edit1: TEdit; Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } function CountUpperCase(const s: string): Integer; end; var Form1: TForm1; implementation {$R *.dfm} { TForm1 } procedure TForm1.Button1Click(Sender: TObject); var Cnt: Integer; begin try Cnt := CountUpperCase(Edit1.Text); Caption := IntToStr(Cnt) + ' заглавных букв'; except on E: ENoUpperCaseLetters do Caption := E.Message; end; end; function TForm1.CountUpperCase(const s: string): Integer; var ch: Char; begin Result := 0; for ch in s do if ch in ['A'..'Z'] then Inc(Result); {Вызываем исключение, если отсутствуют буквы в верхнем регистре} if Result = 0 then raise ENoUpperCaseLetters.Create('Нет заглваных букв'); end; end.
Защита распределения ресурсов
Зарезервированное слово try позволяет построить два различных блока: блок обработчика, исключений и блок защиты ресурсов. Блок обработчика исключений создается с помощью зарезервированного слова except, а блок защиты ресурсов— с помощью зарезервированного слова finally. Синтаксическая структура блока защиты ресурсов в Delphi выглядит следующим образом:
try
...
finally
...
end;
Блоки обработки исключений и защиты ресурсов используются по-разному и работают также по-разному. Операторы обработчика исключений выполняются только в том случае, если операторы в блоке try сгенерировали исключение, а операторы в блоке finally выполняются всегда, даже если операторы в блоке try не сгенерировали никакого исключения. Если в блоке try возникнет исключение, управление будет передано блоку finally, после чего будет выполнен код очистки. Если в блоке try исключения не возникнут, операторы в блоке finally будут выполняться после операторов в блоке try.
Подходящим способом использования блока защиты ресурсов является распределение или, с другой стороны, затребование ресурса перед блоком try. После того как вы затребуете ресурс, поместите операторы, использующие ресурс, внутрь блока try. Когда работа с ресурсом будет завершена, вы должны будете освободить его. Операторы, освобождающие ресурс, должны быть написаны в блоке finally.
{ Запрос ресурса }
try
{ Использование полученного ресурса }
finally
{ Освобождение ресурса }
end;
Блок защиты ресурса часто используется для того, чтобы обеспечить надлежащее освобождение динамически созданных объектов. Например, динамическое создание модальной формы необходимо всегда защищать с помощью блока try-finally.
procedure TForm1.Button1Click(Sender: TObject); var NewForm: TForm; begin NewForm := TForm.Create(Self); try NewForm.ShowModal; finally NewForm.Free; end; end;
В листинге ниже показан более короткий способ динамического создания формы, защищенной блоком try-finally.
procedure TForm1.Button1Click(Sender: TObject); begin with TForm.Create(Self) do try ShowModal; finally Free; end; end;
Другое отличие между блоками обработки исключений и блоками обработки ресурсов заключается в том, что блок обработки ресурсов не обрабатывает исключения. Таким образом, если исключение возникнет, оно будет передано первому доступному обработчику исключений. Например, если вы выполните следующий код, то исключение EDivByZero приведет к тому, что обработчик исключений, используемый по умолчанию, выведет на экран монитора сообщение об ошибке, информирующее пользователя о возникшем исключении.
procedure TForm1.Button1Click(Sender: TObject); begin with TForm.Create(Self) do try {Вызов исключения EDivByZero, поскольку значение Tag равно 0} Caption := IntToStr(Top div Tag); ShowModal; finally Free; end; end;
Если вы хотите обработать исключение EDivByZero (или любое другое исключение) внутри блока защиты ресурсов, вы должны написать вложенный блок обработчика исключений.
Следующий листинг показывает, каким должен быть вложенный обработчик исключений внутри блока защиты ресурсов. Вы можете также вкладывать блоки защиты ресурсов в другие блоки защиты ресурсов или блоки обработки исключений.
procedure TForm1.Button1Click(Sender: TObject); begin with TForm.Create(Self) do try try Caption := IntToStr(Top div Tag); except on EDivByZero do Caption := 'Tag = 0'; end; ShowModal; finally Free; end; end;
ИЗМЕНЕНИЕ ОБРАБОТЧИКА ИСКЛЮЧЕНИЙ, ИСПОЛЬЗУЕМОГО ПО УМОЛЧАНИЮ
Глобальный объект Application отвечает за обработку исключений, не обрабатываемых блоком обработки исключений, который может находиться где-то в приложении. Чтобы изменить обработчик исключений, используемый по умолчанию, мы можем использовать компонент TApplicationEvents относящийся к категории Additional (Дополнительные).
Компонент TApplicationEvents предлагает событие OnException, которое генерируется всякий раз, когда возникает необработанное исключение.
Событие OnException может быть обработано с помощью процедуры типа TExceptionEvent. Процедура, обрабатывающая событие OnException, принимает два параметра: объект Sender и объект Exception.
procedure TMainForm.AppEventsException[Sender:
TObject; E: Exception);
begin
end;
В рамках обработчика события OnException вы можете написать код, который будет обрабатывать исключения иным способом, нежели обработчик, используемый по умолчанию, или же оставить обработчик события пустым. Если вы не хотите, чтобы при возникновении исключения что-либо происходило, оставьте обработчик события пустым. В данном случае потребуется написать внутри блока обработчика события только какой-нибудь комментарий, чтобы редактор Code Editor не удалил код обработчика автоматически.
Обработчик события OnException может использоваться и более конструктивным образом. Например, вы можете написать код, который будет регистрировать все исключения и записывать их в текстовый файл для последующего просмотра.
В листинге ниже показано, как производится регистрация исключений внутри обработчика события OnException.
procedure TForm1.ApplicationEvents1Exception(Sender: TObject; E: Exception); var Log: TextFile; LogFilePath: string; begin LogFilePath := 'c:\exceptions.log'; AssignFile(Log, LogFilePath); try if not FileExists(LogFilePath) then Rewrite(Log) else Append(Log); Writeln(Log, E.ClassType.ClassName, ' исключение с сообщением "', E.Message, '" '); finally CloseFile(Log); end; end; procedure TForm1.Button1Click(Sender: TObject); begin Caption := IntToStr(Top div Tag); end;
Вы можете также модифицировать обработчик исключений, используемый по умолчанию, вручную (без применения компонента TApplicationEvents). Для этого нужно создать метод, который будет принимать те же параметры, что и событие OnException и назначить этот метод событию OnException в глобальном объекте Application.
procedure TForm1.Button1Click(Sender: TObject); begin Caption := IntToStr(Top div Tag); end; procedure TForm1.FormCreate(Sender: TObject); begin Application.OnException := MyHandler; end; procedure TForm1.MyHandler(Sender: TObject; E: Exception); begin MessageDlg('Do you like the "' + E.Message + '" exception?', mtConfirmation, mbYesNo, 0); end;
Источник: Иван Хладни - Внутренний мир Borland Delphi 2006.