Propriétés (Object Pascal)

De Appmethod Topics
Aller à : navigation, rechercher

Remonter à Classes et objets - Index


Cette rubrique traite les sujets suivants :

  • Accès aux propriétés
  • Propriétés tableau
  • Spécificateurs d'indice
  • Spécificateurs de stockage
  • Redéclarations et redéfinitions de propriétés
  • Propriétés de classes

A propos des propriétés

Comme un champ, une propriété définit un attribut d'un objet. Mais alors qu'un champ n'est rien de plus qu'un emplacement de stockage dont le contenu peut être consulté et modifié, une propriété associe des actions spécifiques à la lecture et la modification de ses données. Les propriétés proposent un moyen de contrôler l'accès aux attributs d'un objet et permettent le calcul des attributs.

La déclaration d'une propriété spécifie son nom et son type, et contient au moins un spécificateur d'accès. Une déclaration de propriété a la syntaxe suivante :

property propertyName[indexes]: type index integerConstant specifiers;

where

  • propertyName est un identificateur correct.
  • [indexes] est facultatif, c'est une suite de déclarations de paramètre séparées par des point-virgules. Chaque déclaration de paramètre est de la forme identificateur1, ..., identificateurn: type. Pour plus d'informations, voir la section Propriétés tableau plus loin.
  • type doit être un identificateur de type prédéfini ou déclaré précédemment. C'est-à-dire que les déclarations de propriétés comme property Num: 0..9 ... sont incorrectes.
  • la clause index integerConstant est facultative. Pour plus d'informations, voir la section Spécificateurs d'indice plus loin.
  • specifiers est une suite de spécificateurs read, write, stored, default (ou nodefault) et implements. Chaque déclaration de propriété doit comporter au moins un spécificateur read ou write.

Les propriétés sont définies par leurs spécificateurs d'accès. A la différence des champs, les propriétés ne peuvent être transmises comme paramètre var. De même, il n'est pas possible d'appliquer l'opérateur @ à une propriété. En effet, une propriété n'existe pas nécessairement en mémoire. Elle peut, par exemple, avoir une méthode read qui obtient une valeur d'une base de données ou génère une valeur aléatoire.

Accès aux propriétés

Chaque propriété a un spécificateur read, un spécificateur write ou les deux. Ce sont les spécificateurs d'accès. Ils ont la forme suivante :

read fieldOrMethod

write fieldOrMethod

fieldOrMethod est le nom du champ ou de la méthode déclaré dans la même classe ou dans une classe ancêtre.

  • Si fieldOrMethod est déclaré dans la même classe, il doit apparaître avant la déclaration de la propriété. S'il est déclaré dans une classe ancêtre, il doit être visible dans le descendant ; ce ne peut donc pas être un champ ou une méthode privé d'une classe ancêtre déclarée dans une autre unité.
  • Si fieldOrMethod est un champ, il doit être de même type que la propriété.
  • Si fieldOrMethod est une méthode, elle ne peut être dynamique et, si elle est virtuelle, elle ne peut être surchargée. En outre, les méthodes d'accès pour une propriété publiée doivent utiliser la convention d'appel par défaut register.
  • Dans un spécificateur read, si fieldOrMethod est une méthode, ce doit être une fonction sans paramètre renvoyant une valeur de même type que celui de la propriété. Une exception est une méthode d'accès pour une propriété indicée ou une propriété tableau.
  • Dans un spécificateur write, si fieldOrMethod est une méthode, ce doit être une procédure prenant un seul paramètre valeur ou const du même type que la propriété (ou plus, s'il s'agit d'une propriété tableau ou d'une propriété indicée).

Par exemple, soit ces déclarations :

property Color: TColor read GetColor write SetColor;

la méthode GetColor doit être déclarée comme suit :

function GetColor: TColor;

et la méthode SetColor doit utiliser l'une des déclarations suivantes :


procedure SetColor(Value: TColor);
procedure SetColor(const Value: TColor);

Le nom du paramètre de SetColor n'a pas besoin d'être Value.

Quand une propriété est référencée dans une expression, sa valeur est lue en utilisant le champ ou la méthode indiqué par le spécificateur read. Quand une propriété est référencée dans une instruction d'affectation, sa valeur est modifiée en utilisant le champ ou la méthode indiqué par le spécificateur write.

L'exemple suivant déclare une classe appelée TCompass ayant une propriété publiée Heading. La valeur de Heading est lue via le champ FHeading et écrite via la procédure SetHeading.


type
   THeading = 0..359;
   TCompass = class(TControl)
     private
        FHeading: THeading;
        procedure SetHeading(Value: THeading);
     published
        property Heading: THeading read FHeading write SetHeading;
        ...
    end;

Etant donné cette déclaration, les instructions :


if Compass.Heading = 180 then GoingSouth;
Compass.Heading := 135;

correspondent à :


if Compass.FHeading = 180 then GoingSouth;
Compass.SetHeading(135);

Dans la classe TCompass, aucune action n'est associée à la lecture de la propriété Heading ; l'opération read consiste simplement à récupérer la valeur stockée dans le champ FHeading. Par contre, l'affectation d'une valeur à la propriété Heading se transforme en un appel de la méthode SetHeading qui, probablement, stocke la nouvelle valeur dans le champ FHeading mais effectue également d'autres actions. La méthode SetHeading peut, par exemple, être implémentée de la manière suivante :


procedure TCompass.SetHeading(Value: THeading);
begin
            if FHeading <> Value then
             begin
               FHeading := Value;
               Repaint;    // actualise l'interface utilisateur afin de refléter la nouvelle valeur
             end;
end;

Une propriété dont la déclaration ne contient qu'un spécificateur read est une propriété en lecture seule, et la propriété dont la déclaration ne comporte que le spécificateur write est une propriété en écriture seule. Affecter une valeur à une propriété en lecture seule ou utiliser une propriété en écriture seulement dans une expression sont des erreurs.

Propriétés tableau

Les propriétés tableau sont des propriétés indicées. Elles peuvent représenter les éléments d'une liste, les contrôles enfant d'un contrôle ou les pixels d'un bitmap.

La déclaration d'une propriété tableau inclut une liste de paramètres spécifiant le nom et le type des indices. Par exemple,


property Objects[Index: Integer] : TObject read GetObject write SetObject;
property Pixels[X, Y: Integer] : TColor read GetPixel write SetPixel;
property Values[const Name: string]: string read GetValue write SetValue;

Le format d'une liste de paramètres indice est le même que celui de la liste de paramètres d'une procédure ou d'une fonction, à cette différence que la déclaration des paramètres est placée entre crochets au lieu de parenthèses. A la différence des tableaux qui ne peuvent utiliser que des indices de type scalaire, les propriétés tableau peuvent avoir des indices de type quelconque.

Pour les propriétés tableau, les spécificateurs d'accès doivent indiquer des méthodes et pas des champs. La méthode d'un spécificateur read doit être une fonction ayant les mêmes paramètres, en nombre, en ordre et en type, que ceux indiqués dans la liste de paramètres indice de la propriété et dont le type de résultat est identique au type de la propriété. La méthode indiquée par le spécificateur write doit être une procédure ayant les mêmes paramètres, en nombre, en ordre et en type, que ceux indiqués dans la liste de paramètres indice de la propriété plus un paramètre valeur ou const supplémentaire du même type que la propriété.

Par exemple, les méthodes d'accès des propriétés tableau déclarées dans l'exemple précédent peuvent se déclarer de la manière suivante :


function GetObject(Index: Integer): TObject;
function GetPixel(X, Y: Integer): TColor;
function GetValue(const Name: string): string;
procedure SetObject(Index: Integer; Value: TObject);
procedure SetPixel(X, Y: Integer; Value: TColor);
procedure SetValue(const Name, Value: string);

Vous accédez à une propriété tableau en indiçant l'identificateur de la propriété. Par exemple, les instructions :


if Collection.Objects[0] = nil then Exit;
Canvas.Pixels[10, 20] := clRed;
Params.Values['PATH'] := 'C:\BIN';

correspondent à :


if Collection.GetObject(0) = nil then Exit;
Canvas.SetPixel(10, 20, clRed);
Params.SetValue('PATH', 'C:\BIN');

La définition d'une propriété tableau peut être suivie de la directive default, auquel cas, la propriété tableau devient la propriété par défaut de la classe. Par exemple,


type
   TStringArray = class
    public
       property Strings[Index: Integer] : string ...; default;
          ...
    end;

Si une classe a une propriété par défaut, vous pouvez accéder à cette propriété en utilisant la forme abrégée object[index] équivalente à object.property[index]. Par exemple, étant donné les déclarations précédentes, StringArray.Strings[7] peut s'abréger en StringArray[7]. Une classe peut n'avoir qu'une seule propriété par défaut avec une signature donnée (liste de paramètres en tableau), mais il est possible de surcharger la propriété par défaut. Changer ou masquer la propriété par défaut dans les classes dérivées peut entraîner un comportement inattendu, car le compilateur effectue toujours les liaisons aux propriétés de manière statique.

Spécificateurs d'indice

Les spécificateurs d'indice permettent à plusieurs propriétés de partager la même méthode d'accès alors qu'elles représentent des valeurs différentes. Un spécificateur d'indice est constitué de la directive index suivie d'une constante entière comprise entre -2147483647 et 2147483647. Si une propriété a un spécificateur d'indice, ses spécificateurs read et write doivent indiquer des méthodes et pas des champs. Par exemple,


type
   TRectangle = class
     private
       FCoordinates: array[0..3] of Longint;
       function GetCoordinate(Index: Integer): Longint;
       procedure SetCoordinate(Index: Integer; Value: Longint);
     public
       property Left: Longint index 0 read GetCoordinate write SetCoordinate;
       property Top: Longint index 1 read GetCoordinate write SetCoordinate;
       property Right: Longint index 2 read GetCoordinate write SetCoordinate;
       property Bottom: Longint index 3 read GetCoordinate write SetCoordinate;
       property Coordinates[Index: Integer] : Longint read GetCoordinate write SetCoordinate;
       ...
   end;

La méthode d'accès d'une propriété ayant un spécificateur d'accès doit prendre un paramètre supplémentaire de type Integer. Pour une fonction read, ce doit être le dernier paramètre ; pour une procédure write, ce doit être l'avant-dernier paramètre (avant le paramètre spécifiant la valeur de la propriété). Quand un programme accède à la propriété, la constante entière est automatiquement transmise à la méthode d'accès.

Etant donné les déclarations précédentes, si Rectangle est de type TRectangle, alors :

Rectangle.Right := Rectangle.Left + 100;

correspond à

Rectangle.SetCoordinate(2, Rectangle.GetCoordinate(0) + 100);

Spécificateurs de stockage

Les directives facultatives stored, default et nodefault sont appelées des spécificateurs de stockage. Sans effet sur le comportement du programme, les spécificateurs de stockage indiquent si on enregistre ou non la valeur des propriétés publiées dans les fichiers fiche.

La directive stored doit être suivie par True, False, le nom d'un champ Boolean ou par le nom d'une méthode sans paramètre qui renvoie une valeur Boolean. Par exemple,

property Name : TComponentName read FName write SetName stored False;

Si la propriété n'a pas de directive stored, elle est traitée comme si stored True était spécifié.

La directive default doit être suivie par une constante du même type que la propriété. Par exemple,

property Tag: Longint read FTag write FTag default 0;

Pour redéfinir une valeur default héritée sans en spécifier de nouvelle valeur, utilisez la directive nodefault. Les directives default et nodefault ne sont gérées que pour les types scalaires et pour les types ensemble dont les bornes inférieure et supérieure du type de base de l'ensemble sont des valeurs scalaires comprises entre 0 et 31 ; si une telle propriété est déclarée sans default ni nodefault, elle est traitée comme si nodefault était spécifié. Pour les réels, les pointeurs et les chaînes, il y a une valeur par défaut implicite de 0, nil et '' (la chaîne vide), respectivement.

Remarque :  Vous ne pouvez pas utiliser la valeur scalaire 2147483648 comme valeur par défaut. Cette valeur est utilisée de manière interne pour représenter nodefault.

Quand on enregistre l'état d'un composant, les spécificateurs de stockage des propriétés publiées du composant sont vérifiés. Si la valeur en cours de la propriété est différente de sa valeur par défaut (ou s'il n'y a pas de valeur par défaut) et si le spécificateur stored a la valeur True, alors la valeur de la propriété est enregistrée. Sinon, la valeur de la propriété n'est pas enregistrée.

Remarque :  Les valeurs des propriétés ne sont pas automatiquement initialisées à la valeur par défaut. C'est-à-dire que la directive default agit uniquement lorsque les valeurs des propriétés sont enregistrées dans le fichier fiche, mais pas sur la valeur initiale de l'instance nouvellement créée d'une propriété.

Les spécificateurs de stockage ne sont pas gérés pour les propriétés tableau. La directive default a une signification différente quand elle est utilisée dans une déclaration de propriété tableau. Voir Propriétés tableau, ci-avant.

Redéclarations et redéfinitions de propriétés

Une déclaration de propriété qui ne spécifie pas de type est appelée une redéfinition de propriété. La redéfinition de propriété permet de modifier la visibilité ou les spécificateurs hérités d'une propriété. La redéfinition la plus simple est constituée uniquement du mot réservé property suivi de l'identificateur de la propriété héritée ; cette forme est utilisée pour changer la visibilité d'une propriété. Si, par exemple, une classe ancêtre déclare une propriété comme protégée, une classe dérivée peut la redéclarer dans la section publique ou publiée de la classe. La redéfinition de propriété peut comporter les directives read, write,stored, default et nodefault ; ces directives redéfinissent la directive correspondante héritée. Une directive redéfinie peut remplacer un spécificateur d'accès hérité, ajouter un spécificateur manquant ou augmenter la visibilité d'une propriété. Elle ne peut pas supprimer un spécificateur d'accès ou réduire la visibilité d'une propriété. Une redéfinition peut comporter la directive implements qui s'ajoute à la liste des interfaces implémentées sans en supprimer d'interfaces héritées.

Les déclarations suivantes illustrent la redéfinition de propriété :


type
   TAncestor = class
       ...
     protected
       property Size: Integer read FSize;
       property Text: string read GetText write SetText;
       property Color: TColor read FColor write SetColor stored False;
       ...
   end;

type

   TDerived = class(TAncestor)
       ...
     protected
       property Size write SetSize;
     published
       property Text;
       property Color stored True default clBlue;
       ...
   end;

La redéfinition de Size ajoute un spécificateur write afin de permettre la modification de la propriété. La redéfinition des propriétés Text et Color fait passer la visibilité des propriétés de protégée à publiée. La redéfinition de la propriété Color spécifie également que sa valeur doit être enregistrée quand elle n'est pas clBlue.

Une redéclaration de propriété qui inclut un identificateur de type masque la propriété héritée au lieu de la redéfinir. Cela signifie qu'une nouvelle propriété est créée portant le même nom que celle héritée. Toute déclaration de propriété spécifiant un type doit être une déclaration complète et doit donc comporter au moins un spécificateur d'accès.

Si une propriété est masquée ou redéfinie dans une classe dérivée, le substitut de la propriété est toujours statique. C'est-à-dire que c'est le type déclaré (à la compilation) de la variable utilisée pour identifier un objet qui détermine l'interprétation de ses identificateurs de propriété. Ainsi, une fois le code suivant exécuté, la lecture ou l'affectation d'une valeur à MyObject.Value appelle Method1 ou Method2 même si MyObject contient une instance de TDescendant. Mais vous pouvez transtyper MyObject en TDescendant pour accéder aux propriétés de la classe dérivée et à leurs spécificateurs d'accès.


type
    TAncestor = class
      ...
      property Value : Integer read Method1 write Method2;
    end;

    TDescendant = class(TAncestor)
      ...
      property Value : Integer read Method3 write Method4;
    end;

 var MyObject: TAncestor;
      ...
     MyObject := TDescendant.Create;

Propriétés de classes

Les propriétés de classes sont accessibles sans référence d'objet. Les accesseurs de propriétés de classes doivent être déclarés comme méthodes statiques de classe ou champs classe. Une propriété de classe se déclare à l'aide des mots-clés class property. Les propriétés de classes ne peuvent pas être publiées (published) et ne peut pas avoir de définition de valeur stored ou default.

La déclaration de bloc class var vous permet d'introduire un bloc de champs de classe statique dans une déclaration de classe. Tous les champs déclarés après class var ont des attributs de stockage statique. Un bloc class var se termine par :

  1. Une autre déclaration class var
  2. Une déclaration de procédure ou fonction (c'est-à-dire méthode) (y compris des procédures de classe et des fonctions de classe)
  3. Une déclaration de propriété (y compris des propriétés de classes)
  4. Une déclaration de constructeur ou de destructeur
  5. Un spécificateur de portée de visibilité (public, private, protected, published, strict private et strict protected)

Par exemple :


type
   TMyClass = class
     strict private
       class var         // Remarquez que les champs doivent être déclarés comme champs de classe
          FRed: Integer;
          FGreen: Integer;
          FBlue: Integer;
       public             // termine le bloc class var.
          class property Red: Integer read FRed write FRed;
          class property Green: Integer read FGreen write FGreen;
          class property Blue: Integer read FBlue write FBlue;
   end;

Vous pouvez accéder aux propriétés de classes ci-dessus avec le code :


TMyClass.Red := 0;
TMyClass.Blue := 0;
TMyClass.Green := 0;

Voir aussi