2016-02-26 41 views
2

在我們的代碼庫,我們有提供類似的方法,數據訪問層:這會調用GetEnumerator()多於一次嗎?

public IEnumerable<Foo> GetFoos() 
{ 
    var collection = new Collection<Foo>(); 

    // ...call db 

    while (read results) 
    { 
     collection.Add(item); 
    } 

    return collection; 
} 

因此,儘管方法簽名返回一個IEnumerable,引擎蓋下,它創造了一個集合(這是已經列舉)。這種方法的用戶是否期待IEnumerable實際調用GetEnumerator(),或者他們實際上是否會在不知不覺中使用Collection?

IEnumerable<Foo> myFoos = GetFoos(); 

// will .Any() cause enumeration even though it's a Collection under the hood? 
if (myFoos != null && myFoos.Any()) 
{ 
    // ... 
} 

回答

4

是的,它會一直打電話給GetEnumerator()。如果你關心性能

public static bool Any<TSource>(this IEnumerable<TSource> source) { 
    if (source == null) throw Error.ArgumentNull("source"); 
    using (IEnumerator<TSource> e = source.GetEnumerator()) { 
     if (e.MoveNext()) return true; 
    } 
    return false; 
}   

,但還是要限制消費者的改變集合,你可以考慮返回IReadOnlyCollection<T>的能力:

我們可以通過檢查the implementation of Any() on ReferenceSource.microsoft.com驗證這一點。這實際上只是一個IEnumerable<T>以及一個Count屬性。然後消費者可以檢查Count屬性,但除此之外不能進行任何其他操作。 (除非他們開始做討厭的東西喜歡它鑄造到原來的集合類型...)

+0

但是請注意,它只會查看它是否執行了'MoveNext',因爲這足以確定該集合具有* any *項目。所以當它調用'GetEnumerator'時,它*不會遍歷整個集合。 –

+0

事實上 - 這對於一個系列來說非常有效。 –

+0

是的,比較例如[Count()](http://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,41ef9e39e54d0d0b)。因此'Count()> 0'必須遍歷整個集合,儘管它確實檢查源是否是'ICollection'並且在這種情況下使用'Count'屬性。 –

1

如果你想避免這種情況下集合枚舉,你可以做follwoing:

ICollection col = myFoos as ICollection; 
bool hasItems = false; 
if (col != null && col.Count > 0) 
{ 
    hasItems = true; 
} 
else 
{ 
    hasItems = (myFoos != null && myFoos.Any()); 
} 

但是,這可能比撥打.Any()代替更多的開銷 - 無論您通過檢查.Count獲得的儲蓄是否可能被檢查成本抵消,如果myFoos是是ICollection並加載它。這樣做的唯一好處是,如果您想對該對象進行操作,就像出於某種其他原因它是ICollection一樣。你的另一個選擇是讓你的GetFoos函數返回一個ICollection開頭...

相關問題