Méthodes (Delphi)

De RAD Studio
Aller à : navigation, rechercher

Remonter à Classes et objets - Index

Une méthode est une procédure ou une fonction associée à une classe. Un appel de méthode spécifie l'objet (ou la classe si c'est une méthode de classe) sur lequel la méthode agit. Par exemple, SomeObject.Free appelle la méthode Free de SomeObject.

Cette rubrique couvre les sujets suivants :

  • Déclarations et implémentations de méthodes
  • Liaison de méthodes
  • Surcharge de méthodes
  • Constructeurs et destructeurs
  • Méthodes de messages

A propos des méthodes

Au sein d'une déclaration de classe, les méthodes apparaissent comme des en-têtes de procédures ou de fonctions qui fonctionnent comme les déclarations forward. Quelque part après la déclaration de classe, mais à l'intérieur du même module, chaque méthode doit être implémentée par une déclaration de définition. Si; par exemple, la déclaration de TMyClass contient une méthode appelée DoSomething :

 type
    TMyClass = class(TObject)
       ...
       procedure DoSomething;
       ...
    end;

Une déclaration de définition pour DoSomething doit apparaître plus loin dans le module :

 procedure TMyClass.DoSomething;
 begin
      ...
 end;

Si une classe peut être déclarée dans la section interface ou la section implementation d'une unité, les déclarations de définition des méthodes de classe doivent se trouver dans la section implementation.

Dans l'en-tête d'une déclaration de définition, le nom de la méthode est toujours qualifié par le nom de la classe à laquelle elle appartient. L'en-tête peut répéter la liste de paramètres depuis la déclaration de classe ; si c'est le cas, l'ordre, le type et le nom des paramètres doivent correspondre exactement et, si la méthode est une fonction, la valeur de retour doit aussi correspondre.

Les déclarations des méthodes peuvent inclure des directives spéciales qui ne sont pas utilisées par d'autres fonctions ou procédures. Les directives doivent apparaître uniquement dans la déclaration de classe et pas dans la déclaration de définition, et doivent toujours être dans l'ordre suivant :

reintroduce; overload; liaison; convention d'appel; abstract; avertissement

Où :

  • liaison correspond à virtual, dynamic ou override ;
  • convention d'appel correspond à register, pascal, cdecl, stdcall, or safecall;
  • avertissement correspond à platform, deprecated ou library. Pour plus d'informations sur ces directives d'avertissement (de conseil), voir Directives de conseil.

Toutes les directives Delphi sont listées dans Directives.

Inherited

Le mot réservé inherited joue un rôle particulier dans l'implémentation de comportements polymorphiques. Il peut apparaître dans une définition de méthode avec ou sans identificateur à la suite.

Si inherited est suivi par le nom d'un membre, il représente un appel de méthode normal ou une référence à une propriété ou à un champ, sauf que la recherche du membre référencé commence avec l'ancêtre immédiat de la classe de la méthode englobante. Par exemple, quand l'instruction :

 inherited Create(...);

apparaît dans la définition d'une méthode, elle appelle la méthode Create héritée.

Quand inherited est utilisé sans être suivi d'un identificateur, il désigne la méthode héritée portant le même nom que la méthode en cours ou, si la méthode en cours est un gestionnaire de message, le gestionnaire de message hérité pour le même message. Dans ce cas, inherited ne prend pas de paramètres explicites, mais transmet à la méthode héritée les paramètres utilisés pour l'appel de la méthode englobante. Par exemple :

 inherited;

apparaît fréquemment dans l'implémentation des constructeurs. Cette instruction appelle le constructeur hérité avec les mêmes paramètres que ceux transmis au descendant.

Self

Au sein de l'implémentation d'une méthode, l'identificateur Self référence l'objet dans lequel la méthode est appelée. Voici, par exemple, l'implémentation de la méthode Add de TCollection dans l'unité Classes :

 function TCollection.Add: TCollectionItem;
 begin
     Result := FItemClass.Create(Self);
 end;

