Types procéduraux (Object Pascal)

De Appmethod Topics
Aller à : navigation, rechercher

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


Les types procéduraux permettent de traiter des procédures et des fonctions comme des valeurs pouvant être assigné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 Object Pascal.

A propos des types procéduraux

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 à 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 retirez l'identificateur après le mot procedure ou function, ce qui reste est la partie droite de la déclaration d'un type procédural. Vous pouvez utiliser directement de tels noms de types dans les déclarations de variables (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éthodes

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

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

Ces types représentent des pointeurs de méthodes. Un pointeur de méthode est en fait une paire de pointeurs ; le premier stocke l'adresse d'une méthode et le deuxième 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

nous pouvons effectuer l'affectation suivante :

 
  OnClick := MainForm.ButtonClick;

Deux types procéduraux sont compatibles s'ils ont :

  • la même convention d'appel.
  • la même valeur de retour (ou pas de valeur de retour), et
  • le même nombre de paramètres, avec le même type aux mêmes positions. (Les noms des paramètres sont sans importance.)

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

Les procédures et fonctions imbriquées (routines déclarées au sein d'autres routines) ne peuvent être utilisées en tant que valeurs procédurales, tout comme les fonctions et procédures 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 sur la partie gauche d'une instruction d'affectation, le compilateur attend une valeur procédurale à droite. L'affectation fait de la variable placée à gauche un pointeur sur la fonction ou la procédure indiquée à droite de l'affectation. 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'affectation, le type de la variable à gauche détermine l'interprétation des pointeurs de procédures ou de méthodes à 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 :

if F = MyFunction then ...;

Dans ce cas, 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 référence 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 (plutôt que l'adresse qu'elle contient), utilisez @@. Par exemple, @@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 assignée, utilisez la fonction standard Assigned :

 
  if Assigned(OnClick) then OnClick(X);

Voir aussi