给定一个startIndex和一个,count如何在LINQ中浏览一个集合?
Answers:
几个月前,我写了一篇有关Fluent接口和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。
我解决了这个问题,这与其他人用中继器制作自己的分页器所需要的解决方案有所不同。因此,我首先收集了我拥有的物品的页码:
// 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+,在开始迭代40+之前,您将再次看到10-30的所有副作用。使用的变体IEnumerable直接的接口将记住最后一个逻辑页面末尾的位置,因此不需要显式跳过并且不会重复出现副作用。