2010-04-29 47 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

如果不是懶惰的評估,你的第一個例子將無法按預期的方式工作。