Declarations and Statements (Delphi)

From RAD Studio
Jump to: navigation, search

Go Up to Fundamental Syntactic Elements Index

This topic describes the syntax of Delphi declarations and statements.

Aside from the uses clause (and reserved words like implementation, which demarcate parts of a unit), a program consists entirely of declarations and statements, that are organized into blocks.

This topic covers the following items:

  • Declarations
  • Simple statements such as assignment
  • Structured statements such as conditional tests (for example, if-then, and case), iteration (for example, for, and while).

Declarations

The names of variables, constants, types, fields, properties, procedures, functions, programs, units, libraries, and packages are called identifiers (numeric constants like 26057 are not identifiers). Identifiers must be declared before you can use them; the only exceptions are a few predefined types, routines, and constants that the compiler understands automatically, the variable Result when it occurs inside a function block, and the variable Self when it occurs inside a method implementation.

A declaration defines an identifier and, where appropriate, allocates memory for it. For example:

var Size: Extended;

declares a variable called Size that holds an Extended (real) value, while:

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

declares a function called DoThis that takes two strings as arguments and returns an integer. Each declaration ends with a semicolon. When you declare several variables, constants, types, or labels at the same time, you need only write the appropriate reserved word once:

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

The syntax and placement of a declaration depend on the kind of identifier you are defining. In general, declarations can occur only at the beginning of a block or at the beginning of the interface or implementation section of a unit (after the uses clause). Specific conventions for declaring variables, constants, types, functions, and so forth are explained in the documentation for those topics.

Hinting Directives

The 'hint' directives platform, deprecated, and library may be appended to any declaration. These directives will produce warnings at compile time. Hint directives can be applied to type declarations, variable declarations, class, interface, and structure declarations, field declarations within classes or records, procedure, function, and method declarations, and unit declarations.

When a hint directive appears in a unit declaration, it means that the hint applies to everything in the unit. For example, the Windows 3.1 style OleAuto.pas unit on Windows is completely deprecated. Any reference to that unit or any symbol in that unit produces a deprecation message.

The platform hinting directive on a symbol or unit indicates that it may not exist or that the implementation may vary considerably on different platforms. The library hinting directive on a symbol or unit indicates that the code may not exist or the implementation may vary considerably on different library architectures.

The platform and library directives do not specify which platform or library. If your goal is writing platform-independent code, you do not need to know which platform a symbol is specific to; it is sufficient that the symbol be marked as specific to some platform to let you know it may cause problems for your goal of portability.

In the case of a procedure or function declaration, the hint directive should be separated from the rest of the declaration with a semicolon. Examples:

 procedure SomeOldRoutine; stdcall; deprecated;

 var
   VersionNumber: Real library;

 type
   AppError = class(Exception)
      ...
 end platform;

When source code is compiled in the {$HINTS ON} {$WARNINGS ON} state, each reference to an identifier declared with one of these directives generates an appropriate hint or warning. Use platform to mark items that are specific to a particular operating environment (such as Windows), deprecated to indicate that an item is obsolete or supported only for backward compatibility, and library to flag dependencies on a particular library or component framework.

The Delphi compiler also recognizes the hinting directive experimental. You can use this directive to designate units that are in an unstable development state. The compiler will emit a warning when it builds an application that uses the unit.

For more information about the Delphi hinting directives, see warning directives in method declarations. All the Delphi directives are listed in Directives.

Statements

Statements define algorithmic actions within a program. Simple statements like assignments and procedure calls can combine to form loops, conditional statements, and other structured statements.

Multiple statements within a block and in the initialization or finalization section of a unit are separated by semicolons.

Simple Statements

A simple statement does not contain any other statements. Simple statements include assignments, calls to procedures and functions, and goto jumps.

Assignment Statements

An assignment statement has the form:

variable := expression

where variable is any variable reference, including a variable, variable typecast, dereferenced pointer, or component of a structured variable. The expression is any assignment-compatible expression (within a function block, the variable can be replaced with the name of the function being defined. See Procedures and Functions (Delphi).) The := symbol is sometimes called the assignment operator.

An assignment statement replaces the current value of the variable with the value of the expression. For example:

I := 3;

assigns the value 3 to the variable I. The variable reference on the left side of the assignment can appear in the expression on the right. For example:

I := I + 1;

increments the value of I. Other assignment statements include:

 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;

Procedure and Function Calls