La méthode Add appelle la méthode Create de la classe référencée par le champ FItemClass, qui est toujours un descendant de TCollectionItem. TCollectionItem.Create ne prend qu'un seul paramètre de type TCollection, donc Add le transmet à l'objet instance de TCollection où Add est appelée. Cela est illustré dans le code suivant :

 var MyCollection: TCollection;
     ...
     MyCollection.Add   // MyCollection is passed to the 
                        // TCollectionItem.Create method

Self est utile pour diverses raisons. Par exemple, un identificateur de membre déclaré dans un type classe peut être redéclaré dans le bloc de l'une des méthodes de la classe. Dans ce cas, vous pouvez accéder à l'identificateur du membre d'origine en utilisant Self.Identifier.

Pour plus d'informations sur Self dans les méthodes de classe, voir "Opérateurs de classes" dans Références de classes.

Liaison de méthode

Les liaisons de méthodes peuvent être statiques (par défaut), virtuelles ou dynamiques. Les méthodes virtuelles et dynamiques peuvent être redéfinies et elles peuvent être abstraites. Ces désignations jouent un rôle quand une variable d'un type classe contient une valeur d'un type classe descendant. Elles déterminent quelle implémentation est activée lors de l'appel d'une méthode.

Méthodes statiques

Par défaut les méthodes sont statiques. Quand une méthode statique est appelée, le type déclaré (à la compilation) de la variable classe ou objet utilisé dans l'appel de la méthode détermine l'implémentation à activer. Dans l'exemple suivant, les méthodes Draw sont statiques :

 type
     TFigure = class
       procedure Draw;
     end;
 
     TRectangle = class(TFigure)
       procedure Draw;
     end;

Etant donné ces déclarations, le code suivant illustre l'effet de l'appel d'une méthode statique. Dans le second appel à Figure.Draw, la variable Figure référence un objet de classe TRectangle, mais l'appel invoque l'implémentation de Draw dans TFigure, car le type déclaré de la variable Figure est TFigure :

 var
     Figure: TFigure;
     Rectangle: TRectangle;
 
     begin
             Figure := TFigure.Create;
             Figure.Draw;              // calls TFigure.Draw
             Figure.Destroy;
             Figure := TRectangle.Create;
             Figure.Draw;              // calls TFigure.Draw
 
             TRectangle(Figure).Draw;  // calls TRectangle.Draw
 
             Figure.Destroy;
             Rectangle := TRectangle.Create;
             Rectangle.Draw;          // calls TRectangle.Draw
             Rectangle.Destroy;
     end;

Méthodes virtuelles et dynamiques

