Реализация через делегирование интерфейса не передается потомку

#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 есть та же ошибка / недостаток.