От Delphi 4 к Delphi 5 часть 26

От Delphi 4 к Delphi 5.
Последовательность обработки исключений


Составляя программный блок, вы можете использовать вложенные защищенные блоки, чтобы организовать локальную и глобальную обработку исключительных ситуаций.

Это можно представить следующим образом:


try
{самый верхний уровень обработки исключительной ситуации}

{операторы, обращающиеся к операторам вложенной секции}


try
{внешняя секция except... end. }


try
{вложенная секция except...end. }

{операторы с локальной защитой от ошибок}


except

on EUnderflow do
{локальная обработка исключительных ситуаций}


on EOverflow do

on EZeroDivide do

end;

except

on EMathError do
{обработка исключительной ситуации внешней секцией}


end;

except

on EAbort do
{окончательная обработка исключительной ситуации}


end;


Исключительные ситуации вложенного защищенного блока обрабатываются вложенной секцией except...end. Если она не может обработать исключительную ситуацию, то управление передается внешней секции except...end. Если и она не справляется с обработкой, то управление передается обработчику исключительной ситуации вызывающей подпрограммы.

Теория, как известно, должна подкрепляться практикой. Рассмотрим пример с вложенными защищенными блоками.


procedure TForm1.Button1Click(Sender: TObject);

var

X,Y:Real;

begin

try

try

try

X:= StrToFloat(Edit1.Text);

Y:= StrToFloat(Edit2.Text);

X:=X/Y;

Edit1.Text:=FloatToStr(X);

except

on EUnderflow do

MessageDlg ('Потеря старших разрядов',

mtWarning, [mbOk],0);

end;

except

on EZeroDivide do

MessageDlg ('Произошло деление на нуль; повторите ввод',

mtWarning, [mbOk],0);

end;

except

on EConvertError do

MessageDlg ('Осуществите правильный ввод',

mtWarning, [mbOk],0);

end;

end;

end.


Чтобы посмотреть работу, вложенных блоков защиты, произведите генерацию исключительной ситуации. Для этого введите нулевое значение в текстовый редактор Edit2 и любое числовое значение в текстовый редактор Edit1. При генерации данного исключения осуществляется поиск соответствующего ему обработчика on в том блоке try...except, в котором создалась исключительная ситуация. Для данного примера это секция on UnderFlow do. Если соответствующий обработчик не найден, поиск продолжается и производится в обрамляющем блоке try...except. Для нашего примера — это обработчик события деление на нуль числа с плавающей запятой, on EZeroDivide do, соответствует сгенерированной ситуации. Выдается сообщение, организованное вами в программном коде: " Произошло деление на нуль, повторите ввод".


Сгенерируйте другую исключительную ситуацию, введите буквенное значение в любой редактор текста Edit1, Edit2. Произведите деление, данная исключительная ситуация будет обработана на самом верхнем уровне, это секция try ...except с обработчиком on EConvertError do (ошибка преобразования строк или объектов, в частности StrToFloat ) . Вы получите сообщение, созданное вами, "Осуществите правильный ввод". Как только оператор on, соответствующий данному исключению, найден и выполнен, объект исключения разрушается и управление передается оператору, следующему за тем блоком try...except, в котором осуществлен перехват.

Возможен такой вариант, когда в самом обработчике исключения в процессе обработки возникла исключительная ситуация. В этом случае обработка прерывается, прежнее исключение разрушается и генерируется новое исключение. Его обработчик ищется в блоке try except, внешнем по отношению к тому, в котором возникло новое исключение.

Этот поиск продолжается по всем уровням. И только если он закончился безрезультатно, выполняется стандартная обработка исключения, заключающаяся в выдаче пользователю сообщения о типе исключения.

Для нашего случая произведите замену оператора on EConvertError do на on EmathError do, сгенерируйте исключительную ситуацию вводом буквенного выражения в текстовый редактор Edit1 — вы получите сообщение (рисунок 1) о необходимости ввода вещественного числа.


Преднамеренная генерация исключений, оператор raise.


В ряде случаев возникает необходимость сгенерировать исключительную ситуацию искусственно. Например, вы обработали какое-то исключение, но хотите, чтобы его обработка была завершена обработчиком внешнего по отношению к данному блоку try...except. Т.е. в этом случае необходимо сгенерировать повторно исключение того же типа, что и прежнее, ввиду того, что прежнее разрушено обработчиком исключения.