Pour rendre une méthode virtuelle ou dynamique, incluez la directive virtual ou dynamic dans sa déclaration. Les méthodes dynamiques et virtuelles, à la différence des méthodes statiques, peuvent être redéfinies dans les classes descendantes. Quand une méthode redéfinie est appelée, le type réel (à l'exécution) de la classe ou de l'objet utilisé dans l'appel de la méthode, et non pas le type déclaré de la variable, détermine l'implémentation à activer.

Pour redéfinir une méthode, redéclarez-la avec la directive override. Une déclaration override doit correspondre à la déclaration de l'ancêtre dans l'ordre et le type de ses paramètres, ainsi que dans son type de résultat (le cas échéant).

Dans l'exemple suivant, la méthode Draw déclarée dans TFigure est redéfinie dans deux classes descendantes :

 type
     TFigure = class
       procedure Draw; virtual;
     end;
 
     TRectangle = class(TFigure)
       procedure Draw; override;
     end;
 
     TEllipse = class(TFigure)
       procedure Draw; override;
     end;

Etant donné ces déclarations, le code suivant illustre l'effet de l'appel d'une méthode virtuelle via une variable dont le type réel change à l'exécution :

 var
    Figure: TFigure;
 
    begin
      Figure := TRectangle.Create;
      Figure.Draw;      // calls TRectangle.Draw
      Figure.Destroy;
      Figure := TEllipse.Create;
      Figure.Draw;      // calls TEllipse.Draw
      Figure.Destroy;
    end;

Seules les méthodes virtuelles et dynamiques peuvent être redéfinies. Par contre, toutes les méthodes peuvent être surchargées ; voir Surcharge des méthodes.

Méthodes finales

Le compilateur Delphi prend aussi en charge le concept de méthodes virtuelles et dynamiques finales. Les déclarations des méthodes finales ont la forme suivante :

function|procedure FunctionName; virtual|dynamic; final; 

Ici, la syntaxe virtual|dynamic (deux mots clé et le symbole | entre les mots clés) est utilisée pour spécifier qu'un seul des mots clés virtual ou dynamic doit être utilisé. Seul le mot clé virtual ou dynamic n'a de sens; le symbole de tuyau lui-même doit être supprimé.

Seul le mot clé final est appliqué à une méthode virtuelle ou dynamique, aucune classe descendante ne peut redéfinir cette methode. L'usage du mot clé final est une décision de conception importante qui permet de définir l'utilisation de la classe. Il peut aussi donner au compilateur des conseils lui permettant d'optimiser le code produit.

Remarque : Les mots clés virtual ou dynamic doivent être écrits avant le mot clé final.

Exemple

type
  Base = class
    procedure TestProcedure; virtual;
    procedure TestFinalProcedure; virtual; final;
  end;

  Derived = class(Base)
    procedure TestProcedure; override;
       //Ill-formed: E2352 Cannot override a final method
    procedure TestFinalProcedure; override;
  end;

Comparaison des méthodes virtuelles et des méthodes dynamiques

Dans Delphi pour Win32, d'un point de vue sémantique, les méthodes virtuelles et les méthodes dynamiques sont équivalentes. Toutefois, elles diffèrent dans l'implémentation de la répartition de l'appel de méthode à l'exécution : les méthodes virtuelles optimisent la rapidité alors que les méthodes dynamiques optimisent la taille du code.

En général, les méthodes virtuelles constituent la manière la plus efficace d'implémenter un comportement polymorphique. Les méthodes dynamiques sont utiles quand une classe de base déclare de nombreuses méthodes pouvant être redéfinies qui sont héritées par de nombreuses classes descendantes d'une application, mais rarement redéfinies.

Remarque : N'utilisez les méthodes dynamiques que s'il existe un avantage clair et visible de le faire. Utilisez habituellement les méthodes virtuelles.

Redéfinition ou dissimulation

Si une déclaration de méthode spécifie le même identificateur de méthode et la même signature de paramètre qu'une méthode héritée, sans spécifier override, la nouvelle déclaration masque ou dissimule simplement la méthode héritée sans la redéfinir. Les deux méthodes existent alors dans la classe descendante, où le nom de méthode est lié statiquement. Par exemple :

 type
    T1 = class(TObject)
       procedure Act; virtual;
    end;
 
    T2 = class(T1)
       procedure Act;   // Act is redeclared, but not overridden
    end;
 
 var
    SomeObject: T1;
 
 begin
    SomeObject := T2.Create;
    SomeObject.Act;    // calls T1.Act
 end;

Reintroduce

La directive reintroduce supprime les avertissements du compilateur informant qu'une méthode virtuelle précédemment déclarée est masquée. Par exemple :

 procedure DoSomething; reintroduce; // The ancestor class also 
                                     // has a DoSomething method

Utilisez reintroduce quand vous voulez masquer une méthode virtuelle héritée par une nouvelle méthode.

Méthodes abstraites

Une méthode abstraite est une méthode virtuelle ou dynamique n'ayant pas d'implémentation dans la classe où elle est déclarée. Son implémentation est déléguée à une classe descendante. Les méthodes abstraites doivent être déclarées en spécifiant la directive abstract après virtual ou dynamic. Par exemple :

 procedure DoSomething; virtual; abstract;

Vous ne pouvez appeler une méthode abstraite que dans une classe ou une instance d'une classe dans laquelle la méthode a été redéfinie.

Méthodes de classe

La plupart des méthodes sont appelées méthodes d'instance, car elles opèrent sur une instance individuelle d'un objet. Une méthode de classe est une méthode (autre qu'un constructeur) qui agit sur des classes et pas sur des objets. Il existe deux types de méthodes de classe : les méthodes de classe ordinaires et les méthodes statiques de classe.

Méthodes de classe ordinaires

La définition d'une méthode de classe doit commencer par le mot réservé class. Par exemple :

 type
   TFigure = class
   public
      class function Supports(Operation: string): Boolean; virtual;
      class procedure GetInfo(var Info: TFigureInfo); virtual;
      ...
   end;

La déclaration de définition d'une méthode de classe doit aussi commencer par class. Par exemple :

 class procedure TFigure.GetInfo(var Info: TFigureInfo);
 begin
     ...
 end;

Dans la déclaration de définition d'une méthode de classe, l'identificateur Self représente la classe où la méthode est appelée (ce peut être un descendant de la classe dans laquelle elle est définie). Si la méthode est appelée dans la classe C, alors Self est de type class of C. Vous ne pouvez donc pas utiliser Self pour accéder à des champs d'instance, des propriétés d'instance et des méthodes normales (objet). Vous pouvez utiliser Self pour appeler les constructeurs ou d'autres méthodes de classe, ou pour accéder aux propriétés de classe et aux champs de classe.

Une méthode de classe peut être appelée via une référence de classe ou une référence d'objet. Quand elle est appelée via une référence d'objet, la classe de l'objet devient la valeur de Self.

Méthodes statiques de classe

Comme les méthodes de classe, les méthodes statiques de classe peuvent être accessibles sans une référence d'objet. A la différence des méthodes de classe ordinaires, les méthodes statiques de classe n'ont pas de paramètre Self. Elles ne peuvent pas accéder aux membres d'instance. Elles peuvent toujours accéder aux champs de classe, aux propriétés de classe et aux méthodes de classe. En outre, contrairement aux méthodes de classe, les méthodes statiques de classe ne peuvent pas être déclarées virtual.

Pour rendre une méthode de classe statique, ajoutez le mot static à leur déclaration, par exemple :

 type
    TMyClass = class
      strict private
        class var
          FX: Integer;
 
      strict protected
        // Note: Accessors for class properties
        // must be declared class static.
        class function GetX: Integer; static;
        class procedure SetX(val: Integer); static;
 
      public
        class property X: Integer read GetX write SetX;
        class procedure StatProc(s: String); static;
    end;

Comme pour une méthode de classe, vous pouvez appeler une méthode statique de classe par l'intermédiaire du type de la classe (par exemple, sans référence d'objet), comme suit :

 TMyClass.X := 17;
 TMyClass.StatProc('Hello');

Surcharge des méthodes

Une méthode peut être redéclarée en utilisant la directive overload. Dans ce cas, si la méthode redéclarée a une signature de paramètre différente de celle de son ancêtre, elle surcharge la méthode héritée sans la cacher. L'appel de la méthode dans une classe descendante active l'implémentation qui correspond aux paramètres de l'appel.

Si vous surchargez une méthode virtuelle, utilisez la directive reintroduce quand vous la redéclarez dans les classes descendantes. Par exemple :

 type
   T1 = class(TObject)
     procedure Test(I: Integer); overload; virtual;
   end;
 
   T2 = class(T1)
     procedure Test(S: string); reintroduce; overload;
   end;
   ...
 
 SomeObject := T2.Create;
 SomeObject.Test('Hello!');       // calls T2.Test
 SomeObject.Test(7);              // calls T1.Test

Dans une classe, vous ne pouvez pas publier plusieurs méthodes surchargées portant le même nom. La maintenance des informations de type à l'exécution requiert un nom unique pour chaque membre publié :

 type
     TSomeClass = class
       published
         function Func(P: Integer): Integer;
         function Func(P: Boolean): Integer;   // error
           ...

Les méthodes qui servent de spécificateurs de lecture ou d'écriture de propriétés ne peuvent pas être surchargées.

L'implémentation d'une méthode surchargée doit répéter la liste des paramètres depuis la déclaration de classe. Pour davantage d'informations sur la surcharge, voir Surcharge de procédures et de fonctions dans Procédures et fonctions (Delphi).

Constructeurs

Un constructeur est une méthode spéciale qui crée et initialise des instances d'objet. La déclaration d'un constructeur ressemble à celle d'une procédure, sauf qu'elle commence par le mot constructor. Exemples :

 constructor Create;
 constructor Create(AOwner: TComponent);

Les constructeurs doivent utiliser la convention d'appel register par défaut. Bien que la déclaration ne spécifie pas de valeur de retour, un constructeur renvoie une référence à l'objet qu'il a créé ou qui est appelé.

Une classe peut avoir plusieurs constructeurs mais doit en avoir au moins un. L'habitude veut qu'on appelle le constructeur Create.

Pour créer un objet, appelez la méthode constructeur sur un type classe. Par exemple :

 MyObject := TMyClass.Create;

Cette instruction alloue le stockage pour le nouvel objet, définit la valeur de tous les champs ordinaux sur zéro, assigne nil à tous les champs de type pointeur ou de type classe, et une chaîne vide à tous les champs chaîne. Les autres actions spécifiées dans l'implémentation du constructeur sont effectuées ensuite. Généralement, les objets sont initialisés en fonction des valeurs transmises comme paramètres au constructeur. Enfin, le constructeur renvoie une référence à l'objet qui vient d'être créé et initialisé. Le type de la valeur de retour est le même que le type classe spécifié dans l'appel du constructeur.

Si une exception est déclenchée lors de l'exécution d'un constructeur invoqué sur une référence de classe, le destructeur Destroy est appelé automatiquement pour détruire l'objet inachevé.

Quand un constructeur est appelé en utilisant une référence d'objet (au lieu d'une référence de classe), il ne crée pas d'objet. Le constructeur agit à la place sur l'objet spécifié, en n'exécutant que les instructions de l'implémentation du constructeur et renvoie ensuite une référence à l'objet. Un constructeur est typiquement invoqué sur une référence d'objet en conjonction avec le mot réservé inherited afin d'exécuter un constructeur hérité.

