繁体   English   中英

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

[英]Delphi interface inheritance: Why can't I access ancestor interface's members?

假设您具有以下条件:

//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;

我认为这将允许您调用DoSomething但事实并非如此:

procedure CallDoSomething(Parent: TParent);
begin
  if Parent is TChild then
    TChild(Parent).DoSomething;
end;

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

我可以通过在IParent的类声明中明确包含IParent来解决此TMyClass

 TMyClass = class(TInterfacedObject, IChild, IParent) 

没关系,这不会解决任何问题。

如果实现类未声明其支持继承的接口,则该类的分配将与继承的接口的变量不兼容。 您发布的代码示例应该可以正常工作(使用IChild接口),但是如果您尝试将TMyClass的实例分配给IParent变量,则会遇到麻烦。

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

我敢肯定,大约10或12年前,我写了一篇有关此的文章,但是我的Borland博客在过渡到Embarcadero服务器后仍然无法幸免。

我不记得有可能要更改此行为的编译器指令。

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

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

由于TChild没有方法“ DoSomething ”,因此无法正常工作。 如果年掳 直接实现ICHILD,那么这通常是可能的,因为年掳直接作为ICHILD接口的一部分实现的方法。

但是请注意,如果TChildPRIVATE范围内实现DoSomething ,它将可以通过接口访问,但是常规的作用域规则将意味着您仍然无法使用TChild引用(从类/ uni外部)调用它。

就您而言,您只需要获取适当的接口,然后通过该接口调用所需的方法:

  if Parent is TChild then
    (Parent as IChild).DoSomething;

但是,您正在使用类类型测试来依赖于实现细节(知道TChild实现IChild的知识)来确定(推断)接口的存在。 我建议您改为直接使用接口测试,以将依赖关系与那些实现细节隔离开:

  var
    parentAsChild: IChild;

  begin
    if Parent.GetInterface(IChild, parentAsChild) then
      parentAsChild.DoSomething;
  end;

编辑:此答案不再相关,因为它在修改原始问题之前发布。


可以在Delphi 2010中进行编译:

type
  IParent = interface(IInterface)
    function DoSomething: String;
  end;

  IChild = interface(IParent)
    function DoSomethingElse: string;
  end;

  TMyClass = class(TInterfacedObject, IChild)
  private
  public
    function DoSomething: String;
    function DoSomethingElse: String;
  end;

// ... 

procedure Test;
var
  MyObject : IChild;
begin
  MyObject := TMyClass.Create;
  MyObject.DoSomething;
end;

QueryInterface的Delphi实现不符合标准。 在标题为“人们弄乱的方式IUnknown :: QueryInterface ”的博客文章中 Raymond Chen列举了实现中的常见失败。 最值得注意的是第三点

忘记响应基本接口。 当实现派生接口时,您将隐式实现基本接口,因此也请不要忘记响应它们。

  IShellView *psv = some object; IOleView *pow; psv->QueryInterface(IID_IOleView, (void**)&pow); 

一些对象忘记了,QueryInterface失败并显示E_NOINTERFACE。

除非将继承的接口显式附加到类或其祖先之一,否则Delphi无法找到它。 它仅遍历对象的接口表及其继承的类型,并检查接口ID是否匹配,而不检查基本接口。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM