考虑这个接口及其实现。
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
的行。
当然,我可以通过在writeA
或TAClass
中实现TBClass
来成功编译它,并从那里调用writeA
的TADelegateClass
方法。
TAClass
是通过接口委派进行IIntfA
接口的具体实现,但为什么TBClass
的后代TAClass
不被认为是IIntfA
接口的具体实现?
TAClass
是通过接口委派进行IIntfA
接口的具体实现,但为什么TBClass
的后代TAClass
不被认为是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也有同样的错误/缺点。