Search code examples
delphifor-loopiteratorfor-in-looplistiterator

How to do for in TObjectList?


I am trying to use for in to iterate a TObjectList:

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils, Contnrs;

var
    list: TObjectlist;
    o: TObject;
begin
    list := TObjectList.Create;
    for o in list do
    begin
        //nothing
    end;
end.

And it fails to compile:

[dcc32 Error] Project1.dpr(15): E2010 Incompatible types: 'TObject' and 'Pointer'

It seems as though Delphi's for in construct does not handle the untyped, undescended, TObjectList an as enumerable target.

How do i enumerate the objects in a TObjectList?

What i do now

My current code is:

procedure TfrmCustomerLocator.OnBatchDataAvailable(BatchList: TObjectList);
var
   i: Integer;
   o: TObject;
begin
   for i := 0 to BatchList.Count-1 do
   begin
      o := BatchList.Items[i];

      //...snip...where we do something with (o as TCustomer)
   end;
end;    

For no good reason, i was hoping to change it to:

procedure TfrmCustomerLocator.OnBatchDataAvailable(BatchList: TObjectList);
var
   o: TObject;
begin
   for o in BatchList do
   begin
      //...snip...where we do something with (o as TCustomer)
   end;
end;    

Why use an enumerator? Just cause.


Solution

  • The enumerator for TObjectList is declared in TList, which is the class from which TObjectList is derived. The RTL doesn't bother to declare a more specific enumerator for TObjectList. So you are left with the TList enumerator. Which yields items of type Pointer, that being the type of item held by TList.

    There are probably a few reasons why the RTL designers chose not to do anything with TObjectList. For example, I posit the following potential reasons:

    1. TObjectList, like everything in Contnrs is deprecated and rendered obsolete by the generic containers in Generics.Collections. Why spend resource modifying an obsolete class.
    2. Even if TObjectList had an enumerator that yielded TObject items, you'd still have to cast them. Would an enumerator that yielded TObject really be that much more helpful?
    3. The designers simply forgot that this class existed when they were adding enumerators.

    What should you do. An obvious choice is to use TList<T> or TObjectList<T> from Generics.Collections. If you want to persist with TObjectList, you could sub-class it and add an enumerator that yielded TObject. In case you don't know how to do that, you can find out how to do so from the documentation. Or you could use the inherited enumerator and type cast the pointers that it yields.

    It seems to me that since you are prepared to modify the code from an indexed for loop to a for in loop that implies that you are prepared to make non-trivial changes to the code. In which case the generic container would seem to be the obvious choice.