Voici un exemple du type classe et de son constructeur :

  type
    TShape = class(TGraphicControl)
      private
        FPen: TPen;
        FBrush: TBrush;
        procedure PenChanged(Sender: TObject);
        procedure BrushChanged(Sender: TObject);
      public
        constructor Create(Owner: TComponent); override;
        destructor Destroy; override;
        ...
    end;
 
 constructor TShape.Create(Owner: TComponent);
 begin
     inherited Create(Owner);     // Initialize inherited parts
     Width := 65;          // Change inherited properties
     Height := 65;
     FPen := TPen.Create;  // Initialize new fields
     FPen.OnChange := PenChanged;
     FBrush := TBrush.Create;
     FBrush.OnChange := BrushChanged;
 end;

Généralement, la première action d'un constructeur est d'appeler le constructeur hérité afin d'initialiser les champs hérités de l'objet. Le constructeur initialise ensuite les champs introduits dans la classe descendante. Comme un constructeur efface toujours le stockage alloué à un nouvel objet, tous les champs contiennent au départ zéro (pour les types ordinaux), nil (types pointeur et classe), chaîne vide (types chaîne) ou Unassigned (variants). Il n'est donc pas nécessaire que l'implémentation du constructeur initialise les champs sauf ceux devant contenir une valeur non nulle ou non vide.

