Déclarations et instructions (Object Pascal)

De Appmethod Topics
Aller à : navigation, rechercher

Remonter à Eléments syntaxiques fondamentaux - Index


Cette rubrique décrit la syntaxe des déclarations et instructions Object Pascal.

A part la clause uses (et des mots réservés qui comme implementation, délimitent les parties d'une unité), un programme est entièrement constitué de déclarations et d'instructions, qui sont organisées en blocs.

Cette rubrique traite les sujets suivants :

  • Déclarations
  • Instructions simples (par exemple, affectation)
  • Instructions structurées comme les tests conditionnels (par exemple, if-then et case) et les itérations (par exemple, for et while).

Déclarations

Les noms de variables, constantes, types, champs, propriétés, procédures, fonctions, programmes, unités, bibliothèques et packages sont appelés des identificateurs. Les constantes numériques comme 26 057 ne sont pas des identificateurs. Les identificateurs doivent être déclarés avant de pouvoir être utilisés. Seules exceptions à cette règle : quelques types sont prédéfinis, certaines routines et constantes sont reconnues automatiquement par le compilateur, la variable Result quand elle est utilisée dans un bloc de fonction et la variable Self quand elle est utilisée à l'intérieur d'une implémentation de méthode.

Une déclaration définit un identificateur et, si c'est approprié, lui alloue de la mémoire. Par exemple :

var Size: Extended;

déclare une variable appelée Size contenant une valeur Extended (réelle). De même :

function DoThis(X, Y: string): Integer;

déclare une fonction appelée DoThis qui attend deux chaînes en argument et renvoie un entier. Chaque déclaration se termine par un point-virgule. Quand vous déclarez en même temps plusieurs variables, constantes, types ou labels, il suffit de n'écrire qu'une seule fois le mot réservé approprié :

 
 var
   Size: Extended;
   Quantity: Integer;
   Description: string;

La syntaxe et la position d'une déclaration dépendent du type d'identificateur défini. En général, les déclarations ne peuvent être faites qu'au début d'un bloc ou au début des sections interface ou implementation d'une unité (après la clause uses). Les conventions spécifiques à la déclaration de variables, constantes, types, fonctions, etc., sont présentées dans la documentation abordant ces sujets.

Directives de conseil

Les directives de conseil platform, deprecated et library peuvent être ajoutées à toute déclaration. Ces directives produiront des avertissements à la compilation. Les directives de conseil peuvent être appliquées à des déclarations de type, des déclarations de variable, des déclarations d'interface, de structure et de classe, des déclarations de champ dans les enregistrements ou les classes, des déclarations de méthode, de fonction ou de procédure et des déclarations d'unité.

Quand une directive de conseil apparaît dans une déclaration d'unité, cela signifie que le conseil s'applique à tout élément de l'unité. Par exemple, sur Windows l’unité OleAuto.pas de style Windows 3.1 est complètement obsolète. Toute référence à cette unité ou tout symbole de cette unité produit un message de dépréciation.

La directive de conseil platform sur un symbole ou une unité indique que cet élément risque de ne pas exister ou que son implémentation peut varier considérablement sur les différentes plates-formes. La directive de conseil library sur un symbole ou une unité indique que le code risque de ne pas exister ou que l'implémentation peut varier considérablement sur les différentes architectures de bibliothèques.

Les directives platform et library ne spécifient pas la plate-forme ni la bibliothèque. Si votre but consiste à écrire du code indépendant de la plate-forme, vous n’avez pas à savoir à quelle plate-forme un symbole est spécifique ; il suffit que le symbole soit marqué comme spécifique à une certaine plate-forme pour vous informer d’éventuels problèmes de portabilité.

Dans le cas de la déclaration d'une procédure ou d'une fonction, la directive de conseil doit être séparée du reste de la déclaration par un point-virgule. Exemples :

 procedure SomeOldRoutine; stdcall deprecated;
 
 var
   VersionNumber: Real library;
 
 type
   AppError = class(Exception)
      ...
 end platform;

Quand le code source est compilé dans l'état {$HINTS ON} {$WARNINGS ON}, chaque référence à un identificateur déclaré avec l'une de ces directives génère le conseil ou l'avertissement approprié. Utilisez platform pour marquer les éléments spécifiques à un environnement d'exploitation particulier (comme Windows), deprecated pour indiquer qu'un élément est obsolète, ou supporté uniquement pour la compatibilité descendante, et library pour indiquer les dépendances sur une bibliothèque particulière ou un framework de composant.

Le compilateur Object Pascal reconnaît aussi la directive de conseil experimental. Vous pouvez utiliser cette directive pour désigner des unités qui sont dans un état de développement instable. Le compilateur émet un avertissement quand il construit une application qui utilise l'unité.

Pour de plus amples informations à propos de ces directives de conseil, voir Directives d'avertissement des méthodes Object Pascal . Toutes les directives sont listées dans Eléments syntaxiques fondamentaux#Directives.

Instructions

Les instructions définissent des actions algorithmiques au sein d'un programme. Les instructions simples, comme les affectations ou les appels de procédure, peuvent se combiner pour former des boucles, des instructions conditionnelles ou d'autres instructions structurées.

Plusieurs instructions au sein d'un bloc et dans les sections d'initialisation et de finalisation d'une unité sont séparées par des points-virgules.

Instructions simples

Une instruction simple ne contient pas d'autres instructions. Les instructions simples sont les affectations, les appels aux procédures et aux fonctions et les sauts par goto.

Instructions d'affectation

Une instruction d'affectation a la forme :

variable := expression

variable est une référence de variable, incluant une variable, une variable transtypée, un pointeur déréférencé ou un composant d'une variable structurée. L'expression est une expression compatible avec l'affectation (au sein d'un bloc de fonction, la variable peut être remplacée par le nom de la fonction définie. Voir Procédures et fonctions). Le symbole := est parfois appelé l'opérateur d'affectation.

Une instruction d'affectation remplace la valeur en cours de la variable par la valeur de l'expression. Par exemple :

I := 3;

assigne la valeur 3 à la variable I. La référence de variable de la partie gauche de l'affectation peut apparaître dans l'expression à droite. Par exemple :

I := I + 1;

incrémente la valeur de I. Voici d'autres instructions d'affectation :

 X := Y + Z;
 Done := (I >= 1) and (I < 100);
 Hue1 := [Blue, Succ(C)];
 I := Sqr(J) - I  * K;
 Shortint(MyChar) := 122;
 TByteRec(W).Hi := 0;
 MyString[I] := 'A';
 SomeArray[I + 1] := P^;
 TMyObject.SomeProperty := True;

Appels de procédures et de fonctions

Un appel de procédure est constitué du nom d'une procédure (avec ou sans qualificateur) suivi d'une liste de paramètres (si nécessaire). Par exemple :

 PrintHeading;
 Transpose(A, N, M);
 Find(Smith, William);
 Writeln('Hello world!');
 DoSomething();
 Unit1.SomeProcedure;
 TMyObject.SomeMethod(X,Y);

Avec la syntaxe étendue activée ({$X+}), les appels de fonction, comme les appels de procédure, peuvent être traités comme des instructions :

MyFunction(X);

Quand vous utilisez un appel de fonction de cette manière, sa valeur de retour est perdue.

Pour de plus amples informations sur les procédures et les fonctions, voir Procédures et fonctions.

Instructions goto

Une instruction goto, qui a la forme :

goto label

transfère l'exécution du programme à l'instruction marquée par le label spécifié. Pour marquer une instruction, il faut tout d'abord déclarer le label. Vous devez ensuite faire précéder l'instruction à marquer par le label et deux-points :

label: statement

Déclarez les labels de la manière suivante :

label label;

Vous pouvez déclarer plusieurs labels à la fois :

label label1, ..., labeln;

Un label peut être un identificateur valide quelconque ou un nombre compris entre 0 et 4294967295.

La déclaration label, l'instruction marquée et l'instruction goto doivent appartenir au même bloc. Voir Blocs et portée, ci-dessous. Il n'est donc pas possible en utilisant goto de sortir d'une procédure ou d'une fonction. Ne marquez qu'une seule instruction dans un bloc avec le même label.

Par exemple :

 label StartHere;
     ...
 StartHere: Beep;
 goto StartHere;

crée une boucle infinie qui appelle la procédure Beep sans cesse.

En outre, il n'est pas possible de rentrer ni de sortir d'une instruction try - finally ou try -except.

L'instruction goto est généralement déconseillée en programmation structurée. Elle peut néanmoins être utilisée dans certains cas pour sortir de boucles imbriquées, comme dans l'exemple suivant :

 procedure FindFirstAnswer;
   var X, Y, Z, Count: Integer;
 label FoundAnAnswer;
 begin
   Count := SomeConstant;
   for X := 1 to Count do
     for Y := 1 to Count do
       for Z := 1 to Count do
         if ... { some condition holds on X, Y, and Z } then
           goto FoundAnAnswer;
 
   ... { Code to execute if no answer is found }
   Exit;
 
   FoundAnAnswer:
     ... { Code to execute when an answer is found }
 end;

Notez que l'instruction goto sert à sortir d'une boucle imbriquée. Ne rentrez jamais dans une boucle ou dans d'autres instructions structurées, car cela peut donner des résultats imprévisibles.

Instructions structurées

Les instructions structurées sont construites à partir d'autres instructions. Utilisez une instruction structurée quand vous voulez exécuter d'autres instructions de manière séquentielle, conditionnelle ou répétitive.

  • Une instruction composée ou une instruction with exécute simplement la suite des instructions qui la composent.
  • Une instruction conditionnelle (c'est-à-dire une instruction if ou case) exécute au plus un de ses constituants, selon les critères spécifiés.
  • Une instruction de boucle (repeat, while ou les boucles for) exécute de manière répétitive la suite des instructions constituantes.
  • Un groupe spécial d'instructions (les constructions raise, try...except et try...finally) crée et gère des exceptions. Pour de plus amples informations sur la génération et la gestion des exceptions, voir Exceptions.

Instructions composées

Une instruction composée est une suite d'instructions (simples ou structurées) qui doivent être exécutées dans l'ordre de leur écriture. L'instruction composée est délimitée par les mots réservés begin et end ; les instructions qui la composent sont séparées par des points-virgules. Par exemple :

 begin
    Z := X;
    X := Y;
    X := Y;
 end;

Le dernier point-virgule avant end est facultatif. Le code pourrait donc être écrit comme suit :

 begin
    Z := X;
    X := Y;
    Y := Z
 end;

Les instructions composées sont essentielles dans les contextes où la syntaxe Object Pascal exige une seule instruction. En dehors des blocs de programmes, fonctions, ou procédures, elles peuvent se trouver dans d'autres instructions structurées, comme les instructions conditionnelles ou les boucles. Par exemple :

 begin
   I := SomeConstant;
   while I  > 0 do
   begin
     ...
     I := I - 1;
   end;
 end;

Vous pouvez écrire une instruction composée qui n'est constituée que d'une seule instruction ; comme les parenthèses dans une expression complexe, begin et end servent parfois à éviter des ambiguïtés et à améliorer la lisibilité du code. Vous pouvez aussi utiliser une instruction composée vide pour créer un bloc ne faisant rien :

begin
end;

Instructions with

Une instruction with est un raccourci permettant de référencer les champs d'un enregistrement, ou les champs, propriétés et méthodes d'un objet. L'instruction with a la syntaxe suivante :

with obj do statement

ou :

with obj1, ..., objn do statement

obj est une expression désignant une référence à un enregistrement, une instance d’objet, une instance de classe ou une instance de type interface ou classe (métaclasse), et statement est une instruction simple ou structurée. A l'intérieur du statement, vous pouvez faire référence aux champs, propriétés et méthodes de obj en utilisant seulement leurs identificateurs, c'est-à-dire sans qualificateurs.

Par exemple, soit ces déclarations :

 
 type
   TDate = record
     Day: Integer;
     Month: Integer;
     Year: Integer;
   end;
 
 var
   OrderDate: TDate;

vous pouvez écrire le code suivant en utilisant une instruction with :

 with OrderDate do
   if Month = 12 then
     begin
       Month := 1;
       Year := Year + 1;
   end
   else
     Month := Month + 1;

ou vous pouvez écrire le code suivant sans utiliser une instruction with :

 if OrderDate.Month = 12 then
 begin
   OrderDate.Month := 1;
   OrderDate.Year := OrderDate.Year + 1;
 end
 else
   OrderDate.Month := OrderDate.Month + 1;

Si l'interprétation de obj implique l'indexation des tableaux ou le déréférencement des pointeurs, ces actions ne sont effectuées qu'une seule fois, avant l'exécution de l'instruction. Cela rend les instructions with aussi efficaces que concises. Cela signifie aussi que les affectations d'une variable à l'intérieur de l'instruction ne peuvent changer l'interprétation de obj pendant l'exécution en cours de l'instruction with.

Chaque référence de variable ou nom de méthode d'une instruction with est interprété, si c'est possible, comme un membre de l'objet ou de l'enregistrement spécifié. Pour désigner une autre variable ou méthode portant le même nom que celui auquel vous accédez avec l'instruction with, vous devez le préfixer avec un qualificateur comme dans l'exemple suivant :

 with OrderDate do
   begin
     Year := Unit1.Year;
      ...
   end;

Quand plusieurs objets ou enregistrements apparaissent après with, l'instruction entière est traitée comme une série d'instructions with imbriquées. Ainsi :

with obj1, obj2, ..., objn do statement

est l'équivalent de :

  with obj1 do
   with obj2 do
     ...
     with objn do
       // statement

Dans ce cas, chaque référence de variable ou nom de méthode de l'instruction est interprété, si c'est possible, comme un membre de objn. Sinon, il est interprété, si c'est possible, comme un membre de objn1 ; et ainsi de suite. La même règle s'applique à l'interprétation des objs eux-mêmes. Ainsi, par exemple, si objn est un membre de obj1 et de obj2, il est interprété en tant que obj2.objn.

Puisqu'une instruction with nécessite une variable ou un champ sur lequel opérer, son utilisation avec des propriétés peut être parfois difficile. Une instruction with s'attend à ce que les variables sur lesquelles elle opère soient disponibles par référence.

Voici les points les plus importants à noter lors de l'utilisation de with :

  • Vous pouvez utiliser with sur des propriétés en lecture seule seulement pour la lecture. Une tentative de modification d'un champ dans l'objet ou l'enregistrement exposé entraîne une erreur de compilation.
  • Bien que la propriété autorise un accès en écriture au champ, vous ne pouvez toujours pas utiliser with pour modifier ses champs.

Le code suivant illustre le problème d'utilisation de l'instruction with sur des propriétés en lecture seule exposant un enregistrement. Supposons que vous avez la classe suivante :

  TShape = class
  private
    FCenter: TPoint;
  public
    property Center: TPoint read FCenter;
  end;

où TPoint est un enregistrement déclaré comme suit :

  TPoint = record
    X, Y: Integer;
  end;

Normalement, la propriété Center est accessible en lecture seule et ne vous permet pas de modifier la valeur ou les champs du champ FCenter. Dans ce cas, l'utilisation de l'instruction with comme la suivante échouera avec une erreur de compilation puisque Shape.Center n'est pas une variable et que vous ne pouvez pas la référencer :

  with Shape.Center do
  begin
    X := 100;
    Y := 100;
  end;

La partie délicate lors de l'utilisation de l'instruction with concerne les propriétés en lecture /écriture. Nous avons modifié la classe TShape originale pour permettre un accès en écriture à son champ FCenter :

  TShape = class
  private
    FCenter: TPoint;
  public
    property Center: TPoint read FCenter '''write FCenter''';
  end;

Bien que la propriété Center n'est pas en lecture seule, la même erreur de compilation est émise. La solution à ce problème consiste à modifier ce code :

  with Shape.Center do
  begin
    X := 100;
    Y := 100;
  end;

par le code suivant :

  { Copy the value of Center to a local variable. }
  TempPoint := Shape.Center;
 
  with TempPoint do
  begin
    X := 100;
    Y := 100;
  end;
 
  { Set the value back. }
  Shape.Center := TempPoint;

Instructions if

L'instruction if a deux formes : if...then et if...then...else. La syntaxe d'une instruction if...then est :

if expression then statement

expression renvoie une valeur booléenne. Si expression vaut True, alors statement est exécutée ; sinon elle ne l'est pas. Par exemple :

if J <> 0 then Result := I / J;

La syntaxe d'une instruction if...then...else est :

if expression then statement1 else statement2

expression renvoie une valeur booléenne. Si expression vaut True, alors statement1 est exécutée ; sinon statement2 est exécutée. Par exemple :

 if J = 0 then
    Exit
 else
    Result := I / J;

Les clauses then et else contiennent une seule instruction chacune, mais ce peut être une instruction structurée. Par exemple :

  
 if J <> o then
 begin
   Result := I / J;
   Count := Count + 1;
 end
 else if Count = Last then
   Done := True
 else
   Exit;

Notez qu'il n'y a jamais de point-virgule entre la clause then et le mot else. Vous pouvez placer un point-virgule après une instruction if entière pour la séparer de l'instruction suivante dans son bloc, mais les clauses then et else ne nécessitent rien d'autre qu'un espace ou un retour chariot entre elles. Le fait de placer un point-virgule immédiatement avant else (dans une instruction if) est une erreur de programmation courante.

Un problème particulier se présente quand des instructions if sont imbriquées. Ceci se produit car certaines instructions if ont une clause else alors que d'autres ne l'ont pas, mais la syntaxe des deux types d'instruction est pour le reste la même. Dans une série de conditions imbriquées où il y a moins de clauses else que d'instructions if, il n'est pas toujours évident de savoir à quel if une clause else est rattachée. Considérons une instruction de la forme :

if expression1 then if expression2 then statement1 else statement2;

Il y a deux manières d'analyser cette instruction :

if expression1 then [ if expression2 then statement1 else statement2 ];
if expression1 then [ if expression2 then statement1 ] else statement2;

Cependant, le compilateur analyse toujours de la première manière. C'est-à-dire que dans du véritable code, l'instruction :

 if ... { expression1} then
   if ... {expression2} then
     ... {statement1}
   else
     ... {statement2}

est l'équivalent de :

 if ... {expression1} then
   begin
     if ... {expression2} then
       ... {statement1}
     else
       ... {statement2}
 end;

La règle veut que les conditions imbriquées sont analysées en partant de la condition la plus interne, chaque else étant lié au plus proche if disponible à sa gauche. Pour forcer le compilateur à lire notre exemple de la deuxième manière, vous devez l'écrire explicitement de la manière suivante :

 if ... {expression1} then
   begin
    if ... {expression2} then
      ... {statement1}
    end
 end
 else
    ... {statement2};

Instructions case

L'instruction case propose une alternative plus lisible à l'utilisation de conditions if très imbriquées. Une instruction case a la forme :

 
 case selectorExpression of
   caseList1: statement1;
    ...
   caseListn: statementn;
 end

selectorExpression est une expression de type ordinal inférieure à 32 bits (les types chaîne et les nombres ordinaux supérieurs à 32 bits sont invalides) et chaque caseList est représenté de la manière suivante :

  • Un nombre, une constante déclarée ou une autre expression que le compilateur peut évaluer sans exécuter votre programme. Ce doit être une valeur de type ordinal compatible avec selectorExpression. Ainsi 7, True, 4 + 5 * 3, 'A' et Integer('A') peuvent tous être utilisés en tant que caseLists, mais les variables et la plupart des appels de fonction ne peuvent être utilisés. Certaines fonctions intégrées comme Hi et Lo peuvent s'utiliser dans un caseList. Voir Constantes déclarées.
  • Une étendue de la forme First..Last, où First et Last respectent tous les deux les critères précédents et où First est inférieur ou égal à Last.
  • Une liste de la forme item1, ..., itemn, où chaque item respecte l'un des critères précédents.

Chaque valeur représentée par un caseList doit être unique dans l'instruction case ; les sous-étendues et les listes ne peuvent se chevaucher. Une instruction case peut avoir une clause else finale :

 case selectorExpression of
   caseList1: statement1;
    ...
   caselistn: statementn;
 else
   statements;
 end

statements est une suite d'instructions séparées par des points virgules. Quand une instruction case est exécutée, au plus l'une des instructions statement1 ... statementn est exécutée. Le caseList dont la valeur est égale à celle de selectorExpression détermine l'instruction à utiliser. Si aucun des caseLists n'a la même valeur que selectorExpression, alors ce sont les instructions de la clause else (s'il y en a) qui sont exécutées.

L'instruction case

 
 case I of
   1..5: Caption := 'Low';
   6..9: Caption := 'High';
   0, 10..99: Caption := 'Out of range';
 else
   Caption := '';
 end

est équivalente à la condition imbriquée suivante :

 if I in [1..5] then
   Caption := 'Low';
 else if I in [6..10] then
   Caption := 'High';
   else if (I = 0) or (I in [10..99]) then
     Caption := 'Out of range'
     else
       Caption := '';

Autres exemples d'instructions case

 case MyColor of
   Red: X := 1;
   Green: X := 2;
   Blue: X = 3;
   Yellow, Orange, Black: X := 0;
 end;
 
 case Selection of
   Done: Form1.Close;
   Compute: calculateTotal(UnitCost, Quantity);
 else
   Beep;
 end;

Boucles de contrôle

Les boucles permettent d'exécuter de manière répétitive une suite d'instructions en utilisant une condition ou une variable de contrôle pour déterminer quand arrêter l'exécution de la boucle. Object Pascal dispose de trois sortes de boucles de contrôle : les instructions repeat, while et for.

Vous pouvez utiliser les procédures standard Break et Continue pour contrôler le flux d'une instruction repeat, while ou for. Break termine l'instruction dans laquelle elle se produit, alors que Continue commence l'exécution de l'itération suivante de la séquence.

Instructions repeat

L'instruction repeat a la syntaxe suivante :

repeat statement1; ...; statementn; until expression

expression renvoie une valeur booléenne. Le dernier point-virgule avant until est facultatif. L'instruction repeat exécute répétitivement la séquence d'instructions qu'elle contient en testant expression à chaque itération. Quand expression renvoie True, l'instruction repeat se termine. La séquence est toujours exécutée au moins une fois, car expression n'est évaluée qu'après la première itération.

Voici des exemples d'instructions repeat :

 repeat
   K := I mod J;
   I := J;
   J := K;
 until J = 0;
 
 repeat
   Write('Enter a value (0..9): ');
   Readln(I);
 until (I >= 0) and (I <= 9);

Instructions while

Une instruction while est similaire à une instruction repeat, à cette différence près que la condition de contrôle est évaluée avant la première exécution de la séquence d'instructions. Donc, si la condition est fausse, la séquence d'instructions n'est jamais exécutée.

L'instruction while a la syntaxe suivante :

while expression do statement

expression renvoie une valeur booléenne et statement peut être une instruction composée. L'instruction while exécute répétitivement son statement, en testant expression avant chaque itération. Tant que expression renvoie True, l'exécution se poursuit.

Voici des exemples d'instructions while :

  
 while Data[I] <> X do I := I + 1;
 
 while I > 0 do
 begin
   if Odd(I) then Z := Z * X;
   I := I div 2;
   X := Sqr(X);
 end;
 
 while not Eof(InputFile) do
 begin
   Readln(InputFile, Line);
   Process(Line);
 end;

Instructions for

Une instruction for, à la différence d'une instruction repeat ou while, nécessite la spécification explicite du nombre d'itérations que la boucle doit effectuer. L'instruction for a la syntaxe suivante :

 for counter := initialValue to finalValue do statement

ou :

 for counter := initialValue downto finalValue do statement

où :

  • counter est une variable locale (déclarée dans le bloc contenant l'instruction for) de type ordinal sans aucun qualificateur.
  • initialValue et finalValue sont des expressions compatibles pour l'affectation avec counter.
  • statement est une instruction simple ou structurée qui ne change pas la valeur de counter.

L'instruction for assigne la valeur de initialValue à counter, puis exécute répétitivement statement, en incrémentant ou en décrémentant counter après chaque itération. La syntaxe for...to incrémente counter, alors que la syntaxe for...downto le décrémente. Quand counter renvoie la même valeur que finalValue, statement est exécutée une dernière fois puis l'instruction for se termine. En d'autres termes, statement est exécutée une fois pour chaque valeur de l'étendue allant de initialValue à finalValue. Si initialValue est égale à finalValue, statement est exécutée une seule fois. Si initialValue est supérieure à finalValue dans une instruction for...to, ou inférieure à finalValue dans une instruction for...downto, statement n'est alors jamais exécutée. Après la fin de l'instruction for (à condition que cet arrêt n’ait pas été imposé par une procédure Break ou Exit), la valeur de counter est non définie.

Avertissement : La variable d'itération counter ne peut pas être modifiée dans la boucle. Ainsi, par exemple, vous ne pouvez pas assigner ni transmettre la variable à un paramètre var d'une procédure. Si vous essayez de le faire, un avertissement de compilation se produit.

Afin de contrôler l'exécution de la boucle, les expressions initialValue et finalValue ne sont évaluées qu'une seule fois, avant le commencement de la boucle. Donc, l'instruction for...to est presque identique à la construction while suivante :

 
 begin
   counter := initialValue;
   while counter <= finalValue do
   begin
     ... {statement};
     counter := Succ(counter);
   end;
 end

La différence entre cette construction et l'instruction for...to est que la boucle while réévalue finalValue avant chaque itération. Ceci peut réduire nettement la vitesse d'exécution si finalValue est une expression complexe. De plus, ceci signifie qu'une modification de la valeur de finalValue dans statement peut affecter l'exécution de la boucle.

Exemples d'instructions for :

 for I := 2 to 63 do
   if Data[I]  > Max then
     Max := Data[I];
 
 for I := ListBox1.Items.Count - 1 downto 0 do
   ListBox1.Items[I] := UpperCase(ListBox1.Items[I]);
 
 for I := 1 to 10 do
   for J := 1 to 10 do
   begin
     X := 0;
     for K := 1 to 10 do
       X := X + Mat1[I,K] * Mat2[K,J];
     Mat[I,J] := X;
    end;
for C := Red to Blue do Check(C);

Itération sur des conteneurs avec des instructions For

Object Pascal supporte l'itération de style for-element-in-collection sur les conteneurs. Les modèles suivants d'itération sur les conteneurs sont reconnus par le compilateur :

  • for Element in ArrayExpr do Stmt;
  • for Element in StringExpr do Stmt;
  • for Element in SetExpr do Stmt;
  • for Element in CollectionExpr do Stmt;
  • for Element in Record do Stmt;

Le type de la variable d'itération Element doit correspondre au type indiqué dans le conteneur. Avec chaque itération de la boucle, la variable d'itération contient le membre de la collection en cours. Comme avec les boucles for normales, la variable d'itération doit être déclarée dans le même bloc que l'instruction for.

Avertissement : La variable d'itération ne peut pas être modifiée dans la boucle. Ainsi, par exemple, vous ne pouvez pas assigner ni transmettre la variable à un paramètre var d'une procédure. Si vous essayez de le faire, un avertissement de compilation se produit.

Les expressions des tableaux peuvent être des tableaux unidimensionnels, multidimensionnels, à longueur fixe ou dynamiques. Le tableau est parcouru dans l'ordre croissant, en commençant à la limite inférieure du tableau et en terminant à la taille du tableau moins un. Le code suivant montre un exemple de traversée de tableaux unidimensionnels, multidimensionnels et dynamiques :


 
 type
   TIntArray        = array[0..9] of Integer;
   TGenericIntArray = array of Integer;
 
 var
   IArray1: array[0..9] of Integer   = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
   IArray2: array[1..10] of Integer  = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
   IArray3: array[1..2] of TIntArray = ((11, 12, 13, 14, 15, 16, 17, 18, 19, 20),
                                        (21, 22, 23, 24, 25, 26, 27, 28, 29, 30));
   MultiDimTemp: TIntArray;
   DynArray: TGenericIntArray;
 
   I: Integer;
 
 begin
   for I in IArray1 do
   begin
     { Do something with I... }
   end;
 
   { Indexing begins at lower array bound of 1. }
   for I in IArray2 do
   begin
     { Do something with I... }
   end;
 
   { Iterating a multidimensional array }
   for MultiDimTemp in IArray3 do   // Indexing from 1..2
     for I in MultiDimTemp do       // Indexing from 0..9
     begin
       { Do something with I... }
     end;
 
   { Iterating over a dynamic array }
   DynArray := TGenericIntArray.Create(1, 2, 3, 4);
 
   for I in DynArray do
   begin
     { Do something with I... }
   end;
 end.

L'exemple suivant illustre une itération sur des expressions de type chaîne :

 
 var
   C: Char;
   S1, S2: String;
   Counter: Integer;
 
   OS1, OS2: ShortString;
   AC: AnsiChar;
 
 begin
 
   S1 := 'Now is the time for all good men to come to the aid of their country.';
   S2 := ''''''';
 
   for C in S1 do
     S2 := S2 + C;
 
   if S1 = S2 then
     Writeln('SUCCESS #1')
   else
     Writeln('FAIL #1');
 
   OS1 := 'When in the course of human events it becomes necessary to dissolve...';
   OS2 := ''''''';
 
   for AC in OS1 do
     OS2 := OS2 + AC;
 
   if OS1 = OS2 then
     Writeln('SUCCESS #2')
   else
     Writeln('FAIL #2');
 
 end.

L'exemple suivant illustre une itération sur des expressions d'ensemble :


 type
 
   TMyThing = (one, two, three);
   TMySet   = set of TMyThing;
   TCharSet = set of Char;
 
 var
   MySet:   TMySet;
   MyThing: TMyThing;
 
   CharSet: TCharSet;
   C: Char;
 
 begin
 
   MySet := [one, two, three];
   for MyThing in MySet do
    begin
     // Do something with MyThing...
    end;
 
 
   CharSet := [#0..#255];
   for C in CharSet do
     begin
       // Do something with C...
     end;
 
 end.

Pour utiliser la construction de boucle for-in sur une classe ou une interface, cette dernière doit implémenter un modèle de collection prescrit. Un type qui implémente le modèle de collection doit avoir les attributs suivants :

  • La classe ou l'interface doit contenir une méthode d'instance publique appelée GetEnumerator(). La méthode GetEnumerator() doit renvoyer un type classe, interface ou enregistrement.
  • La classe, l'interface ou l'enregistrement renvoyé par GetEnumerator() doit contenir la méthode d'instance publique appelée MoveNext(). La méthode MoveNext() doit renvoyer une valeur booléenne. La boucle for-in appelle premièrement cette méthode pour s'assurer que le conteneur n'est pas vide.
  • La classe, l'interface ou l'enregistrement renvoyé par GetEnumerator() doit contenir une propriété en lecture seule, d'instance publique, appelée Current. Le type de la propriété Current doit être le type contenu dans la collection.


Le code suivant illustre l'itération sur un conteneur énumérable dans Object Pascal.

type
  TMyIntArray   = array of Integer;
  TMyContainerEnumerator = class;

  TMyContainer  = class
  public
    Values: TMyIntArray;
    function GetEnumerator: TMyContainerEnumerator;
  end;

  TMyContainerEnumerator = class
    Container : TMyContainer;
    Index     : Integer;
  public
    constructor Create(AContainer : TMyContainer);
    function GetCurrent: Integer;
    function MoveNext:   Boolean;
    property Current:    Integer read GetCurrent;
  end;

constructor TMyContainerEnumerator.Create(AContainer : TMyContainer);
begin
  inherited Create;
  Container := AContainer;
  Index     := - 1; 
end;

function TMyContainerEnumerator.MoveNext: Boolean;
begin
  Result := Index < High(Container.Values);
  if Result then
    Inc(Index);
end;

function TMyContainerEnumerator.GetCurrent: Integer;
begin
  Result := Container.Values[Index];
end;

function TMyContainer.GetEnumerator: TMyContainerEnumerator;
begin
  Result := TMyContainerEnumerator.Create(Self);
end;

var
  MyContainer : TMyContainer;
  I           : Integer;
  Counter     : Integer;
begin
  MyContainer        := TMyContainer.Create;
  MyContainer.Values := TMyIntArray.Create(100, 200, 300);

  Counter := 0;
  for I in MyContainer do
    Inc(Counter, I);

  Writeln('Counter = ', Counter, ' (should be 600)');
  ReadLn;
end.

Les classes suivantes et leurs descendants supportent la syntaxe for-in :

Blocs et portée

Les déclarations et les instructions sont organisées en blocs qui définissent des noms de nommage locaux (ou portées) pour les labels et les identificateurs. Les blocs permettent à un même identificateur, par exemple un nom de variable, d'avoir des significations différentes dans différentes parties d'un programme. Chaque bloc fait partie de la déclaration d'un programme, d'une fonction ou d'une procédure ; la déclaration de chaque programme, fonction ou procédure est composée d'un seul bloc.

Blocs

Un bloc est composé d'une série de déclarations suivies d'une instruction composée. Toutes les déclarations doivent se trouver rassemblées au début du bloc. Un bloc a donc la forme suivante :

{declarations}
begin
  {statements}
end

La section declarations peut inclure, dans un ordre quelconque, des déclarations de variables, constantes (y compris des chaînes de ressource), types, procédures, fonctions et labels. Dans un bloc de programme, la section declarations peut aussi inclure une ou plusieurs clauses exports (voir Bibliothèques et packages).

Par exemple, dans une déclaration de fonction comme suit :

 function UpperCase(const S: string): string;
 var
   Ch: Char;
   L: Integer;
   Source, Dest: PChar;
 begin
    ...
 end;

la première ligne de la déclaration est l'en-tête de fonction et toutes les lignes suivantes constituent le bloc. Ch, L, Source et Dest sont des variables locales ; leur déclaration n'est valable que dans le bloc de la fonction UpperCase et redéfinit (uniquement dans ce bloc) toute déclaration des mêmes identificateurs faite dans le bloc du programme ou dans les sections interface ou implementation d'une unité.

Portée

Un identificateur, par exemple un nom de fonction ou de variable, ne peut être utilisé qu'à l'intérieur de la portée de sa déclaration. L'emplacement d'une déclaration détermine sa portée. La portée d'un identificateur déclaré dans la déclaration d'un programme, d'une fonction ou d'une procédure est limitée au bloc dans lequel il a été déclaré. Un identificateur déclaré dans la section interface d'une unité a une portée qui inclut toutes les autres unités ou programmes utilisant l'unité où cette déclaration est faite. Les identificateurs ayant une portée plus restreinte (en particulier les identificateurs déclarés dans les fonctions et procédures) sont parfois dits locaux alors que les identificateurs ayant une portée plus étendue sont appelée globaux.

Les règles déterminant la portée d'un identificateur sont résumées ci-dessous :

Si l'identificateur est déclaré dans ... sa portée s'étend ...

la section declaration d'un programme, d'une fonction ou d'une procédure.

du point de sa déclaration jusqu'à la fin du bloc en cours, y compris tous les blocs inclus dans cette portée.

la section interface d'une unité.

du point de sa déclaration jusqu'à la fin de l'unité, et dans toute autre unité ou programme utilisant cette unité. Voir Programmes et unités.

la section implementation d'une unité, mais hors du bloc d'une fonction ou d'une procédure.

du point de sa déclaration jusqu'à la fin de l'unité. L'identificateur est disponible dans toute fonction ou procédure de l'unité, y compris les sections initialization et finalization, le cas échéant.

la définition d'un type enregistrement (c'est-à-dire que l'identificateur est le nom d'un champ de l'enregistrement)

du point de sa déclaration jusqu'à la fin de la définition du type enregistrement. Voir "Enregistrements" dans Types structurés.

la définition d'une classe (c'est-à-dire que l'identificateur est le nom d'une méthode ou d'une propriété champ de données de la classe).

du point de sa déclaration jusqu'à la fin de la définition du type classe, et inclut aussi les descendants de la classe et les blocs de toutes les méthodes de la classe et de ses descendants. Voir Classes et objets.

Conflits de nom

Quand un bloc en comprend un autre, le premier est appelé bloc externe et l'autre est appelé bloc interne. Si un identificateur déclaré dans un bloc externe est redéclaré dans un bloc interne, la déclaration interne a priorité sur la déclaration externe et détermine la signification de l'identificateur pour la durée du bloc interne. Si, par exemple, vous avez déclaré une variable appelée MaxValue dans la section interface d'une unité, puis si vous déclarez une autre variable de même nom dans une déclaration de fonction de cette unité, toute occurrence non qualifiée de MaxValue dans le bloc de la fonction est régit par la deuxième déclaration, locale. De même, une fonction déclarée à l'intérieur d'une autre fonction crée une nouvelle portée interne, dans laquelle les identificateurs utilisés par la fonction externe peuvent être localement redéclarés.

L'utilisation de plusieurs unités complique davantage la définition de portée. Chaque unité listée dans une clause uses impose une nouvelle portée qui inclut les unités restantes utilisées et le programme ou l'unité contenant la clause uses. La première unité d'une clause uses représente la portée la plus externe, et chaque unité successive représente une nouvelle portée interne à la précédente. Si plusieurs unités déclarent le même identificateur dans leur section interface, une référence non qualifiée à l'identificateur sélectionne la déclaration de la portée la plus interne, c'est-à-dire dans l'unité où la référence est faite, ou, si cette unité ne déclare pas l'identificateur, dans la dernière unité de la clause uses qui déclare l'identificateur.

Les unités System et SysInit sont automatiquement utilisées par chaque programme ou unité. Les déclarations de System ainsi que les types prédéfinis, les routines et les constantes reconnues automatiquement par le compilateur ont toujours la portée la plus externe.

Vous pouvez redéfinir ces règles de portée et court-circuiter une déclaration interne en utilisant un identificateur qualifié (voir "Identificateurs qualifiés" dans Eléments syntaxiques fondamentaux) ou une instruction with (voir "Instructions with" ci-dessus).

Voir aussi