Почему мой класс реализует дочерние интерфейсы, а не их родителей? - PullRequest
0 голосов
/ 15 мая 2018

Я обнаружил (по крайней мере для меня) неожиданное поведение при использовании наследования интерфейса в Delphi.

У меня есть простая иерархия классов и интерфейсов:

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

Я хотел объявить переменную типа IMyBase. Создайте TMyObj и назначьте его моей переменной. IHMO это нормальная ООП практика. Но оказалось, что он не компилируется.

Я также пытался объявить переменную типа 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 Ответ

0 голосов
/ 15 мая 2018

Это может показаться немного противоречивым, но ваш класс должен объявить, что он также реализует родительский интерфейс.Объявление вашего класса должно выглядеть так:

TMyObj = class(TInterfacedObject, IMyBase, IMyIntf)

Дэнни Торп, бывший инженер Borland, объяснил причину этого поведения в ответе на связанный вопрос :

Если реализующий класс не объявляет, что он поддерживает унаследованный интерфейс, то этот класс не будет совместимым по присваиванию с переменными унаследованного интерфейса.Размещенный вами пример кода должен работать нормально (с использованием интерфейса IChild), но если вы попытаетесь назначить из экземпляра TMyClass переменную IParent, у вас возникнут проблемы.

Причина в том, чтоCOM и ActiveX позволяют реализации реализовать дочерний интерфейс (ваш IChild), но отрицают предка этого интерфейса (IParent).Поскольку интерфейсы Delphi предназначены для совместимости с COM, отсюда и возникает этот тупой артефакт.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...