1

次のコードがあります。

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 個のリクエストは完全にスキップする必要があります。

これを達成するために使用できるパターンはありますか?

4

5 に答える 5

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);
}
于 2012-11-14T22:43:49.060 に答える
2

「ページ」の遅延評価を行いたい場合は、while ループでロードしないでください。

代わりに、ラムダを返し、そこからページを返すことができます。たとえば、次のようになります。

// 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());
}
于 2012-11-14T22:27:52.713 に答える
1

これが私の見解です。ラムダ式を使用して 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());
    }
}
于 2012-11-15T00:40:53.847 に答える
0

これをメソッドの最初のパラメーターにし、デフォルト値を指定しないでください。

于 2012-11-14T22:14:31.047 に答える
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();
于 2012-11-16T17:28:42.617 に答える