Types procéduraux (Delphi)

De RAD Studio
Aller à : navigation, rechercher

Remonter à Types de données, variables et constantes - Index


Les types procédure permettent de traiter des procédures et des fonctions comme des valeurs pouvant être affectées à des variables ou transmises à d'autres procédures ou fonctions.

Cette rubrique ne fait pas référence au nouveau type procédural utilisé avec les méthodes anonymes, c'est-à-dire une "référence à une procédure". Voir Méthodes anonymes dans Delphi.

A propos des types procédure

L'exemple suivant décrit l'usage d'un type procédural. Supposons que vous définissez une fonction appelée Calc> qui prend deux paramètres entiers et renvoie un entier :

 function Calc(X,Y: Integer): Integer;

Vous pouvez assigner la fonction Calc a225621d à la variable F :

 var F: function(X,Y: Integer): Integer;
 F := Calc;

Si vous prenez un en-tête de fonction ou de procédure et supprimez l'identificateur suivant le mot procedure ou function, ce qui reste est la définition d'un type procédure. Vous pouvez utiliser directement cette déclaration de type dans une déclaration de variable (comme dans l'exemple précédent) ou déclarer de nouveaux types :

 type
   TIntegerFunction = function: Integer;
   TProcedure = procedure;
   TStrProc = procedure(const S: string);
   TMathFunc = function(X: Double): Double;
 var
   F: TIntegerFunction; // F is a parameterless function that returns an integer
   Proc: TProcedure;    // Proc is a parameterless procedure 
   SP: TStrProc;        // SP is a procedure that takes a string parameter 
   M: TMathFunc;        // M is a function that takes a Double (real)
                        // parameter and returns a Double
 
   procedure FuncProc(P: TIntegerFunction);  // FuncProc is a procedure
                        // whose only parameter is a parameterless
                        // integer-valued function

Pointeurs de méthode

Les variables présentées dans l'exemple précédent sont toutes des pointeurs de procédure, c'est-à-dire des pointeurs sur l'adresse d'une procédure ou d'une fonction. Pour pointer la méthode d'une instance d'objet (voir Classes et objets), vous devez ajouter les mots of object au nom de type. Par exemple :

 type
   TMethod      = procedure of object;
   TNotifyEvent = procedure(Sender: TObject) of object;

Ces types sont des pointeurs de méthode. Un pointeur de méthode est en fait une paire de pointeurs, le premier stocke l'adresse d'une méthode et le second une référence à l'objet auquel appartient la méthode. Etant donné les déclarations suivantes :

 type
   TNotifyEvent = procedure(Sender: TObject) of object;
   TMainForm = class(TForm)
     procedure ButtonClick(Sender: TObject);
      ...
   end;
 var
   MainForm: TMainForm;
   OnClick: TNotifyEvent

vous pouvez utiliser l'assignation suivante :

 OnClick := MainForm.ButtonClick;

Deux types de procédure sont compatibles si :

  • Ils ont la même convention d'appel.
  • Il renvoient le même type de valeur ou pas de valeur.
  • Ils ont le même nombre de paramètres, avec le même type aux mêmes positions. Le nom des paramètres est sans importance.

Les types pointeurs de procédures sont toujours incompatibles avec les types pointeurs de méthodes. La valeur nil peut être assignée à tous les types de procédure.

Les procédures et fonctions imbriquées (c'est-à-dire les routines déclarées à l'intérieur d'autres routines) ne peuvent s'utiliser comme valeur procédurale (de type procédure) tout comme les fonctions et procédure prédéfinies. Si vous voulez utiliser une routine prédéfinie comme Length en tant que valeur procédurale, écrivez une routine qui l'encapsule :

 function FLength(S: string): Integer;
 begin
   Result := Length(S);
 end;

Types procéduraux dans les instructions et les expressions

Quand une variable procédurale se trouve dans la partie gauche d'une instruction d'assignation, le compilateur attend également une valeur procédurale à droite. L'assignation fait de la variable placée à gauche un pointeur sur la fonction ou la procédure indiquée à droite de l'assignation. Néanmoins, dans d'autres contextes, l'utilisation d'une variable procédurale produit un appel de la procédure ou de la fonction référencée. Vous pouvez même utiliser une variable procédurale pour transmettre des paramètres :

 var
   F: function(X: Integer): Integer;
   I: Integer;
   function SomeFunction(X: Integer): Integer;
     ...
   F := SomeFunction;    // assign SomeFunction to F
   I := F(4);            // call function; assign result to I

Dans les instructions d'assignation, le type de la variable à gauche détermine l'interprétation des pointeurs de procédure ou de méthode à droite. Par exemple,

 var
   F, G: function: Integer;
   I: Integer;
   function SomeFunction: Integer;
     ...
   F := SomeFunction;      // assign SomeFunction to F
   G := F;                 // copy F to G
   I := G;                 // call function; assign result to I

La première instruction assigne une valeur procédurale à F. La deuxième instruction copie cette valeur dans une autre variable. La troisième instruction appelle la fonction référencée et assigne le résultat à I. Comme I est une variable entière et pas une variable procédurale, la dernière instruction appelle réellement la fonction (qui renvoie un entier).

Dans certaines situations, l'interprétation d'une variable procédurale n'est pas toujours aussi évidente. Soit l'instruction :

function Calc(X,Y: Integer): Integer;

Ici, l'occurrence de F produit un appel de fonction : le compilateur appelle la fonction pointée par F puis la fonction MyFunction et compare les résultats. La règle veut qu'à chaque fois qu'une variable procédurale apparaît dans une expression, elle représente un appel à la procédure ou la fonction référencée. Dans le cas où F référence une procédure, qui ne renvoie pas de valeur, ou si F désigne une fonction nécessitant des paramètres, l'instruction précédente déclenche une erreur de compilation. Pour comparer la valeur procédurale de F à MyFunction, utilisez :

 if @F = @MyFunction then ...;

@F convertit F en une variable pointeur sans type contenant une adresse et @MyFunction renvoie l'adresse de MyFunction.

Pour obtenir l'adresse mémoire d'une variable procédurale et pas l'adresse qu'elle contient, utilisez @@. Ainsi, @@F renvoie l'adresse de F.

L'opérateur @ peut également être utilisé pour assigner une valeur de pointeur sans type à une variable procédurale. Par exemple,

 var StrComp: function(Str1, Str2: PChar): Integer;
    ...
 @StrComp := GetProcAddress(KernelHandle, 'lstrcmpi');

appelle la fonction GetProcAddress et fait pointer StrComp sur le résultat.

Toute variable procédurale peut contenir la valeur nil, ce qui signifie qu'elle ne pointe sur rien. Mais essayer d'appeler une variable procédurale de valeur nil est une erreur. Pour tester si une variable procédurale est initialisée, utilisez la fonction standard Assigned:

 if Assigned(OnClick) then OnClick(X);

Voir aussi