пятница, 18 марта 2011 г.

Структура и синтаксис модулей

Перевод из справочной системы Delphi

Модуль состоит из типов, констант, переменных и подпрограмм (функций и процедур). Каждый модуль определяется в своем собственном файле (.pas).

Файл модуля начинается заголовком, за которым следует ключевое слово interface. За ключевым словом interface следует раздел подключения модулей, который определяет зависимости модуля. Затем следует секция implementation, за которой идут необязательные секции initialization и finalization. Скелет файла с программным кодом модуля выглядит следующим образом:


unit Unit1;

interface

uses // список зависимостей...
  // секция Interface

implementation

uses // список зависимостей...

// Реализация методов классов, процедур и функций...

initialization

// Программный код инициализации модуля...

finalization

// Программный код финализации модуля...

end.

Модуль должен оканчиваться зарезервированным словом end с точкой.

Заголовок модуля

Заголовок модуля определяет его имя. Он состоит из зарезервированного слова unit, за которым должен следовать допустимый идентификатор и точка с запятой. Для приложений, разрабатываемых при помощи инструментария Embarcadero идентификатор должен совпадать с именем файла. Так, заголовок модуля:

unit MainForm;

может оказаться в файле с именем MainForm.pas, а файл, содержащий скомпилированный модуль должен быть MainForm.dcu. Имена модулей должны быть уникальны внутри проекта. Даже в том случае, когда файлы находятся в разных директориях, два модуля с одинаковыми именами не могут быть подключены в программе.

Секция Interface

Секция interface начинается с зарезервированного слова interface и продолжается до начала секции implementation. Секция interface объявляет константы, типы, переменные, процедуры и функции, которые доступны клиентам модуля, (другим модулям и программам которым необходимо воспользоваться элементами из этого модуля). Эти сущности обозначаются как public, поскольку программный код в других модулях может получать доступ к ним, так же, как если бы они были объявлены в этих модулях.

Объявления процедур и функций в секции interface содержат только их заголовки, то есть имя подпрограммы, параметры и тип возвращаемого значения (для функций). Блок, содержащий исполняемый код для процедур или функций следует в секции implementation. То есть объявления процедур и функций в секции interface работают как упреждающие объявления.

Объявление класса в секции interface должно включать все элементы класса: поля, свойства, процедуры и функции.

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

Секция Implementation

Секция implementation начинается зарезервированным словом implementation и продолжается до начала секции initialization или, если эта секция отсутствует, до конца модуля. Секция implementation определяет процедуры и функции, которые объявлены в секции interface. Внутри секции implementation эти процедуры и функции могут быть определены в любом порядке. Вы можете опустить список параметров в заголовках публичных процедур и функций, когда вы объявляете их в секции implementation, но, если вы включили список параметров, он должен совпадать с их объявлением в секции interface.

Кроме определения публичных процедур и функций, в секции implementation могут быть объявлены константы, типы (включая классы), переменные, процедуры и функции, которые являются частными (private) для модуля. То есть, в отличие от секции interface, сущности, объявленные в секции implementation недоступны снаружи модулей.

Секция implementation может включать свой собственный раздел подключения модулей, который должен следовать сразу за зарезервированным словом implementation. Идентификаторы, объявленные внутри секции implementation доступны для использования только непосредственно в секции implementation. Вы не можете ссылаться на эти идентификаторы из секции interface.

Секция Initialization

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

Для модулей, указанных в разделе подключения модулей секции interface, инструкции секций initialization модулей, подключенных к клиентам, выполняются в том же порядке, в котором они следуют в разделе подключения клиентов.

Устаревший синтаксис "begin ... end." все еще функционирует. Фактически зарезервированное слово "begin" может быть использовано вместо initialization, за которым может следовать произвольное количество инструкций (0 или больше). В программном коде, в котором используется устаревший синтаксис "begin ... end." Нельзя определить секцию finalization. В этом случае финализация осуществляется назначением процедуры переменной /ExitProc. Этот метод не рекомендован для развивающихся приложений, но вы можете встретить его в старом программном коде.

Секция Finalization

Секция finalization необязательна и может быть включена в модуль только в том случае, если он имеет секцию initialization. Секция finalization начинается зарезервированным словом finalization и продолжается до конца модуля. Она содержит инструкции, которые выполняются в момент завершения основного приложения (за исключением тех случаев, когда для завершения использована процедура Halt). Используйте секцию finalization для освобождения ресурсов, выделенных в секции initialization.

Секции Finalization выполняются в порядке, обратном выполнению секций initialization. Например, если ваше приложение инициализирует модули A, B, C (в таком же порядке), финализироваться они будут в порядке C, B, A.

Если, код инициализации модуля начинал выполняться, соответствующие секции финализации будут выполнены гарантированно при завершении приложения. По этому необходимо, чтобы инструкции секции finalization могли обрабатывать неполностью инициализированные данные (на тот случай, если при выполнении инструкций секции инициализации возникнет ошибка, и код этой секции не будет выполнен полностью).

Связи модулей и раздел подключения

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

  • Секцию interface модуля
  • Секцию implementation модуля

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

