Delphi接口继承:为什么我不能访问祖先接口的成员?

Ken*_*ran 11 delphi inheritance interface delphi-2009

假设您有以下内容:

//Note the original example I posted didn't reproduce the problem so
//I created an clean example  
  type
    IParent = interface(IInterface)
    ['{85A340FA-D5E5-4F37-ABDD-A75A7B3B494C}']
      procedure DoSomething;
    end;

    IChild = interface(IParent)
    ['{15927C56-8CDA-4122-8ECB-920948027015}']
      procedure DoSomethingElse;
    end;

    TGrandParent = class(TInterfacedObject)
    end;

    TParent = class(TGrandParent)
    end;

    TChild = class(TParent, IChild)
    private
      FChildDelegate: IChild;
    public
      property ChildDelegate:IChild read FChildDelegate implements IChild;
    end;

    TChildDelegate = class(TInterfacedObject, IChild)
    public
      procedure DoSomething;
      procedure DoSomethingElse;
    end;
Run Code Online (Sandbox Code Playgroud)

我认为这可以让你打电话,DoSomething但似乎并非如此:

procedure CallDoSomething(Parent: TParent);
begin
  if Parent is TChild then
    TChild(Parent).DoSomething;
end;
Run Code Online (Sandbox Code Playgroud)

很明显,编译器正在强制执行接口继承,因为除非实现了成员,否则这两个类都不会编译IParent.尽管如此,编译器无法解析IParent实例化和使用类时的成员.

我可以通过明确包含IParent在类声明中来解决这个问题 TMyClass:

TMyClass = class(TInterfacedObject, IChild, IParent)
Run Code Online (Sandbox Code Playgroud)

没关系,这不适用于任何事情.

dth*_*rpe 14

如果实现类没有声明它支持继承的接口,那么该类将不会与继承接口的变量进行赋值兼容.您发布的代码示例应该可以正常工作(使用IChild接口),但是如果您尝试从TMyClass的实例分配给IParent变量,那么您将遇到麻烦.

原因是因为COM和ActiveX允许实现实现后代接口(您的IChild)但拒绝该接口的祖先(IParent).由于Delphi接口旨在与COM兼容,因此这个愚蠢的工件来自于此.

我很确定我在10年或12年前写过一篇关于这篇文章的文章,但是我的Borland博客并没有幸免于过渡到Embarcadero服务器.

可能有一个编译器指令来改变这种行为,我不记得了.

  • 实际上,您的 dcc 博客在回程机器上:http://web.archive.org/web/*/http://blogs.borland.com/dcc (2认同)

Del*_*ics 8

问题不在接口声明或类实现中,而是在您的使用者代码中:

procedure CallDoSomething(Parent: TParent);
begin
  if Parent is TChild then
    TChild(Parent).DoSomething;  // << This is wrong
end;
Run Code Online (Sandbox Code Playgroud)

不会起作用,因为TChild没有方法" DoSomething ".如果年掳实施ICHILD 直接,那么这将通常是可能的,因为年掳将直接实现方法作为的一部分ICHILD接口.

但是请注意,如果TChildPRIVATE范围内实现了DoSomething,它仍然可以通过接口访问,但是正常的范围规则意味着您仍然无法使用TChild引用调用它(来自class/uni之外).

在您的情况下,您只需要获取适当的接口,然后通过接口调用您需要的方法:

  if Parent is TChild then
    (Parent as IChild).DoSomething;
Run Code Online (Sandbox Code Playgroud)

但是,您正在使用类类型测试来确定(推断)接口的存在,依赖于实现细节(TChild实现IChild的知识).我建议您应该直接使用接口测试,将这种依赖性与这些实现细节隔离开来:

  var
    parentAsChild: IChild;

  begin
    if Parent.GetInterface(IChild, parentAsChild) then
      parentAsChild.DoSomething;
  end;
Run Code Online (Sandbox Code Playgroud)