Delphi接口实现
我希望引用计数应该在接口实现中的外部聚合对象上起作用。 如果我可以参考另一个例子: 以下是该行为的最低限度再现:Delphi接口实现,delphi,interface,delphi-2007,implements,Delphi,Interface,Delphi 2007,Implements,我希望引用计数应该在接口实现中的外部聚合对象上起作用。 如果我可以参考另一个例子: 以下是该行为的最低限度再现: program SO16210993; {$APPTYPE CONSOLE} type IFoo = interface procedure Foo; end; TFooImpl = class(TInterfacedObject, IFoo) procedure Foo; end; TContainer = class(TInterfac
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
类中实现接口,则析构函数将运行。这里发生的是调用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');//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方法进行聚合,以委托给
控制接口
聚合对象是由多个接口组成的对象
物体。每个对象实现自己的行为和接口,但是
所有对象共享相同的引用计数,即
控制器对象。在容器模式中,控制器是
容器对象
TAggregatedObject本身不支持任何接口。但是,
是典型的聚合,它确实实现了
界面,由从它下降的对象使用。
因此,TAggregatedObject充当
实现用于创建对象的接口,这些对象是
聚合
TAggregatedObject用作创建包含
对象和连接对象。使用TAggregatedObject可以确保
对接口方法的调用委托给控制接口
总的来说
在构造函数中为指定了控制界面
TAggregatedObject,由控制器属性指示
此外,源代码注释中还有以下内容:
TAggregatedObject和TContainedObject是合适的基类
对于拟聚合或包含在
外部控制对象。当在上使用“implements”语法时
接口属性在外部对象类声明中,使用
类型来实现内部对象
由聚合对象代表
控制器不应与其他接口区分开来
由控制员提供。聚合对象不能维护
它们自己的引用计数-它们的生存期必须与
他们的管制员。为了实现这一点,聚合对象反映了
将计数方法引用到控制器
TAggregatedObject只反映对其
控制器。从这样一个聚合对象中,可以获得任何
控制器支持的接口,并且只有
控制器支持。这对于实现控制器非常有用
类,该类使用一个或多个内部对象来实现
在控制器类上声明的接口。聚合促进
跨对象层次结构实现共享
TAggregatedObject是大多数聚合对象应该继承的对象
来自,尤其是与“机具”结合使用时
语法
“我错过什么了吗?”我不知道。但我们确实是。你忘了包括代码!需要演示行为的完整程序。否则我们只能猜测。你有一些额外的引用或引用循环。添加TFirstSecond.\u AddRef和TFirstSecond.\u释放并在那里放置断点,获取引用的完整列表,并查看哪些引用未清除。问题是您的接口被委派。不知道为什么会有这种行为,问得好。我冒昧地制作了一个非常简单的演示测试用例。委托的想法是Delphi中一个很好的官方特性,但它似乎不起作用?@FabricioAraujo我最终找到了文档。有个打字错误!!我在别处发现的无数例子误导了我。引用计数必须发生在某个地方,这是非常合理的。谢谢你的回答,不客气。这对我来说是一次有趣的学习经历!Marcos Santos提出了一个在我看来非常优雅的解决方案,允许将接口实现用作常规对象和聚合对象。解决方案是将实现类从TInterfacedObject中降序,因此只要将同一个实现与关键字implements一起使用,就可以将其用作独立对象和从TAggregatedObject中降序的装饰器。