Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

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

Поиск

Интерфейс является особым типом данных: он может быть реализован в одной программе, а использоваться из другой. Для этого нужно обеспечить идентификацию интерфейса при межпрограммном взаимодействии. Программный идентификатор интерфейса для этого не подходит – разные программы пишутся разными людьми, а разные люди подчас дают одинаковые имена своим творениям. Поэтому каждому интерфейсу выдается своеобразный «паспорт» — глобально-уникальный идентификатор (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 и заключается в квадратные скобки, например:

IInterface = interface

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

...

end;

 

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

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

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

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

Const

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

Function TestInterface(constIID: TGUID): Boolean;

Beginп

...

TestInterface(ITextReader);

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

TestInterface(IID_ITextReader);

...

end;

 

Совместимость интерфейсов и классов

Совместимость интерфейсов подчиняется определенным правилам. Если интерфейс создан расширением уже существующего интерфейса:

IExtendedTextReader = interface(ITextReader)

то интерфейсной переменной базового типа может быть присвоено значение интерфейсной переменной производного типа, но не наоборот. Любая интерфейсная переменная совместима с типом данных IInterface – прародителем всех интерфейсов.

Интерфейсной переменной можно присвоить значение объектной переменной при условии, что объект (точнее его класс) реализует упомянутый интерфейс:

 

Var

Intf: ITextReader; // интерфейсная переменная

Obj: TTextReader; // объектная переменная

Begin

Intf:= Obj; // В переменную Intf копируется ссылка на объект Obj

end;

 

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

 

ITExtendedTextReader = class(TInterfacedObject, ITextReader, IExtendedTextReader);

 

Исключением из только что описанного правила является совместимость всех снабженных интерфейсами объектов с интерфейсом IInterface.

 

Получение интерфейса через другой интерфейс

 

Через интерфейсную переменную у объекта всегда можно запросить интерфейс другого типа. Для этого используется оператор as, например:

Var

Intf: IInterface;

Begin

...

With Intf as ITextReader do

Active:= True;

...

end;

 

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

В действительности оператор as преобразуется компилятором в вызов метода QueryInterface:

Var

Intf: IInterface;

IntfReader: ITextReader;

...

IntfReader:= Intf asITextReader; // Intf.QueryInterface(ITextReader, IntfReader);

 

Напомним, что метод QueryInterface описан в интерфейсе IInterface и попадает автоматически во все интерфейсы. Стандартная реализация этого метода находится в классе TInterfacedObject.

Представление интерфейса в памяти

Интерфейс по сути выступает дополнительной таблицей виртуальных методов, ссылка на которую укладывается среди полей объекта (таблица методов интерфейса). В ней хранятся указатели на методы класса, реализующие методы интерфейса.

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

 

Intf:= Obj; // где Intf: ITextReader иObj: TTextReader

 

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

 



Поделиться:


Последнее изменение этой страницы: 2016-08-14; просмотров: 453; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.116.118.214 (0.006 с.)