What is happening here is that you call TContainer.Create
and create an instance to an object. But you then assign that instance to an interface reference, the global variable Foo
. Because that variable is of type IFoo
, the interface delegation means that the implementing object is the instance of TFooImpl
and not the instance of TContainer
.
Hence nothing ever takes a reference to the instance of TContainer
, its reference count is never increased, and so it is never destroyed.
I don't think there's a very easy way around this. You may be able to use TAggregatedObject
but it may not solve your problem. It would force you to declare TContainer.FFoo
to be of type TFooImpl
which I imagine you do not want to do. Anyhow, here's what it looks like re-cast that way:
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.
The documentation does talk about this:
The class you use to implement the delegated interface should derive from TAggregationObject.
Initially I could not find any documentation for this TAggregationObject
. And finally I realised that it's actually named TAggregatedObject
and is documented.
TAggregatedObject provides the functionality for an inner object of an
aggregate by implementing the IInterface methods to delegate to the
controlling IInterface.
An aggregated object is an object composed of several interfaced
objects. Each object implements its own behavior and interfaces, but
all the objects share the same reference count, which is that of the
controller object. In the container pattern, the controller is the
container object.
TAggregatedObject does not itself support any interfaces. However, as
is typical of an aggregate, it does implement the methods of
IInterface, which are used by the objects that descend from it.
TAggregatedObject, therefore, serves as a base for classes that
implement interfaces for creating objects that are part of an
aggregate.
TAggregatedObject is used as a base for classes that create contained
objects and connecting objects. Using TAggregatedObject ensures that
calls to the IInterface methods delegate to the controlling IInterface
of the aggregate.
The controlling IInterface is specified in the constructor for
TAggregatedObject and is indicated by the Controller property.
In addition there is this from the source code comments:
TAggregatedObject and TContainedObject are suitable base classes
for interfaced objects intended to be aggregated or contained in an
outer controlling object. When using the "implements" syntax on an
interface property in an outer object class declaration, use these
types to implement the inner object.
Interfaces implemented by aggregated objects on behalf of the
controller should not be distinguishable from other interfaces
provided by the controller. Aggregated objects must not maintain
their own reference count - they must have the same lifetime as
their controller. To achieve this, aggregated objects reflect the
reference count methods to the controller.
TAggregatedObject simply reflects QueryInterface calls to its
controller. From such an aggregated object, one can obtain any
interface that the controller supports, and only interfaces that the
controller supports. This is useful for implementing a controller
class that uses one or more internal objects to implement the
interfaces declared on the controller class. Aggregation promotes
implementation sharing across the object hierarchy.
TAggregatedObject is what most aggregate objects should inherit
from, especially when used in conjunction with the "implements"
syntax.