Quand il est invoqué par le biais d'un identificateur de type classe, un constructeur déclaré comme virtual est équivalent à un constructeur statique. Quand ils sont combinés avec des types référence de classe, les constructeurs virtuels permettent une construction polymorphique des objets, c'est-à-dire la construction d'objets dont le type est inconnu à la compilation. (Voir Références de classes.)

Destructeurs

Un destructeur est une méthode spéciale qui détruit l'objet à l'endroit de son appel et libère sa mémoire. La déclaration d'un destructeur ressemble à celle d'une procédure, mais elle commence par le mot destructor. Exemple :

 destructor SpecialDestructor(SaveData: Boolean);
 destructor Destroy; override;

Les destructeurs sur Win32 doivent utiliser la convention d'appel register par défaut. Même si une classe peut avoir plusieurs destructeurs, il est conseillé que chaque classe redéfinisse la méthode Destroy héritée et ne déclare pas d'autres destructeurs.

Pour appeler un destructeur, vous devez référencer une instance d'objet. Par exemple :

 MyObject.Destroy;

Lors de l'appel d'un destructeur, les actions spécifiées dans l'implémentation du destructeur sont d'abord exécutées. Typiquement, cela consiste à détruire les objets incorporés et à libérer les ressources allouées par l'objet. Ensuite, le stockage alloué à l'objet est libéré.

