簡體   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