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

 ̄綄美尐妖づ 提交于 2019-11-28 00:56:05

The problem is not in the interface declarations or class implementations, but in your consumer code:

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

Is not going to work because TChild does not have a method "DoSomething". If TChild implemented IChild directly, then this would normally be possible because TChild would implement the method directly AND as part of the IChild interface.

Note however, that if TChild implemented DoSomething in PRIVATE scope, it would remain accessible thru the interface but normal scoping rules would mean that you still couldn't invoke it (from outside the class/uni) using a TChild reference either.

In your case, you simply need to obtain the appropriate interface and then invoke the method you require thru the interface:

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

However, you are using a class type test to determine (infer) the presence of an interface, relying on an implementation detail (knowledge that TChild implements IChild). I suggest you should instead be using interface testing directly, to isolate this dependency from those implementation details:

  var
    parentAsChild: IChild;

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

If an implementing class does not declare that it supports an inherited interface, then the class will not be assignment compatible with variables of the inherited interface. The code sample you posted should work fine (using the IChild interface), but if you try to assign from an instance of TMyClass to a variable of IParent, then you'll run into trouble.

The reason is because COM and ActiveX allow an implementation to implement a descendent interface (your IChild) but deny the ancestor of that interface (IParent). Since Delphi interfaces are intended to be COM compatible, that's where this goofy artifact comes from.

I'm pretty sure I wrote an article about this about 10 or 12 years ago, but my Borland blog did not survive the transition to the Embarcadero server.

There may be a compiler directive to change this behavior, I don't recall.

edit: This answer is no longer relevant because it was posted before the original question got modified.


This compiles in 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;

The Delphi implementation of QueryInterface is not up to standard. In the blog entry titled The ways people mess up IUnknown::QueryInterface Raymond Chen enumerates common failures in implementation. Most notable is the third point

Forgetting to respond to base interfaces. When you implement a derived interface, you implicitly implement the base interfaces, so don't forget to respond to them, too.

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

Some objects forget and the QueryInterface fails with E_NOINTERFACE.

Unless an inherited interface is explicitly attached to a class or one of its ancestors Delphi fails to find it. It simply traverses the interface table of the object and its inherited types and checks for a match of the interface IDs, it does not check base interfaces.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!