2010-04-29 17 views
25

的简单的例子是 “无限” 的IEnumerable将如何处理“无限”的IEnumerable?

IEnumerable<int> Numbers() { 
    int i=0; 
    while(true) { 
    yield return unchecked(i++); 
    } 
} 

我知道,

foreach(int i in Numbers().Take(10)) { 
    Console.WriteLine(i); 
} 

var q = Numbers(); 
foreach(int i in q.Take(10)) { 
    Console.WriteLine(i); 
} 

都工作得不错(和打印出来的数字0〜 9)。

但是在复制或处理像q这样的表达式时是否有任何缺陷?我能依靠这样一个事实,他们总是被评估为“懒惰”吗?是否有产生无限循环的危险?

+1

只是要挑剔,当我= Int32.MaxValue和你做我++时,你的“无限”示例会抛出一个异常吗?还是它循环到Int32.MinValue?嗯..... – 2010-04-29 19:05:20

+0

你说得对。它可能会抛出一个溢出异常...我会编辑它。 – Danvil 2010-04-29 19:06:28

+0

只是在这里挑剔,你的观点仍然存在。 :)另外,我试了一下,它确实循环到Int32.MinValue。没有OverflowException,所以它实际上是一个无限循环。 – 2010-04-29 19:08:45

回答

8

是的,你保证上面的代码将被执行懒惰。虽然它看起来(在你的代码),就像你永远循环下去,你的代码实际上产生这样的:

IEnumerable<int> Numbers() 
{ 
    return new PrivateNumbersEnumerable(); 
} 

private class PrivateNumbersEnumerable : IEnumerable<int> 
{ 
    public IEnumerator<int> GetEnumerator() 
    { 
     return new PrivateNumbersEnumerator(); 
    } 
} 

private class PrivateNumbersEnumerator : IEnumerator<int> 
{ 
    private int i; 

    public bool MoveNext() { i++; return true; } 

    public int Current 
    { 
     get { return i; } 
    } 
} 

(这显然是不准确什么会产生,因为这是相当特定于你的代码,但它仍然相似,应该告诉你为什么它会被懒惰地评估)。

18

只要你只调用懒惰,无缓冲的方法,你应该没问题。所以SkipTakeSelect等都没问题。然而,Min,Count,OrderBy等会发疯。

它可以工作,但你需要谨慎。或者注入一个Take(somethingFinite)作为一种安全措施(或其他自定义扩展方法,在数据太多后引发异常)。

例如:

public static IEnumerable<T> SanityCheck<T>(this IEnumerable<T> data, int max) { 
    int i = 0; 
    foreach(T item in data) { 
     if(++i >= max) throw new InvalidOperationException(); 
     yield return item; 
    } 
} 
+1

+1我有一个相互竞争的答案,但我只需要提供一个upvote为IEnumerable扩展方法调用* SanityCheck *。有史以来最好的函数名称。我要去执行... – 2010-04-29 19:07:58

+0

+1指出这一点。此外,如果OP实际上调用'foreach(int i在Numbers()) '这也会导致问题。 – Felype 2014-10-26 20:57:19

4

你必须避免尝试读取结束所有贪婪的功能。这将包括Enumerable扩展,如:CountToArray/ToList,并集合Avg/Min/Max

这没有什么错无限的懒列表的,但你必须对如何处理它们有意识的决定。

使用Take可通过设置上限来限制无限循环的影响,即使您不需要它们全部。

2

是的,你的代码将永远工作,没有无限循环。稍后有人可能会来,并搞砸了。假设他们想要这样做:

var q = Numbers().ToList(); 

然后,你被打败了!许多“聚合”功能会杀死你,如Max()

0

如果不是懒惰的评估,你的第一个例子将无法按预期的方式工作。