Повторная генерация исключительной ситуации осуществляется ключевым словом raise.

try
{внешняя секция except... end. }


try
{вложенная секция except...end. }

{операторы с локальной защитой от ошибок}


except

on <
тип исключения > {локальная обработка исключительных ситуаций}


begin

<
обработка исключений внутренней секцией >

raise

end;

end;

except

on <операторы внешнего блока> do
{обработка исключительной ситуации внешней секцией}


end;


С помощью ключевого слова raise можно сгенерировать не только повторно исключение, но и исключение любого типа в любом месте программы.

Можно указать тип исключения и один из его конструкторов:


raise <
тип исключения >.Create (<текст сообщения>);


Тип исключения может быть любым из определенных в Delphi.


Рассмотрим пример:


if (Edit1.Text='') then

raise EZeroDivide.Create('
Сделайте правильный ввод ')


при неправильном вводе прерывается расчет, генерируется исключение типа EZeroDivide.


Таким образом, можно использовать генерацию исключительных ситуаций, не связанную с их первоначальным смыслом, для своих целей.


Создание своего типа исключения.


Конечно, лучше всего создать свой класс исключительных ситуаций, чтобы не вносить путаницу в программный код.

Классы исключительных ситуаций в Delphi являются производными от класса Exception. Созданное вами исключение будет содержать свойства и методы класса Exception, но они могут содержать дополнительную информацию, определенную вами. Если нет дополнительной информации, то объявление нового класса исключений производится в разделе type следующим образом:


type

< имя класса исключения > class(Exception)

end;


Имена новых классов исключений должны начинаться с символа E.


Рассмотрим пример создания собственного типа исключения. В приложении производится анализ содержимого окна текстового редактора Edit1, на ввод выражения "Слово". В случае неправильного ввода произойдет генерация исключительной ситуации с выдачей сообщения (рисунок 2).


type

EOwn = class(Exception)

end;

var

Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);

begin

if Edit1.Text = 'Слово' then

Edit1.Text:='Ввод правильный'

else

raise EOwn.Create('Введите кодовое слово');

end;

end.


Класс Exception наследует все функции родителя TObject. В интерфейсе класса Exception описаны восемь конструкторов, которые наследуют все исключения.

Конструктор Create(const Msg: string) передает строку сообщения Msg свойству Message.


Конструктор CreateFmt(const Msg: string; const Args: array of const) формирует строку свойства Message, исходя из строки описания формата Msg и массива аргументов Args.


Конструктор CreateRes(Ident: Integer) задает строку свойства Message идентификатором Ident строки сообщения в ресурсах проекта.

Конструктор CreateResFmt(Ident: Integer; const Args: array of const) задает строку свойства Message идентификатором Ident строки описания формата в ресурсах проекта и массивом аргументов Args.


Конструктор CreateHelp(const Msg: string; HelpContext: Integer) передает строку сообщения Msg свойству Message; передает свойству HelpContext идентификатор HelpContext экрана контекстно-зависимой справки по этому исключению.

Конструктор CreateFmtHelp(const Msg: string; const Args: array of const: HelpContext: Integer) формирует строку свойства Message, исходя из строки описания формата Msg и массива аргументов Args. Передает свойству HelpContext экрана контекстно-зависимой справки по этому исключению.

Конструктор CreateResHelp(Ident, HelpContext: Integer) задает строку свойства Message идентификатором Ident строки сообщения в ресурсах проекта; передает свойству HelpContext идентификатор HelpContext экрана контекстно-зависимой справки по этому исключению.

Конструктор CreateResFmtHelp(Ident: Integer; const Args: array of const; HelpContext: Integer) формирует строку свойства Message исходя из строки описания формата в ресурсах проекта, указываемой идентификатором Ident, и массива аргументов Args. Передает свойству HelpContext идентификатор HelpContext экрана контекстно-зависимой справки по этому исключению.

Таким образом, конструкторы, не имеющие в своем имени компонентов Fmt или Res, принимают параметр Msg типа string, являющийся текстом, который заносится в свойство Message и в дальнейшем при обработке исключения системным обработчиком отображается в окне сообщений. Вы уже использовали конструктор такого типа в примере


{генерация собственного исключения}


raise EOwn.Create('Введите кодовое слово');
или


{генерация стандартного исключения с измененным сообщением}

raise EZeroDivide.Create('Произошло деление на нуль');.


