如果您有一个集合,如何在 LINQ 中分页 startIndex 和一个 count?

有帮助吗?

解决方案

几个月前,我写了一篇关于 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.

其他提示

这非常简单 SkipTake 扩展方法。

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>)。这是你可以使用的方法 SkipTake 以及从中选择索引 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的接口直接,而是会记住最后一个逻辑页面末尾的位置,因此不需要显式跳过,并且不会重复副作用。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top