【问题标题】:Passing object which implements a child interface as its parent interface parameter传递实现子接口的对象作为其父接口参数
【发布时间】:2020-07-29 17:21:49
【问题描述】:

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

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

编译时出现以下错误:

[dcc32 错误] Unit1.pas(46): E2010 不兼容的类型:'IParent' 和 '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.

【问题讨论】:

    标签: delphi interface delphi-xe7


    【解决方案1】:

    您也必须将IParent 添加到TForm1 的声明中:

     TForm1 = class(TForm, IParent, IChild)
    

    【讨论】:

    • 确实如此。如果没有 IParent 的列表,TForm1.DoSomething 实际上是 IChild.DoSomething 的实现,而不是 IParent.Dosomething.
    【解决方案2】:

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

    TForm1 = class(TForm, IParent, IChild)
    

    Embarcadero 的 DocWiki 中记录了这种行为:

    Implementing Interface References

    接口类型表达式不能引用其类实现了后代接口的对象,除非该类(或它继承自的类)也显式地实现了祖先接口。

    例如:

    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 的实例,但您不能使用它来访问 TSomething 的 P2 方法,因为 P2 不是 IDescendant 的方法。但是如果你把 D 的声明改为:

    D: TSomething;
    

    第二个错误将成为有效的方法调用。

    另外,由于IChild 派生自IParent,您可以显式TForm1 对象首先转换为IChild,然后让编译器将IChild 转换为@ 987654336@给你:

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

    这种行为也被记录在案:

    Interface Assignment Compatibility

    给定类类型的变量与该类实现的任何接口类型赋值兼容。接口类型的变量与任何祖先接口类型的赋值兼容。值nil 可以分配给任何接口类型的变量。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-12
      • 1970-01-01
      • 2011-02-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多