В Delphi все интерфейсы являются наследниками IUnknown
(или IInterface
- Hallvard Vassbotn), но в мире C есть ещё более базовая сущность - интерфейсы, не являющиеся наследниками IUnknown
. Я встретил такой интерфейс в одной DLL, которую мне надо было использовать. Но я не могу это сделать в Delphi.
Да, это верно, что в Delphi создание интерфейса всегда подразумевает неявное наследование от
IUnknown
или
IInterface
(два имени для отличий COM и Delphi интерфейсов). И это означает, что все интерфейсы в Delphi будут иметь три метода
QueryInterface
,
_AddRef
и
_Release
. Это, конечно, делает сложным (или невозможным) реализацию чистого интерфейса из не-COM DLL с использованием синтаксиса объявления интерфейсов в Delphi.
В далёкие тёмные времена Delphi 2, у нас не было явных интерфейсов, а поддержка COM строилась на том факте, что структура таблицы VMT совпадает с бинарным контрактом COM, а "интерфейсы" были объявлены с использованием полностью абстрактных классов. К примеру, именно так до сих пор интерфейсы объявляются в C++. Класс, который хочет реализовать такой "интерфейс", просто наследуется от чистого абстрактного класса, замещая и реализуя все его абстрактные методы. Это достаточно ограниченный способ написания интерфейсов; поскольку Delphi не поддерживает множественное наследование классов, то вы точно захотите уметь поддерживать множество интерфейсов - и тогда вам придётся писать по одному новому классу на каждый интерфейс и вручную писать метод
QueryInterface
, чтобы он возвращал корректную ссылку. В C++ нет ограничения на множественное наследование и, быть может, поэтому в C++ нет отдельной языковой конструкции для интерфейсов - по сравнению с языками вроде Java, Delphi и C#, которые имеют модель единственности наследования.
Этот небольшой урок истории должен дать нам подсказку о том, как мы можем избежать методов IUnknown
обычных интерфейсов - мы можем просто объявить и реализовать чисто абстрактный класс.
К примеру:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39 |
program TestPureInterface;
{$APPTYPE CONSOLE}
type
TMyPureInterface = class
procedure FirstMethod; stdcall; virtual; abstract;
procedure SecondMethod; stdcall; virtual; abstract;
end ;
TMyImplementation = class (TMyPureInterface)
public
procedure FirstMethod; override;
procedure SecondMethod; override;
end ;
procedure TMyImplementation . FirstMethod;
begin
Writeln ( 'TMyImplementation.FirstMethod' );
end ;
procedure TMyImplementation . SecondMethod;
begin
Writeln ( 'TMyImplementation.SecondMethod' );
end ;
procedure TestClient(PureInterface: TMyPureInterface);
begin
PureInterface . FirstMethod;
PureInterface . SecondMethod;
end ;
var
MyImplementation: TMyImplementation;
begin
MyImplementation := TMyImplementation . Create;
TestClient(MyImplementation);
readln;
end .
|
Этот маленький пример кода сначала объявляет чистый абстрактный класс с виртуальными абстрактными методами. Это объявление не-COM интерфейса в стиле C++ для DLL из вопроса. Мы реализуем "интерфейс" созданием класса-наследника от этого базового полностью абстрактного класса, в котором реализуем все методы. Заметьте, что компилятор не требует повторно указывать соглашение вызова при замещении методов:
1 |
procedure FirstMethod; override;
|
И, наконец, я написал немного тестового кода, который осуществляет вызов реализованных методов через ссылку на "интерфейс". Это соответствует коду в DLL, для которой мы обеспечиваем реализацию интерфейсов.