继承接口的接口委托是否需要包装器class?

Does interface delegation of an inherited interface require a wrapper class?

Delphi 允许使用 implements 关键字进行接口委托。

例如

IIndep1 = interface
  function foo2: integer;
end;

IIndep2 = interface
  function goo2: integer;
end;

TIndep1And2 = class(TInterfacedObject, IIndep1, IIndep2)
private
  FNested : IIndep1; //e.g. passed via constructor or internally created (not shown here)
public
   Constructor Create(AIndep1: IIndep1);
  function goo2: integer;
   property AsIndep1 : IIndep1 read FNested implements IIndep1;
end;

这很有效,但不适用于继承的接口。 (错误信息 "Missing implementation of interface method ILev1.foo")

ILev1 = interface
  function foo: Integer;
end;

ILev2 = interface(ILev1)
  function goo: Integer;
end;

TLev2Fails = class(TInterfacedObject, ILev1, ILev2) //Also fails with ILev2 alone (Error: "ILev1 not mentioned in interface list")
private
  FNested : ILev1; //passed via constructor or internally created 
public
   Constructor Create(AILev1: ILev1);
   function goo: Integer;
   property AsLev1 : ILev1 read FNested implements ILev1;
end;

解决方法是添加一个额外的祖先 class

TLev1Wrapper = class(TInterfacedObject, ILev1)
private
  FNested : ILev1; //passed via constructor or internally created 
public
   Constructor Create(AILev1: ILev1);
   property AsLev1 : ILev1 read FNested implements ILev1;
end;

TLev2Works = class(TLev1Wrapper, ILev2)
public
  function goo: Integer;
end;

有没有办法避免包装器 class 祖先?

[编辑] 请注意接口委托,使用 implements 的目的是避免直接满足接口,而是将该要求传递给聚合或组合成员。提供完整的界面并手动委派给组合成员会破坏使用 implements 来指导界面所获得的好处。事实上,在那种情况下,implements 关键字和 属性 可能会被删除。

这看起来像是编译器试图强制执行 the expectations (read: requirements) of IUnknown.QueryInterface:

For any one object, a specific query for the IUnknown interface on any of the object's interfaces must always return the same pointer value.

如果您能够在自己实现派生接口的同时委托基接口的实现,那么:

obj := TLev2Fails.Create(otherLev1);  // Assuming your class could compile

lev1 := obj as ILev1;     // yields reference to otherLev1 implementor
lev2 := obj as ILev2;     // yields reference to TLev2Fails instance

unk1 := lev1 as IUnknown;   // returns IUnknown of otherLev1 implementor
unk2 := lev2 as IUnknown;   // returns IUnknown of obj TLev2fails instance

如果您的嵌套对象正确实现为 TAggregatedObject 派生 class,则情况不会如此,但编译器无法知道这是否是更不用说强制执行它了,相反,它似乎只是要求,如果您实现派生接口,那么您还必须直接实现接口本身继承的任何接口。

这种情况下的编译器错误不是很有帮助,尽管它可以被解读为告诉你你需要做什么,而不是为什么 在这种情况下你需要这样做,这对于编译器错误来说并不罕见。

如果您想在这种情况下进行委托,那么您必须 "delegate manually".

虽然这失去了 implements 设施的好处,但它至少保留了重复使用的好处,只是不太方便。

注意:即使您的委托实现基于TAggregatedObject,编译器仍然无法确定这些实现细节是否满足 QueryInterface 的要求,因此您仍然会收到此错误(即使对委托接口使用 class 引用)。

您仍然必须手动委派。

说了这么多,我目前看不出这对于没有继承关系的接口有什么不同,但这很可能是有效的,我只是没有完成所有的工作有必要 'thought experiments' 向自己证明这一点。 :)

可能是编译器在它认为 can/should 的情况下格外小心,而文档根本没有提到 implements.

的这种结果限制

或者它可能是编译器中的错误,尽管我认为该行为有足够明显的原因并且该行为本身已经建立且一致(在 Delphi 7 中重现了完全相同的行为) 相关文档中的遗漏是更可能的解释。

Is there a way to avoid the wrapper class ancestor?

不,继承的接口必须绑定到祖先 class。 Delphi 不隐式绑定继承的接口,原因已解释 here。此外,它无法绑定到当前 class,除非它手动将所有调用委托给组合项。

我们只有一个选择。我们可以绑定 ILev1

的祖先包装器 class