为什么我的类实现了子接口,但没有实现它们的父接口?

10

在使用Delphi中的接口继承时,我发现了一个(至少对我来说)意外的行为。

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

+---------------+
| << IMyBase >> |
+---------------+
        ^
        |
+---------------+
| << IMyIntf >> |
+---------------+
        ^
        |
   +---------+
   | TMyObj  |
   +---------+

我想声明一个类型为 IMyBase 的变量。创建一个 TMyObj 并将其赋值给我的变量。在我看来,这是正常的面向对象编程实践。但是结果表明它无法编译。

我还尝试声明一个类型为 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.

你可以看到我有一个有效的类/接口结构,但是有些部分无法编译,而有些部分则无法按预期执行。

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

这是有效的Pascal/Delphi行为还是一个错误?在我看来,Supports() 应该返回 true,并且 TMyObj 应该是有效的 IMyBase(因此可分配)。

1个回答

10

这可能看起来有点违反直觉,但是你的类必须声明它也实现了父接口。你的类声明必须像这样:

TMyObj = class(TInterfacedObject, IMyBase, IMyIntf)

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

如果一个实现类没有声明它支持一个继承接口,那么该类将不能分配给继承接口类型的变量。你发布的代码示例应该可以正常工作(使用 IChild 接口),但如果你尝试将 TMyClass 的实例分配给 IParent 类型的变量,则会遇到麻烦。

原因是因为 COM 和 ActiveX 允许实现一个后代接口(即 IChild),但拒绝其祖先接口(IParent)。由于 Delphi 接口旨在与 COM 兼容,因此出现了这种奇怪的现象。


2
并且没有像Danny想象中可能存在的这样的编译器指令。 - Rudy Velthuis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接