缓冲LINQ查询 [英] Buffering a LINQ query

查看:215
本文介绍了缓冲LINQ查询的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

最后修改

我选择的蒂莫西的的答案,但如果你想要一个可爱的实现,它利用C#收益率语句检查的埃蒙的的答案: http://stackoverflow.com/a/19825659/145757


在默认情况下的 LINQ 查询是懒洋洋地流

的ToArray / 了ToList 提供的全缓冲,但首先他们的渴望其次它可能需要相当长的一段时间来完成与无限序列。

有什么办法让这两个行为的结合:缓冲作为生成飞价值观,他们,以便下次查询将不会触发的产生是已经被查询的元素

下面是一个基本的用例:

 静态的IEnumerable< INT>号码
{
    得到
    {
        INT I = -1;

        而(真)
        {
            Console.WriteLine(生成{0},我+ 1);
            收益回报++我;
        }
    }
}

静态无效的主要(字串[] args)
{
    IEnumerable的< INT> evenNumbers = Numbers.Where(ⅰ= I标记%2 == 0);

    的foreach(INT N的evenNumbers)
    {
        Console.WriteLine(读{0},N);
        如果(N == 10)打破;
    }

    Console.WriteLine(==========);

    的foreach(INT N的evenNumbers)
    {
        Console.WriteLine(读{0},N);
        如果(N == 10)打破;
    }
}
 

下面是输出:

 生成0。
读0。
生成1。
生成2。
阅读2。
生成3。
生成4。
阅读4。
生成5。
生成6。
读6。
生成7。
生成8。
阅读8。
生成9。
生成10。
阅读10。
==========
生成0。
读0。
生成1。
生成2。
阅读2。
生成3。
生成4。
阅读4。
生成5。
生成6。
读6。
生成7。
生成8。
阅读8。
生成9。
生成10。
阅读10。
 

的产生code被触发22次。

我想它触发了11次,第一次枚举迭代。

然后在第二次迭代将受益于已生成的值。

这将是这样的:

 的IEnumerable< INT> evenNumbers = Numbers.Where(ⅰ= I标记%2 == 0).Buffer();
 

对于那些熟悉的接收这是类似于行为 ReplaySubject

解决方案

的IEnumerable< T> .Buffer()扩展方法

 公共静态EnumerableExtensions
{
    公共静态BufferEnumerable< T>缓冲区(这IEnumerable的< T>源)
    {
        返回新BufferEnumerable< T>(来源);
    }
}

公共类BufferEnumerable< T> :IEnumerable的< T&GT ;,了IDisposable
{
    IEnumerator的< T>资源;
    名单< T>缓冲;
    公共BufferEnumerable(IEnumerable的< T>源)
    {
        this.source = source.GetEnumerator();
        this.buffer =新的名单,其中,T>();
    }
    公众的IEnumerator< T>的GetEnumerator()
    {
        返回新BufferEnumerator< T>(源,缓冲区);
    }
    公共无效的Dispose()
    {
        source.Dispose()
    }
}

公共类BufferEnumerator< T> :IEnumerator的< T>
{
    IEnumerator的< T>资源;
    名单< T>缓冲;
    INT I = -1;
    公共BufferEnumerator(IEnumerator的< T>源,列表< T>缓冲区)
    {
        this.source =来源;
        this.buffer =缓冲区;
    }
    大众T电流
    {
        {返回缓冲区[I] }
    }
    公共BOOL的MoveNext()
    {
        我++;
        如果(ⅰ&其中; buffer.Count)
            返回true;
        如果(!source.MoveNext())
            返回false;
        buffer.Add(source.Current);
        返回true;
    }
    公共无效复位()
    {
        I = -1;
    }
    公共无效的Dispose()
    {
    }
}
 

用法

 使用(VAR evenNumbers = Numbers.Where(I = I标记%2 == 0).Buffer())
{
    ...
}
 

建议

这里的关键点是,的IEnumerable< T>源作为输入到缓存方法不仅具有的GetEnumerator 调用一次,不管如何很多次缓存的结果列举。所有调查员对缓存的结果具有相同源枚举和内部列表。

FINAL EDIT:

I've chosen Timothy's answer but if you want a cuter implementation that leverages the C# yield statement check Eamon's answer: http://stackoverflow.com/a/19825659/145757


By default LINQ queries are lazily streamed.

ToArray/ToList give full buffering but first they're eager and secondly it may take quite some time to complete with an infinite sequence.

Is there any way to have a combination of both behaviors : streaming and buffering values on the fly as they are generated, so that the next querying won't trigger the generation of the elements that have already been queried.

Here is a basic use-case:

static IEnumerable<int> Numbers
{
    get
    {
        int i = -1;

        while (true)
        {
            Console.WriteLine("Generating {0}.", i + 1);
            yield return ++i;
        }
    }
}

static void Main(string[] args)
{
    IEnumerable<int> evenNumbers = Numbers.Where(i => i % 2 == 0);

    foreach (int n in evenNumbers)
    {
        Console.WriteLine("Reading {0}.", n);
        if (n == 10) break;
    }

    Console.WriteLine("==========");

    foreach (int n in evenNumbers)
    {
        Console.WriteLine("Reading {0}.", n);
        if (n == 10) break;
    }
}

Here is the output:

Generating 0.
Reading 0.
Generating 1.
Generating 2.
Reading 2.
Generating 3.
Generating 4.
Reading 4.
Generating 5.
Generating 6.
Reading 6.
Generating 7.
Generating 8.
Reading 8.
Generating 9.
Generating 10.
Reading 10.
==========
Generating 0.
Reading 0.
Generating 1.
Generating 2.
Reading 2.
Generating 3.
Generating 4.
Reading 4.
Generating 5.
Generating 6.
Reading 6.
Generating 7.
Generating 8.
Reading 8.
Generating 9.
Generating 10.
Reading 10.

The generation code is triggered 22 times.

I'd like it to be triggered 11 times, the first time the enumerable is iterated.

Then the second iteration would benefit from the already generated values.

It would be something like:

IEnumerable<int> evenNumbers = Numbers.Where(i => i % 2 == 0).Buffer();

For those familiar with Rx it's a behavior similar to a ReplaySubject.

解决方案

IEnumerable<T>.Buffer() extension method

public static EnumerableExtensions
{
    public static BufferEnumerable<T> Buffer(this IEnumerable<T> source)
    {
        return new BufferEnumerable<T>(source);
    }
}

public class BufferEnumerable<T> : IEnumerable<T>, IDisposable
{
    IEnumerator<T> source;
    List<T> buffer;
    public BufferEnumerable(IEnumerable<T> source)
    {
        this.source = source.GetEnumerator();
        this.buffer = new List<T>();
    }
    public IEnumerator<T> GetEnumerator()
    {
        return new BufferEnumerator<T>(source, buffer);
    }
    public void Dispose()
    {
        source.Dispose()
    }
}

public class BufferEnumerator<T> : IEnumerator<T>
{
    IEnumerator<T> source;
    List<T> buffer;
    int i = -1;
    public BufferEnumerator(IEnumerator<T> source, List<T> buffer)
    {
        this.source = source;
        this.buffer = buffer;
    }
    public T Current
    {
        get { return buffer[i]; }
    }
    public bool MoveNext()
    {
        i++;
        if (i < buffer.Count)
            return true;
        if (!source.MoveNext())
            return false;
        buffer.Add(source.Current);
        return true;
    }
    public void Reset()
    {
        i = -1;
    }
    public void Dispose()
    {
    }
}

Usage

using (var evenNumbers = Numbers.Where(i => i % 2 == 0).Buffer())
{
    ...
}

Comments

The key point here is that the IEnumerable<T> source given as input to the Buffer method only has GetEnumerator called once, regardless of how many times the result of Buffer is enumerated. All enumerators for the result of Buffer share the same source enumerator and internal list.

这篇关于缓冲LINQ查询的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