If we're talking regular LINQ, then we're focusing on IEnumerable<T>
(LINQ-to-Objects) and IQueryable<T>
(LINQ-to-most-other-stuff). Since IQueryable<T> : IEnumerable<T>
, it is automatic that you can use foreach
- but what this means is very query-specific, since LINQ is generally lazily spooling data from an underlying source. Indeed, that source can be infinite:
public IEnumerable<int> Forever() {
int i = 0;
while(true) yield return i++;
}
...
foreach(int i in Forever()) {
Console.WriteLine(i);
if(Console.ReadLine() == "exit") break;
}
However, a for
loop requires the length and an indexer. Which in real terms, typically means calling ToList()
or ToArray()
:
var list = source.ToList();
for(int i = 0 ; i < list.Count ; i++) { do something with list[i] }
This is interesting in various ways: firstly, it will die for infinite sequences ;p. However, it also moves the spooling earlier. So if we are reading from an external data source, the for
/foreach
loop over the list will be quicker, but simply because we've moved a lot of work to ToList()
(or ToArray()
, etc).
Another important feature of performing the ToList()
earlier is that you have closed the reader. You might need to operate on data inside the list, and that isn't always possible while a reader is open; iterators break while enumerating, for example - or perhaps more notably, unless you use "MARS" SQL Server only allows one reader per connection. As a counterpoint, that reeks of "n+1", so watch for that too.
Over a local list/array/etc, is is largely redundant which loop strategy you use.