четверг, 23 февраля 2012 г.

Применение пространств имен в Delphi

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

В Delphi модуль является основным контейнером для типов, а пространство имен – основным контейнером для модулей. В отличии от традиционных модулей Delphi, пространства имен могут встраиваться в иерархическое содержимое. Встроенные пространства имен дают возможность организовать идентификаторы и типы. Они используются для того, чтобы отличать типы, имеющие одинаковые имена. Поскольку пространства имен являются контейнерами для модулей Delphi, с их помощью можно различать модули с одинаковыми именами, находящимися в разных пакетах. Например, класс MyClass, который расположен в MyNameSpace, отличается от MyClass, который расположен в YourNamespace.

Далее рассматриваются следующие темы:

  • Объявление пространства имен и пространство имен проекта по умолчанию;
  • Пространство имен и область видимости;
  • Применение пространств имен в модулях Delphi.

Объявление пространства имен

В RAD Studio, файл проекта (программы, библиотеке или пакета) явно определяет свое собственное пространство имен, называемое пространством имен проекта по умолчанию. Модуль может быть членом пространства имен проекта или явным образом объявить себя членом другого пространства имен. Во всех случаях модуль объявляет о членстве в пространстве имен в своем заголовке. Для примера рассмотрим следующее явное объявление пространства имен:

unit MyCompany.MyWidgets.MyUnit;

Прежде всего обратим внимание на то, что части названия пространства имен разделяются точками. Пространство имен не включает никаких дополнительных символов между точками, а точки являются частью имени модуля. Файл с программным кодом для этого примера - MyCompany.MyWidgets.MyUnit.pas, а скомпилированный файл будет иметь имя MyCompany.MyWidgets.MyUnit.dcu. Во-вторых, следует обратить внимание на то, что точки обозначают концептуальное встраивание или включение одного пространства имен в другое. Вышеприведенный пример объявляет модуль MyUnit, как члена пространства имен MyWidgets, которое, в свою очередь содержится в пространстве имен MyCompany. Однако важно понимать, что это включение важно только с точки зрения документирования. Пространство имен проекта по умолчанию объявляет пространство имен для всех модулей в проекте. Рассмотрим следующие объявления:

Program MyCompany.Programs.MyProgram;
Library MyCompany.Libs.MyLibrary;
Package MyCompany.Packages.MyPackage;

Эти инструкции создают пространство имен проекта по умолчанию для программы, библиотеки и пакета соответственно. Пространство имен определяется удалением из объявления крайнего правого идентификатора (и точки). Модуль, в объявлении которого опущено явная принадлежность к пространству имен, называется общим модулем (generic unit). Общий модуль автоматически становится членом пространства имен проекта по умолчанию. С учетом предыдущего объявления программы, нижеприведенное объявление модуля приведет к тому, что компилятор будет обрабатывать MyUnit как члена пространства имен MyCompany.Programs.

unit MyUnit;

Пространство имен по умолчанию не влияет на имя файла с программным кодом общего модуля. Для предыдущего примера имя файла с программным кодом будет MyUnit.pas. Тем не менее, компилятор предварит имя dcu файла именем пространства имен проекта по умолчанию. В приведенном примере результирующий dcu файл будет иметь имя MyCompany.Programs.MyUnit.dcu. Названия пространств имен не чувствительны к регистру символов. Компилятор рассматривает названия пространств имен, отличающихся только регистром, как эквивалентные. Тем не менее, компилятор сохраняет регистр названий пространства имен и будет использовать сохраненный регистр в именах выходных файлов, сообщениях об ошибках и идентификаторах RTTI. RTTI для имен классов и типов будет включать полную спецификацию пространства имен.

Поиск в пространствах имен

Модуль должен объявлять модули от которых он зависит. В архитектуре пратформы Win32 компилятор должен выполнять поиск идентификаторов в этих модулях. Для модулей с явным объявлением пространства имен область поиска известна заранее, а для общих модулей компилятор должен установить эту область поиска самостоятельно. Рассмотрим следующее объявление модуля с разделом подключения модулей:

unit MyCompany.ProjectX.ProgramY.MyUnit1;
uses MyCompany.Libs.Unit2, Unit3, Unit4;

Эти объявления создают MyUnit1 как члена пространства имен MyCompany.ProjectX.ProgramY. MyUnit1 зависит от трех других модулей: MyCompany.Libs.Unit2 и двух общих модулей: Unit3 и Unit4. Компилятор может разрешить имена идентификаторов в Unit2, поскольку раздел подключения определяет точное имя модуля. Для разрешения идентификаторов в Unit3 и Unit4 компилятору придется определить очередность поиска в пространстве имен.

Порядок поиска в пространстве имен

Места для поиска могут определяться тремя источниками: опциями компилятора, пространством имен проекта по умолчанию и текущим пространством имен модуля. Компилятор разрешает имена идентификаторов в следующем порядке:

  1. Текущее пространство имен модуля (если таковое существует);
  2. Пространство имен проекта по умолчанию (если таковое существует);
  3. Пространства имен, определенные опциями компилятора.

Пример поиска в пространстве имен

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

// объявление в файле проекта...
program MyCompany.ProjectX.ProgramY;

// объявление в файле модуля...
unit MyCompany.ProjectX.ProgramY.MyUnit1;

В данном примере программы компилятор будет искать идентификаторы в пространстве имен в следующем порядке:

1. MyCompany.ProjectX.ProgramY; 2. MyCompany.ProjectX; 3. Пространства имен, определенные опциями компилятора.

Следует заметить, что если текущий модуль является общим (то есть он не имеет явного объявления пространства имен в заголовке модуля), разрешение начинается с пространства имен проекта по умолчанию.

Применение пространств имен

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

Полные имена модулей

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

unit MyCompany.Libs.MyUnit1
uses MyCompany.Libs.Unit2,  // полное имя.
  UnitX;                   // общее имя.

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

uses MyCompany.Libs.Unit2;

begin
  writeln(MyCompany.Libs.Unit2.SomeString);
  writeln(SomeString);
end.

Полное имя идентификатора должно включать спецификацию пространства имен. В приведенном примере, ошибочным было бы обращение к SomeString с использованием только части имени пространства имен:

writeln(Unit2.SomeString);       // Ошибка!
writeln(Libs.Unit2.SomeString);  // Ошибка!
writeln(MyCompany.Libs.Unit2.SomeString);      // Правильно.
writeln(SomeString);                           // Правильно.

Ошибкой также было бы обращение к части названия пространства имен в разделе подключения. Нет механизма для импорта всех модулей и идентификаторов из пространства имен. Следующий пример не импортирует все модули и идентификаторы из пространства имен MyCompany:

uses MyCompany;   // Ошибка!

Это ограничение также имеет отношение к инструкции with-do. Следующий пример приведет к ошибке компиляции:

with MyCompany.Libs do    // Ошибка!

Многомодульные пространства имен

К пространству имен может относиться несколько модулей в том случае, если их объявления относятся к одному пространству имен. Например, можно создать unit1.pas и unit2.pas с объявлением:

// в файле 'unit1.pas'
unit MyCompany.ProjectX.ProgramY.Unit1

// в файле 'unit2.pas'
unit MyCompany.ProjectX.ProgramY.Unit2

В этом примере пространство имен MyCompany.ProjectX.ProgramY logically включает в себя все идентификаторы из секции interface модулей unit1.pas и unit2.pas. Символьные имена должны быть уникальны внутри пространства имен. В вышеприведенном примере попытка одновременного объявления идентификатора mySymbol в модулях Unit1 и Unit2 приведет к ошибке.

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

Как и отдельные модули, раздел подключения модулей может ссылаться на пространство имен. В этом случае, обращение к полному идентификатору из модуля, указанного в разделе подключения может быть выполнено с с использованием действительного имени модуля или полного имени идентификатора (включая наименование пространства имен и модуля). Две формы обращения идентичны и указывают на один и тот же идентификатор.

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

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

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