2016-05-21 13 views
4

Delphiは、ジェネリッククラスのコードを複製するという厄介な習慣を持っています。たとえそのコードが本当に同じであっても、ジェネリック型は似ているからです。
異なるクラスを格納するための重複を避けたい。
私のジェネリックコンテナでは、必要な場合にのみFreeを使用します。汎用クラスの生成コードの重複を防ぐにはどうすればよいですか?

は、私はそうのような汎用コンテナがあるとします。

unit Unit1; 

interface 

uses Generics.Collections; 

type 
    TMyContainer<T> = class(TObject) 
    strict private 
    FData: TList<T>; 
    public 
    constructor Create; virtual; 
    end; 

私はTは、多くの場合、オブジェクトになります知っています。すべてのオブジェクトは実際にはTObjectなので、私のコンテナにはさまざまなタイプのオブジェクトに対して重複した汎用コードを作成したくありません。

次のトリックは重複を防ぐために機能しますか?

A-クラス関数とコンストラクタを代入:

unit Unit2; 

uses Unit1; 

type 
    TMyContainer<T> = class(Unit1.TMyContainer<T>) 
    public 
    class function Create: TMyContainer<T>; static; 
    end; 

B:そうようなクラスの機能Createを実装:

class function TMyContainer<T>.Create: TMyContainer<T>; 
var 
    X: TObject; 
begin 
    if GetTypeKind(T) = tkClass then begin 
    X:= Unit1.TMyContainer<TObject>.Create; 
    end else begin 
    X:= Unit1.TMyContainer<T>.Create; 
    end; 
    TObject(Result):= X; 
end; 

このトリック作業が重複するコードを生成するから、コンパイラを防ぐために、ウィル私は間違った仮定を使用しているため、これは失敗しますか?
私は自分のデータに非ジェネリックストアを使用することに頼らないことに注意してください。

完全なサンプルコードは、以下の

unit Unit49; 

interface 

uses Generics.Collections; 

type 
    TMyContainer<T> = class(TObject) 
    strict private 
    FData: TList<T>; 
    public 
    constructor Create; virtual; 
    end; 

implementation 

constructor TMyContainer<T>.Create; 
begin 
    inherited Create; 
    FData:= TList<T>.Create; 
end; 

end. 

サンプル・プログラムこのトリックの作品は、オブジェクトの種類ごとに コードを複製生成するから、コンパイラを防ぐためだろう

program Project85; 

{$APPTYPE CONSOLE} 

{$R *.res} 

uses 
    System.SysUtils, 
    Unit49 in 'Unit49.pas'; 

type 
    TMyContainer<T> = class(Unit49.TMyContainer<T>) 
    public 
    class function Create: TMyContainer<T>; static; 
    end; 

{ TMyContainer<T> } 

class function TMyContainer<T>.Create: TMyContainer<T>; 
var 
    Y: T; 
    X: TObject; 
begin 
    if GetTypeKind(T) = tkClass then begin 
    X:= Unit49.TMyContainer<TObject>.Create; 
    end else begin 
    X:= Unit49.TMyContainer<T>.Create; 
    end; 
    TObject(Result):= X; 
end; 

var 
    A: TMyContainer<TObject>; 
    B: TMyContainer<TLanguages>; 

begin 
    A:= TMyContainer<TObject>.Create; 
    B:= TMyContainer<TLanguages>.Create; 
    readln; 
end. 
+0

私はとにかくだけでなく、ポート自体がないレガシーコード.... – Johan

+1

を持っているので、私は認識トリックが最近Generics.Collectionsに例示されていると思います。私はXE7に対してXE8の差分を読んでいました。 Spring4dも同様のことを考えています。 –

+0

OK、私はそれをチェックアウトします。 – Johan

答えて

4

、または私は の間違った仮定を使っているので、これは失敗しますか?

いいえ、動作しません。

基本的に、コンパイラはクラス階層全体を通してTをフォローし、特定の型に置き換えます。あなたのコンテナがFData: TList<T>として宣言されているので、あなたのトリックのコレクションは、あなたのクラスの関数でTMyContainer<T> = class(Unit49.TMyContainer<T>)Tジェネリックとコード全体から継承基本的に役に立たない

はスタートのために、あなたはその後、TObjectTLanguagesの両方のために生成された別々のTList<T>コードを持っています。

Unit49.TMyContainer<TLanguages>クラスのコードとUnit49.TMyContainer<TObject>クラスのコードが重複して生成されます。

あなたの例から、コードの重複を回避しようとしているとは言い難いです。コンテナクラスがあなたの例で書いたのと同じくらい簡単なら、すべてのコードの重複はTList<T>クラスから来ます。あなたがそれを避けようとしているなら、簡単な方法がありません。

あなたの問題の一部は、あなたが何でもできるTを持っているという事実から来ています。それを最適化するのは難しいです。あなたが得ることができる最大の最適化は、array of Tを使用してデータを格納し、操作関数を委任して、TObjectをすべてのクラスのベースとして使用し、他の場合は平文Tを使用することができます。

最新バージョンのTList<T>では、同様の手法で少し最適化されているため、上記でどれくらい得ることができますかは、使用するDelphiのバージョンによって異なります。しかし

、あなたはクラスや他のタイプのための別々の容器を持つことができるならば、あなたはTObjectList<TObject>(またはWindows上でも非ジェネリックTObjectList)を使用してTObjectと子孫コンテナの折りたたみコードを達成することができ、すべての特定のクラスを格納して薄いラッパー関数を実装するためのあなたが必要とする型安全な関数の型変換。もちろん、そのような関数には、特定の型ごとに生成されたコードがありますが、型クラスごとに完全なTList<T>を使用する場合と同じくらいコードにならない型キャストラッパーに過ぎません。

TMyObjectContainer<T> = class(TObject) 
    strict private 
    FData: TObjectList<TObject>; 
    public 
    constructor Create; virtual; 
    destructor Destroy; override; 
    function Data(index: integer): T; 
    end; 

constructor TMyObjectContainer<T>.Create; 
begin 
    inherited; 
    FData := TObjectList<TObject>.Create; 
end; 

constructor TMyObjectContainer<T>.Create; 
begin 
    FData.Free; 
    inherited; 
end; 

function TMyObjectContainer<T>.Data(index: integer): T; 
begin 
    Result := T(FData.Items[index]); 
end; 
関連する問題