Enumerators & for-in

for X in Container do works for arrays, strings, sets, and any type that provides GetEnumerator, returning an object or record with:

32.1 What for-in needs

for X in Container do works for arrays, strings, sets, and any type that provides GetEnumerator, returning an object or record with:

a function MoveNext: Boolean a property Current: T (read-only)

32.2 A range enumerator

program RangeEnum;
{$IFDEF FPC}{$mode delphi}{$ENDIF}
{$APPTYPE CONSOLE}

type
  TRangeEnumerator = record
  private
    FCurrent, FLast, FStep: Integer;
  public
    function MoveNext: Boolean;
    property Current: Integer read FCurrent;
  end;

  TRange = record
    First, Last, Step: Integer;
    function GetEnumerator: TRangeEnumerator;
  end;

function TRangeEnumerator.MoveNext: Boolean;
begin
  Inc(FCurrent, FStep);
  Result := FCurrent <= FLast;
end;

function TRange.GetEnumerator: TRangeEnumerator;
begin
  Result.FCurrent := First - Step;    // MoveNext is called before the first Current
  Result.FLast := Last;
  Result.FStep := Step;
end;

function Range(AFirst, ALast: Integer; AStep: Integer = 1): TRange;
begin
  Result.First := AFirst;
  Result.Last := ALast;
  Result.Step := AStep;
end;

var
  I: Integer;
begin
  for I in Range(0, 20, 5) do
    Write(I, ' ');                    // 0 5 10 15 20
  Writeln;
end.

32.3 Enumerating a custom collection

type
  TNode = class
    Value: string;
    Next: TNode;
  end;

  TLinkedList = class;

  TLinkedListEnumerator = class
  private
    FCurrent: TNode;
    FStarted: Boolean;
    FList: TLinkedList;
    function GetCurrent: string;
  public
    constructor Create(AList: TLinkedList);
    function MoveNext: Boolean;
    property Current: string read GetCurrent;
  end;

  TLinkedList = class
  private
    FHead: TNode;
  public
    procedure AddFirst(const S: string);
    function GetEnumerator: TLinkedListEnumerator;   // for-in frees a class enumerator automatically
  end;

constructor TLinkedListEnumerator.Create(AList: TLinkedList);
begin
  inherited Create;
  FList := AList;
end;

function TLinkedListEnumerator.MoveNext: Boolean;
begin
  if not FStarted then
  begin
    FCurrent := FList.FHead;
    FStarted := True;
  end
  else
    FCurrent := FCurrent.Next;
  Result := FCurrent <> nil;
end;

function TLinkedListEnumerator.GetCurrent: string;
begin
  Result := FCurrent.Value;
end;

procedure TLinkedList.AddFirst(const S: string);
var
  N: TNode;
begin
  N := TNode.Create;
  N.Value := S;
  N.Next := FHead;
  FHead := N;
end;

function TLinkedList.GetEnumerator: TLinkedListEnume
…

32.4 Built-in enumerable types

static/dynamic arrays members in ordinal order TList, TCollection, TComponent (owned components) TList<T>, TQueue<T>, TStack<T>

TPair<K,V>; also .Keys and .Values FPC: any type with an operator enumerator

Do not modify a collection (add/remove items) while enumerating it — collections raise exceptions or behave unpredictably. Iterate backwards with an index loop when deleting.