Модули System и SysInit подключаются автоматически каждым приложением и не могут быть явно перечислены в разделе подключения. (Модуль System реализует подпрограммы ввода/вывода, работу со строками, операции с плавающей точкой, динамическое выделение памяти и так далее). Другие модули стандартных библиотек, например, SysUtils должны явно прописываться в разделе подключения. В большинстве случаев все необходимые модули включаются в раздел подключения средой разработки, по мере того, как вы добавляете или удаляете модули из своего проекта.

Чувствительность к регистру: при объявлении модулей и в разделах подключения модулей имена модулей должны совпадать с именами файлов модулей по регистру. В других случаях (при обращении к идентификаторам) имена модулей не чувствительны к регистру. Чтобы избежать проблем со связями модулей ссылайтесь явно на файл модуля: uses MyUnit in "myunit.pas";

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

uses Myunit;

Синтаксис раздела подключения модулей

Раздел подключения модулей состоит из зарезервированного слова uses, за которым следуют одно или больше имен модулей, разделяемых запятыми, за которыми следует точка с запятой. Примеры:

uses Forms, Main;

uses
  Forms,
  Main;

uses Windows, Messages, SysUtils, Strings, 
Classes, Unit2, MyUnit;

В разделе подключения модулей программы или библиотеки за именем любого модуля может следовать зарезервированное слово in и имя файла с программным кодом модуля с указанием (или без указания) пути к файлу в одинарных кавычках. Пути могут быть абсолютными или относительными. Примеры:

uses
  Windows, Messages, SysUtils,
  Strings in 'C:\Classes\Strings.pas', Classes;

Используйте слово in после имени модуля в тех случаях, когда вам нужно указать файл с программным кодом модуля. Поскольку IDE ожидает, что имена модулей совпадают по регистру с их именами в файлах, в которых хранятся эти модули, обычно нет необходимости делать это. Использование in необходимо только когда местонахождение файла с программным кодом неясно. Например, когда:

  • Вы использовали файл с программным кодом, который находится в директории, отличном от директория файла проекта;
  • В различных директориях в путях поиска компилятора находятся модули с одинаковыми именами;
  • Вы компилируете консольное приложение из командной строки и называете модуль с идентификатором, который не совпадает с именем файла, содержащего программный код модуля.

Компилятор полагается на конструкцию in ... для того, чтобы определить, какие модуля являются частью проекта. Только те модули, которые указаны в разделе подключения в файле проекта (.dpr) с указанием имени файла могут считаться частью проекта. Остальные модули в разделе подключения используются проектом, но не принадлежат ему. Это различие не играет роли при компиляции, но влияет на инструментарий IDE, например, на Project Manager.

В разделе подключения модуля вы не можете зарезервированное слово in для указания компилятору на местоположение файла с программным кодом. Каждый модуль должен находиться в директориях, включенных в путь поиска компилятора. Более того, имена модулей должны совпадать с именами файлов, содержащих их программный код.

Множественные и косвенные связи модулей

Порядок, в котором модули следуют в разделе подключения, определяют порядок их инициализации и влияют на то, как компилятор выполняет поиск идентификаторов. Если два модуля объявляют переменные, константы, типы, процедуры или функции с одинаковыми именами, компилятор использует одну из модуля, указанного последним в разделе подключения. (Для доступа к идентификатору из другого модуля необходимо указать спецификатор: UnitName.Identifier.)

Раздел подключения должен включать только модули, используемые непосредственно программой или модулем, в котором они упоминаются. То есть, если модуль А ссылается на константы, типы, переменные, процедуры или функции, объявленные в модуле В, тогда А должен явно ссылаться на В. Если В в свою очередь ссылается на идентификаторы из модуля С, тогда А косвенно зависит от С. В этом случае С не должен добавляться в раздел подключения А, но компилятор должен иметь возможность найти В и С для того, чтобы обработать А.

Следующий пример иллюстрирует косвенную зависимость:

program Prog;
uses Unit2;
const a = b;
// ...

unit Unit2;
interface
uses Unit1;
const b = c;
// ...

unit Unit1;
interface
const c = 1;
// ...

В этом примере Prog явно зависит от Unit2, который явно зависит от Unit1. То есть Prog косвенно зависит от Unit1. Поскольку Unit1 не указан в разделе подключения Prog, идентификаторы, объявленные в Unit1, недоступны для Prog.

Для компиляции клиентского модуля компилятору необходимо найти все модули, от которых явно или косвенно зависит клиент. До тех пор, пока программный код этих модулей не меняется, компилятору достаточно файлов .dcu.

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

Циклические ссылки на модули

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

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

unit Unit1;
interface
uses Unit2;
// ...

unit Unit2;
interface
uses Unit1;
// ...

Однако два модуля могут корректно ссылаться друг на друга, если одна из ссылок идет через секцию implementation:

unit Unit1;
interface
uses Unit2;
// ...

unit Unit2;
interface
//...

implementation
uses Unit1;
// ...

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

Комментариев нет:

Отправить комментарий