Если я правильно понимаю ваш вопрос, вы пытаетесь создать ленивую реализацию перечислителя, которая разбивает большую коллекцию элементов на меньшие перечисляемые коллекции.
Например, последовательность из миллиона чисел может быть разбита на «секции», каждая из которых производит только 100 из них, и вы хотите, чтобы все это выполнялось лениво, т.е. не собирать 100 предметов в список до их изготовления.
Во-первых, ваши попытки будут многократно повторять коллекцию, что плохо, поэтому возникает проблема с производительностью.
Если вы пытаетесь создать чистую ленивую реализацию, вам следует рассмотреть следующие вопросы:
- Вы хотите перебрать базовую коллекцию только один раз
- Вы должны вернуть перечислимые элементы, которые повторно используют базовый перечислитель
- Вам необходимо учитывать, что возвращаемый вами раздел не полностью перечислен (например, для кода вызова требовались только первые 50 из этих 100 элементов).
Редактировать : Прежде чем перейти к своему упрощенному решению, вот несколько предостережений по этому поводу:
- Вы не можете сохранить каждый раздел на потом, т.е. вы не можете сделать:
collection.Sequence(10).ToArray()
, чтобы получить массив разделов.
- Нельзя перечислять по каждому разделу более одного раза, поскольку при этом изменяется скрытая базовая структура данных.
В основном: Мое решение не общего назначения . Вы должны пойти с комментарием @ LBushkin о MoreLinq Batch, если вам это нужно, и я без колебаний поместил бы мой код в библиотеку классов, где он должен быть локальным необходимо или переименовано во что-то, что четко предупреждает вас о проблемах с ним.
Вот упрощенная реализация, я вполне уверен, что здесь есть ошибки, поэтому вы можете захотеть взглянуть на реализацию тонны модульного тестирования для крайних случаев:
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApplication20
{
class SectionEnumerable<T> : IEnumerable<T>
{
private readonly IEnumerator<T> _Enumerator;
public SectionEnumerable(IEnumerator<T> enumerator, int sectionSize)
{
_Enumerator = enumerator;
Left = sectionSize;
}
public IEnumerator<T> GetEnumerator()
{
while (Left > 0)
{
Left--;
yield return _Enumerator.Current;
if (Left > 0)
if (!_Enumerator.MoveNext())
break;
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public int Left { get; private set; }
}
static class SequenceExtensions
{
public static IEnumerable<IEnumerable<T>> Section<T>(this IEnumerable<T> collection, int sectionSize)
{
if (collection == null)
throw new ArgumentNullException("collection");
if (sectionSize < 1)
throw new ArgumentOutOfRangeException("sectionSize");
using (IEnumerator<T> enumerator = collection.GetEnumerator())
{
while (enumerator.MoveNext())
{
SectionEnumerable<T> enumerable = new SectionEnumerable<T>(enumerator, sectionSize);
yield return enumerable;
for (int index = 0; index < enumerable.Left; index++)
if (!enumerator.MoveNext())
yield break;
}
}
}
}
class Program
{
static void Main(string[] args)
{
var sequence = Enumerable.Range(0, 100);
var sections = sequence.Section(10);
foreach (var section in sections)
{
Console.WriteLine(
String.Join(", ",
section.Take(5).ToArray().Select(i => i.ToString()).ToArray()));
}
Console.ReadLine();
}
}
}
Выход:
0, 1, 2, 3, 4
10, 11, 12, 13, 14
20, 21, 22, 23, 24
30, 31, 32, 33, 34
40, 41, 42, 43, 44
50, 51, 52, 53, 54
60, 61, 62, 63, 64
70, 71, 72, 73, 74
80, 81, 82, 83, 84
90, 91, 92, 93, 94
Вещи, которые вы должны тестировать:
- Пустой входной набор не содержит разделов
- Коллекция, имеющая только правильное количество элементов, производит только один раздел
- Коллекция, содержащая множество элементов размера раздела (т. Е. 10, 20, 30 и т. Д. Количество элементов с размером раздела 5 или 10), не создает пустой раздел после всех ожидаемые
- То, что на самом деле лениво, если вы перечислите по первому 10-элементному разделу, но только по первым 5 из второго раздела, только первые 15 элементов базовой коллекции будут перечислены по