为什么我的类实现子接口,而不是父母?

lin*_*luk 10 delphi inheritance interface

在Delphi中使用接口继承时,我发现了(至少对我而言)意外行为.

我有这个简单的类和接口层次结构:

+---------------+
| << IMyBase >> |
+---------------+
        ^
        |
+---------------+
| << IMyIntf >> |
+---------------+
        ^
        |
   +---------+
   | TMyObj  |
   +---------+
Run Code Online (Sandbox Code Playgroud)

我想声明一个类型的变量IMyBase.创建一个TMyObj并将其分配给我的变量.IHMO这是正常的OOP练习.但事实证明它没有编译.

我也尝试声明一个类型的变量IMyIntf并检查它是否支持IMyBase,恕我直言它应该支持它,但事实并非如此.

这是一个简单的测试代码:

program interface_inheritance;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils;

type
  IMyBase = interface
    ['{CC7C61B8-3FBA-481F-AF0D-A93C603B5202}']
    procedure Hello;
  end;

  IMyIntf = interface(IMyBase)
    ['{01CE01D9-A753-431C-A30E-64BAEC6C4E26}']
    //
  end;

  TMyObj = class(TInterfacedObject, IMyIntf)
    procedure Hello;
  end;

{ TMyObj }

procedure TMyObj.Hello;
begin
  Writeln('Hello World');
end;

var
  b: IMyBase;
  i: IMyIntf;
begin
(*  // Compile Error E2010
  b := TMyObj.Create;
  b.Hello;*)

  // Does not work as Expected
  // Does not call Hello()
  i := TMyObj.Create;
  if Supports(i, IMyBase, b) then begin
    // Why does i not support IMyBase ??
    b.Hello;
  end;

  // Works but unsafe!
  // Hard cast, without check.
  i := TMyObj.Create;
  b := IMyBase(i);
  b.Hello;

  // Works, of course!
  i := TMyObj.Create;
  i.Hello;

  Readln;
end.
Run Code Online (Sandbox Code Playgroud)

如您所见,我有一个有效的类/接口结构.但有些部分不能编译.有些不按预期执行.

  1. 为什么会b := TMyObj.Create;出现不兼容的类型错误?
  2. 为什么要Supports(i, IMyBase, b)回报false
  3. 还有另一种(更好的)方法来解决这样的问题吗?没有检查没有硬铸?(if i is IMyBase不起作用,因为接口不支持is运算符.)

这是有效的Pascal/Delphi行为还是错误?恕我直言Supports()应该回来true.并且TMyObj应该是有效的IMyBase(因此可以分配).

Dav*_*nan 10

这可能看起来有点反直觉,但是你的类必须声明它也实现了父接口.您的类声明必须如下:

TMyObj = class(TInterfacedObject, IMyBase, IMyIntf)
Run Code Online (Sandbox Code Playgroud)

前Borland工程师Danny Thorpe在回答相关问题时解释了这种行为背后的理由:

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

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

  • 没有这样的编译器指令,Danny认为它可能存在. (2认同)