Как вы просматриваете коллекцию в LINQ, если у вас есть a startIndex
и a count
?
86
Несколько месяцев назад я написал сообщение в блоге о Fluent Interfaces и LINQ, в котором использовались метод расширения IQueryable<T>
и другой класс, чтобы обеспечить следующий естественный способ разбивки на страницы коллекции LINQ.
var query = from i in ideas
select i;
var pagedCollection = query.InPagesOf(10);
var pageOfIdeas = pagedCollection.Page(2);
Вы можете получить код со страницы галереи кода MSDN: конвейеры, фильтры, Fluent API и LINQ to SQL .
Это очень просто с методами расширения Skip
и Take
.
var query = from i in ideas
select i;
var paggedCollection = query.Skip(startIndex).Take(count);
Я решил это немного иначе, чем другие, поскольку мне пришлось сделать свой собственный пагинатор с повторителем. Итак, я сначала сделал набор номеров страниц для коллекции элементов, которые у меня есть:
// assumes that the item collection is "myItems" int pageCount = (myItems.Count + PageSize - 1) / PageSize; IEnumerable<int> pageRange = Enumerable.Range(1, pageCount); // pageRange contains [1, 2, ... , pageCount]
Используя это, я мог легко разделить коллекцию элементов на набор «страниц». Страница в этом случае - это просто набор элементов (
IEnumerable<Item>
). Вот как вы можете сделать это, используяSkip
иTake
вместе с выбором индекса изpageRange
созданного выше:IEnumerable<IEnumerable<Item>> pageRange .Select((page, index) => myItems .Skip(index*PageSize) .Take(PageSize));
Конечно, вы должны обрабатывать каждую страницу как дополнительную коллекцию, но, например, если вы размещаете повторители, это действительно легко сделать.
Один вкладыш TLDR версия будет таким:
var pages = Enumerable .Range(0, pageCount) .Select((index) => myItems.Skip(index*PageSize).Take(PageSize));
Что можно использовать как это:
for (Enumerable<Item> page : pages) { // handle page for (Item item : page) { // handle item in page } }
источник
Этот вопрос несколько старый, но я хотел опубликовать свой алгоритм разбиения по страницам, который показывает всю процедуру (включая взаимодействие с пользователем).
const int pageSize = 10; const int count = 100; const int startIndex = 20; int took = 0; bool getNextPage; var page = ideas.Skip(startIndex); do { Console.WriteLine("Page {0}:", (took / pageSize) + 1); foreach (var idea in page.Take(pageSize)) { Console.WriteLine(idea); } took += pageSize; if (took < count) { Console.WriteLine("Next page (y/n)?"); char answer = Console.ReadLine().FirstOrDefault(); getNextPage = default(char) != answer && 'y' == char.ToLowerInvariant(answer); if (getNextPage) { page = page.Skip(pageSize); } } } while (getNextPage && took < count);
Однако, если вам нужна производительность, а в производственном коде мы все - производительность, вам не следует использовать разбиение на страницы LINQ, как показано выше, а базовый
IEnumerator
компонент для самостоятельной реализации разбиения на страницы. По сути, он так же прост, как и показанный выше алгоритм LINQ, но более производительный:const int pageSize = 10; const int count = 100; const int startIndex = 20; int took = 0; bool getNextPage = true; using (var page = ideas.Skip(startIndex).GetEnumerator()) { do { Console.WriteLine("Page {0}:", (took / pageSize) + 1); int currentPageItemNo = 0; while (currentPageItemNo++ < pageSize && page.MoveNext()) { var idea = page.Current; Console.WriteLine(idea); } took += pageSize; if (took < count) { Console.WriteLine("Next page (y/n)?"); char answer = Console.ReadLine().FirstOrDefault(); getNextPage = default(char) != answer && 'y' == char.ToLowerInvariant(answer); } } while (getNextPage && took < count); }
Объяснение: Обратной стороной
Skip()
многократного использования «каскадным способом» является то, что в действительности он не будет хранить «указатель» итерации, на которой он был пропущен в последний раз. - Вместо этого исходная последовательность будет предварительно загружена вызовами пропуска, что приведет к многократному "потреблению" уже "потребленных" страниц. - Вы можете убедиться в этом сами, создав последовательность,ideas
которая дает побочные эффекты. -> Даже если вы пропустили 10-20 и 20-30 и хотите обработать 40+, вы снова увидите все побочные эффекты 10-30, прежде чем начнете повторять 40+. Вариант, использующийIEnumerable
интерфейс напрямую, вместо этого запомнит позицию конца последней логической страницы, поэтому явный пропуск не требуется, и побочные эффекты не будут повторяться.источник