Thursday, September 25, 2008

A simple Generic List: TList<T>

Delphi 2009 has support for generics, and has 'built in' generic types like TList, TArrays etc. If you want to use them you must add Generics.Collections to your uses clause.

Generic List: TList
Suppose we want a list with persons representing the TPerson class:

// Our class TPerson
type
TPerson
= class
FFirstName : string;
FLastName : string;
private
function GetFullName: string;
published
public
constructor Create(ALastName : String;
AFirstName : string);
property FirstName : string read FFirstName
write FFirstName;
property LastName : string read FLastName
write FLastName;
property FullName : string read GetFullName;
end;

//Define the generic list
PersonsList : TList<TPerson>;

//Method to fill the list
procedure TForm4.btnPopulateClick(Sender: TObject);
begin
//Filll the personslist
PersonsList.Add(TPerson.Create('Delphi','James'));
PersonsList.Add(TPerson.Create(
'Generic','Mister'));
PersonsList.Add(TPerson.Create(
'Anon', 'Method'));
//etc.
end;

//Traveling this list to fill a TListBox
procedure TForm4.FillListBox;
var
p : TPerson;
begin
ListBox1.Items.Clear;
for p in PersonsList do begin
ListBox1.Items.Add(p.FullName);
end;
end;

As you can see, no rocking science needed to implement a generic list!

Sorting a generic TList
The next step is to implement a sorting method to sort the list on, for example, the Lastname property.

A TList has a Sort property which has an IComparer interface that can be implemented like this using an anonymous method:

(You will have to add generics.default to your uses clause)
procedure TForm4.btnSortClick(Sender: TObject);
begin
PersonsList.Sort(TComparer
<TPerson>.Construct(
function(const Item1,Item2:TPerson): Integer
begin
Result :
=
CompareText(Item1.LastName, Item2.LastName);
end));
end;

Generics is a great new feature of Delphi 2009. Just as in C# you will use them all the time. In this context, the use of anonymous methods is great.

Besides TList Delphi has more standard generics typs including TArray, TEnumarable and more, so there is always more to explore!

Update 25-09-2008 10:58
As animal commented you must of course create the list first, which I did not include in the code snippets.

PersonsList:= TList.Create;

And you will have to free it also at sometime. (Don't forget to free the TPerson objects as well!)

for p in PersonsList do begin
p.free;
end;
PersonsList.Free;

11 comments:

Oliver said...

I guess you left out the construction/deconstruction for TList<TPerson> for briefness only, didn't you?

Olaf Monien said...

There are two things that should be mentioned in that context:

- the non-generic versions of all these container classes still exist (in Contnrs.pas), in other words the non-generic and generic classes are "parallel" implementations.

- the generic version of TList is of course no longer pointer based, but the fully object aware predecessor of TObjectList.

Roland Beenhakker said...

@animal
Of course you are right that you will have to create the TList and free it.
Added it to the post.

Anonymous said...

So is there TObjectList{T} or something similar to handle object lifetime?

Bruce McGee said...

@anonymous,

Yes.

TList{T}, TQueue{T}, TStack{T} and TDictionary{TKey, TValue} all have object counterparts to manage lifetimes. e.g. TObjectList{T}

Anonymous said...

And what about Thread safety and ThreadLists, InterfaceLists etc?

Just thought that TList{T} should work ok with interfaces!?

Olaf Monien said...

no, there is no generic counterpart for TThreadList.

Anonymous said...

Great article, that's the first useful use of anonymous methods in Delphi I've seen.

Anonymous said...

Great article!
But D2009 IDE shows the code hint:
PersonsList.Add[]; // square bracket, not parenthesis.
This is a low-level bug.

Anonymous said...

In update 25-09-2008, where you say:

PersonsList:= TList.Create;

...you probably meant:

PersonsList:= TList<TPerson>.create

But your blog software removed what it thought was an unrecognized html tag.

Anonymous said...

Excellent - simple code that actually works!
One typo - Uses clause should contain Generics.Defaults not generics.default

Use an image as your UIBarButtonItem

Using an image as your UIBarButtonItem in your navigationcontroller bar can only be achieved by using a common UIButton as the BarButtonItem...