4

Delphi XE2 で、Copy(owntype) メソッドが必要なオブジェクトを操作するジェネリック コレクション クラスを作成したいのですが、これを宣言する最善の方法がわかりません。

この例のようなものが必要です (簡単にするために、1 つのアイテムのコレクション)。

//------ Library ------
Type
  TBaseCopyable = class
    S: string;
//    procedure Copy(OtherObject: TBaseCopyable); overload;
    procedure Copy(OtherObject: TBaseCopyable); virtual;
  end;

  MyCollection<T: TBaseCopyable, constructor> = class
    TheItem: T;
    procedure SetItem(AItem: T); 
    function  GetItem: T;
  end;

[...]

function MyCollection<T>.GetItem: T;
Var
  NewItem: T;
begin
  NewItem := T.Create;
  NewItem.Copy(TheItem);
  Result := NewItem;
end;


//------ Usage ------
Type
  TMyCopyable = class(TBaseCopyable)
    I: integer;
//  procedure Copy(OtherObject: TMyCopyable); overload;
    procedure Copy(OtherObject: TMyCopyable); override;
  end;

[...]
  Col: MyCollection<TMyCopyable>;

重要な問題は、Col では、TMyCopyable.Copy を見つけるために MyCollection の汎用実装が必要なことです。当然のことながら、過負荷も仮想も仕事をしません。

  • オーバーロードを使用すると、コードはコンパイルされますが、MyCollection.GetItem は TMyCopyable.Copy ではなく TBaseCopyable.Copy を検出します。
  • virtual/override では、2 つの Copy 宣言の署名が一致しないため、これはコンパイルされません。

したがって、おそらく継承の代わりに、TBaseCopyable の仕様でジェネリックを使用する必要があると考えています。しかし、主に型パラメーターを TBaseCopyable に渡す必要が特にないため、どのようにすればよいかわかりません。単に Copy 引数の型が「それ自身のクラスの型」を一般的な方法で参照する必要があるだけです。

アイデア?ありがとう!

4

2 に答える 2

6

GenericTBaseCopyableクラスに変換し、その Generic 型を に適用すると、オーバーライドできます。たとえばCopy()、次のようになります。TMyCopyable

type
  TBaseCopyable<T> = class
    S: string;
    procedure Copy(OtherObject: T); virtual;
  end;

  MyCollection<T: TBaseCopyable<T>, constructor> = class
    TheItem: T;
    procedure SetItem(AItem: T);
    function  GetItem: T;
  end;

type
  TMyCopyable = class(TBaseCopyable<TMyCopyable>)
    I: integer;
    procedure Copy(OtherObject: TMyCopyable); override;
  end;

または、同じことを行うだけですTPersistent.Assign()(ジェネリックを使用しないため)。

type
  TBaseCopyable = class
    S: string;
    procedure Copy(OtherObject: TBaseCopyable); virtual;
  end;

  MyCollection<T: TBaseCopyable, constructor> = class
    TheItem: T;
    procedure SetItem(AItem: T);
    function  GetItem: T;
  end;

type
  TMyCopyable = class(TBaseCopyable)
    I: integer;
    procedure Copy(OtherObject: TBaseCopyable); override;
  end;

procedure TMyCopyable.Copy(OtherObject: TBaseCopyable);
begin
  inherited;
  if OtherObject is TMyCopyable then
    I := TMyCopyable(OtherObject).I;
end;
于 2013-09-22T03:40:43.543 に答える
0

私自身の質問に答えるか、少なくとも調査結果を要約します。

私の知る限り、私が提起した質問に対する完全な答えはありません。私が学んだことはこれです:

[1] Remy のソリューションは、基本アイテム クラス (ここでは TBaseCopyable) に状態がなく、abstract であるか、メソッドが同じ型の他のオブジェクトを参照する必要がない場合の方法です。(例: TBaseCopyable にはフィールドがなく、抽象メソッドのみがあります。)

[2] 重大な問題は、子孫クラスがメソッド引数を指定し、それらを囲むクラスと同じ型の値を返すことができるジェネリック クラスを指定する方法です。Remy の例では、それは子孫クラスの宣言で実現されます。

      TMyCopyable = クラス(TBaseCopyable<TMyCopyable>)

これは、ジェネリック クラスでは、T が目的の最終クラスに置き換えられることを意味します。

[3] ただし、TBaseCopyable の一般的な宣言内では、T が常に TBaseCopyable であるという情報が利用できないため、TBaseCopyable の実装では、型 T のオブジェクトへの参照は TBaseCopyable のメソッドまたはフィールドを参照できません。

T に制約を設定して、T が TBaseCopyable であることをコンパイラに伝えることができれば、これは解決されます。

それは明らかに C# のアプローチです: http://blogs.msdn.com/b/ericlippert/archive/2011/02/03/curiouser-and-curiouser.aspx

Delphi では、次のようになると思います。

  タイプ
    TBaseCopyable<T: TBaseCopyable<T> > = クラス
    ...

Remy が MyCollection で示しているように。ただし、TBaseCopyable がまだ完全に定義されていないため、その構文は同じクラス宣言内では有効ではありません (エラー: 宣言されていない識別子 TBaseCopyable)。TBaseCopyable の前方宣言を作成することを考えるかもしれませんが (非ジェネリック クラスの場合と同様)、それはエラーをスローし、明らかにコンパイラでサポートされていません。

[4] ジェネリック クラスが実装を継承する可能性はありますか?

これを行ったらどうなるでしょうか:

  タイプ
    TBaseCopyable<T> = class(TBaseCopyableImpl) ...

これにより、TBaseCopyable は相互に参照できるいくつかのフィールドとメソッドを持つことができます。ただし、これらのメソッドが仮想であったとしても、子孫に固定の引数/戻り値の型を課すことになります。これを回避することが、そもそもジェネリックを使用する理由でした。

したがって、この戦略は、子孫の型に特化する必要のないフィールドとメソッドにのみ適しています...たとえば、オブジェクトカウンターです。

結論

この質問は、ある程度知られている「好奇心旺盛に繰り返されるテンプレート パターン」に関するものであることが判明しました: http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern。達成しようとしていることは単純に見えますが、その背後には理論的な問題があります。

この状況では、「囲んでいるクラスと同じ型」などを意味する言語キーワードが必要なようです。ただし、これは明らかに共分散/反分散の問題につながります-継承階層でどのタイプをどのタイプに置き換えることができるかというルールの違反。そうは言っても、Delphi は C# ほど部分的なソリューションを許可していないようです。

もちろん、さらに先へ進む方法があることを知りたいです。

ああ、私はこの問題の根底にたどり着くのに苦労しているわけではありません。Ken Arnold でさえ難しいと考えています。 #コメント-828994

:-)

于 2013-09-24T02:11:00.377 に答える