Déclaration des génériques
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.
Sommaire |
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' 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 et d'enregistrement 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;
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 du type d'argument
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 du type d'argument 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.