我有以下代码:
class Program
{
static void Main(string[] args)
{
foreach (var item in GetEnumerable().Skip(100))
{
Console.WriteLine(item);
}
}
static IEnumerable<int> GetEnumerable(int? page = null, int limit = 10)
{
var currentPage = page ?? 1;
while (true)
{
Thread.Sleep(1000); // emulates slow retrieval of a bunch of results
for (int i = limit * (currentPage - 1); i < limit * currentPage; i++)
{
yield return i;
}
currentPage++;
}
}
}
我希望能够使用.Skip(n)
有效地跳过我不需要的结果。因此,例如,如果我使用Skip(100)
并且每个请求检索10个项目,则应完全跳过前10个请求。
我可以用一种模式来实现这个目标吗?
答案 0 :(得分:3)
您可以创建自己的IEnumerable<int>
类型并提供自己的Skip
实施:
public class PagedEnumerable : IEnumerable<int>
{
private readonly int currentPage;
private readonly int limit;
public PagedEnumerable(int currentPage, int limit)
{
this.currentPage = currentPage;
this.limit = limit;
}
public PagedEnumerable Skip(int count)
{
int pages = count / this.limit;
return new PagedEnumerable(this.currentPage + pages, this.limit);
}
public IEnumerator<int> GetEnumerator()
{
int pageNo = this.currentPage;
while (true)
{
Thread.Sleep(1000);
for (int i = this.limit * (pageNo - 1); i < (this.limit * pageNo); i++)
{
yield return i;
}
pageNo++;
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
然后,您可以将GetEnumerable
替换为:
static PagedEnumerable GetEnumerable(int? page = null, int limit = 10)
{
var currentPage = page ?? 1;
return new PagedEnumerable(currentPage, limit);
}
答案 1 :(得分:2)
如果您想对“页面”进行延迟评估,则不应在while循环中加载它。
相反,你可以做的是返回一个lambda并从那里返回页面,就像这样。
// return a list of funcs, where each one returns a loaded page
static IEnumerable<Func<int>> GetEnumerable(int? page = null, int limit = 10)
{
var currentPage = page ?? 1;
while (true)
{
for (int i = limit * (currentPage - 1); i < limit * currentPage; i++)
{
yield return () => {
Thread.Sleep(1000);
return i;
};
}
currentPage++;
}
}
从消费者线程中,您可以简单地执行返回的函数来获取页面。
foreach (var item in GetEnumarable().Skip(100).Take(10))
{
Console.WriteLine(item());
}
答案 2 :(得分:1)
这是我对此的看法。我使用lambda表达式构建了Patrick的想法,但我已经修复了它,以便仅在需要时评估每个批处理,并且不超过一次。
static IEnumerable<Func<int>> GetEnumerable(int? page = null, int limit = 10)
{
var currentPage = page ?? 1;
while (true)
{
var thisPage = currentPage;
List<int> thisPageResult = null;
// Function that evaluates this batch and returns the result
Func<List<int>> getPageResult = () =>
{
// only evaluate this batch once
if (thisPageResult == null)
{
// slow retrieval of a bunch of results happens here
Thread.Sleep(1000);
// store the result for future calls
thisPageResult = Enumerable.Range(limit * (thisPage - 1), limit).ToList();
}
return thisPageResult;
};
for (int i = 0; i < limit; i++)
{
var j = i;
// lazy: evaluate the batch only if requested by client code
yield return () => getPageResult()[j];
}
currentPage++;
}
}
static void Main(string[] args)
{
foreach (var func in GetEnumerable().Skip(100).Take(10))
{
Console.WriteLine(func());
}
}
答案 3 :(得分:0)
使其成为方法中的第一个参数,并且不给它一个默认值。
答案 4 :(得分:0)
希望有人发现这种方法很有帮助。 .NET中的通用Lazy类适用于这种情况。
//Enumerable over all of the pages you want to possibly retrieve from:
IEnumerable<Lazy<Page>> pages = Enumerable
.Range(0, 5)
.Select(i => new Lazy<Page>(() => LoadPage(i)));
//Now if each page contains 10 items, and you want to skip the first
//35 items (and thus not load the first 3 pages), do this:
var items = pages
.SelectMany(page => Enumerable
.Range(0, 10)
.Select(i => () => page.Value.GetItem(i)))
.Skip(35) //any combination of Take, Skip, etc. could go here
.Select(itemGetter => itemGetter())
.ToList();