TList
and TOjectList
in Generics.Collections
have a .List
property, which is an enumerator.
For instance:
oList := TObjectList<TItem>.Create;
// Add items to oList
for Item in oList.List do begin
// Do something with Item
end;
This is neat, but has a drastic consequence. .List
just reads FList
(a private declaration on TList
and TObjectList
), which is merely an arrayofT
.
Since a dynamic array is doubled in size whenever an item is added beyond its size, it means it has space for non-used items.
In case you have added 3 TItem
s, the actual FList
is 4 items long, with the fourth (and last) item being nil
.
Thus using TObjectList
's .List
is unsafe, because it will likely throw an access violation, if your TObjectList
doesn't have a .Count
value with a power of 2 (e.g. 1, 2, 4, 8, 16, etc.).
The following code could likely throw an access violation:
for Item in oList.List do begin
Writeln(Item.ClassName);
end;
Of course, the safe solution is a simple iteration using .Count
:
for I := 0 to oList.Count - 1 do begin
Item := oList.Items[I];
Writeln(Item.ClassName);
end;
This is just not as pretty as the enumerator. (You can also check if Item
is nil
, of course.)
My questions are thus:
.List
isn't an actual enumerator?TList
/TObjectList
have an actual enumerator?Here is an example from a TForm
(where btn1
simply adds a line and mmo1
is a TMemo
).
procedure TForm2.btn1Click(Sender: TObject);
var
Line: string;
begin
Line := 'Line';
mmo1.Lines.Add(Line);
fList.Add(Line);
mmo1.Lines.Add(Format('Count: %d; Actual length: %d', [fList.Count, Length(fList.List)]));
for Line in fList.List do begin
mmo1.Lines.Add(Format('Found: "%s"', [Line]));
end;
end;
Now, using string
does not throw an access violation. But when I have clicked 3 times, I get the following:
Count: 3; Actual length: 4
Found: "Line"
Found: "Line"
Found: "Line"
Found: ""
When to Use Generic Collections. Using generic collections is generally recommended, because you gain the immediate benefit of type safety without having to derive from a base collection type and implement type-specific members.
Several generic collection types do not have nongeneric counterparts. They include the following: LinkedList<T> is a general-purpose linked list that provides O (1) insertion and removal operations.
Delphi has long had generic collections, like TList<T> but it also retains the old style TList. Which should you use? Since Delphi got generic types, in the 2009 version, its runtime library (RTL) has a generic collections unit.
Using the wrong type can restrict your use of the collection. Learn how to use built-in C# iterators and how to create your own custom iterator methods. Learn about C# properties, which include features for validation, computed values, lazy evaluation, and property changed notifications. This chapter defines arrays.
TList<T>
andTOjectList<T>
inGenerics.Collections
have aList
property, which is an enumerator.
No that is not so. The List
property is a dynamic array. And dynamic arrays have built in support for enumeration.
Since a dynamic array is doubled in size whenever an item is added beyond its size, it means it has space for non-used items.
That is also not true. Dynamic arrays are not automatically resized. They have to be explicitly resized with a call to SetLength
. The TList<T>
class manages capacity of the underlying dynamic array that stores the list content using calls to SetLength
.
Why is
List
not an actual enumerator?
The List
property was added recently, in XE3 if I recall. Its purpose is to allow direct access to the underlying list which is possible no other way.
Sometimes for the sake of efficiency you might prefer to avoid taking copies if you want to modify the content of the list. For instance suppose your list contains records that are 1KB in size. If you want to modify a single boolean within each record, without using the List
property, you end up copying the entire 1KB record twice. Just to modify one boolean.
Of course the cost for gaining access to the underlying storage is that you are exposed to the internal implementation details. Embarcadero could have implemented functionality that would give you this access in a safer way but for whatever reason they chose this route. I think I might have made an indexed property that returns a pointer to an item.
So, that is really the only use case for the List
property. Unless you really need direct access to the underlying storage, do not use List
. Of course, it would have been nice had the documentation bothered to explain any of this, but there it is.
Does
TList<T>
have an actual enumerator?
Yes it does.
var
Item: SomeType;
MyList: TList<SomeType>;
....
for Item in MyList do
Item.Foo();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With