Reputation: 257153
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
?
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.
Upvotes: 7
Views: 17210
Reputation: 34947
How do i enumerate the objects in a TObjectList?
To answer the specific question, this is an example of introducing an enumerator.
Note that you will have to create a descendant of TObjectList
in order to add the GetEnumerator
function. You could do without subclassing with a class helper, but I leave that as an exercise for the interested reader.
type
TObjectListEnumerator = record
private
FIndex: Integer;
FList: TObjectList;
public
constructor Create(AList: TObjectList);
function GetCurrent: TObject;
function MoveNext: Boolean;
property Current: TObject read GetCurrent;
end;
constructor TObjectListEnumerator.Create(AList: TObjectList);
begin
FIndex := -1;
FList := AList;
end;
function TObjectListEnumerator.GetCurrent;
begin
Result := FList[FIndex];
end;
function TObjectListEnumerator.MoveNext: Boolean;
begin
Result := FIndex < FList.Count - 1;
if Result then
Inc(FIndex);
end;
//-- Your new subclassed TObjectList
Type
TMyObjectList = class(TObjectList)
public
function GetEnumerator: TObjectListEnumerator;
end;
function TMyObjectList.GetEnumerator: TObjectListEnumerator;
begin
Result := TObjectListEnumerator.Create(Self);
end;
This implementation of an enumerator uses a record instead of a class. This has the advantage of not allocating an extra object on the heap when doing for..in
enumerations.
procedure TfrmCustomerLocator.OnBatchDataAvailable(BatchList: TObjectList);
var
o: TObject;
begin
for o in TMyObjectList(BatchList) do // A simple cast is enough in this example
begin
//...snip...where we do something with (o as TCustomer)
end;
end;
As others have noted, there is a generics class that is a better option to use, TObjectList<T>
.
Upvotes: 4
Reputation: 1397
Using generics you can have a typed objectlist (just noticed the comment but ill finish this anyhow)
And if you're looking for a good reason to use a TObjectList<T>
that reason will be that it saves you a lot of type casting in your code
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, Generics.Collections;
Type
TCustomer = class
private
public
//Properties and stuff
end;
var
list: TObjectlist<TCustomer>;
c: TCustomer;
begin
list := TObjectList<TCustomer>.Create;
for c in list do
begin
//nothing
end;
end.
Upvotes: 5
Reputation: 613612
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:
TObjectList
, like everything in Contnrs
is deprecated and rendered obsolete by the generic containers in Generics.Collections
. Why spend resource modifying an obsolete class.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?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.
Upvotes: 3