Конструкторы с именем Fmt принимают параметр Msg типа string, являющийся строкой описания формата текста, и параметр Args, являющийся массивом переменных, значения которых включаются в текст в соответствии с форматом, записанным в Msg. Значение свойства Message формируется обращением к функции Format в модуле SysUtils, которая и формирует текст сообщения в соответствии со строкой описания формата и списком массива.


Строка описания формата
.

Строка описания формата определяет способ форматирования данных и используется в таких функциях, как Format, FormatBuf, FmtStr, StrFmt, StrLFmt конструкторах исключений и др.

Строка описания формата содержит два типа объектов: обычные символы, которые непосредственно копируются в форматированную строку, и спецификаторы формата, которые определяют формат записи в результирующую строку списка аргументов.

Спецификатор формата имеет вид


%[<индекс>:][-][<ширина>][ <точность>]<тип>


Спецификатор формата начинается с символа "%". Затем без пробелов следует ряд необязательных полей:


[<индекс>:]
определяет индекс (номер) аргумента в заданном списке, к которому относится данный спецификатор формата.


[-]
индикатор выравнивания влево.


[<ширина>]
устанавливает ширину поля.


[<точность>]
спецификатор точности.

Затем так же без пробела размещается обязательное единственное поле <тип>, определяющее, как и в каком формате будет интерпретироваться аргумент.

Ниже приводится таблица спецификаторов типа:


d —
десятичное целое. Значение преобразуется в строку десятичных цифр;


e — н
аучный формат значения с плавающей запятой. Значение преобразуется в формат вида "-d.ddd...E+ddd", где d означает цифру;


f —
формат с фиксированной точкой значения с плавающей запятой. Значение преобразуется в формат вида "-ddd.ddd...";


g —
обобщенный формат чисел с плавающей запятой. Значение преобразуется в формат научный или с фиксированной точкой, в зависимости от того, какой из них дает более короткую запись;


n —
формат, подобный формату с фиксированной точкой для чисел с плавающей запятой, но отличающийся наличием в результирующей строке разделителей тысяч. Иными словами, число представляется в форме "-d,ddd,ddd.ddd...";


m —
монетарный формат чисел с плавающей запятой. Значение преобразуется в строку, представляющую собой денежную сумму;


p —
формат отображения указателей. Значение преобразуется в строку вида "XXXX:YYYY", где XXXX and YYYY — сегмент и смещение указателя, выражаемые четырьмя шестнадцатеричными цифрами;


s —
формат строки для аргументов вида символ, строка или строка типа PChar . Строка или символ просто вставляются в результирующую строку. Если задан спецификатор точности, то он определяет максимальное число вставляемых символов. Если вставляемая строка длиннее, она усекается;


x —
шестнадцатеричный формат целых чисел. Значение аргумента преобразуется в строку шестнадцатеричных цифр. Если задан спецификатор точности, то он указывает минимальное число цифр в строке; если строка оказывается короче, она дополняется слева нулями.

Функция Format возвращает отформатированную строку, представляющую собой результат применения строки описания формата Format к открытому массиву аргументов Args. Например, оператор


s:=Format('Задано %d параметров из %d ', [N1, N2]);


при переменных N1=5 и N2=10 присвоит переменной s значение "Задано 5 параметров из 10".

Использование функции формат:


raise EOwn.Create(Format)('
'Задано %d параметров из %d ', [N1, N2]);

Функция формат используется для форматированного вывода информации о значениях переменных N1 и N2.


Компонент DrawGrid.


Данный компонент является мощным инструментом для создания и обслуживания табличных структур данных. Он обеспечивает двумерное представление данных, упорядоченных по столбцам и рядам. Для обеспечения работоспособности таблицы в ней необходимо определить обработчик события OnDrawCell, которое возникает при необходимости прорисовать ту или иную ячейку.

Компонент DrawGrid является родителем для строковой таблицы, поэтому описанные свойства для компонента StringGrid полностью подходят и для компонента DrawGrid, кроме относящихся к тексту, т.е. кроме свойств Cells, Cols, Rows, Objects. С этой точки зрения компонент StringGrid обладает большими возможностями, чем DrawGrid.


В основном компонент DrawGrid используется для создания в приложении таблицы, которая может содержать графические изображения.

