Подтвердить что ты не робот

LINQ Skip все еще перечисляет пропущенные объекты

В следующем тесте:

int[] data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
Func<int, int> boom = x => { Console.WriteLine(x); return x; };
var res = data.Select(boom).Skip(3).Take(4).ToList();
Console.WriteLine();
res.Select(boom).ToList();

Результат:

1
2
3
4
5
6
7

4
5
6
7

По существу, я заметил, что в этом примере Skip() и Take() работают хорошо, Skip() не так ленив, как Take(). Кажется, что Skip() все еще перечисляет элементы, пропущенные, даже если они не возвращают их.

То же самое относится, если я сначала Take(). Мое лучшее предположение состоит в том, что ему нужно перечислить, по крайней мере, первый пропустить или принять, чтобы посмотреть, куда идти со следующей.

Почему он это делает?

4b9b3361

Ответ 1

Skip() и Take() работают на IEnumerable<>.

IEnumerable<> не поддерживает пропуски вперед - он может давать только один элемент за раз. Имея это в виду, вы можете думать о Skip() больше как о фильтре - он по-прежнему касается всех элементов в исходной последовательности, но он отфильтровывает все, о чем вы говорите. И что важно, он отфильтровывает их от получения до следующего, а не того, что перед ним.

Итак, сделав это:

data.Select(boom).Skip(3)

Вы выполняете boom() для каждого элемента перед тем, как перейти к фильтру Skip().

Если вы изменили его на это, он будет фильтровать до Select, и вы вызываете boom() только на оставшиеся элементы:

data.Skip(3).Take(4).Select(boom)

Ответ 2

Если вы декомпилируете Enumerable, вы увидите следующую реализацию Skip:

while (count > 0 && e.MoveNext())
  --count;

и следующую реализацию Take:

foreach (TSource source1 in source)
{
  yield return source1;
  if (--count == 0) break;
}

Итак, оба этих метода LINQ фактически перечисляют эти элементы. Разница заключается в том, будет ли перечисленный элемент помещен в результирующую коллекцию или нет. То, как работает IEnumerable.

Ответ 3

Они итерации коллекции. А затем явка с окончательной коллекцией. Его просто как простой for loop, имеющий в нем условие if else.

Кроме того, вы выбираете его сначала, используя boom, он печатает элемент коллекции.

В этом примере вы не можете сказать, выполняет ли skip() или take() всю коллекцию или нет, но дело в том, что они делают.

Ответ 4

Предполагая, что я правильно понял ваш вопрос.

функция прыжка выполняется перед выполнением операции пропуска.

попробуйте выбрать данные после пропустить и взять, вы получите точное.

Ответ 5

Такое поведение, возможно, будет изменено в будущем, как можно видеть здесь в этом обсуждении о запросе Pull, который оптимизирует поведение Пропустить метод для источников, реализующих интерфейс IList.

Ответ 6

Не ответ, но подумайте о том, как Skip(...) будет реализован. Вот один из способов сделать это:

public static IEnumerable<T> Skip<T>(this IEnumerable<T> list, int count)
{
    foreach (var item in list)
    {
        if (count > 0) count--;
        else yield return item;
    }
}

Обратите внимание, что весь аргумент list указан, хотя возвращается только подмножество.