Déclaration des génériques

De RAD Studio
Aller à : navigation, rechercher

Remonter à Génériques - Index

La déclaration d'un générique est similaire à la déclaration d'un type de classe, d'enregistrement ou d'interface normal. La différence réside dans le fait qu'un ou plusieurs paramètres de type placés entre les crochets angulaires (< et >) suivent l'identificateur de type dans la déclaration d'un générique.

Un paramètre de type peut être utilisé comme un identificateur de type typique dans un corps de méthode et une déclaration de type de conteneur.

Par exemple :

 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;
Remarque: Vous devez appeler le constructeur par défaut et initialiser les champs de classe avant d'appeler la méthode GetValue.

Argument de type

Les types génériques sont instanciés en fournissant des arguments de type. Dans Delphi, vous pouvez utiliser n'importe quel type comme argument de type, à l'exception des types suivants : un tableau statique, une chaîne courte ou un type d'enregistrement qui contient (de façon récursive) un champ d'un ou de plusieurs de ces deux types.

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

Types imbriqués

Un type imbriqué dans un générique est lui-même un générique.

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

Pour accéder au type imbriqué TBar, vous devez spécifier une construction du premier type TFoo :

 var
   N: TFoo<Double>.TBar;

Un générique peut également être déclaré dans une classe normale en tant que type imbriqué :

type 
 TBaz = class                        // A regular class
  type
    TQux<T> = class
      X: Integer;
    end;
  end;
 
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;

Types de base

Le type de base d'un type de classe ou d'interface paramétré peut être un type réel ou un type construit. Le type de base ne peut pas être un paramètre de type seul.

  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;

Si TFoo2<String> est instancié, une classe ancêtre devient TBar2<String> et TBar2<String> est automatiquement instancié.

Types de classe, d'interface et d'enregistrement

Les types de classe, d'interface, d'enregistrement et de tableau peuvent être déclarés avec des paramètres de type.

Par exemple :

 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>;

Types procéduraux

Le type de procédure et le pointeur de méthode peuvent être déclarés avec des paramètres de type. Les types de paramètre et les types de résultat peuvent également utiliser des paramètres de type.

Par exemple :

 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.

Méthodes paramétrées

Les méthodes peuvent être déclarées avec des paramètres de type. Les types de paramètre et les types de résultat peuvent utiliser des paramètres de type. Cependant, les constructeurs et les destructeurs ne possèdent pas de paramètres de types, et ni les uns ni les autres ne peuvent avoir de méthodes virtuelles, dynamiques ou de message. Les méthodes paramétrées sont semblables à des méthodes surchargées.

Il existe deux façons d'instancier une méthode :

  • Spécifier explicitement l'argument de type
  • Déduire automatiquement de l'argument de type

Par exemple :

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.

Portée des paramètres de type

La portée d'un paramètre de type couvre la déclaration de type et les corps de tous ses membres, mais n'inclut pas les types descendants.

Par exemple :

 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.

Voir aussi