Если мы говорим о обычном LINQ, то мы фокусируемся на IEnumerable<T>
(LINQ-to-Objects) и IQueryable<T>
(LINQ-to-большинству других вещей).Начиная с IQueryable<T> : IEnumerable<T>
, вы можете автоматически использовать foreach
, но это означает, что очень специфично для запроса, поскольку LINQ обычно лениво спулингует данные из базового источника.Действительно, этот источник может быть бесконечным:
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;
}
Однако для цикла for
требуется длина и индексатор .Что в реальном выражении обычно означает вызов ToList()
или ToArray()
:
var list = source.ToList();
for(int i = 0 ; i < list.Count ; i++) { do something with list[i] }
Это интересно по-разному: во-первых, он умрет для бесконечных последовательностей; p .Тем не менее, это также перемещает буферизацию раньше.Так что если мы будем читать из внешнего источника данных, цикл for
/ foreach
по списку будет быстрее, но просто потому, что мы переместили большую работу на ToList()
(илиToArray()
и т. Д.)
Еще одна важная особенность выполнения ToList()
ранее заключается в том, что вы закрыли считыватель .Вам может нужно работать с данными внутри списка, и это не всегда возможно, когда читатель открыт;например, итераторы ломаются при перечислении - или, что еще важнее, если вы не используете «MARS», SQL Server допускает только одно чтение на соединение.В качестве контрапункта, который пахнет "n + 1", так что следите за этим тоже.
Для локального списка / массива / и т. Д., В значительной степени избыточно, какую стратегию цикла вы используете.