Voici un exemple d'implémentation d'un destructeur :

 destructor TShape.Destroy;
 begin
     FBrush.Free;
     FPen.Free;
     inherited Destroy;
 end;

Typiquement, la dernière action de l'implémentation d'un destructeur est l'appel du destructeur hérité afin de détruire les champs hérités de l'objet.

Quand une exception est déclenchée lors de la création d'un objet, Destroy est appelée automatiquement afin de libérer l'objet inachevé. Cela signifie que Destroy doit être préparé à libérer des objets partiellement construits. Comme un constructeur définit les champs d'un nouvel objet sur zéro ou sur des valeurs vides avant d'effectuer d'autres actions, les champs de type classe ou pointeur d'un objet partiellement construit sont toujours définis sur nil. Un destructeur doit donc tester les valeurs nil avant d'agir sur des champs de type classe ou pointeur. L'appel de la méthode Free (définie dans TObject) au lieu de Destroy, permet de tester facilement les valeurs nil avant de détruire un objet.

Constructeurs de classes

Un constructeur de classe est une méthode de classe spéciale qui n'est pas accessible aux développeurs. Les appels des constructeurs de classes sont automatiquement insérés par le compilateur dans la section d'initialisation de l'unité où la classe est définie. Normalement, les constructeurs de classes sont utilisés pour initialiser les champs statiques de la classe ou effectuer un type d'initialisation, nécessaire avant que la classe ou toute instance de classe puisse fonctionner correctement. Bien que le même résultat puisse être obtenu en plaçant le code d'initialisation de la classe dans la section initialization, les constructeurs de classes ont l'avantage d'aider le compilateur à déterminer les classes qui doivent être incluses dans le fichier binaire final et celles qui doivent être retirées.

L'exemple suivant illustre la façon habituelle d'initialiser les champs de classes :

 type
   TBox = class
   private
     class var FList: TList<Integer>;
   end;
 
 implementation
 
 initialization
   { Initialize the static FList member }
   TBox.FList := TList<Integer>.Create();
 
 end.

Cette méthode a un gros désavantage : même si une application peut inclure l'unité dans laquelle TBox est déclaré, elle ne peut jamais réellement utiliser la classe TBox. Dans l'exemple en cours, la classe TBox est incluse dans le binaire résultant, car elle est référencée dans la section initialization. Pour alléger ce problème, considérons l'utilisation des constructeurs de classes :

 type
   TBox = class
   private
     class var FList: TList<Integer>;
     class constructor Create;
   end;
 
 implementation
 
 class constructor TBox.Create;
 begin
   { Initialize the static FList member }
   FList := TList<Integer>.Create();
 end;
 
 end.

Dans ce cas, le compilateur vérifie si TBox est réellement utilisé quelque part dans l'application, et en cas d'utilisation, un appel au constructeur de classe est ajouté automatiquement à la section initialization de l'unité.

Remarque : Même si le compilateur fait attention à l'ordre d'initialisation des classes, l'ordre peut devenir aléatoire dans certains scénarios complexes. Cela se produit quand le constructeur d'une classe dépend de l'état d'une autre classe qui, à son tour, dépend de la première classe.

Remarque : Le constructeur de classe relatif à un enregistrement ou une classe générique peut s'exécuter plusieurs fois. Le nombre exact de fois que le constructeur de classe est exécuté dans ce cas dépend du nombre de versions spécialisées du type générique. Par exemple, le constructeur de classe pour une classe TList<String> spécialisée peut s'exécuter plusieurs fois dans la même application.

