Classes et objets Delphi


Types classes Langage DELPHI

 
Une classe (un type classe), définit une structure composée de champs, de méthodes et de propriétés. Les instances d'un type classe sont appelées des objets. Les champs, méthodes et propriétés d'une classe sont appelés ses composants ou ses membres.
  • Un champ est essentiellement une variable faisant partie d'un objet. Comme les champs d'un enregistrement, un champ de classe représente des éléments de données qui existent dans chaque instance de la classe.
  • Une méthode est une procédure ou une fonction associée à la classe. La plupart des méthodes portent sur des objets, c'est-à-dire sur des instances d'une classe. Certaines méthodes, appelées méthodes de classe, portent sur les types classe mêmes.
  • Une propriété est une interface avec les données associées à un objet (souvent stockées dans un champ). Les propriétés ont des spécificateurs d'accès qui déterminent comment leurs données sont lues et modifiées. Pour le reste d'un programme (hors de l'objet même), une propriété apparaît à bien des points de vue comme un champ.
Les objets sont des blocs de mémoire alloués dynamiquement dont la structure est déterminée par leur type de classe. Chaque objet détient une copie unique de chaque champ défini dans la classe. Par contre, toutes les instances d'une classe partagent les mêmes méthodes. Les objets sont créés et détruits par des méthodes spéciales appelées constructeurs et destructeurs.
 
Une variable de type classe est en fait un pointeur qui référence un objet. Plusieurs variables peuvent donc désigner le même objet. Comme les autres pointeurs, les variables de type classe peuvent contenir la valeur nil. Cependant, il n'est pas nécessaire de déréférencer explicitement une variable de type classe pour accéder à l'objet qu'elle désigne. Par exemple, SomeObject.Size := 100 affecte la valeur 100 à la propriété Size de l'objet référencé par SomeObject ; vous ne devez pas l'écrire sous la forme SomeObject^.Size := 100.

Un type classe doit être déclaré et nommé avant de pouvoir être instancié. Il n'est donc pas possible de définir un type classe dans une déclaration de variable. Déclarez les classes uniquement dans la portée la plus large d'un programme ou d'une unité, mais pas dans une déclaration de procédure ou de fonction. 
La déclaration d'un type classe a la forme suivante :

type 
   className = class [abstract | sealed] (ancestorClass)
      memberList  
   end;
className est un identificateur valide, le mot-clé sealed ou abstract est facultatif, (ancestorClass) est facultatif et memberList déclare les membres (les champs, méthodes et propriétés) de la classe. Si vous omettez (ancestorClass), la nouvelle classe hérite directement de la classe prédéfinie TObject. Si vous précisez (ancestorClass) en laissant vide memberList, vous pouvez omettre le end final. Une déclaration de type classe peut également contenir une liste des interfaces implémentées par la classe ;

Si une classe est marquée sealed, elle ne peut pas être étendue par le biais de l'héritage. Si une classe est marquée abstract, elle ne peut pas être directement instanciée en utilisant le constructeur Create. Il est possible de déclarer toute une classe abstract même si elle ne contient aucune méthode virtuelle abstraite. Une classe ne peut pas être à la fois abstract et sealed
Les méthodes apparaissent dans une déclaration de classe sous la forme d'en-tête de fonction ou de procédure sans le corps. La déclaration de définition de chaque méthode est faite ailleurs dans le programme.

Voici par exemple la déclaration de la classe TMemoryStream de l'unité Classes.

     type TMemoryStream = class(TCustomMemoryStream) 
            private 
              FCapacity: Longint; 
              procedure SetCapacity(NewCapacity: Longint); 
            protected 
              function Realloc(var NewCapacity: Longint): Pointer; virtual; 
              property Capacity: Longint read FCapacity write SetCapacity; 
            public 
              destructor Destroy; override;  
              procedure Clear; 
              procedure LoadFromStream(Stream: TStream); 
              procedure LoadFromFile(const FileName: string); 
              procedure SetSize(NewSize: Longint); override; 
              function Write(const Buffer; Count: Longint): Longint; override; 
          end;

TMemoryStream descend de TCustomMemoryStream (dans l'unité Classes), et hérite de la plupart de ses membres. Mais elle définit, ou surcharge, plusieurs propriétés et méthodes, y compris la méthode destructeur, Destroy. Son constructeur, Create, est hérité sans modification de TObject et n'est donc pas redéclaré. Chaque membre est déclaré comme private, protected ou public (cette classe n'a pas de membre published). Ces termes sont expliqués ci-dessous.
Etant donné cette déclaration, vous pouvez créer une instance de TMemoryStream comme ceci :

 var stream: TMemoryStream; 
     stream := TMemoryStream.Create;
 
Héritage et portée
 
Quand vous déclarez une classe, vous pouvez spécifier son ancêtre immédiat. Par exemple,
type TSomeControl = class(TControl);

déclare une classe appelée TSomeControl qui descend (dérive) de TControl. Un type classe hérite automatiquement de tous les membres de son ancêtre immédiat. Chaque classe peut déclarer de nouveaux membres et redéfinir les membres hérités. Par contre, une classe ne peut supprimer des membres définis dans son ancêtre. Ainsi TSomeControl contient tous les membres définis dans TControl et dans chacun des ancêtres de TControl. 
La portée de l'identificateur d'un membre commence à l'endroit où le membre est déclaré et se poursuit jusqu'à la fin de la déclaration de la classe et s'étend à tous les descendants de la classe et les blocs de toutes les méthodes définies dans la classe et ses descendants.

TObject et TClass

La classe TObject, déclarée dans l'unité System, est l'ancêtre ultime de toutes les autres classes. TObject définit seulement quelques méthodes, dont un constructeur et un destructeur de base. Outre la classe TObject, l'unité System déclare le type référence de classe TClass : 

TClass = class of TObject;
Quand la déclaration d'un type classe ne spécifie pas d'ancêtre, la classe hérite directement de TObject. Donc

type TMyClass = class
      ... 
     end;
est équivalent à

type TMyClass = class(TObject) 
      ... 
     end;
Cette dernière forme est recommandée dans un souci de lisibilité.

Compatibilité des types classe

Un type classe est compatible pour l'affectation avec ses ancêtres. Une variable d'un type classe peut donc référencer une instance de tout type descendant. Par exemple, soit ces déclarations :
 
type 
     TFigure = class(TObject); 
     TRectangle = class(TFigure); 
     TSquare = class(TRectangle); 
var 
     Fig: TFigure;
il est possible d'affecter à la variable Fig des valeurs de type TFigure, TRectangle et TSquare.

Types objet

Le compilateur Delphi Win32 propose une autre syntaxe aux types classe, c'est-à-dire que vous pouvez déclarer des types objet en utilisant la syntaxe :

type objectTypeName = object (ancestorObjectType)
        memberList
     end;

objectTypeName est un identificateur valide, (ancestorObjectType) est facultatif et memberList déclare les champs, méthodes et propriétés. Si (ancestorObjectType) n'est pas spécifié, le nouveau type n'a pas d'ancêtre. Les types objet ne peuvent pas avoir de membres publiés. 
Comme les types objet ne descendent pas de TObject, ils ne disposent pas de constructeurs, de destructeurs ou d'autres méthodes prédéfinies. Vous pouvez créer des instances d'un type objet en utilisant la procédure New et les détruire en utilisant la procédure Dispose. Vous pouvez aussi déclarer tout simplement des variables d'un type objet comme pour un enregistrement.

Les types objet sont uniquement proposés dans un souci de compatibilité ascendante. Leur utilisation n'est pas recommandée sous Win32, et ils ont été complètement désapprouvés dans le compilateur Delphi .NET.

Visibilité des membres de classe
 
Chaque membre d'une classe a un attribut appelé visibilité, indiqué par l'un des mots réservés suivants : private, protected, public, published ou automated. Par exemple,

published property Color: TColor read GetColor write SetColor;

déclare une propriété publiée appelée Color. La visibilité détermine où et comment il est possible d'accéder à un membre : private (privée) représente l'accès minimum, protected (protégée) représente un niveau intermédiaire d'accès, public (publique), published (publiée) et automated (automatisée) représentant l'accès le plus large. 
Si la déclaration d'un membre ne spécifie pas sa visibilité, le membre a la même visibilité que celui qui le précède. Les membres au début de la déclaration d'une classe dont la visibilité n'est pas spécifiée sont par défaut publiés si la classe a été compilée dans l'état {$M+} ou si elle dérive d'une classe compilée à l'état {$M+} ; sinon ces membres sont publics
Dans un souci de lisibilité, il est préférable d'organiser la déclaration d'une classe en fonction de la visibilité, en plaçant tous les membres privés ensemble, suivis de tous les membres protégés, etc. De cette manière, chaque mot réservé spécifiant la visibilité apparaît au maximum une fois et marque le début d'une nouvelle "section" de la déclaration. Une déclaration de classe standard doit donc avoir la forme : 

type 
    TMyClass = class(TControl) 
      private 
         ...  { déclarations privées } 
      protected 
         ...  { déclarations protégées } 
      public 
         ...  { déclarations publiques } 
      published 
         ...  { déclarations publiées } 
    end;

Vous pouvez augmenter la visibilité d'un membre dans une classe dérivée en le redéclarant, mais il n'est pas possible de réduire sa visibilité. Par exemple, une propriété protégée peut être rendue publique dans un descendant mais pas privée. De plus, les membres publiés ne peuvent devenir publics dans une classe dérivée.

Membres privés, protégés et publics

Un membre privé est invisible hors de l'unité ou du programme dans lequel la classe est déclarée. En d'autres termes, une méthode privée ne peut être appelée depuis un autre module, et une propriété ou un champ privé ne peut être lu ou écrit depuis un autre module. En plaçant les déclarations de classes associées dans un même module, vous pouvez donner aux classes un accès aux membres privés sans les rendre plus largement accessibles. 
Un membre protégé est visible partout dans le module où la classe est déclarée et dans toute classe descendante, indépendamment du module où la classe descendante est définie. Une méthode protégée peut être appelée et un champ ou une propriété lu ou écrit, dans la définition de toute méthode appartenant à une classe qui dérive de celle où le membre protégé est déclaré. Généralement, les membres qui ne servent qu'à l'implémentation des classes dérivées sont protégés. 
Un membre public est visible partout où la classe peut être référencée.

Spécificateurs de visibilité stricte

Outre les spécificateurs de visibilité private et protected, le compilateur Delphi prend en charge des paramètres de visibilité supplémentaires avec des contraintes d'accès plus importantes. Il s'agit des visibilités strict private et strict protected. Ces paramètres sont strictement conformes à la spécification CLS (Common Language Specification) .NET, et ils peuvent également être utilisés dans des applications Win32.

Les membres de classes dont la visibilité est privée stricte ne sont accessibles que dans la classe dans laquelle ils sont déclarés. Ils ne sont pas visibles pour les procédures ou fonctions déclarées dans la même unité. Les membres de classes dont la visibilité est protégée stricte sont visibles dans la classe dans laquelle ils sont déclarés et dans toutes les classes dérivées, quel que soit l'endroit où elles sont déclarées. De plus, lorsque des membres d'instance (ceux déclarés sans les mots-clés class ou class var) sont déclarés strict private ou strict protected, ils ne sont pas accessibles en dehors de l'instance d'une classe dans laquelle ils apparaissent. Une instance d'une classe ne peut pas accéder aux membres d'instance strict private ou strict protected d'autres instances de la même classe.

Le spécificateur de visibilité privée traditionnel de Delphi correspond à la visibilité assemblage du CLR. Le spécificateur de visibilité protégée de Delphi correspond à la visibilité assemblage ou famille du CLR.

Remarque: Le mot strict est traité comme une directive dans le contexte d'une déclaration de classe. Dans une déclaration de classe, vous ne pouvez pas déclarer un membre nommé 'strict', mais vous pouvez l'utiliser à l'extérieur d'une déclaration de classe.

Membres publiés

Les membres publiés ont la même visibilité que les membres publics. La différence est que des informations de type à l'exécution (RTTI) sont générées pour les membres publiés. Ces informations permettent à une application d'interroger dynamiquement les champs et les propriétés d'un objet et de localiser ses méthodes. Les RTTI sont utilisés pour accéder aux valeurs des propriétés lors de la lecture ou de l'enregistrement des fichiers fiche, pour afficher les propriétés dans l'inspecteur de propriété et pour associer des méthodes spécifiques (appelées gestionnaires d'événements) à des propriétés spécifiques (appelées événements).

Les propriétés publiées sont limitées à certains types de données. Les types scalaire, chaîne, classe, interface, variant et pointeur de méthode peuvent être publiés. Tout comme les types ensemble, dans la mesure où les bornes inférieure et supérieure du type de base ont des valeurs scalaires comprises entre 0 et 31. En d'autres termes, l'ensemble doit tenir dans un octet, un mot ou un double mot. Tous les types réels, à l'exception de Real48, peuvent être publiés. Les propriétés d'un type tableau (différentes des propriétés tableau, traitées ci-dessous) ne peuvent être publiées. 
Certaines propriétés, bien que publiables, ne sont pas totalement prises en charge par le système de flux. Il s'agit des propriétés de types enregistrement, des propriétés tableau de tous les types publiables, et des propriétés des types énumérés qui incluent des valeurs anonymes. Si vous publiez une telle propriété, l'inspecteur d'objets ne l'affichera pas correctement, et la valeur de la propriété ne sera pas conservée quand les objets seront enregistrés sur disque.

Toutes les méthodes sont publiables, mais une classe ne peut publier plusieurs méthodes surchargées portant le même nom. Les champs ne peuvent être publiés que s'ils sont de type classe ou interface. 
Une classe ne peut avoir de membres publiés que si elle est compilée avec l'état {$M+} ou si elle descend d'une classe compilée avec l'état {$M+}. La plupart des classes contenant des membres publiés dérivent de TPersistent qui est compilée avec l'état {$M+}, il est donc rarement nécessaire d'utiliser la directive $M.

Remarque: Les identificateurs contenant des caractères Unicode ne sont pas autorisés dans les sections publiées de classes ou dans des types employés par des membres publiés.

Membres automatisés (Win32 uniquement)

Les membres automatisés ont la même visibilité que les membres publics. La différence est que des informations de type Automation (requises pour les serveurs Automation) sont générées pour les membres automatisés. Les membres automatisés n'apparaissent généralement que dans les classes Win32 et le mot réservé automated a été désapprouvé dans le compilateur .NET. Le mot réservé automated est conservé dans un souci de compatibilité ascendante. La classe TAutoObject de l'unité ComObj n'utilise pas automated
Les restrictions suivantes s'appliquent aux méthodes et propriétés déclarées comme automatisées :
  • Le type de toutes les propriétés, des paramètres de propriété tableau, des paramètres de méthode et des résultats de fonction doivent être automatisables. Les types automatisables sont Byte, Currency, Real, Double, Longint, Integer, Single, Smallint, AnsiString, WideString, TDateTime, Variant, OleVariant, WordBool et tous les types interface.
  • Les déclarations de méthode doivent utiliser la convention d'appel par défaut register. Les méthodes peuvent être virtuelles mais pas dynamiques.
  • Les déclarations de propriété peuvent comporter des spécificateurs d'accès (read ou write) mais les autres spécificateurs (index, stored, default ou nodefault) sont interdits. Les spécificateurs d'accès doivent indiquer un identificateur de méthode utilisant la convention d'appel par défaut register ; les identificateurs de champ ne sont pas autorisés.
  • Les déclarations de propriétés doivent spécifier un type. Il n'est pas permis de redéfinir les propriétés.
La déclaration d'une méthode ou d'une propriété automatisée peut inclure la directive dispid. La spécification d'un numéro d'identification déjà utilisé dans une directive dispid provoque une erreur. 
Sur la plate-forme Win32, cette directive doit être suivie d'une constante entière qui spécifie un numéro d'identification de répartition Automation pour le membre. Sinon, le compilateur assigne automatiquement au membre un numéro d'identification de répartition plus grand que le plus grand numéro d'identification de répartition utilisé par les méthodes et les propriétés de la classe et de ses ancêtres.

Aucun commentaire: