【发布时间】:2013-11-18 10:39:49
【问题描述】:
最终编辑:
我选择了 Timothy 的答案,但如果您想要一个利用 C# yield 语句的更可爱的实现,请查看 Eamon 的答案:https://stackoverflow.com/a/19825659/145757
默认情况下,LINQ 查询是延迟流式传输。
ToArray/ToList 提供完全缓冲,但首先他们渴望,其次可能需要相当长的时间才能完成无限序列。 p>
有什么方法可以结合这两种行为:streaming 和 buffering 值在生成时即时生成,以便不会触发下一个查询已经查询到的元素的生成。
这是一个基本用例:
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;
}
}
这是输出:
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.
生成代码被触发22次。
我希望它被触发 11 次,第一次迭代可枚举。
那么第二次迭代将受益于已经生成的值。
应该是这样的:
IEnumerable<int> evenNumbers = Numbers.Where(i => i % 2 == 0).Buffer();
对于那些熟悉 Rx 的人来说,这是一种类似于 ReplaySubject 的行为。
【问题讨论】:
-
真正需要缓存的不是 LINQ,而是
IEnumerable,already on the internet 有几个例子。 -
昨天在 reddit (here) 上出现了这种情况。我宁愿不窃取该作者的解决方案。
-
@ScottChamberlain:谢谢你的链接,谷歌不是我的朋友。
-
@AustinSalonen:疯狂的巧合,感谢您的链接。 :)
-
这个的总称是“memoization”。请注意,这里的许多实现都处理了一些简单的情况,但不处理在一个完全完成之前枚举结果的多个枚举器,不处理不同枚举器的并行枚举,如果整个序列没有迭代,等等。要处理这些更复杂的问题,您最好使用现有的库实现。
标签: c# .net linq ienumerable