Enumerables in Delphi

It is not a surprise that most programming languages in our time have the built-in support for enumerators/iterators. This is a mandatory feature since enumerators and enumerable collections simplify the development of applications and make the code cleaner. Languages such as C#, Java or Delphi have built-in syntax constructs that allow simplified and clean ways of enumerating over a collection by the means of  “foreach” or special “for” loops.

In Delphi, enumerators have been added to the language for some time now in form of a special “for” loop (you can read in the Help about it more). It allows enumerating elements of an array, string and in most collection classes. While in the case of intrinsic types, the compiler simply transforms the loop into an ordinary “for” loop, in the case of collection classes things become more interesting. But let’s take each case separately:

Enumerating over a String:

  LString: String;
  LChar: Char;
  LString := 'Hello World';

  for LChar in LString do

It’s simple as that! It does not have such a negative impact on the speed and make your code look much cleaner, it also helps avoid using an index variable. The same method can be applied for AnsiString, WideString and ShortString.

Enumerating over an Array:

  LArr: TBytes;
  LByte: Byte;
  LArr := TBytes.Create(1, 2, 3, 4, 5);

  for LByte in LArr do

Looks simple. And indeed it is! There are more use cases for different types of arrays, but you can find that in the Help.

Enumerating over a collection:

  List: TList<Integer>;
  I: Integer;
  List := TList<Integer>.Create();
  List.AddRange([1, 2, 3, 4, 5]);

  for I in List do

In this case the overhead for the “for” loop is higher since a call to List.GetEnumerator() is made to obtain an enumerator object. Then at each iteration the MoveNext() and Current() methods are called on the enumerator to move to the next element in the list and retrieve its value.

There are in fact only a few rules that you must abide in order to support enumeration in your collections:

  1. You must have either a class, interface or record type.
  2. Your class, interface or record must expose a GetEnumerator() function that returns an enumerator.
  3. The enumerator can be either a class, interface or a record type.
  4. The enumerator must expose a MoveNext() function that returns a Boolean and a Current property that returns the current element.
  5. When the enumerator is created there is no current element selected. Only after the first call to MoveNext() your enumerator must select the first element in the collection.
  6. MoveNext() must return true if the next element was selected or false if the collection is finished.

Extreme case — enumerating over a record using a record enumerator:

  { The enumerator record }
  TRecordEnumerator = record
    FArray: TBytes;
    FIndex: Integer;

    function GetCurrent: Byte;
    function MoveNext(): Boolean;
    property Current: Byte read GetCurrent;

  { The record/collection that will be enumerated }
  TRecordCollection = record
    FArray: TBytes;
    function GetEnumerator(): TRecordEnumerator;

{ TRecordCollection }

function TRecordCollection.GetEnumerator: TRecordEnumerator;
  Result.FArray := FArray;
  Result.FIndex := -1;

{ TRecordEnumerator }

function TRecordEnumerator.GetCurrent: Byte;
  Result := FArray[FIndex];

function TRecordEnumerator.MoveNext: Boolean;

  if FIndex >= Length(FArray) then


  LColl: TRecordCollection;
  B: Byte;

  LColl.FArray := TBytes.Create(1, 2, 3, 4, 5, 6);

  for B in LColl do


The compiler doesn’t really care what it is enumerating and what it uses to do that. It simply must find the required methods exposed in the enumerated collection and in the enumerator.


  1. It’s a Delphi for .Net feature, so it is in the language from the first version of Delphi that included a .Net personality. It isn’t exclusive to .Net of course. You can use it in the Win32 personality as well.

  2. Bare in mind also, that templates was introduced in Delphi 2009, so the specific example that uses templates (the one with the TList variable) won’t work in earlier versions, even though the enumerable language feature is available.

  3. No 🙂
    All Delphi versions (including the Win32 one) starting with Delphi.NET times.

    Enumerators were present in Delphi.NET and support was added to win32 version to make the languages compatible.

  4. The first version that supported .Net would have been Delphi 8 according to wikipedia. But wikipedia says that there was no Win32 version of Delphi 8, so the first Win32 version of Delphi where these enumerators are available is Delphi 2005, as far as I can see.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.