Рассмотрим пример использования компонента DrawGrid для отображения картинок.

    1. Запустите Delphi.
    2. Запомните файл модуля под именем Accomodation_.pas, а файл проекта под именем Accomodation.dpr.
    3. Для отображения картинок в компоненте DrawGrid необходимо скопировать файлы картинок, например, из папки Clipart или из папки, в которой хранятся картинки, созданные вами, в папку, в которой вы сохранили свой проект. Таким образом, вы освобождаетесь от указания маршрута поиска файлов.
    4. Поместите на форму компонент DrawGrid, установите следующие атрибуты. Свойству Align определите значение AlClient. Свойствам ColCount и RowCount установите значение 2. Свойствам FixedCols, FixedRows установите значение 0. Установите в комплексном свойстве Options свойству goEditing значение True. Свойство Scrollbars установите в ssNone, т.е. полосы прокрутки не вставлять. Определите размер для ячеек компонента DrawGrid (DefaultRowHeight = 300, DefaultColWidth = 400). Ниже представлен программный код с комментариями действий.

public

slBitMap: TStringList;

{ Это поле будет использоваться для хранения текстовых строк

и картинок }

end;

var

fmDrawGrid: TfmDrawGrid;

implementation

{$R *.DFM}

procedure TfmDrawGrid.FormCreate(Sender: TObject);

{Создание и наполнение списка slBitMap}

const

FNames: array [0..3] of String = ('Самолет.bmp','Яхта.bmp','Натуралист.bmp',

'Программист.bmp'); {имена файлов с картинками}

var

K:Integer;

begin

{Создание списка строк}

slBitmap:= TStringList.Create;

{Вставка названия файлов и картинок}

with slBitMap do for K:= 0 to 3 do

begin

Add(Fnames[K]);

Objects[K]:= TBitMap.Create;

(Objects[K] as TBitMap). LoadFromFile(FNames[K]);

end;

end;

procedure TFmDrawGrid.FormDestroy(Sender: TObject);

begin

{Уничтожение списка строк}

slBitMap.Free;

end;

{Редактирование текста}

procedure TfmDrawGrid.dgDrawGetEditText(Sender: TObject; ACol,

ARow: Integer; var Value: String);

begin

Value:= slBitMap[2* ACol + ARow];

end;

procedure TfmDrawGrid.dgDrawSetEditText(Sender: TObject; ACol,

ARow: Integer; const Value: String);

begin

{Прорисовка картинки из slBitmap. Objects и текста из slBitMap}

slBitMap[2* ACol + ARow]:= Value;

end;

procedure TfmDrawGrid.dgDrawDrawCell(Sender: TObject; ACol, ARow: Integer;

Rect: TRect; State: TGridDrawState);

var

K:Integer;

begin

{Пересчет координат ячейки в индекс списка}

K:=2*ACol + ARow;

with dgDraw.Canvas, Rect, slBitMap do

begin

{Уменьшение высоты картинки для вывода под ней текста}

Bottom:=Bottom — TextHeight('1') -2;

{Рисование картинки}

StretchDraw(Rect,(Objects[K] as TBitMap));

{Рисование текста по горизонтали и его вывод}

TextOut(Left + (Right — Left — TextWidth(slBitMap[K])) div 2,

Bottom + 1, slBitMap[K]);

end;

end;

procedure TfmDrawGrid.dgDrawResize(Sender: TObject);

begin

{Изменение размера ячеек при изменении размеров окна, запас в один пиксель необходим для предотвращения автопрокрутки}

with dgDraw do

begin

DefaultColWidth:=ClientRect.Right div 2 -1;

DefaultRowHeight:=ClientRect.Bottom div 2 — 1;

end;

end;

end.


На рисунке 3 показан результат работы программы Accomodation.

Литература:

    1. Марко Канту. Delphi 2 для Windows 95/NT. Москва. ООО "Малип". 1997 г.
    2. Джон Матчо. Дэвид Р. Фолкнер. Delphi. Москва. БИНОМ. 1995 г.
    3. Эндрю Возневич. Delphi. Освой самостоятельно. Москва. Восточная книжная компания. 1996 г.
    4. В.В.Фаронов. Delphi 5. Учебный курс. Москва. Издательство Нолидж. 2000 г.
    5. А. Я. Архангельский. Программирование в Delphi 5. Москва. ЗАО "Издательство Бином". 2000 г.
Владимир Скуратов

(c) компьютерная газета


Компьютерная газета. Статья была опубликована в номере 35 за 2001 год в рубрике программирование :: delphi

©1997-2024 Компьютерная газета