将实现子接口的对象作为其父接口参数传递

Passing object which implements a child interface as its parent interface parameter

我有一个父接口 (IParent)、一个子接口 (IChild) 和一个实现子接口的对象。

我正在尝试通过传递实现子接口的对象数组来调用接受 array of IParent 参数的函数。

编译时出现以下错误:

[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.

您必须将 IParent 添加到 TForm1 的声明中,以便:

 TForm1 = class(TForm, IParent, IChild)

为了将 TForm1 对象 直接 分配给 IParent,您必须在 TForm1 中包含 IParent的声明:

TForm1 = class(TForm, IParent, IChild)

Embarcadero 的 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.

或者,由于 IChild 派生自 IParent,您可以先 显式 TForm1 对象转换为 IChild,然后让编译器为您将 IChild 转换为 IParent

Unit1.CallAllDoSomething([IChild(Self)]);

此行为也记录在案:

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.