Is there a foreach generic method in Delphi for that can be called with anonymous function

Mihaela picture Mihaela · Mar 9, 2012 · Viewed 15.2k times · Source

I can do the following in C++

for_each(vec->begin(), vec->end(), [](int n){cout << n << " " << endl;});

I would like to do the same/similar in Delphi. I asked a question earlier to C++ developers, and wanted to produce similar example in OOP Pascal. The lambda (anonymous method) part of the question is not relevant really, but I was wondering If I could do the same in Delphi XE2.

I'm asking because I have XE, and I'm not sure whether it has been added. Thanks.

Answer

David Heffernan picture David Heffernan · Mar 9, 2012

Probably the closest Delphi analogue to std::vector<T> is TList<T>. You can iterate over the list with a for in loop:

var
  Item: Integer;
  List: TList<Integer>;
....
for Item in List do
  Writeln(Item);

If you have a dynamic array rather than a TList<T> then you can use for in to iterate over the elements. In fact, all the built in containers support for in and it is easy to add support for for in to your own classes.

In C++ there is nothing like a for in loop and so the idiom is to use an STL algorithm. That's what drives you to using an anonymous function. In Delphi with the for in syntax you can express "iterate over all members of the container" in a natural way without resorting to anonymous methods.

Generics were added to Delphi in Delphi 2009 and the for in loop was added in Delphi 2005, so all this is available to you in XE. For what it's worth, anonymous were also added in Delphi 2009.

What you must realise is that Delphi generics are less powerful than C++ templates. Although you talk about a generic foreach, your code is not generic in the sense that it has specialised to int. You could write a generic version of your code in C++ but this would be much harder to do with Delphi generics due to the inherent limitations of generics when compared to templates. An attempt to write the above code in a generic way in Delphi would founder at the point where you tried to call Writeln. Whilst that would be trivial with C++ templates it is frustratingly out of reach for generics.

Update: In the comments you ask if there is a slick way to add the contents of one container to another. The AddRange method does that. TList<T>.AddRange() has three overloaded variants that receive one of the following input parameters: array of T, Collection: IEnumerable<T> or Collection: TEnumerable<T>. All the standard generic containers follow a similar pattern.