如果你有startIndex
和count
,你如何在LINQ中翻阅集合?
答案 0 :(得分:61)
使用Skip
和Take
扩展方法非常简单。
var query = from i in ideas
select i;
var paggedCollection = query.Skip(startIndex).Take(count);
答案 1 :(得分:39)
几个月前,我写了一篇关于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代码库页面获取代码:Pipelines, Filters, Fluent API and LINQ to SQL。
答案 2 :(得分:12)
我解决这个问题的方式与其他人有所不同,因为我必须使用转发器制作自己的分页器。所以我首先为我拥有的项目集合制作了一组页码:
// 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
}
}
答案 3 :(得分:9)
这个问题有些陈旧,但我想发布我的分页算法,显示整个过程(包括用户交互)。
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
时,您可以证明自己,以便产生副作用。 - &GT;即使你已经跳过了10-20和20-30并想要处理40+,你会看到10-30的所有副作用再次被执行,然后再开始迭代40+。
直接使用IEnumerable
接口的变体将记住最后一个逻辑页面结尾的位置,因此不需要显式跳过,也不会重复副作用。