Delphi接口实现

时间:2013-04-25 09:21:53

标签: delphi interface delphi-2007 implements

我希望引用计数应该适用于接口实现中的外部聚合对象。 如果我可以参考另一个例子:Clarity in classes implementing multiple interfaces (alternative to delegation):

这是行为的最小再现:

program SO16210993;

{$APPTYPE CONSOLE}

type
  IFoo = interface
    procedure Foo;
  end;

  TFooImpl = class(TInterfacedObject, IFoo)
    procedure Foo;
  end;

  TContainer = class(TInterfacedObject, IFoo)
  private
    FFoo: IFoo;
  public
    constructor Create;
    destructor Destroy; override;
    property Foo: IFoo read FFoo implements IFoo;
  end;

procedure TFooImpl.Foo;
begin
  Writeln('TFooImpl.Foo called');
end;

constructor TContainer.Create;
begin
  inherited;
  FFoo := TFooImpl.Create;
end;

destructor TContainer.Destroy;
begin
  Writeln('TContainer.Destroy called');//this line never runs
  inherited;
end;

procedure Main;
var
  Foo : IFoo;
begin
  Foo := TContainer.Create;
  Foo.Foo;
end;

begin
  Main;
  Readln;
end.

如果不是使用implements,而是在TImplementor类中实现接口,那么析构函数就会运行。

1 个答案:

答案 0 :(得分:15)

这里发生的是您调用TContainer.Create并为对象创建实例。但是,然后将该实例分配给接口引用,即全局变量Foo。由于该变量的类型为IFoo,因此接口委派意味着实现对象是TFooImpl的实例,而不是 TContainer的实例。

因此,没有任何内容引用TContainer的实例,它的引用计数永远不会增加,因此它永远不会被销毁。

我认为这不是一个非常简单的方法。您可以使用TAggregatedObject但它可能无法解决您的问题。它会强制您声明TContainer.FFooTFooImpl类型,我想您不想这样做。无论如何,这就是重新演绎的方式:

program SO16210993_TAggregatedObject;

{$APPTYPE CONSOLE}

type
  IFoo = interface
    procedure Foo;
  end;

  TFooImpl = class(TAggregatedObject, IFoo)
    procedure Foo;
  end;

  TContainer = class(TInterfacedObject, IFoo)
  private
    FFoo: TFooImpl;
    function GetFoo: IFoo;
  public
    destructor Destroy; override;
    property Foo: IFoo read GetFoo implements IFoo;
  end;

procedure TFooImpl.Foo;
begin
  Writeln('TFooImpl.Foo called');
end;

destructor TContainer.Destroy;
begin
  Writeln('TContainer.Destroy called');//this line does run
  FFoo.Free;
  inherited;
end;

function TContainer.GetFoo: IFoo;
begin
  if not Assigned(FFoo) then
    FFoo := TFooImpl.Create(Self);
  Result := FFoo;
end;

procedure Main;
var
  Foo : IFoo;
begin
  Foo := TContainer.Create;
  Foo.Foo;
end;

begin
  Main;
  Readln;
end.

documentation确实谈到了这个:

  

用于实现委托接口的类应该派生自TAggregationObject。

最初我找不到此TAggregationObject的任何文档。最后我意识到它实际上被命名为TAggregatedObject并且是documented

  

TAggregatedObject为一个内部对象提供了功能   通过实施IInterface方法聚合到   控制IInterface。

     

聚合对象是由多个接口组成的对象   对象。每个对象都实现自己的行为和接口,但是   所有对象共享相同的引用计数,即   控制器对象。在容器模式中,控制器是   容器对象。

     

TAggregatedObject本身不支持任何接口。但是,作为   是典型的聚合,它确实实现了方法   IInterface,由从它下降的对象使用。   因此,TAggregatedObject充当类的基础   实现用于创建属于的对象的接口   聚合

     

TAggregatedObject用作创建包含的类的基础   对象和连接对象。使用TAggregatedObject确保   调用IInterface方法委托给控制IInterface   总计。

     

控制IInterface在构造函数中指定   TAggregatedObject并由Controller属性指示。

此外还有来自源代码的评论:

  

TAggregatedObject和TContainedObject是合适的基类   对于要聚合或包含在的接口对象   外部控制对象。在上使用“implements”语法时   外部对象类声明中的interface属性,使用这些   实现内部对象的类型。

     

由聚合对象代表的接口实现的   控制器不应与其他接口区分开来   由控制器提供。不得维护聚合对象   他们自己的引用计数 - 它们必须具有相同的生命周期   他们的控制器为实现这一目标,聚合对象反映了这一点   控制器的引用计数方法。

     

TAggregatedObject只是反映了对其的QueryInterface调用   控制器。从这样的聚合对象,可以获得任何   控制器支持的接口,只有接口   控制器支持。这对于实现控制器很有用   使用一个或多个内部对象来实现的类   在控制器类上声明的接口。聚合促进   跨对象层次结构实现共享。

     

TAggregatedObject是大多数聚合对象应该继承的东西   来自,特别是与“工具”一起使用时   语法。