#interface #freepascal #delegation
#интерфейс #freepascal #делегирование
Вопрос:
Рассмотрим этот интерфейс и его реализации.
unit utest;
interface
{$MODE OBJFPC}
type
IIntfA = interface
procedure writeA();
end;
IIntfB = interface(IIntfA)
procedure writeB();
end;
TADelegateClass = class(TInterfacedObject, IIntfA)
public
procedure writeA();
end;
TAClass = class(TInterfacedObject, IIntfA)
private
delegateA : IIntfA;
public
constructor create(const AInst : IIntfA);
destructor destroy(); override;
property A : IIntfA read delegateA implements IIntfA;
end;
TBClass = class(TAClass, IIntfB)
public
procedure writeB();
end;
implementation
procedure TADelegateClass.writeA();
begin
writeln('Implement IIntfA through delegation');
end;
constructor TAClass.create(const AInst : IIntfA);
begin
delegateA := AInst;
end;
destructor TAClass.destroy();
begin
inherited destroy();
delegateA := nil;
end;
procedure TBClass.writeB();
begin
writeln('Implement IIntfB');
end;
end.
Следующая программа не будет компилироваться.
program test;
{$MODE OBJFPC}
uses
utest;
var b : IIntfB;
begin
b := TBClass.create(TADelegateClass.create());
b.writeA();
b.writeB();
end.
Free Pascal (версия 3.0.4) жалуется
Error: No matching implementation for interface method "writeA;" found
.
в строке, где TBClass
объявлено.
Конечно, я могу успешно скомпилировать его, внедрив writeA
либо в TAClass
, либо TBClass
и вызвав writeA
метод TADelegateClass
оттуда.
TAClass
является ли конкретная реализация IIntfA
интерфейса через делегирование интерфейса, но почему TBClass
то, что является потомком TAClass
, не считается конкретной реализацией IIntfA
интерфейса?
Ответ №1:
TAClass
является ли конкретная реализацияIIntfA
интерфейса через делегирование интерфейса, но почемуTBClass
то, что является потомкомTAClass
, не считается конкретной реализациейIIntfA
интерфейса?
Краткий ответ: проблема не в IIntfA
этом, а в IIntfB
том, что она неполная.
Длинный ответ: Наследование интерфейса — это наследование vtable на C , которое иногда не интуитивно понятно.
В примере:
IIntfB = interface(IIntfA)
procedure writeB();
end;
на самом деле может быть записано как
IIntfB = interface
procedure writeA();
procedure writeB();
end;
При реализации нескольких интерфейсов общие части повторно не используются. Компилятор настраивает отдельные таблицы из методов реализации, таких как:
TADelegateClass:
QueryInterface(IIntfA) = Self.vtable_IIntfA
vtable_IIntfA.writeA <- Self.writeA
TAClass:
QueryInterface(IIntfA) = delegateA.vtable_IIntfA
TBClass:
QueryInterface(IIntfA) = inherited delegateA.vtable_IIntfA
QueryInterface(IIntfB) = vtable_IIntfB
vtable_IIntfB.writeA <- (this is missing!)
vtable_IIntfB.writeB <- Self.writeB
TBClass действительно не имеет реализации IIntfB.writeA
.
Это можно проверить, вручную назначив метод конкретному интерфейсу и наблюдая за исчезновением ошибки:
TBClass = class(TAClass, IIntfB)
public
procedure IIntfB.writeA = writeB;
// dummy method, shows IIntfB.writeA is missing
К сожалению, я не знаю ни одного способа сообщить компилятору о доступе к сопоставлению из другого интерфейса. Черт возьми, в Delphi есть та же ошибка / недостаток.