A procedure call consists of the name of a procedure (with or without qualifiers), followed by a parameter list (if required). Examples include:

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

With extended syntax enabled ({$X+}), function calls such as calls to procedures can be treated as statements in their own right:

MyFunction(X);

When you use a function call this way, its return value is discarded.

For more information about procedures and functions, see Procedures and Functions (Delphi).

Goto Statements

A goto statement, which has the form:

goto label

transfers program execution to the statement marked by the specified label. To mark a statement, you must first declare the label. Then, you must precede the statement you want to mark with the label and a colon:

label: statement

Declare labels like this:

label label;

You can declare several labels at once:

label label1, ..., labeln;

A label can be any valid identifier or any numeral from 0 through 4294967295.

The label declaration, marked statement, and goto statement must belong to the same block. (See Blocks and Scope, below.) Hence, it is not possible to jump into or out of a procedure or function. Do not mark more than one statement in a block with the same label.

For example:

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

creates an infinite loop that calls the Beep procedure repeatedly.

Additionally, it is not possible to jump into or out of a try - finally or try -except statement.

The goto statement is generally discouraged in structured programming. It is, however, sometimes used as a way of exiting from nested loops, as in the following example:

 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;

Notice that we are using goto to jump out of a nested loop. Never jump into a loop or other structured statement, because this can have unpredictable effects.

Structured Statements

Structured statements are built from other statements. Use a structured statement when you want to execute other statements sequentially, conditionally, or repeatedly.

  • A compound or with statement simply executes a sequence of constituent statements.
  • A conditional statement that is an if or case statement executes at most one of its constituents, depending on specified criteria.
  • Loop statements including repeat, while, and for loops execute a sequence of constituent statements repeatedly.
  • A special group of statements including raise, try...except, and try...finally constructions create and handle exceptions. For information about exception generation and handling, see Exceptions (Delphi).

Compound Statements

A compound statement is a sequence of other (simple or structured) statements to be executed in the order in which they are written. The compound statement is bracketed by the reserved words begin and end, and its constituent statements are separated by semicolons. For example:

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

The last semicolon before end is optional. So this could have been written as:

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

Compound statements are essential in contexts where Delphi syntax requires a single statement. In addition to program, function, and procedure blocks, they occur within other structured statements, such as conditionals or loops. For example:

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

You can write a compound statement that contains only a single constituent statement; like parentheses in a complex term, begin and end sometimes serve to disambiguate and to improve readability. You can also use an empty compound statement to create a block that does nothing:

begin
end;

With Statements

A with statement is a shorthand for referencing the fields of a record or the fields, properties, and methods of an object. The syntax of a with statement is:

with obj do statement

or:

with obj1, ..., objn do statement

where obj is an expression yielding a reference to a record, object instance, class instance, interface or class type (metaclass) instance, and statement is any simple or structured statement. Within the statement, you can refer to fields, properties, and methods of obj using their identifiers alone, that is, without qualifiers.

For example, given the declarations:

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

 var
   OrderDate: TDate;

you could write the following code using a with statement:

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

or you could write the following code without using a with statement:

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

If the interpretation of obj involves indexing arrays or dereferencing pointers, these actions are performed once, before statement is executed. This makes with statements efficient as well as concise. It also means that assignments to a variable within statement cannot affect the interpretation of obj during the current execution of the with statement.

Each variable reference or method name in a with statement is interpreted, if possible, as a member of the specified object or record. If there is another variable or method of the same name that you want to access from the with statement, you need to prepend it with a qualifier, as in the following example:

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

When multiple objects or records appear after with, the entire statement is treated like a series of nested with statements. Thus:

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

is equivalent to:

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

In this case, each variable reference or method name in statement is interpreted, if possible, as a member of objn; otherwise it is interpreted, if possible, as a member of objn1; and so forth. The same rule applies to interpreting the objs themselves, so that, for instance, if objn is a member of both obj1 and obj2, it is interpreted as obj2.objn.

Since a with statement requires a variable or a field to operate upon, using it with properties can be tricky at times. A with statement expects variables it operates on to be available by reference.

The most important things to note when you are using with:

  • You can use with on read-only properties only for reading. Trying to modify a field in the exposed record or object results in a compile-time error.
  • Even though the property allows write access to the field, you still cannot use with to modify its fields.

The following code exemplifies the problem in using the with statement on read-only properties exposing a record. Assuming you have the following class:

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

