ジェネリックスの概要
ジェネリックス:インデックス への移動
Delphi では、ジェネリックスを使用できます。
目次
ジェネリックスの動作の仕組み
ジェネリックスまたはジェネリック型という用語は、プラットフォーム内に存在する物のうち、型でパラメータ化できるものの集合を表します。 ジェネリックスという用語は、ジェネリック型かジェネリック メソッド(ジェネリック手続きおよびジェネリック関数)のどちらかを指します。
ジェネリックスは、アルゴリズム(手続き、関数など)やデータ構造(クラス、インターフェイス、レコードなど)を、そこで使用される 1 つ以上の特定の型から切り離せるようにする一連の抽象化ツールです。
定義で他の型を使用するメソッドやデータ型は、1 つ以上の特定の型を型パラメータに置き換えることで、より一般的なものにすることができます。 次に、メソッドやデータ構造の宣言でそれらの型パラメータを型パラメータ リストに追加します。 これは、手続きをより一般的なものにする方法(本体のリテラル定数のインスタンスをパラメータ名に置き換え、そのパラメータを手続きのパラメータ リストに追加する)と似ています。
たとえば、オブジェクト(TObject 型)のリストを保持する TMyList クラスは、TObject を使用している箇所を型パラメータ名('T' など)に置き換え、その型パラメータをクラスの型パラメータ リストに追加して TMyList<T> などにすることで、再利用性と型安全性を高めることができます。
ジェネリック型やジェネリック メソッドを実際に使用(インスタンス化)するには、使用時にそのジェネリック型またはジェネリック メソッドに型引数を指定します。 型引数を指定すると、ジェネリック定義に出現する型パラメータが対応する型引数に置き換えられて、新しい型やメソッドが効果的に生成されます。
たとえば、上記のリストが TMyList<Double> として使用されるとしましょう。 その場合は、新しい型 TMyList<Double> が生成されますが、その定義は、含まれている 'T' の部分がすべて 'Double' に置き換えられていること以外は、TMyList<T> とまったく同じです。
なお、抽象化メカニズムとしてのジェネリックスの機能は多態性(ポリモーフィズム)の機能の多くと共通していますが、特徴は異なります。 新しい型やメソッドはインスタンス化の時点で生成されるので、より早く、つまり実行時ではなくコンパイル時に型エラーを検出できます。 これにより、最適化の機会も広がりますが、トレードオフもあります。インスタンス化のたびに最終的に動作するアプリケーションのメモリ使用量が増え、結果的にパフォーマンスが低下するおそれがあります。
コード例
たとえば、TSIPair はクラスです。これには 2 つのデータ型 String と Integer があります。
type
TSIPair = class
private
FKey: String;
FValue: Integer;
public
function GetKey: String;
procedure SetKey(Key: String);
function GetValue: Integer;
procedure SetValue(Value: Integer);
property Key: TKey read GetKey write SetKey;
property Value: TValue read GetValue write SetValue;
end;
クラスをデータ型から独立させるには、データ型を型パラメータで置き換えます。
type
TPair<TKey,TValue> = class // TPair 型を 2 つの型パラメータで宣言
private
FKey: TKey;
FValue: TValue;
public
function GetKey: TKey;
procedure SetKey(Key: TKey);
function GetValue: TValue;
procedure SetValue(Value: TValue);
property Key: TKey read GetKey write SetKey;
property Value: TValue read GetValue write SetValue;
end;
type
TSIPair = TPair<String,Integer>; // インスタンス化された型の宣言
TSSPair = TPair<String,String>; // 他のデータ型で宣言
TISPair = TPair<Integer,String>;
TIIPair = TPair<Integer,Integer>;
TSXPair = TPair<String,TXMLNode>;
プラットフォームごとの要件と相違点
ジェネリック型は、Delphi コンパイラでサポートされています。
実行時型情報
Win32 では、ジェネリックスとメソッドには実行時型情報(RTTI)がなく、インスタンス化された型に RTTI があります。 インスタンス化された型は、ジェネリックスと一連のパラメータが組み合わさったものです。 クラスのメソッドの RTTI は、そのクラス全体の RTTI の一部になります。 ジェネリックスは実行時ではなくコンパイル時にインスタンス化されるため、非ジェネリック クラスにジェネリック メソッドがあっても、そのメソッドは、そのクラスについて生成される RTTI には含まれません。
インターフェイス GUID
Win32 では、インスタンス化されたインターフェイス型にはインターフェイス GUID がありません。
インターフェイス内のパラメータ化されたメソッド
パラメータ化されたメソッド(型パラメータ付きで宣言されたメソッド)をインターフェイスで宣言することはできません。
インスタンス化のタイミング
ジェネリック型はコンパイル時にインスタンス化され、実行可能ファイルや再配置可能ファイルに出力されます。 ジェネリック型のインスタンス変数は、クラスの場合は実行時に、ジェネリック レコードの場合はコンパイル時に、それぞれインスタンス化されます。 ジェネリック クラスの RTTI は、それらのクラスがインスタンス化されるときにのみ生成されます。 インスタンス化されたクラスの RTTI は、非ジェネリック クラスの場合と同様です。 ジェネリック クラスにジェネリック メソッドがあっても、インスタンス化されたジェネリック クラスには、そのジェネリック メソッドの RTTI はありません。
動的インスタンス化
実行時の動的インスタンス化はサポートされていません。
インターフェイス制約
Win32 インターフェイスは "軽量" インターフェイスではありません。 つまり、インターフェイス制約の付いた型パラメータはすべて、COM IUnknown のメソッドである _AddRef、_Release、QueryInterface を常にサポートするか TInterfacedObject から継承します。 レコード型ではインターフェイス制約付きパラメータを指定できません。