2010-04-29 18 views
25

bir örnegiydi bir "sonsuz" IEnumerableBir "sonsuz" IEnumerable nasıl ele alınır?

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

ve

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

çok iyi çalışıyor (ve sayı 0- çıktısını bildiğim

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

olurdu 9).

q gibi ifadeleri kopyalarken veya işlerken herhangi bir tuzak var mı? Her zaman "tembel" olarak değerlendirildikleri gerçeğine güvenebilir miyim? Sonsuz döngü oluşturma tehlikesi var mı?

+1

Sadece seçici olmak için, "sonsuz" örneğiniz, i = Int32.MaxValue ve i ++ yapmak istediğinizde bir istisna alamaz mı?Yoksa Int32.MinValue'ye mi geçiyor? Hmm ..... –

+0

Haklısınız. Muhtemelen bir taşma istisnası atacaktır ... Düzenleyeceğim. – Danvil

+0

Sadece seçici olmak için, noktanız hala karşımıza çıktı. :) Ayrıca, denedim ve Int32.MinValue için döngü yapar. OverflowException yok, bu yüzden aslında sonsuz bir döngü oldu. –

cevap

8

Evet, yukarıdaki kodun tembel olarak yürütülmesi garanti edilir. Eğer, sonsuza kodunuzu döngü olur gibi (Kodunuzdaki) bakarken aslında böyle bir şey üretir:

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; } 
    } 
} 

(Bu tabii değildir bu oldukça olduğundan, oluşturulacak tam olarak ne kodunuza özgü, ancak yine de benzer ve neden tembel olarak değerlendirilecek olduğunu göstermelidir).

18

Yalnızca tembel, arabelleğe alınmamış yöntemleri çağırdığınız sürece iyi olmalısınız. Yani Skip, Take, Select, vb. Bununla birlikte, Min, Count, OrderBy vb. Çıldırırdı.

Çalışabilir, ancak dikkatli olmanız gerekir. Veya bir güvenlik önlemi olarak Take(somethingFinite) enjekte edin (veya çok fazla veriden sonra bir istisna atar başka bir özel uzantı yöntemi). Örneğin

:

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 Rakip bir cevabım var, ama sadece bir cevap vermek zorundayım IEnumerable için bir uzantı yöntemi için * SanityCheck * En iyi işlev adı hiç.Bunu belirtmek için bunu uygulamaya başlayacağım ... –

+0

+1 bunu belirtmek için de eğer OP aslında "foreach" (int (Sayılarda)() içinde) Bu da sorunlara neden olur. – Felype

4

Sonuna kadar okuma girişiminde herhangi açgözlü işlevleri kaçınmak gerekir. Bu gibi Enumerable uzantıları içerir: Count, ToArray/ToList ve, Avg/Min/Max toplayan vb

Orada sonsuz tembel listeleri ile yanlış bir şey, ama bunların nasıl işleneceğine konusunda bilinçli kararlar almak zorundadır.

Take kullanın, sonsuz bir döngünün etkisini, hepsine ihtiyacınız olmasa bile bir üst sınır ayarlayarak sınırlayın.

2

Evet, kodunuz her zaman sonsuz döngü olmadan çalışır. Birisi daha sonra olsa gelebilir ve bir şeyler dağıtabilir. Bunu yapmak istediklerini varsayalım:

O zaman, sen kazandın! Birçok "toplama" işlevi Max() gibi sizi öldürür.

0

Tembel bir değerlendirme değilse, ilk örneğiniz ilk etapta beklendiği gibi çalışmayacaktır.

İlgili konular