Ранее в этой главе мы уже выполняли неявные приведения типов — обычно при преобразовании символьных значений в целочисленные:
При попытке присвоения большого значения переменной, которая не может его сохранить, компилятор выведет сообщение об ошибке "Constant expression violates subrange bounds" ("Константное выражение нарушает границы поддиапазона") или "Incompatible types: 'Type' and Type'" ("Несовместимые типы: 'Тип' и 'Тип'"). В случае возникновения одной из таких ошибок потребуется выполнить либо неявное, либо явное приведение типа.
При возникновении ошибки нарушения границ поддиапазона следует подумать о выборе другого типа данных, который поддерживает большие значения. Как, по-вашему, каким будет значение переменной после неявного приведения Byte (1000) ? Это значение не может быть равным 1000, поскольку значение 1000 занимает 2 байта в памяти, а переменная типа Byte — только один байт. При преобразовании числа 1000 (шестнадцатеричное 03Е8) в значение типа Byte в переменной типа Byte сохраняется только младший байт (шестнадцатеричное Е8). Таким образом, после неявного приведения типа значение будет равно 232.
Присваивание значения типа Double целочисленной переменной также приводит к ошибке компиляции, поскольку целочисленные переменные не могут хранить действительные числа. Присваивая действительное значение целочисленной переменной, всегда необходимо выполнять явное приведение типа. Это приведение реализуется с использованием стандартных функций Round или Trunc.
Функция Trunc отбрасывает дробную часть числа, функция Round учитывает ее. Обычно эти функции возвращают различные результаты. Их можно использовать также в объявлениях констант.
[spoiler]
var
i: Integer;
d: Double; begin
d := 12.8;
i := Round(d); { i = 13 ) i := Trunc(d); { i = 12 ) end.
[/spoiler]
В Delphi приведение типа константного значения отличается от приведения типа переменной. Приведение типа значения в действительности представляет собой неявное приведение типа, которое использовалось до сих пор. Приведение типа значения можно применять только в правой части оператора присваивания.
Приведение типа переменной можно использовать в обеих частях оператора присваивания, если, конечно, оба типа совместимы.
[spoiler]
var
b: Byte;
с: Char; begin
b := Byte('a'); { приведение типа значения )
с := Char(b); { приведение типа переменной }
Byte(с) := 65; { временное приведение типа переменной )
end.
[/spoiler]
Квалифицированный программист должен уметь создавать собственные процедуры. Процедуры позволяют повторно использовать собственный код в одном или более проектах и разбивать логику приложения на более удобные в работе меньшие фрагменты кода. Они способствуют также уменьшению количества ошибок в приложении. Если процедура содержит ошибку, достаточно ее исправить только один раз внутри процедуры, и проблема будет решена в рамках всего приложения.
Простейшая процедура выглядит подобно показанной ниже:
procedure Hello;
begin
end;
Как видите, для создания простой процедуры достаточно определить ее имя в заголовке процедуры и создать блок процедуры. Проблема заключается в определении места размещения реализации процедуры. При наличии каких-либо сомнений относительно местоположения фрагмента кода следует вспомнить, что в Delphi все элементы программы должны быть объявлены до того, как их можно будет использовать. Таким образом, поскольку мы собираемся использовать эту процедуру в главном блоке приложения, она должна быть записана перед главным блоком. И действительно, она должна находиться между списком uses и главным блоком приложения, как показано в листинге 5.3.
Листинг 5.3. Наша первая процедура
[spoiler]
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils;
procedure Hello;
begin
end;
begin
end.
[/spoiler]
Чтобы рассмотреть работу процедуры, в процедуру Hello потребуется вставить дополнительный код:
[spoiler]
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils; var
i: Integer;
procedure Hello;
begin
WriteLn('I ', #3, 'DelphiComponent.ru');
end;
begin
end.
[/spoiler]
Если выполнить этот код, сообщение процедуры Hello не отобразится на экране, поскольку она не была вызвана в главном блоке приложения. Независимо от количества процедур, присутствующих в исходном коде, выполнение приложения всегда начинается с начала главного блока.
Листинг 5.4. Вызов процедуры Hello
[spoiler]
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils;
var
i: Integer;
procedure Hello;
begin
WriteLn('I ', #3, 'DelphiComponent.ru');
end;
begin
for i := 1 to 20 do
Hello;
ReadLn;
end.
[/spoiler]
При создании функций необходимо определить, по меньшей мере, два элемента: имя функции и тип данных результата. Например, следующая функция возвращает строковое значение:
[spoiler]
function ReturnString: string;
begin
end;
[/spoiler]
Заголовок функции указывает Delphi, что функция возвращает строку, но в действительности она может не возвращать никаких значений. Чтобы возвратить значение из функции, это значение нужно присвоить специальной переменной Result.
Переменная Result — специальная переменная, которая неявно создается в каждой функции. Таким образом, чтобы функция возвращала строку "Delphi", потребуется записать следующее:
[spoiler]
function ReturnString: string;
begin
Result := 1;
end;
[/spoiler]
Поскольку эта функция всегда возвращает одно и то же значение, ее называют также функцией-константой. Хотя функции-константы могут казаться не особенно полезными, в действительности это не так. особенно в больших проектах.
В приложении функцию-константу можно использовать вместо переменной или константы для обеспечения единообразного способа считывания значения. По мере увеличения сложности приложения в функцию можно добавлять код — возможно, для выполнения проверки диапазона значений или выполнения аналогичных задач. В данном случае достаточно изменить реализацию функции, и, поскольку она была использована с самого начала, приложение сохранит работоспособность. Если бы вместо функции была использована переменная или константа, пришлось бы тратить время на поиск переменной и ее замену вызовом функции.
Если проект завершен и функция-константа по-прежнему возвращает только конкретное постоянное значение, для повышения производительности приложения ее можно заменить переменной с таким же именем. Если предполагается использовать только Delphi 2005 или последующие версии Delphi, более рациональным решением была бы пометка функции-константы директивой inline. Подробнее эта директива описывается далее в этой статье
Сокращенное вычисление — это используемый по умолчанию режим вычисления булевских выражений, который приводит к ускорению выполнения кода по сравнению с полным вычислением упомянутых выражений. В подавляющем большинстве случаев сокращенное вычисление прекрасно работает, но при вызове функций внутри оператора if-then, который не просто возвращает значение, использование этого режима может приводить к возникновению проблем. Следующий пример кода содержит скрытую ошибку, возникающую вследствие сокращенного вычисления.
Листинг 5.5. Ошибка, возникающая вследствие сокращенного вычисления
[spoiler]
01.program Project1;
02.{$APPTYPE CONSOLE}
03.uses
04.SysUtils;
05.var
06.x: Integer;
07.FunctionCall: Boolean;
08.function MakeTrue: string;
09.begin
10.FunctionCall := True;
11.Result := 'Hello';
12.end;
13.begin
14.FunctionCall := False;
15.Write('Enter number 1: ');
16.ReadLn(x);
17.if (x = 1) and (MakeTrue = 'Hello') then
18.WriteLn('Everything works fine.');
19.if FunctionCall = False then
20.WriteLn('If you can see this, something is wrong.');
21.ReadLn;
22.end.
[/spoiler]
В этом примере использована весьма простая функция с именем MakeTrue. Ее назначение состоит в возврате строки "Hello" и изменении значения переменной FunctionCall на True.
При запуске приложения переменная FunctionCall инициализируется значением False. Затем пользователю предлагается ввести число 1 и введенное значение сохраняется в переменной х. Проблема связана с оператором if-then, который проверяет значение переменной х. Если пользователь ввел значение 1, первая часть оператора if-then возвращает True, и программа вызывает функцию MakeTrue во время вычисления второй части оператора if-then. Функция изменяет значение переменной FunctionCall на True и приложение работает так. как было задумано.
В этом случае сообщение "If you can see this, something is wrong." {"Если вы видите этой сообщение, что-то пошло не так.") никогда не отображается.
Если же пользователь вводит любое значение, отличное от 1. первая часть оператора if-then дает значение False. Это приведет к пропуску всего оператора и программа никогда не вызовет функцию MakeTrue, поскольку вторая часть оператора if-then никогда не будет вычисляться. Значение переменной FunctionCall остается равным False, что, как видно на рис. 5.1, приводит к появлению сообщения об ошибке, которое никогда не должно было бы отображаться.
Если оператор if-then содержит только один вызов функции, эту проблему можно решить, предварительно проверив результат выполнения функции:
if (MakeTrue = 'Hello') and (x = 1) then WriteLn(1 Everything works fine.');
Еще одно возможное решение предусматривает временное включение полного вычисления булевских выражений.
Этот метод следует использовать при наличии вызовов нескольких функции, которые глобально влияют на все приложение:
[spoiler]
{SB+)
if (х = 1) and (MakeTrue = 'Hello1) then
WriteLn('Everything works fine.');
{SB-}
[/spoiler]
До сих пор мы создавали функции и процедуры в главном файле проекта Delphi. Хотя в действительно небольших проектах этот подход допустим и удобен, следует стараться создавать функции и процедуры в отдельных файлах исходного кода (модулях). Модуль (unit) — это обычный текстовый файл, содержащий исходный код Delphi. Модули позволяют использовать функции и процедуры в нескольких проектах.
Delphi позволяет создавать новый модуль даже при отсутствии каких-либо открытых проектов, но компиляция отдельного модуля невозможна. Delphi выполняет компиляцию только проектов. Поэтому, прежде чем можно будет создать новый модуль, потребуется создать новый проект консольного приложения и сохранить его на диске.
Чтобы создать новый модуль, можно выбрать команду меню File -> New -> Unit - Delphi for Win32 (Файл->Создать->Модуль - Delphi для Win32) или дважды щелкнуть на элементе Unit (Модуль) в категории Delphi Projects/Delphi Files (Проекты Delphi/ ФайлыDelphi) окна Tool Palette (Палитра инструментов). Delphi автоматически добавит в проект новый модуль (рис. 5.2) и отобразит исходный код модуля в окне Code Editor (Редактор кода):
[spoiler]
unit Unit1;
interface
implementation
end.
[/spoiler]
Рис. 5.2. Новый модуль
Первая строка этого кода — заголовок модуля, задающий его имя. Имя модуля, указанное после зарезервированного слова unit, должно совпадать с именем файла. Таким образом, если именем модуля является Unitl, то именем файла должно быть Unit1 .pas.
Каждый модуль разделен на два раздела: interface (интерфейс) и implementation (реализация). Раздел интерфейса — общедоступная часть модуля, содержащая объявления, которые могут использоваться из файлов других модулей и проектов. Он начинается с зарезервированного слова interface и заканчивается зарезервированным словом implementation.
Раздел реализации — приватная часть модуля, содержащая объявления и операторы, которые могут быть использованы только другими операторами этого же модуля. Эта часть модуля начинается зарезервированным словом implementation и, как правило, завершается концом всего модуля.
При добавлении нового модуля в проект Delphi автоматически добавляет ссылку на модуль в список uses проекта:
[spoiler]
uses
SysUtils,
Unitl in 'Unitl.pas';
[/spoiler]
В списке uses Delphi использует синтаксис ИмяМодуля in ИмяФайла, если точное местоположение модуля не известно или если модуль не указан в пути поиска. Синтаксическая форма in служит также для различения модулей, принадлежащих данному проекту, от остальных. В данном случае синтаксис in используется потому, что файл не был сохранен на диске и, следовательно, невозможно определить, указан ли он в пути поиска. Если сохранить модуль вне пути поиска или за пределами каталога проекта. Delphi добавит абсолютный путь файла в список uses:
[spoiler]
uses
SysUtils,
Unitl in 1С:\Unitl-pas';
[/spoiler]
Если сохранить модуль в каталоге проекта, Delphi добавит только имя файла модуля, но все же использует синтаксис ИмяМодуля in ИмяФайла. В этом случае применение синтаксиса in служит признаком того, что модуль является локальным модулем проекта. Применение синтаксиса in влияет только на функционирование IDE-среды Delphi. Например, если ссылка на модуль выполнена с использованием синтаксиса in, модуль можно открывать с помощью двойного щелчка на его имени в окне Project Manager (Диспетчер проектов). Если же удалить это ключевое слово. Delphi будет считать модуль внешним, и несколько возможностей IDE-среды станут недоступными.
При создании модулей, содержащих вспомогательные функции и процедуры, которые можно использовать в нескольких проектах, для всех модулей следует создать отдельный каталог и включить его в путь поиска.
Путь поиска можно изменять в диалоговых окнах Project Options (Параметры проекта) или Default Project Options (Параметры проекта по умолчанию). Если в данный момент какой-либо проект открыт для работы, доступно только окно Project Options (Параметры проекта), которое можно открыть, выбрав команду меню Projects-> Options (Проект->Параметры).
Если IDE-среда пуста (ни один проект не открыт), можно открыть диалоговое окно Default Project Options (Параметры проекта по умолчанию), выбрав пункт меню Projects Default Options -> Delphi for Win32 (Проект -> Параметры -> Delphi для Win32).
Чтобы увидеть текущий путь поиска, выберите элемент Directories/Conditionals (Каталоги/Условия) в левой части диалогового окна). Если путь поиска пуст, можно использовать только стандартные модули Delphi, хранящиеся в подкаталоге lib каталога установки Delphi.
Чтобы добавить модули в путь поиска, щелкните на кнопке ... слева от текстового поля Search path (Путь поиска).
Открывающееся при этом диалоговое окно Directories (Каталоги) позволяет добавлять каталоги в путь поиска.
После того, как все необходимые каталоги добавлены в путь поиска, эти параметры можно сделать используемыми по умолчанию во всех новых проектах. Если изменение параметров выполняется в диалоговом окне Default Project Options (Параметры проекта по умолчанию), они автоматически становятся используемыми по умолчанию во всех новых проектах. Если же изменение параметров выполняется в диалоговом окне Project Options (Параметры проекта), и их требуется установить в качестве используемых по умолчанию, необходимо выбрать опцию Default (По умолчанию) в нижнем левом углу диалогового окна.
Модули могут содержать код, который является приватным для данного модуля или же общедоступным. Если константа или переменная определена в разделе интерфейса, она общедоступна и может использоваться вне модуля.
Листинг 5.6А. Общедоступные константы и переменные модуля
[spoiler]
01.unit Unit1;
02.interface
03.const
04.PUBLIC_CONST = 'Hello';
05.var
06.PublicVar: string;
07.procedure About;
08.implementation
09.procedure About;
10.begin
11.WriteLn(PUBLIC_CONST, ' from Unit1.');
12.end;
13.end.
[/spoiler]
Листинг 5.6Б. Использование общедоступных констант и переменных program Project1;
[spoiler]
01.program Project1;
02.{$APPTYPE CONSOLE}
03.uses
04.SysUtils,
05.Unit1 in 'Unit1.pas';
06.begin
08.About;
10.PublicVar := PUBLIC_CONST;
11.WriteLn(PublicVar);
12.ReadLn;
13.end.
[/spoiler]
В отличие от объявлений констант и переменных, процедуры не могут быть добавлены в раздел интерфейса модуля. Процедуры и функции могут быть написаны только в разделе реализации (implementation) модуля.
[spoiler]
01.unit Unit1;
02.interface
03.const
04.PUBLIC_CONST = 'Hello'; var
05.PublicVar: string;
06.implementation
07.procedure About; begin
08.WriteLn(PUBLIC_CONST, ' from Unitl.'); end;
09.end.
[/spoiler]
В данный момент процедура About может вызываться только другими процедурами этого же модуля. Если ее необходимо использовать в других модулях, ее нужно объявить в разделе интерфейса модуля. Для этого потребуется скопировать ее заголовок в раздел интерфейса.
[spoiler]
01.unit Unit1;
02.interface
03.const
04.PUBLIC_CONST = 'Hello'; var
05.PublicVar: string;
06.procedure About;
07.implementation
08.procedure About; begin
09.WriteLn(PUBLIC_CONST, 1 from Unitl.');
end;
end.
[/spoiler]
Модули Delphi содержат также третий раздел — раздел инициализации. Этот раздел предоставляет возможность выполнять процедуры или операторы во время начального запуска приложения. Если модуль содержит раздел инициализации, этот раздел должен следовать за разделом реализации.
Листинг 5.7. Инициализация модуля
[spoiler]
01.unit Unit1;
02.interface
03.proce