这里发生的情况是你调用了
TContainer.Create
,创建了一个对象实例。但是你将该实例分配给接口引用,全局变量
Foo
。由于该变量类型为
IFoo
,接口代理意味着实现对象是
TFooImpl
的实例,而
不是TContainer
的实例。
因此,没有任何东西对
TContainer
的实例进行引用,其引用计数从未增加,因此它永远不会被销毁。
我认为没有非常简单的解决方法。你可能可以使用
TAggregatedObject
,但它可能无法解决你的问题。它将强制你声明
TContainer.FFoo
的类型为
TFooImpl
,我想你不想这样做。无论如何,以下是重构后的代码:
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');
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”语法来实现接口属性时,请使用这些类型来实现内部对象。由代表控制器的聚合对象实现的接口不应与控制器提供的其他接口有所区别。聚合对象不能维护自己的引用计数-它们必须与其控制器具有相同的生命周期。为了实现这一点,聚合对象将引用计数方法反映到控制器上。TAggregatedObject只是将QueryInterface调用反映到其控制器上。从这样的聚合对象中,可以获取控制器支持的任何接口,以及仅控制器支持的接口。这对于实现使用一个或多个内部对象来实现控制器类中声明的接口的控制器类非常有用。聚合促进了对象层次结构中的实现共享。大多数聚合对象都应该从TAggregatedObject继承,特别是与“implements”语法一起使用时。