Delphi接口(interface)实现

标签 delphi interface delphi-2007 implements

我希望引用计数应该在接口(interface)实现中的外部聚合对象上工作。 如果我可以引用另一个例子: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 类中实现接口(interface),那么析构函数就会运行。

最佳答案

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

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

我认为没有一个非常简单的方法可以解决这个问题。您也许可以使用TAggreatedObject,但它可能无法解决您的问题。它会迫使您将 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.

documentation确实谈到了这一点:

The class you use to implement the delegated interface should derive from TAggregationObject.

最初我找不到此 TAggregationObject 的任何文档。最后我意识到它实际上被命名为 TAggregatedObject 并且是 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.

此外,源代码注释中有这样的内容:

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.

关于Delphi接口(interface)实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16210993/

相关文章:

delphi - 如何动态添加面板到TCategoryPanelGroup?

windows - 在 Windows 中创建菜单按钮

.net - 代码页 ID 到代码页名称 : GetEncoding equivalent in Delphi?

xml - 使用TXMLTransformProvider时控制XML节点的顺序

java - 如何通过示例使用 Java 创建我自己的自定义界面

delphi - 用 for in construction 创建的 Enumerator 是如何被销毁的?

java - 普通接口(interface)类和只有抽象方法的抽象类有什么区别吗?

java - 从自定义 DialogBox 类检索输入

delphi - 如何按描述对 TcxImageComboBox 的项目进行排序?

delphi - 如何访问 TCRDBGrid 组件中的搜索文本?