Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Программирование на языке Delphi_3.doc
Скачиваний:
37
Добавлен:
28.03.2015
Размер:
1.71 Mб
Скачать

6.3. Расширение интерфейса

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

type

IExtendedTextReader = interface(ITextReader)

procedure SkipLines(Count: Integer);

end;

Определенный таким образом интерфейс включает все методы и свойства своего предшественника и добавляет к ним свои собственные. Несмотря на синтаксическое сходство с наследованием классов, расширение интерфейсов имеет другой смысл. В классах наследуется реализация, а в интерфейсах просто расширяется набор методов и свойств.

В языке Delphi существует предопределенный интерфейс IInterface, который служит неявным базовым интерфейсом для всех остальных интерфейсов. Это означает, что объявление

type

ITextReader = interface

...

end;

эквивалентно следующему:

type

ITextReader = interface(IInterface)

...

end;

Мы рекомендуем использовать вторую, более полную форму записи.

Описание интерфейса IInterface находится в стандартном модуле System:

type

IInterface = interface

['{00000000-0000-0000-C000-000000000046}']

function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;

function _AddRef: Integer; stdcall;

function _Release: Integer; stdcall;

end;

Непонятная последовательность нулей и других цифр в квадратных скобках — это так называемый глобально-уникальный идентификатор интерфейса. Мы к нему еще вернемся, а сейчас рассмотрим методы.

Методы интерфейса IInterface явно или неявно попадают во все интерфейсы и имеют особое назначение. Метод QueryInterface нужен для того, чтобы, имея некоторый интерфейс, запросить у объекта другой интерфейс. Этот метод автоматически вызывается при преобразовании одних интерфейсов в другие. Метод _AddRef автоматически вызывается при присваивании значения интерфейсной переменной. Метод _Release автоматически вызывается при уничтожении интерфейсной переменной. Последние два метода позволяют организовать подсчет ссылок на объект и автоматическое уничтожение объекта, когда количество ссылок на него становится равным нулю. Вызовы всех трех методов генерируются компилятором автоматически, и вызывать их явно нет необходимости, однако программист должен позаботиться об их реализации.

6.4. Глобально-уникальный идентификатор интерфейса

Интерфейс является особым типом данных: он может быть реализован в одной программе, а использоваться из другой. Для этого нужно обеспечить идентификацию интерфейса при межпрограммном взаимодействии. Понятно, что программный идентификатор интерфейса для этого не подходит — разные программы пишутся разными людьми, а разные люди подчас дают одинаковые имена своим творениям. Поэтому каждому интерфейсу выдается своеобразный «паспорт» — глобально-уникальный идентификатор (Globally Unique Identifier — GUID).

Глобально-уникальный идентификатор — это 16-ти байтовое число, представленное в виде заключенной в фигурные скобки последовательности шестнадцатеричных цифр:

{DC601962-28E5-4BF7-9583-0CE22B605045}

В среде Delphi глобально-уникальный идентификатор описывается типом данных TGUID:

type

PGUID = ^TGUID;

TGUID = packed record

D1: Longword;

D2: Word;

D3: Word;

D4: array[0..7] of Byte;

end;

Константы с типом TGUID разрешено инициализировать строковым представлением глобально-уникального идентификатора. Компилятор сам преобразует строку в запись с типом TGUID. Пример:

const

InterfaceID: TGUID = '{DC601962-28E5-4BF7-9583-0CE22B605045}';

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

type

IInterface = interface

['{00000000-0000-0000-C000-000000000046}']

...

end;

В будущем нашему интерфейсу ITextReader понадобится глобально-уникальный идентификатор. Но как его выбрать так, чтобы он оказался уникальным? Очень просто — нажмите в редакторе кода комбинацию клавиш Ctrl+Shift+G.

type

ITextReader = interface

['{DC601962-28E5-4BF7-9583-0CE22B605045}'] // Результат нажатия Ctrl+Shift+G

...

end;

Генерация глобально-уникальных идентификаторов осуществляется системой Windows по специальному алгоритму, в котором задействуется адрес сетевого адаптера, текущее время и генератор случайных чисел. Можете смело полагаться на уникальность всех получаемых идентификаторов.

Наличие глобально-уникального идентификатора в описании интерфейса не является обязательным, однако использование интерфейса без такого идентификатора ограничено, например, запрещено использовать оператор as для преобразования одних интерфейсов в другие.

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

const

IID_ITextReader: TGUID = '{DC601962-28E5-4BF7-9583-0CE22B605045}';

function TestInterface(const IID: TGUID): Boolean;

begin

...

TestInterface(ITextReader);

// эквивалентно

TestInterface(IID_ITextReader);

...

end;