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.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 から派生する必要があります。

最初は、これに関するドキュメントが見つかりませんでしたTAggregationObjectTAggregatedObjectそして最後に、実際に名前が付けられ、文書化されていることに気付きました。

TAggregatedObject は、IInterface メソッドを実装して制御 IInterface に委譲することにより、集約の内部オブジェクトに機能を提供します。

集約オブジェクトは、いくつかのインターフェース化されたオブジェクトで構成されるオブジェクトです。各オブジェクトは独自の動作とインターフェイスを実装しますが、すべてのオブジェクトは同じ参照カウント (コントローラー オブジェクトの参照カウント) を共有します。コンテナー パターンでは、コントローラーはコンテナー オブジェクトです。

TAggregatedObject 自体はインターフェイスをサポートしていません。ただし、集合体の典型であるように、集合体から派生するオブジェクトによって使用される IInterface のメソッドを実装します。したがって、TAggregatedObject は、集約の一部であるオブジェクトを作成するためのインターフェイスを実装するクラスのベースとして機能します。

TAggregatedObject は、包含オブジェクトと接続オブジェクトを作成するクラスのベースとして使用されます。TAggregatedObject を使用すると、IInterface メソッドの呼び出しが集約の制御 IInterface に委譲されるようになります。

制御 IInterface は TAggregatedObject のコンストラクターで指定され、Controller プロパティによって示されます。

さらに、ソースコードのコメントからこれがあります:

TAggregatedObject と TContainedObject は、外部の制御オブジェクトに集約または含まれることを意図したインターフェイス オブジェクトに適した基底クラスです。外側のオブジェクト クラス宣言のインターフェイス プロパティで "implements" 構文を使用する場合、これらの型を使用して内側のオブジェクトを実装します。

コントローラーに代わって集約されたオブジェクトによって実装されるインターフェイスは、コントローラーによって提供される他のインターフェイスと区別できません。集約されたオブジェクトは、独自の参照カウントを維持してはなりません。それらは、コントローラーと同じ有効期間を持つ必要があります。これを実現するために、集約されたオブジェクトは参照カウント メソッドをコントローラーに反映します。

TAggregatedObject は、そのコントローラーへの QueryInterface 呼び出しを単純に反映します。このような集約されたオブジェクトから、コントローラーがサポートする任意のインターフェイスを取得でき、コントローラーがサポートするインターフェイスのみを取得できます。これは、1 つ以上の内部オブジェクトを使用してコントローラー クラスで宣言されたインターフェイスを実装するコントローラー クラスを実装する場合に役立ちます。集約により、オブジェクト階層全体で実装の共有が促進されます。

TAggregatedObject は、特に "implements" 構文と組み合わせて使用​​する場合に、ほとんどの集約オブジェクトが継承する必要があるオブジェクトです。

于 2013-04-25T15:53:42.590 に答える