В процессе разработки на Delphi часто возникают вопросы, связанные с использованием интерфейсов и их реализацией в классах. Одной из распространенных проблем является несоответствие типов при передаче объектов, реализующих интерфейс-потомок, в функции, ожидающие интерфейс-предок. В данной статье мы рассмотрим такую ситуацию и предложим решение проблемы.
Описание проблемы
Разработчик столкнулся с проблемой при передаче объекта, который реализует интерфейс IChild, в функцию, принимающую массив интерфейса IParent. В контексте, описанном в вопросе, имеются следующие определения:
IParent - интерфейс с методом DoSomething.
IChild - интерфейс, наследующий IParent и имеющий метод DoSomethingElse.
TForm1 - класс, наследующий TForm и реализующий интерфейсы IChild и IParent (в альтернативном ответе указано, что IParent также должен быть включен в список реализованных интерфейсов).
При попытке вызова функции CallAllDoSomething, которая принимает массив интерфейсов IParent, с массивом объектов TForm1, возникает ошибка несоответствия типов:
[dcc32 Error] Unit1.pas(46): E2010 Incompatible types: 'IParent' and 'TForm1'
Пример кода
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs;
type
IParent = interface
procedure DoSomething();
end;
IChild = interface(IParent)
procedure DoSomethingElse();
end;
TForm1 = class(TForm, IChild)
// ...
public
procedure DoSomething();
procedure DoSomethingElse();
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure CallAllDoSomething(AArray : array of IParent);
var
i : integer;
begin
i := 0;
while(i < Length(AArray)) do
begin
AArray[i].DoSomething();
Inc(i);
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Unit1.CallAllDoSomething([Self]);
end;
procedure TForm1.DoSomething();
begin
ShowMessage('Something');
end;
procedure TForm1.DoSomethingElse();
begin
ShowMessage('Something else');
end;
end.
Подтвержденное решение
Чтобы объект TForm1 мог быть напрямую присвоен интерфейсу IParent, необходимо включить IParent в список реализованных интерфейсов класса TForm1:
Это поведение документально подтверждено в справочной системе Embarcadero DocWiki.
Альтернативное решение
Также можно явно привести объект TForm1 к интерфейсу IChild, а затем позволить компилятору выполнить преобразование в IParent:
Unit1.CallAllDoSomething([IChild(Self)]);
Это также подтверждено документацией Embarcadero.
Заключение
В данной статье мы рассмотрели проблему несоответствия типов при передаче объектов, реализующих интерфейс-потомок, в функцию, ожидающую интерфейс-предок, и предложили два способа решения этой проблемы. Оба метода основаны на правильном использовании интерфейсов и их наследования в Delphi.
В статье рассматривается ошибка приведения типов в Delphi, возникающая при передаче объекта, реализующего интерфейс `IChild`, в функцию, которая ожидает массив интерфейса `IParent`.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS