如何检查一个类是否实现了一个接口,尊重超集?

Dan*_*all 4 delphi oop com interface

我正在学习COM和接口,并有以下实验代码:

type
  IA = interface(IInterface)
  ['{C9C5C992-3F67-48C5-B215-7DCE6A61F0E8}']
  end;

  IB = interface(IA)
  ['{F1799437-AD12-471B-8716-F1D93D1692FC}']
  end;

  IC = interface(IB)
  ['{01780E8C-C47D-468E-8E42-4BFF3F495D51}']
  end;

  TBO = class(TInterfacedObject, IB)
  end;

procedure TForm1.FormCreate(Sender: TObject);
var
  x: TBO;
  a: IInterface;
begin
  x := TBO.Create;
  IInterface(x)._AddRef;

  if Assigned(TBO.GetInterfaceEntry(IA)) then memo1.lines.add('GetInterfaceEntry IA: OK'); // Why not?
  if Assigned(TBO.GetInterfaceEntry(IB)) then memo1.lines.add('GetInterfaceEntry IB: OK');
  if Assigned(TBO.GetInterfaceEntry(IC)) then memo1.lines.add('GetInterfaceEntry IC: OK');

  if x.QueryInterface(IA, a)=S_OK then memo1.lines.add('QueryInterface TA: OK'); // Why not?
  if x.QueryInterface(IB, a)=S_OK then memo1.lines.add('QueryInterface TB: OK');
  if x.QueryInterface(IC, a)=S_OK then memo1.lines.add('QueryInterface TC: OK');

  if Supports(TBO, IA) then memo1.lines.add('Supports TA: OK'); // Why not?
  if Supports(TBO, IB) then memo1.lines.add('Supports TB: OK');
  if Supports(TBO, IC) then memo1.lines.add('Supports TC: OK');

  if Supports(x, IA, a) then memo1.lines.add('Supports(2) TA: OK'); // Why not?
  if Supports(x, IB, a) then memo1.lines.add('Supports(2) TB: OK');
  if Supports(x, IC, a) then memo1.lines.add('Supports(2) TC: OK');
end;
Run Code Online (Sandbox Code Playgroud)

输出:

GetInterfaceEntry IB: OK
QueryInterface TB: OK
Supports TB: OK
Supports(2) TB: OK
Run Code Online (Sandbox Code Playgroud)

但是我需要:

GetInterfaceEntry IA: OK
GetInterfaceEntry IB: OK
QueryInterface TA: OK
QueryInterface TB: OK
Supports TA: OK
Supports TB: OK
Supports(2) TA: OK
Supports(2) TB: OK
Run Code Online (Sandbox Code Playgroud)

据我所知,由于接口继承,这IB是一个超集IA.据我所知,自TBO实现以来IB,它会自动实现IA.但是为什么Supports(),QueryInterface(),GetInterfaceEntry()返回false?

如何直接或间接查询TBO实现IA,即通过实现IA?的超集?我需要两者,像静态类函数GetInterfaceEntry和动态对象引用变体一样QueryInterface.

Rem*_*eau 5

这是德尔福的一个着名的怪癖.即使IB从中继承IA,也TBO必须明确指定两者IA,IB以便Supports()检索两个接口.

TBO = class(TInterfacedObject, IA, IB)
Run Code Online (Sandbox Code Playgroud)

我忘了这个的技术原因.与编译器如何为其生成接口表的限制有关TBO.它不会自动包含继承的接口.

  • 你正在咆哮着错误的树,林特奇.只需修复`TBO`的声明,以便它提及所有*它支持的接口.接口继承不像类继承.它不是一个*is-a*关系,而是一个*拥有所有相同成员的同一订单关系. (3认同)

Dis*_*ned 5

正如MarcoCantù在这里解释的那样:接口"继承"与类继承不同,实际上将它称为接口扩展可能更好.

如果我们假设一个类自动实现了一个基接口,就会出现一些严重的异常.

考虑:

IA = interface
<guid>
end;
IA1 = interface(IA)
<guid>
end;
IA2 = interface(IA)
<guid>
end;
Run Code Online (Sandbox Code Playgroud)

还有一个聚合IA1和IA2的类,其中两个都被委托:

TAggregate = class(TInterfacedObject, IA1, IA2)
private
  FIA1: IA1;
  FIA2: IA2;
protected
  property ImplIA1: IA1 read FIA1 implements IA1; 
  property ImplIA2: IA2 read FIA2 implements IA2;
end;
Run Code Online (Sandbox Code Playgroud)

现在,如果你问一个TAggregate实例的IA接口,它应该返回哪个实现?

关键是接口"继承"并不是真正的继承.因此,除非明确地这样做,否则该对象不会实现"祖先"接口.


还有一些其他事情要注意:

如果添加的方法procedure MethodIA;对IA界面还明确添加IA作为一个接口TAggregate,代码将无法编译.你会收到错误:

未申报的身份证明:'MethodIA'

您必须MethodIA直接添加到类中,或者选择将实现委托给哪个FIA1FIA2委派实现.例如,选择FIA2:

  property ImplIA: IA2 read FIA2 implements IA;
Run Code Online (Sandbox Code Playgroud)

此属性声明显示了Delphi识别接口的方式 -关系.即不是通过实现类,而是通过接口引用.

注意:即使在编译时,编译器也不会假定类实现了基接口,除非明确说明.

TIA1Implementor = class(TInterfacedObject, IA1)
protected
  procedure MethodIA;
end;

var
  LImplObj: TIA1Implementor;
  LA1: IA1;
  LA: IA;
begin
  LImplObj := TIA1Implementor.Create;
  LA1 := LImplObj; //Valid: TIA1Implementor implements IA1
  LA := LImplObj; //Does not compile: TIA1Implementor does not implement IA
  LA := LA1; //Valid: IA1 is an extension of IA
end;
Run Code Online (Sandbox Code Playgroud)

在编译时,扩展是已知的,并且为其基础分配扩展引用是完全合法的(没有任何强制转换或"支持"检查).但是直接从对象引用中分配不是.


有趣的是,该TypInfo单元显示接口类型确实知道他们的"父"接口.

PTypeData = ^TTypeData;
TTypeData = packed record
  case TTypeKind of
    tkInterface: (
      IntfParent : PPTypeInfo; { ancestor }
      IntfFlags : TIntfFlagsBase;
      Guid : TGUID;
      IntfUnit : ShortStringBase;
     {PropData: TPropData});
Run Code Online (Sandbox Code Playgroud)

但是考虑到上面的讨论,我不确定这些知识在运行时是如何有益的.我宁愿支持编译时检查.