Механизм вызова метода объекта через интерфейс 


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



ЗНАЕТЕ ЛИ ВЫ?

Механизм вызова метода объекта через интерфейс



Алгоритм вызова метода интерфейса такой же, как алгоритм вызова метода класса. Когда через интерфейсную переменную выполняется вызов метода, Intf. NextLine, реализуется следующий алгоритм:

1) из интерфейсной переменной извлекается адрес (по нему хранится адрес таблицы методов интерфейса);

2) по полученному адресу извлекается адрес таблицы методов интерфейса;

3) на основании порядкового номера метода в интерфейсе из таблицы извлекается адрес соответствующей подпрограммы;

4) вызывается код, находящийся по этому адресу. Этот код является переходником от метода интерфейса к методу объекта. Его задача – восстановить из ссылки на интерфейс значение указателя Self (путем вычитания заранее известного значения) и выполнить прямой переход на код метода класса.

 

Обычными средствами процедурного программирования этот алгоритм реализуется так:

Type

TMethodTable = array[0..9999] ofPointer;

TNextLineFunc = function(Self: ITextReader): Boolean;

Var

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

IntfPtr: Pointer; // адрес внутри интерфейсной переменной

TablePtr: ^TMethodTable; // указатель на таблицу методов интерфейса

MethodPtr: Pointer; // указатель на метод

Begin

...

IntfPtr:= Pointer(Intf); // 1) извлечение адреса из интерфейсной

Переменной

TablePtr:= Pointer(IntfPtr^); // 2) извлечение адреса таблицы методов

Интерфейса

MethodPtr:= TablePtr^[3]; // 3) извлечение адреса нужного метода из таблицы

TNextLineFunc(MethodPtr)(Intf); // 4) вызов метода через переходник

...

End.

 

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

Для доступа к объекту через интерфейс нужна интерфейсная переменная:

Var

Intf: ITextReader;

 

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

Var

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

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

Begin

...

Intf:= Obj;

...

end;

После инициализации интерфейсную переменную Intf можно использовать для вызова методов объекта Obj:

Intf.Active:= True; // -> Obj.SetActive(True);

Через интерфейсную переменную доступны только те методы и свойства объекта, которые есть в интерфейсе:

Intf.Free; // Ошибка! У интерфейса ITextReadaer нет методаFree.

Obj.Free; // Метод Free можно вызвать только так.

Алгоритм вызова метода интерфейса такой же, как алгоритм вызова метода класса. Когда через интерфейсную переменную выполняется вызов метода, Intf.NextLine, реализуется следующий алгоритм:

1) из интерфейсной переменной извлекается адрес (по нему хранится адрес таблицы методов интерфейса);

2) по полученному адресу извлекается адрес таблицы методов интерфейса;

3) на основании порядкового номера метода в интерфейсе из таблицы извлекается адрес соответствующей подпрограммы;

Вызывается код, находящийся по этому адресу. Этот код является переходником от метода интерфейса к методу объекта. Его задача – восстановить из ссылки на интерфейс значение указателя Self (путем вычитания заранее известного значения) и выполнить прямой переход на код метода класса.

 

Обычными средствами процедурного программирования этот алгоритм реализуется так:

Type

TMethodTable = array[0..9999] ofPointer;

TNextLineFunc = function(Self: ITextReader): Boolean;

Var

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

IntfPtr: Pointer; // адрес внутри интерфейсной переменной

TablePtr: ^TMethodTable; // указатель на таблицу методов интерфейса

MethodPtr: Pointer; // указатель на метод

Begin

...

IntfPtr:= Pointer(Intf); // 1) извлечение адреса из интерфейсной

Переменной

TablePtr:= Pointer(IntfPtr^); // 2) извлечение адреса таблицы методов

Интерфейса

MethodPtr:= TablePtr^[3]; // 3) извлечение адреса нужного метода из таблицы

TNextLineFunc(MethodPtr)(Intf); // 4) вызов метода через переходник

...

End.

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

Алгоритм вызова метода интерфейса такой же, как алгоритм вызова метода класса. Когда через интерфейсную переменную выполняется вызов метода, Intf.NextLine, реализуется следующий алгоритм:

1) из интерфейсной переменной извлекается адрес (по нему хранится адрес таблицы методов интерфейса);

2) по полученному адресу извлекается адрес таблицы методов интерфейса;

3) на основании порядкового номера метода в интерфейсе из таблицы извлекается адрес соответствующей подпрограммы;

Вызывается код, находящийся по этому адресу. Этот код является переходником от метода интерфейса к методу объекта. Его задача – восстановить из ссылки на интерфейс значение указателя Self (путем вычитания заранее известного значения) и выполнить прямой переход на код метода класса.

 

Один класс может содержать реализацию нескольких интерфейсов.

 

Применение интерфейса для доступа к объекту динамически-подключаемой библиотеки

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

Сначала вынесем описание интерфейса ITextReader в отдельный модуль (например, ReaderIntf), чтобы этот модуль в дальнейшем можно было подключить к главной программе:

unit ReadersIntf;

Interface

Type

ITextReader = interface(IInterface)

...

end;

Implementation

End.

Затем удалим описание интерфейса из модуля ReadersUnit, а вместо него подключим модуль ReaderIntf:

 

unit ReadersUnit;

Interface

Uses

ReaderIntf;

...

Наконец включим скорректированный модуль ReadersUnit в DLL-библиотеку, которую назовем ReadersLib:

 

library ReadersLib;

Uses

SysUtils, Classes, ReadersUnit;

{$R *.res}

Begin

End.

Вроде бы все готово, и теперь в главной программе достаточно подключить модуль ReaderIntf и работать с объектами через интерфейс ITextReader.

 

 

А как в программе создавать объекты классов, находящихся в DLL-библиотеке? Ведь в нтерфейсе нет методов для создания объектов! Для этого определим в DLL-библиотеке специальную функцию и экспортируем ее:

 

Library ReadersLib;

...

Function GetDelimitedReader(constFileName: string; constDelimiter: Char = ';'): ITextReader;

Begin

Result:= TDelimitedReader.Create(FileName, Delimiter);

end;

Exports

GetDelimitedReader;

Begin

End.

 

В главной программе импортируйте функцию GetDelimitedReader, чтобы с ее помощью создавать объекты класса TDelimitedReader:

Program Example;

Uses

ReadersIntf;

function GetDelimitedReader(constFileName: string; constDelimiter: Char = ';'): ITextReader;

external 'ReadersLib.dll' name 'GetDelimitedReader';

Var

Intf: ITextReader;

Begin

Intf:= GetDelimitedReader;

...

End.

 



Поделиться:


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

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