ジェネリックスの宣言

提供: RAD Studio
移動先: 案内検索

ジェネリックス:インデックス への移動

ジェネリック宣言は、通常のクラス型、レコード型、インターフェイス型の宣言と似ています。違いは、ジェネリック宣言では、1 つ以上の型パラメータから成るリストを不等号かっこ(< と >)で囲んだものが型識別子の後に続く点です。

型パラメータは、コンテナ型の宣言およびメソッド本体内部で典型的な型識別子として使用することができます。

次に例を示します。

 type
   TPair<TKey,TValue> = class   // TKey and TValue are type parameters
     FKey: TKey;
     FValue: TValue;
     function GetValue: TValue;
   end;
 
 function TPair<TKey,TValue>.GetValue: TValue;
 begin
   Result := FValue;
 end;

メモ: デフォルト コンストラクタの呼び出しとクラス フィールドの初期化は、GetValue メソッドを呼び出す前に行う必要があります。

型引数

ジェネリック型のインスタンス化は、型引数を指定して行われます。Delphi では、静的配列、短い文字列、これら 2 つの型のどちらかを型とするフィールドを 1 つ以上(場合によっては再帰的に)含むレコード型を除く任意の型を型引数として使用できます。

 type
   TFoo<T> = class
     FData: T;
   end;
 var
   F: TFoo<Integer>; // 'Integer' is the type argument of TFoo<T>
 begin
   ...
 end.

ネスト型

ジェネリック型内部のネスト型は、それ自体がジェネリック型となります。

type
  TFoo<T> = class
  type
    TBar = class
      X: Integer;
      // ...
    end;
  end;

  // ...
  TBaz = class
  type
    TQux<T> = class
      X: Integer;
      // ...
    end;
    // ...
  end;

ネスト型 TBar にアクセスするには、次のように、TFoo 型のインスタンスをまず指定する必要があります。

 var
   N: TFoo<Double>.TBar;

ジェネリック型は、次のように、通常のクラスの内部でネスト型として宣言することもできます。

 type
   TOuter = class
   type
     TData<T> = class
       FFoo1: TFoo<Integer>;         // declared with closed constructed type
       FFoo2: TFoo<T>;               // declared with open constructed type
       FFooBar1: TFoo<Integer>.TBar; // declared with closed constructed type
       FFooBar2: TFoo<T>.TBar;       // declared with open constructed type
       FBazQux1: TBaz.TQux<Integer>; // declared with closed constructed type
       FBazQux2: TBaz.TQux<T>;       // declared with open constructed type
       ...
     end;
   var
     FIntegerData: TData<Integer>;
     FStringData: TData<String>;
   end;

基底型

パラメータ化クラス/インターフェイス型の基底型は、実際の型または生成型である可能性があります。基底型は型パラメータ単独ではないことがあります。

  type
    TFoo1<T> = class(TBar)            // Actual type
    end;
 
    TFoo2<T> =  class(TBar2<T>)       // Open constructed type
    end;

    TFoo3<T> = class(TBar3<Integer>)  // Closed constructed type
    end;

TFoo2<String> がインスタンス化されると、上位クラスが TBar2<String> になり、TBar2<String> は自動的にインスタンス化されます。

クラス型、インターフェイス型、レコード型

クラス型、インターフェイス型、レコード型、配列型は、型パラメータを付けて宣言することができます。

次に例を示します。

 type
   TRecord<T> = record
     FData: T;
   end;
 
 type
   IAncestor<T> = interface
     function GetRecord: TRecord<T>;
   end;
 
   IFoo<T> = interface(IAncestor<T>)
     procedure AMethod(Param: T);
   end;
 
 type
   TFoo<T> = class(TObject, IFoo<T>)
     FField: TRecord<T>;
     procedure AMethod(Param: T);
     function GetRecord: TRecord<T>;
   end;

 type
   anArray<T>= array of T;
   IntArray= anArray<integer>;

手続き型

手続き型とメソッド ポインタは型パラメータを付けて宣言することができます。パラメータ型と結果型でも型パラメータを使用できます。

次に例を示します。

 type
   TMyProc<T> = procedure(Param: T);
   TMyProc2<Y> = procedure(Param1, Param2: Y) of object;
 type
   TFoo = class
     procedure Test;
     procedure MyProc(X, Y: Integer);
   end;
 
 procedure Sample(Param: Integer);
 begin
   Writeln(Param);
 end;
 
 procedure TFoo.MyProc(X, Y: Integer);
 begin
   Writeln('X:', X, ', Y:', Y);
 end;
 
 procedure TFoo.Test;
 var
   X: TMyProc<Integer>;
   Y: TMyProc2<Integer>;
 begin
   X := Sample;
   X(10);
   Y := MyProc;
   Y(20, 30);
 end;
 
 var
   F: TFoo;
 begin
   F := TFoo.Create;
   F.Test;
   F.Free;
 end.

パラメータ化メソッド

メソッドは、型パラメータを付けて宣言することができます。パラメータ型と結果型で型パラメータを使用することができます。ただし、コンストラクタとデストラクタは型パラメータを持てず、virtual、dynamic、message の各修飾子の付いたメソッドも型パラメータを持てません。パラメータ化メソッドはオーバーロード メソッドと似ています。

メソッドをインスタンス化する方法は次の 2 とおりあります。

  • 型引数の明示的な指定
  • 型引数からの自動的な推論

次に例を示します。

type
  TFoo = class
    procedure Test;
    procedure CompareAndPrintResult<T>(X, Y: T);
  end;

procedure TFoo.CompareAndPrintResult<T>(X, Y: T);
var
  Comparer : IComparer<T>;
begin
  Comparer := TComparer<T>.Default;
  if Comparer.Compare(X, Y) = 0 then
    WriteLn('Both members compare as equal')
  else
    WriteLn('Members do not compare as equal');
end;

procedure TFoo.Test;
begin
  CompareAndPrintResult<String>('Hello', 'World');
  CompareAndPrintResult('Hello', 'Hello');
  CompareAndPrintResult<Integer>(20, 20);
  CompareAndPrintResult(10, 20);
end;

var
  F: TFoo;
begin
  F := TFoo.Create;
  F.Test;
  ReadLn;
  F.Free;
end.

型パラメータのスコープ

型パラメータのスコープには、型宣言とそのすべてのメンバの本体が含まれますが、下位の型は含まれません。

次に例を示します。

 type
   TFoo<T> = class
     X: T;
   end;
 
   TBar<S> = class(TFoo<S>)
     Y: T;  // error!  unknown identifier "T"
   end;
 
 var
   F: TFoo<Integer>;
 begin
   F.T  // error! unknown identifier "T"
 end.

関連項目