Чтобы назначить TForm1
объект напрямую для IParent
, необходимо включить IParent
в объявление TForm1
:
TForm1 = class(TForm, IParent, IChild)
Это поведение задокументировано в DocWiki Embarcadero:
Реализация ссылок на интерфейсы
Выражение типа интерфейса не может ссылаться на объект, класс которого реализует интерфейс потомка, если только класс (или тот, от которого он наследует) также явно реализует интерфейс предка.
Например:
type
IAncestor = interface
end;
IDescendant = interface(IAncestor)
procedure P1;
end;
TSomething = class(TInterfacedObject, IDescendant)
procedure P1;
procedure P2;
end;
// ...
var
D: IDescendant;
A: IAncestor;
begin
D := TSomething.Create; // works!
A := TSomething.Create; // error
D.P1; // works!
D.P2; // error
end;
В этом примере A объявлено как переменная типа IAncestor. Поскольку TSomething не перечисляет IAncestor среди интерфейсов, которые он реализует, экземпляр TSomething не может быть назначен A. Но если вы изменили объявление TSomething на:
TSomething = class(TInterfacedObject, IAncestor, IDescendant)
// ...
, первая ошибка станет допустимым назначением. D объявлен как переменная типа IDescendant. Хотя D ссылается на экземпляр TSomething, вы не можете использовать его для доступа к методу TS2 Pomething, поскольку P2 не является методом IDescendant. Но если вы измените объявление D на:
D: TSomething;
, вторая ошибка станет допустимым вызовом метода.
В качестве альтернативы, поскольку IChild
происходит от IParent
, вы можете явно привести объект TForm1
к IChild
, а затем позволить компилятору преобразовать IChild
в IParent
для вас:
Unit1.CallAllDoSomething([IChild(Self)]);
Это поведение также задокументировано:
Совместимость назначения интерфейса
Переменные данного типа класса совместимы по присваиванию с любым типом интерфейса, реализованным классом. Переменные типа интерфейса совместимы по присваиванию с любым типом интерфейса предка. Значение nil
может быть присвоено любой переменной типа интерфейса.