Déclaration des génériques

De Appmethod Topics
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 >) suit l'identificateur de type dans la déclaration d'un générique.

Les paramètres de type peuvent être utilisés comme un identificateur de type typique dans le corps de la méthode et la déclaration de type du conteneur.

Par exemple :

 type
   TPair<TKey,TValue> = class   // TKey et TValue sont des paramètres de type
     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 Object Pascal, 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' est l'argument de type de TFoo<T>
       begin
          ...
       end.

Types imbriqués

Un type imbriqué dans un type paramétré est lui-même un type paramétré.

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

  // ...
  TBaz = class
  type
    TQux<T> = 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 type paramétré peut également être déclaré dans une classe standard en tant que type imbriqué :

 type
   TOuter = class
     type
       TData<T> = class
         FFoo1: TFoo<Integer>;         // déclaré avec le type construit fermé         
         FFoo2: TFoo<T>;               // déclaré avec le type construit ouvert
         FFooBar1: TFoo<Integer>.TBar; // déclaré avec le type construit fermé
         FFooBar2: TFoo<T>.TBar;       // déclaré avec le type construit ouvert
         FBazQux1: TBaz.TQux<Integer>; // déclaré avec le type construit fermé
         FBazQux2: TBaz.TQux<T>;       // déclaré avec le type construit ouvert
         ...
       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)            // Type réel
      end;
 
    TFoo2<T> =  class(TBar2<T>)       // Type construit ouvert
       end;
    TFoo3<T> = class(TBar3<Integer>)  // Type construit fermé
        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édure

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.
 
       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. Les méthodes paramétrées sont similaires aux 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
   TMyProc2<Y> = procedure(Param1, Param2: Y) of object;
     TFoo = class
       procedure Test;
       procedure MyProc2<T>(X, Y: T);
     end;
 
     procedure TFoo.MyProc2<T>(X, Y: T);
       begin
         Write('MyProc2<T>');
         {$IFDEF CIL}
           Write(X.ToString);
           Write(', ');
           Writeln(Y.ToString);
         {$ENDIF}
         WR
       end;
 
       procedure TFoo.Test;
         var
           P: TMyProc2<Integer>;
           begin
             MyProc2<String>('Hello', 'World');    //type spécifié
             MyProc2('Hello', 'World');            // déduit de l'argument de type
             MyProc2<Integer>(10, 20);
             MyProc2(10, 20);
             P := MyProc2<Integer>;
             P(40, 50);
          end;
 
      var
        F: TFoo;
          begin
            F := TFoo.Create;
            F.Test;
            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;  // erreur!  identificateur "T" inconnu
     end;
 
     var
        F: TFoo<Integer>;
         begin
           F.T  // erreur!  identificateur "T" inconnu
         end.

Voir aussi