通过接口委派的实现不会传递给后代

时间:2019-03-14 10:31:35

标签: interface freepascal delegation

考虑此接口及其实现。

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.

免费Pascal(版本3.0.4)抱怨

Error: No matching implementation for interface method "writeA;" found

在声明TBClass的行中。

当然,我可以通过在writeATAClass中实现TBClass并从那里调用writeA的{​​{1}}方法来成功地编译它。

TADelegateClass是通过接口委托实现的TAClass接口的具体实现,但是为什么IIntfA的后代TBClass却不被认为是{{1}的具体实现}界面?

1 个答案:

答案 0 :(得分:2)

  

TAClassIIntfA接口通过以下方式的具体实现   接口委派,但为什么TBClassTAClass的后代,   不被认为是IIntfA接口的具体实现?

简短的回答:不是IIntfA才是问题,不是IIntfB才是问题。

长答案:接口继承是C ++ vtable继承,有时不直观。

在示例中:

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

可悲的是,我不知道有什么方法可以告诉编译器从另一个接口访问映射。 FWIW,Delphi具有相同的错误/缺点。