Destructeurs de classes

Les destructeurs de classes sont l'opposé des constructeurs de classes, dans le fait qu'ils réalisent la finalisation de la classe. Les destructeurs de classes présentent les mêmes avantages que les constructeurs de classes, à l'exception des fonctions de finalisation.

L'exemple suivant est construit sur l'exemple présenté dans les constructeurs de classes et il introduit la routine de finalisation :

 type
   TBox = class
   private
     class var FList: TList<Integer>;
     class constructor Create;
     class destructor Destroy;
   end;
 
 implementation
 
 class constructor TBox.Create;
 begin
   { Initialize the static FList member }
   FList := TList<Integer>.Create();
 end;
 
 class destructor TBox.Destroy;
 begin
   { Finalize the static FList member }
   FList.Free;
 end;
 
 end.

Remarque : Le destructeur de classe relatif à un enregistrement ou une classe générique peut s'exécuter plusieurs fois. Le nombre exact de fois que le destructeur de classe est exécuté dans ce cas dépend du nombre de versions spécialisées du type générique. Par exemple, le destructeur de classe pour une classe TList<String> spécialisée peut s'exécuter plusieurs fois dans la même application.

Méthodes de messages

Les méthodes de messages implémentent des réponses à des messages répartis dynamiquement. La syntaxe des méthodes de messages est supportée sur toutes les plates-formes. La VCL utilise des méthodes de messages pour répondre aux messages Windows.

Une méthode de message est créée en incluant la directive message dans une déclaration de méthode, suivie d'une constante entière comprise entre 1 et 49151 qui spécifie l'identificateur du message. Pour les méthodes de messages des contrôles VCL, la constante entière peut être l'un des identificateurs de messages Win32, avec les types d'enregistrements correspondants, dans l'unité Messages. Une méthode de message doit être une procédure qui n'attend qu'un seul paramètre var.

Par exemple :

 type
     TTextBox = class(TCustomControl)
       private
        procedure WMChar(var Message: TWMChar); message WM_CHAR;
        ...
     end;

Une méthode de message n'a pas besoin d'inclure la directive override pour redéfinir une méthode de message héritée. En fait, il n'est pas nécessaire de spécifier le même nom de méthode ou type de paramètre que la méthode qu'elle redéfinit. L'identificateur du message détermine seul les messages auxquels la méthode répond et s'il s'agit d'une redéfinition.

Implémentation des méthodes de messages

L'implémentation d'une méthode de message peut appeler la méthode de message héritée, comme dans l'exemple suivant :

 procedure TTextBox.WMChar(var Message: TWMChar);
 begin
    if Message.CharCode = Ord(#13) then
       ProcessEnter
    else
       inherited;
 end;

L'instruction inherited effectue une recherche arrière dans la hiérarchie des classes et invoque la première méthode de message ayant le même identificateur que la méthode en cours, et lui transmet automatiquement l'enregistrement message. Si aucune classe ancêtre n'implémente de méthode de message pour l'identificateur spécifié, inherited appelle la méthode DefaultHandler définie initialement dans TObject.

L'implémentation de DefaultHandler dans TObject rend simplement le contrôle sans rien faire. En redéfinissant DefaultHandler, une classe peut implémenter sa propre gestion par défaut des messages. Sous Win32, la méthode DefaultHandler pour les contrôles appelle DefWindowProc de l'API Win32.

Répartition des messages

Les gestionnaires de messages sont rarement appelés directement. A la place, les messages sont répartis sur un objet en utilisant la méthode Dispatch héritée de TObject :

 procedure Dispatch(var Message);

Le paramètre Message transmis à Dispatch doit être un enregistrement dont la première entrée est un champ de type Word contenant un identificateur de message.

Dispatch effectue une recherche arrière dans la hiérarchie des classes, en commençant par la classe de l'objet où elle est appelée, et invoque la première méthode de message pour l'identificateur qui lui a été transmis. Si aucune méthode de message n'est trouvée pour l'identificateur donné, Dispatch appelle DefaultHandler.

Voir aussi