14

我希望引用计数应该在接口实现中的外部聚合对象上起作用。如果我可以参考另一个示例:实现多个接口的类中的清晰度(替代委托):

这是该行为的最小再现:

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然后析构函数运行。

4

1 回答 1

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.

文档确实谈到了这一点:

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

最初我找不到任何文档TAggregationObject。最后我意识到它实际上被命名TAggregatedObject记录在案

TAggregatedObject 通过实现 IInterface 方法委托给控制 IInterface 为聚合的内部对象提供功能。

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

TAggregatedObject 本身不支持任何接口。然而,作为典型的聚合,它确实实现了 IInterface 的方法,这些方法由它的后代对象使用。因此,TAggregatedObject 用作​​实现接口的类的基础,这些接口用于创建作为聚合的一部分的对象。

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

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

此外,源代码注释中还有这个:

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

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

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

TAggregatedObject 是大多数聚合对象应该继承的,尤其是与“实现”语法结合使用时。

于 2013-04-25T15:53:42.590 回答