Reputation: 8053
I have a parent interface (IParent
), a child interface (IChild
) and an object which implements the child interface.
I'm trying to call a function which accepts an array of IParent
parameter by passing an array of objects implementing the child interface.
On compiling I get the following error:
[dcc32 Error] Unit1.pas(46): E2010 Incompatible types: 'IParent' and 'TForm1'
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, 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)
procedure FormCreate(Sender: TObject);
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.
Upvotes: 2
Views: 320
Reputation: 598134
In order to assign a TForm1
object directly to an IParent
, you have to include IParent
in TForm1
's declaration:
TForm1 = class(TForm, IParent, IChild)
This behavior is documented in Embarcadero's DocWiki:
Implementing Interface References
An interface-type expression cannot reference an object whose class implements a descendent interface, unless the class (or one that it inherits from) explicitly implements the ancestor interface as well.
For example:
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;
In this example, A is declared as a variable of type IAncestor. Because TSomething does not list IAncestor among the interfaces it implements, a TSomething instance cannot be assigned to A. But if you changed TSomething's declaration to:
TSomething = class(TInterfacedObject, IAncestor, IDescendant) // ...
the first error would become a valid assignment. D is declared as a variable of type IDescendant. While D references an instance of TSomething, you cannot use it to access TSomething's P2 method, since P2 is not a method of IDescendant. But if you changed D's declaration to:
D: TSomething;
the second error would become a valid method call.
Alternatively, since IChild
derives from IParent
, you can explicitly cast the TForm1
object to IChild
first, and then let the compiler convert the IChild
into an IParent
for you:
Unit1.CallAllDoSomething([IChild(Self)]);
This behavior is also documented:
Interface Assignment Compatibility
Variables of a given class type are assignment-compatible with any interface type implemented by the class. Variables of an interface type are assignment-compatible with any ancestor interface type. The value
nil
can be assigned to any interface-type variable.
Upvotes: 3
Reputation: 2007
You have to add IParent
to the declaration of TForm1
, too:
TForm1 = class(TForm, IParent, IChild)
Upvotes: 4