Programmes et unités (Delphi)

De RAD Studio
(Redirigé depuis Programmes et unités)
Aller à : navigation, rechercher

Remonter à Programmes et unités - Index

Cette section décrit la structure globale d'une application Delphi : l'en-tête de programme, la syntaxe de déclaration de l'unité et la clause uses.

  • Diviser de grands programmes en modules qui peuvent être modifiés séparément.
  • Créer des bibliothèques qui peuvent être partagées par plusieurs programmes.
  • Distribuer des bibliothèques à d'autres développeurs sans leur donner le code source.

Structure et syntaxe d'un programme

Une application Delphi exécutable complète se compose de plusieurs modules d'unité, tous reliés par un module de code source unique appelé fichier projet. En programmation Pascal classique, tout le code source, notamment le programme principal, est stocké dans des fichiers .pas. Les outils Embarcadero utilisent l'extension de fichier .dpr pour désigner le module source du programme principal, tandis que le reste du code source réside dans les fichiers unité avec l'extension traditionnelle .pas. Pour construire un projet, le compilateur doit disposer, pour chaque unité, du fichier source projet et, soit un fichier source, soit un fichier unité compilé.

Remarque : Strictement parlant, il n'est pas nécessaire d'utiliser explicitement des unités dans un projet, mais tous les programmes utilisent automatiquement les unités System et SysInit.

Le fichier du code source d'une application Delphi exécutable contient :

  • un en-tête de programme,
  • une clause uses (facultative) et
  • un bloc de déclarations et des instructions exécutables.


Le compilateur, et donc l'EDI, s'attend à trouver ces trois éléments dans un seul fichier projet (.dpr).

L'en-tête du programme

L'en-tête du programme spécifie le nom du programme exécutable. Il est composé du mot réservé program suivi d'un identificateur valide, puis d'un point-virgule. Pour les applications développées à l'aide d'outils Embarcadero, l'identificateur doit correspondre au nom du fichier source du projet.

L'exemple suivant présente le fichier source du projet pour un programme appelé Editor. Comme le nom du programme est appelé Editor, ce fichier projet s'appelle Editor.dpr.

program Editor;

  uses Forms, REAbout, // An "About" box
       REMain;         // Main form

  {$R *.res}

  begin
   Application.Title := 'Text Editor';
   Application.CreateForm(TMainForm, MainForm);
   Application.Run;
  end.

La première ligne contient l'en-tête du programme. La clause uses de cet exemple spécifie une dépendance sur trois unités supplémentaires : Forms, REAbout et REMain. La directive de compilation $R lie le fichier ressource du projet dans le programme. Enfin, le bloc d'instructions placé entre les mots clés begin et end est exécuté à l'exécution du programme. Le fichier projet, comme tous les fichiers source Delphi, se termine par un point (non par un point-virgule).

Les fichiers projet Delphi sont généralement courts car l'essentiel de la logique du programme se trouve dans les fichiers unité. Un fichier projet Delphi contient généralement suffisamment de code pour lancer la fenêtre principale de l'application et démarrer la boucle de traitement des événements. Les fichiers projet sont générés et gérés automatiquement par l'EDI ; il est donc rarement nécessaire de les modifier manuellement.

En Pascal standard, un en-tête de programme peut inclure des paramètres après le nom du programme :

program Calc(input, output);

Le compilateur Delphi de Embarcadero ignore ces paramètres.

Dans RAD Studio, l'en-tête de programme introduit son propre espace de nommage, qui est appelé l'espace de nommage par défaut du projet.

La clause uses d'un programme

La clause uses liste les unités incorporées dans le programme. Ces unités peuvent, à leur tour, avoir leur propre clause uses. Pour de plus amples informations sur la clause uses au sein d'un fichier source d'unité, voir Références d'unité et la clause uses ci-dessous.

La clause uses se compose du mot clé uses, suivi de la liste des unités dont dépend directement le fichier projet, délimitée par des virgules.

Le bloc

Le bloc contient une instruction simple ou structurée qui est exécutée au lancement du programme. Dans la plupart des fichiers programme, le bloc se compose d'une instruction composée (encadrée par les mots réservés begin et end) contenant des instructions qui sont de simples appels de méthodes de l'objet Application du projet. La plupart des projets ont une variable Application globale qui contient une instance de Vcl.Forms.TApplication, Web.WebBroker.TWebApplication ou Vcl.SvcMgr.TServiceApplication. Le bloc peut aussi contenir des déclarations de constantes, types, variables, procédures et de fonctions ; ces déclarations doivent précéder la partie instruction du bloc. Notez que le end qui représente la fin du source d'un programme doit être suivi d'un point (.) :

begin
  .
  .
  .
end.

Syntaxe et structure d'unité

Une unité est constituée de types (notamment les classes), de constantes, de variables et de routines (fonctions et procédures). Chaque unité est définie dans son propre fichier source (.pas).

Un fichier unité commence par un en-tête d'unité, suivi du mot clé interface. Après le mot clé interface, la clause uses spécifie une liste de dépendances d'unités. Enfin, on trouve la section implementation, suivie des sections facultatives initialization et finalization. Un squelette de fichier source d'unité a la forme suivante :

unit Unit1;

interface

uses // List of unit dependencies goes here...
  // Interface section goes here

implementation

uses // List of unit dependencies goes here...

// Implementation of class methods, procedures, and functions goes here...

initialization

// Unit initialization code goes here...

finalization

// Unit finalization code goes here...

end.

L'unité doit se terminer par le mot réservé end suivi d'un point.

L'en-tête d'unité

L'en-tête d'unité spécifie le nom de l'unité. Il est composé du mot réservé unit, suivi d'un identificateur valide puis d'un point-virgule. Pour les applications développées à l'aide d'outils Embarcadero, l'identificateur doit correspondre au nom du fichier unité. Ainsi, l'en-tête d'unité :

unit MainForm;

doit se trouver dans un fichier source appelé MainForm.pas et le fichier contenant l'unité compilée se nomme MainForm.dcu. Les noms d'unités doivent être uniques au sein d'un projet. Même si leurs fichiers unités sont dans des répertoires différents, deux unités portant le même nom ne peuvent être utilisées dans un même programme.

La section interface

La section interface d'une unité commence par le mot réservé interface et se poursuit jusqu'au début de la section implementation. La section interface déclare les constantes, types, variables, procédures et fonctions accessibles aux clients. C'est-à-dire aux unités ou programmes qui souhaitent utiliser les éléments de cette unité. Ces entités sont dites publiques car le code d'autres unités peut y accéder comme si elles étaient déclarées dans l'unité même.

La déclaration interface d'une procédure ou d'une fonction n'inclut que la signature de la routine. C'est-à-dire le nom, les paramètres et le type de retour (pour les fonctions) de la routine. Le bloc contenant le code exécutable de la procédure ou de la fonction suit dans la section implementation. Ainsi les déclarations de procédure ou de fonction de la section interface fonctionnent comme des déclarations forward.

La déclaration interface d'une classe doit inclure les déclarations pour tous les membres de classe : champs, propriétés, procédures et fonctions.

La section interface peut inclure sa propre clause uses, qui doit apparaître immédiatement après le mot clé interface.

La section implementation

La section implementation d'une unité commence par le mot réservé implementation et se poursuit jusqu'au début de la section initialization ou, en l'absence d'une section initialization, jusqu'à la fin de l'unité. La section implementation définit les procédures et fonctions déclarées dans la section interface. Au sein de la section implementation, ces procédures et fonctions peuvent être définies et appelées dans un ordre quelconque. Vous pouvez omettre la liste des paramètres des en-têtes de procédures et fonctions publiques quand vous les définissez dans la section implementation. Si vous incluez une liste de paramètres, elle doit correspondre exactement à la déclaration de la section interface.

Outre les définitions des procédures et fonctions publiques, la section implementation peut déclarer des constantes, des types (notamment les classes), des variables, des procédures et des fonctions qui sont privés pour l'unité. Contrairement à la section interface, les entités déclarées dans la section implementation ne sont pas accessibles aux autres unités.

La section implementation peut inclure sa propre clause uses, qui doit apparaître immédiatement après le mot clé implementation. Les identificateurs déclarés au sein des unités spécifiées dans la section implementation ne peuvent être utilisés que dans la section implementation même. Vous ne pouvez pas faire référence à ces identificateurs dans la section interface.

La section initialization

La section initialization est facultative. Elle commence par le mot réservé initialization et se poursuit jusqu'au début de la section finalization ou, en l'absence d'une section finalization, jusqu'à la fin de l'unité. La section initialization contient des instructions qui sont exécutées, dans l'ordre où elles apparaissent, au démarrage du programme. Si, par exemple, vous avez défini des structures de données devant être initialisées, vous pouvez le faire dans la section initialization.

Pour les unités de la liste uses de la section interface, les sections initialization des unités utilisées par un client sont exécutées dans l'ordre de leur énumération dans la clause uses du client.

L'ancienne syntaxe "begin ... end." fonctionne toujours. Au fond, le mot réservé "begin" peut être utilisé à la place de initialization suivi de zéro à plusieurs instructions d'exécution. Le code utilisant l'ancienne syntaxe "begin ... end." ne peut pas spécifier de section finalization. Dans ce cas, la finalisation est accomplie en fournissant une procédure à la variable ExitProc. Cette méthode n'est pas recommandée pour les nouveaux codes, mais vous pouvez voir cette méthode dans l'ancien code source.

La section finalization

La section facultative finalization ne peut apparaître que dans les unités ayant une section initialization. La section finalization commence par le mot réservé finalization et se poursuit jusqu'à la fin de l'unité. Elle contient des instructions qui sont exécutées lors de l'arrêt du programme principal (à moins que la procédure Halt ne soit utilisée pour terminer le programme). Utilisez la section finalization pour libérer les ressources allouées dans la section initialization.

Les sections finalization sont exécutées dans l'ordre inverse des sections initialization. Si, par exemple, votre application initialise, dans cet ordre, les unités A, B et C ; elle les finalise dans l'ordre C, B et A.

Une fois que le code d'initialisation d'une unité a commencé à s'exécuter, la section finalization correspondante s'exécute obligatoirement à l'arrêt de l'application. La section finalization doit donc être capable de gérer des données dont l'initialisation a été incomplète. En effet, si une erreur d'exécution se produit, il est possible que le code d'initialisation ne soit pas complètement exécuté.

Références d'unité et la clause uses

Une clause uses liste les unités utilisées par le programme, la bibliothèque ou l'unité dans lequel la clause apparaît. Une clause uses peut apparaître dans :

  • le fichier projet d'un programme ou d'une bibliothèque
  • la section interface d'une unité
  • la section implementation d'une unité

La plupart des fichiers projet contiennent une clause uses tout comme la section interface de la plupart des unités. La section implementation d'une unité peut aussi contenir sa propre clause uses.

Les unités System et SysInit sont utilisées automatiquement par toutes les applications et ne peuvent être listées explicitement dans la clause uses. (System implémente les routines pour les E/S de fichiers, la gestion des chaînes, les opérateurs en virgule flottante, les allocations dynamiques de mémoire, et ainsi de suite.) D'autres unités de bibliothèque standard, comme SysUtils, peuvent être explicitement incluses dans la clause uses. Dans la plupart des cas, toutes les unités nécessaires sont placées dans la clause uses par l'EDI quand vous ajoutez ou retirez des unités de votre projet.

Sensibilité à la casse : Dans les déclarations unit et les clauses uses, les noms d'unités doivent avoir la même casse que les noms de fichiers. Dans d'autres contextes (comme les identificateurs qualifiés), les noms d'unités ne sont pas sensibles à la casse. Pour éviter des problèmes avec les références d'unités, faites référence de façon explicite au fichier source de l'unité :

uses MyUnit in "myunit.pas";

Si une telle référence explicite apparaît dans le fichier projet, les autres fichiers source peuvent faire référence à l'unité avec une simple clause uses qui n'a pas besoin de respecter la casse :

uses Myunit;

La syntaxe de la clause uses

Une clause uses est constituée du mot réservé uses, suivi d'un ou de plusieurs noms d'unités séparés par des virgules, et se termine par un point-virgule. Exemples :

uses Forms, Main;

uses
  Forms,
  Main;

uses Windows, Messages, SysUtils, Strings, Classes, Unit2, MyUnit;

Dans la clause uses d'un programme ou d'une bibliothèque, chaque nom d'unité peut être suivi du mot réservé in puis, entre apostrophes, du nom d'un fichier source avec ou sans chemin de répertoire. Les chemins de répertoires peuvent être absolus ou relatifs. Exemples :

uses
  Windows, Messages, SysUtils,
  Strings in 'C:\Classes\Strings.pas', Classes;

Utilisez le mot clé in après un nom d'unité si vous avez besoin de spécifier le fichier source de l'unité. Comme l'EDI suppose que les noms des unités correspondent aux noms des fichiers source dans lesquels elles résident, il n'est généralement pas nécessaire d'utiliser cette option. Il n'est nécessaire d'utiliser in que si l'emplacement du fichier source est ambigu, par exemple si :

  • Vous avez utilisé un fichier source placé dans un répertoire différent de celui du fichier projet, et si ce répertoire n'est pas dans le chemin de recherche du compilateur.
  • Différents répertoires du chemin de recherche du compilateur contiennent des unités portant le même nom.
  • Vous compilez une application console depuis la ligne de commande et le nom (identificateur) attribué à l'unité ne correspond pas à celui de son fichier source.

Le compilateur se base aussi sur la construction in ... pour déterminer les unités qui font partie d'un projet. Seules les unités qui apparaissent dans la clause uses d'un fichier projet (.dpr) suivies de in et d'un nom de fichier sont considérées comme faisant partie du projet ; les autres unités de la clause uses sont utilisées par le projet mais ne lui appartiennent pas. Cette distinction n'a pas d'effet sur la compilation, mais affecte les outils de l'EDI comme le gestionnaire de projets.

Dans la clause uses d'une unité, vous ne pouvez utiliser in pour indiquer au compilateur où se trouve un fichier source. Chaque unité doit se trouver dans le chemin de recherche du compilateur. De plus, les noms d'unités doivent correspondre aux noms de leurs fichiers source.

Références d'unité multiples et indirectes

L'ordre d'énumération des unités dans la clause uses détermine l'ordre de leur initialisation et affecte la manière dont les identificateurs sont trouvés par le compilateur. Si deux unités déclarent une variable, une constante, un type, une procédure ou une fonction portant le même nom, le compilateur utilise l'élément défini dans l'unité listée en dernier dans la clause uses. Pour accéder à l'identificateur de l'autre unité, il faut ajouter un qualificateur : UnitName.Identifier.)

Une clause uses ne doit inclure que les unités utilisées directement par le programme ou l'unité dans lequel la clause apparaît. Par exemple, si l'unité A fait référence à des constantes, types, variables, procédures ou fonctions déclarées dans l'unité B, alors A doit utiliser B explicitement. Si B fait, à son tour, fait référence à des identificateurs de l'unité C, alors A dépend indirectement de C. Dans ce cas, il n'est pas nécessaire d'inclure C dans la clause uses de A, mais le compilateur doit néanmoins pouvoir trouver B et C pour pouvoir traiter A.

L'exemple suivant est une illustration de dépendance indirecte :

program Prog;
uses Unit2;
const a = b;
// ...

unit Unit2;
interface
uses Unit1;
const b = c;
// ...

unit Unit1;
interface
const c = 1;
// ...

Dans cet exemple, Prog dépend directement de Unit2, qui à son tour dépend directement de Unit1. De ce cas, Prog dépend indirectement de Unit1. Comme Unit1 n'apparaît pas dans la clause uses de Prog, les identificateurs déclarés dans Unit1 ne sont pas utilisables dans Prog.

Pour compiler un module client, le compilateur a besoin de trouver toutes les unités dont dépend, directement ou indirectement, le client. Si le code source de ces unités n'a pas été modifié, le compilateur n'a besoin que de leurs fichiers .dcu, et non de leurs fichiers source (.pas).

Quand vous avez apporté des modifications à la section interface d'une unité, les autres unités qui dépendent de celles-ci doivent être recompilées. Mais si la modification n'est faite que dans la section implementation ou dans une autre section d'une unité, il n'est pas nécessaire de recompiler les unités dépendantes. Le compilateur gère automatiquement ces dépendances et ne recompile les unités que lorsque cela est nécessaire.

Références d'unité circulaires

Quand des unités, directement ou pas, se font mutuellement référence, les unités sont dites mutuellement dépendantes. Les dépendances mutuelles sont autorisées tant qu'il n'existe pas de chemins circulaires connectant la clause uses d'une section interface à la clause uses d'une autre. En d'autres termes, partant de la section interface d'une unité, il ne faut pas pouvoir revenir à cette unité en suivant les références des sections interface des autres unités. Pour qu'un pattern de dépendances mutuelles soit valide, chaque chemin de référence circulaire doit aboutir à la clause uses d'au moins une section implementation.

Dans le cas le plus simple de deux unités mutuellement dépendantes, cela signifie que les unités ne peuvent se référencer mutuellement dans la clause uses de leur interface. Ainsi, l'exemple suivant produit une erreur de compilation :

unit Unit1;
interface
uses Unit2;
// ...

unit Unit2;
interface
uses Unit1;
// ...

Par contre, les deux unités peuvent sans problème se référencer mutuellement si l'une des références est déplacée dans la section implementation :

unit Unit1;
interface
uses Unit2;
// ...

unit Unit2;
interface
//...

implementation
uses Unit1;
// ...

Afin de réduire les risques de références circulaires, il est préférable à chaque fois que cela est possible de lister les unités dans la clause uses de l'implémentation. C'est uniquement si des identificateurs d'une autre unité sont utilisés dans la section interface qu'il est nécessaire de lister cette unité dans la clause uses de l'interface.

Voir aussi