where TPoint is a records declared as follows:

  TPoint = record
    X, Y: Integer;
  end;

Normally, the Center property is read-only and does not allow you to modify the value or the fields of FCenter field. In this case, using a with statement like the following will fail with a compile-time error since Shape.Center is not a variable and you cannot have a reference to it:

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

The tricky part when using the with statement comes for read/write properties. We have changed the original TShape class to allow write access to its FCenter field:

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

Even though the Center property is not read-only, the same compile-time error is emitted. The solution to this problem is to change code that looks like this:

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

into code that looks like this:

  { 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;

If Statements

There are two forms of the if statement: if...then and the if...then...else. The syntax of an if...then statement is:

if expression then statement

where expression returns a Boolean value. If expression is True, then statement is executed; otherwise it is not. For example:

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

The syntax of an if...then...else statement is:

if expression then statement1 else statement2

where expression returns a Boolean value. If expression is True, then statement1 is executed; otherwise statement2 is executed. For example:

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

The then and else clauses contain one statement each, but it can be a structured statement. For example:

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

Notice that a semicolon between the then clause and the word else is never used. You can place a semicolon after an entire if statement to separate it from the next statement in its block, but the then and else clauses require nothing more than a space or carriage return between them. Placing a semicolon immediately before else (in an if statement) is a common programming error.

A special difficulty arises in connection with nested if statements. This happens because some if statements have else clauses while others do not, but the syntax for the two kinds of statement is otherwise the same. In a series of nested conditionals where there are fewer else clauses than if statements, it may not seem clear which else clauses are bound to which ifs. Consider a statement of the form:

if expression1 then if expression2 then statement1 else statement2;

It appears that there are two ways to parse this:

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

However, the compiler always parses in the first way. That is, in real code, the statement:

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

is equivalent to:

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

The rule is that nested conditionals are parsed starting from the innermost conditional, with each else bound to the nearest available if on its left. To force the compiler to read our example in the second way, you have to write it explicitly as:

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

Case Statements

The case statement may provide a readable alternative to deeply nested if conditionals. A case statement has the form:

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

where selectorExpression is any expression of an ordinal type smaller than 32 bits (string types and ordinals larger than 32 bits are invalid) and each caseList is one of the following:

  • A numeral, declared constant, or other expression that the compiler can evaluate without executing your program. It must be of an ordinal type compatible with selectorExpression. Thus, 7, True, 4 + 5 * 3, 'A', and Integer('A') can all be used as caseLists, but variables and most function calls cannot. (A few built-in functions like Hi and Lo can occur in a caseList. See Declared Constants.)
  • A subrange having the form First..Last, where First and Last both satisfy the criterion above and First is less than or equal to Last.
  • A list having the form item1, ..., itemn, where each item satisfies one of the criteria above.

Each value represented by a caseList must be unique in the case statement; subranges and lists cannot overlap. A case statement can have a final else clause:

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

where statements is a semicolon-delimited sequence of statements. When a case statement is executed, at most one of statement1 ... statementn is executed. Whichever caseList has a value equal to that of selectorExpression determines the statement to be used. If none of the caseLists has the same value as selectorExpression, then the statements in the else clause (if there is one) are executed.

The case statement

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

is equivalent to the nested conditional:

 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 := '';

Other examples of case statements

 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;

Control Loops

Loops allow you to execute a sequence of statements repeatedly, using a control condition or variable to determine when the execution stops. Delphi has three kinds of control loops: repeat statements, while statements, and for statements.

You can use the standard Break and Continue procedures to control the flow of a repeat, while, or for statement. Break terminates the statement in which it occurs, while Continue begins executing the next iteration of the sequence.

Repeat Statements

The syntax of a repeat statement is:

repeat statement1; ...; statementn; until expression

where expression returns a Boolean value. (The last semicolon before until is optional.) The repeat statement executes its sequence of constituent statements continually, testing expression after each iteration. When expression returns True, the repeat statement terminates. The sequence is always executed at least once, because expression is not evaluated until after the first iteration.

Examples of repeat statements include:

 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);

While Statements

A while statement is similar to a repeat statement, except that the control condition is evaluated before the first execution of the statement sequence. Hence, if the condition is false, the statement sequence is never executed.

The syntax of a while statement is:

while expression do statement

where expression returns a Boolean value and statement can be a compound statement. The while statement executes its constituent statement repeatedly, testing expression before each iteration. As long as expression returns True, execution continues.

Examples of while statements include:

 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;

For Statements

A for statement, unlike a repeat or while statement, requires you to specify explicitly the number of iterations you want the loop to go through. The syntax of a for statement is:

 for counter := initialValue to finalValue do statement

or:

 for counter := initialValue downto finalValue do statement

where:

  • counter is a local variable (declared in the block containing the for statement) of ordinal type, without any qualifiers.
  • initialValue and finalValue are expressions that are assignment-compatible with counter.
  • statement is a simple or structured statement that does not change the value of counter.

The for statement assigns the value of initialValue to counter, then executes statement repeatedly, incrementing or decrementing counter after each iteration. (The for...to syntax increments counter, while the for...downto syntax decrements it.) When counter returns the same value as finalValue, statement is executed once more and the for statement terminates. In other words, statement is executed once for every value in the range from initialValue to finalValue. If initialValue is equal to finalValue, statement is executed exactly once. If initialValue is greater than finalValue in a for...to statement, or less than finalValue in a for...downto statement, then statement is never executed. After the for statement terminates (provided this was not forced by a Break or an Exit procedure), the value of counter is undefined.

Warning: The iteration variable counter cannot be modified within the loop. This includes assignment and passing the variable to a var parameter of a procedure. Doing so results in a compile-time warning.

For purposes of controlling the execution of the loop, the expressions initialValue and finalValue are evaluated only once, before the loop begins. Hence, the for...to statement is almost, but not quite, equivalent to this while construction:

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

The difference between this construction and the for...to statement is that the while loop reevaluates finalValue before each iteration. This can result in noticeably slower performance if finalValue is a complex expression, and it also means that changes to the value of finalValue within statement can affect the execution of the loop.

Examples of for statements

 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);

Iteration Over Containers Using For Statements

Delphi supports for-element-in-collection style iteration over containers. The following container iteration patterns are recognized by the compiler:

  • 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;

The type of the iteration variable Element must match the type held in the container. With each iteration of the loop, the iteration variable holds the current collection member. As with regular for-loops, the iteration variable must be declared within the same block as the for statement.

Warning: The iteration variable cannot be modified within the loop. This includes assignment and passing the variable to a var parameter of a procedure. Doing so results in a compile-time warning.

Array expressions can be single or multidimensional, fixed length, or dynamic arrays. The array is traversed in increasing order, starting at the lowest array bound and ending at the array size minus one. The following code shows an example of traversing single, multidimensional, and dynamic arrays:


 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.

The following example demonstrates iteration over string expressions:

 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.

The following example demonstrates iteration over set expressions:


 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.

To use the for-in loop construct on a class or interface, the class or interface must implement a prescribed collection pattern. A type that implements the collection pattern must have the following attributes:

  • The class or interface must contain a public instance method called GetEnumerator(). The GetEnumerator() method must return a class, interface, or record type.
  • The class, interface, or record returned by GetEnumerator() must contain a public instance method called MoveNext(). The MoveNext() method must return a Boolean. The for-in loop calls this method first to ensure that the container is not empty.
  • The class, interface, or record returned by GetEnumerator() must contain a public instance, read-only property called Current. The type of the Current property must be the type contained in the collection.


The following code demonstrates iterating over an enumerable container in Delphi.

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.

As part of the collection enumerator types, RAD Studio formally defines the enumerator status when the iteration has completed: “The enumerator state is not valid after MoveNext returned False and enumerator must be released or recreated and should not be accessed any further”.

Iteration Over Datasets Using For Statements

Delphi supports for-in syntax construction to iterate over datasets. The compiler recognizes the following dataset iteration pattern:

  • for Record in Dataset do Smth;

where Record is represented by the TDataSet API. It is safe to assume, that Record is equal to Dataset.

The following code snippet iterates over a dataset in Delphi. This sample code explains how to output the values of the Name column to a Memo control.

var 
 ds: TDataSet;
//
FQuery1.SQL.Text := 'SELECT Name FROM Table1'; 
Memo1.Lines.Clear;
for ds in FDQuery1 do 
  Memo1.Lines.Add(ds.FieldByName('Name').AsString);


Note: The dataset enumeration is not a reenterable operation. This means that for a dataset you can use only one enumeration at each moment. If you need to simultaneously execute several for-in loops for the same dataset, use the TDataSet.View method instead (see later in this topic). In this scenario, in the for-in loop, a Record may be not equal to a Dataset.


The following code snippet illustrates how to use the TDataSet.View method to enumerate a dataset.

var
  ds: TDataSet;
//...
Memo1.Lines.Clear;
for ds in FDQuery1.View(dmAllowClone) do
  Memo1.Lines.Add(ds.FieldByName('name').AsString);

List of Supported Classes

The following classes and their descendants support the for-in syntax:

Note: The TStringBuilder enumerator does not support the TStringBuilder modification while the Enumerator loop is In Progress.

Blocks and Scope

Declarations and statements are organized into blocks that define local namespaces (or scopes) for labels and identifiers. Blocks allow a single identifier, such as a variable name, to have different meanings in different parts of a program. Each block is part of the declaration of a program, function, or procedure; each program, function, or procedure declaration has one block.

Blocks

A block consists of a series of declarations followed by a compound statement. All declarations must occur together at the beginning of the block. So the form of a block is:

{declarations}
begin
  {statements}
end

The declarations section can include, in any order, declarations for variables, constants (including resource strings), types, procedures, functions, and labels. In a program block, the declarations section can also include one or more exports clauses (see Libraries and Packages (Delphi).)

For example, in a function declaration like this:

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

the first line of the declaration is the function heading and all of the succeeding lines make up the block. Ch, L, Source, and Dest are local variables; their declarations apply only to the UpperCase function block and override, in this block only, any declarations of the same identifiers that may occur in the program block or in the interface or implementation section of a unit.

Scope

An identifier such as a variable or function name can be used only within the scope of its declaration. The location of a declaration determines its scope. An identifier declared within the declaration of a program, function, or procedure has a scope limited to the block in which it is declared. An identifier declared in the interface section of a unit has a scope that includes any other units or programs that use the unit where the declaration occurs. Identifiers with narrower scope, especially identifiers declared in functions and procedures, are sometimes called local, while identifiers with wider scope are called global.

The rules that determine identifier scope are summarized below.

If the identifier is declared in ... its scope extends ...

the declaration section of a program, function, or procedure

from the point where it is declared to the end of the current block, including all blocks enclosed within that scope.

the interface section of a unit

from the point where it is declared to the end of the unit, and to any other unit or program that uses that unit. (See Programs and Units (Delphi).)

the implementation section of a unit, but not within the block of any function or procedure

from the point where it is declared to the end of the unit. The identifier is available to any function or procedure in the unit, including the initialization and finalization sections, if present.

the definition of a record type (that is, the identifier is the name of a field in the record)

from the point of its declaration to the end of the record-type definition. (See "Records" in Structured Types (Delphi).)

the definition of a class (that is, the identifier is the name of a data field property or method in the class)

from the point of its declaration to the end of the class-type definition, and also includes descendants of the class and the blocks of all methods in the class and its descendants. (See Classes and Objects (Delphi).)

Naming Conflicts

When one block encloses another, the former is called the outer block and the latter, the inner block. If an identifier declared in an outer block is redeclared in an inner block, the inner declaration takes precedence over the outer one and determines the meaning of the identifier for the duration of the inner block. For example, if you declare a variable called MaxValue in the interface section of a unit, and then declare another variable with the same name in a function declaration within that unit, any unqualified occurrences of MaxValue in the function block are governed by the second, local declaration. Similarly, a function declared within another function creates a new, inner scope in which identifiers used by the outer function can be redeclared locally.

The use of multiple units further complicates the definition of scope. Each unit listed in a uses clause imposes a new scope that encloses the remaining units used and the program or unit containing the uses clause. The first unit in a uses clause represents the outermost scope and each succeeding unit represents a new scope inside the previous one. If two or more units declare the same identifier in their interface sections, an unqualified reference to the identifier selects the declaration in the innermost scope, that is, in the unit where the reference itself occurs, or, if that unit does not declare the identifier, in the last unit in the uses clause that does declare the identifier.

The System and SysInit units are used automatically by every program or unit. The declarations in System, along with the predefined types, routines, and constants that the compiler understands automatically, always have the outermost scope.

You can override these rules of scope and bypass an inner declaration by using a qualified identifier (see "Qualified Identifiers" in Fundamental Syntactic Elements (Delphi)) or a with statement (see "With Statements" above.)

See Also