Оптимизация C # LINQ

#c# #linq #optimization

#c# #linq #оптимизация

Вопрос:

Каков был бы правильный способ оптимизации следующего вида операторов:

 IEnumerable<T> sequence = BuildSequence();
// 'BuildSequence' makes some tough actions and uses 'yield return'
// to form the resulting sequence.
  

Теперь, если я готов использовать только некоторые из первых элементов, я мог бы использовать что-то вроде:

 sequence.Take(5);
  

Итак, если моя последовательность из BuildSequence на самом деле содержит несколько тысяч элементов, я, очевидно, не хочу, чтобы все они были сконструированы, потому что мне понадобилось бы всего 5 из них.

LINQ Оптимизирует ли этот вид операций или мне придется что-то изобретать самому?

Ответ №1:

Блок итератора ( yield return ) обрабатывает это за вас; блок итератора — это потоковый API; работа происходит только во время каждого MoveNext() вызова итератора (или на Dispose() ). Поскольку Take() также не считывается весь поток, это поведение сохраняется.

Однако обратите внимание, что для некоторых операций требуется локальная буферизация данных — GroupBy и OrderBy это наиболее заметно; но пока вы используете операции без буферизации, все в порядке.

Например:

 static IEnumerable<int> ReadInts() {
    var rand = new Random();
    while(true) yield return rand.Next();
}
...
int count = ReadInts().Take(10).Count(); // 10 - it doesn't loop forever
  

Комментарии:

1. Этот цикл while выглядит довольно пугающим на первом сайте, и, честно говоря, я мог видеть, как некоторые разработчики сходят с ума, если они видят это в исходном коде и не понимают, как работает перечисление / выход.

2. @m-y почти достаточная причина, чтобы добавить это в код — дать им повод волноваться и учиться

Ответ №2:

Вам следует взглянуть на это:

LINQ и отложенное выполнение

Комментарии:

1. строго говоря, это больше относится к буферизованному / небуферизованному, чем к отложенному. Отложенная, но буферизованная операция все равно должна была бы использовать весь набор данных … просто «не совсем сейчас».