2010-07-05 67 views
5

考慮以下代碼:瞭解擴展的ElementAt(指數)

int size = 100 * 1000 * 1000; 
var emu = Enumerable.Range(0, size); 
var arr = Enumerable.Range(0, size).ToArray(); 

當我打電話emu.ElementAt(大小-10)和arr.ElementAt(大小-10)和測量時間的ARR更快(數組爲0.0002s,而IEnumerable爲0.59s)。

據我所知,進一步擴展方法ElementAt()具有簽名

public static TSource ElementAt<TSource>(this IEnumerable<TSource> source, int index) 

並且由於「源」是一個IEnumerable的執行是相似的邏輯 - 反對我所看到的,其中陣列直接訪問。

可能有人請解釋這:)

回答

5

這是在執行時執行的優化。雖然呼叫未超載,但它能夠檢查(使用isas)源是否實際上是IList<T>。如果是,它可以直接轉到正確的元素。

其他各種調用都會執行此操作 - 值得注意的是Count(),它針對ICollection<T>和(自.NET 4起)非接口ICollection接口進行了優化。

擴展方法的缺點之一是,所有這些優化必須由實現本身執行 - 類型不能覆蓋任何內容來「優化」以優化擴展方法。這意味着優化必須被原始實現者知道:(

+0

你可以通過讓自定義集合類型實現IList 來間接地選擇優化,但是明確地實現它和/或只將它公開爲'IEnumerable '。這並不理想,但基本上這些都是優化的'鉤子',如果你願意,你可以爲每個自定義擴展方法提供一個自定義的接口,允許類編寫器重寫擴展方法的行爲,儘管這可能會有點混亂,像'WidgetExtensions.ToggleWidget (這個Widget小部件)'和'WidgetExtensions.IToggleWidget '。 – 2010-07-05 17:12:39

12

通過項目的IEnumerable<T>將循環調用ElementAt,直到達到所需的索引。 (一個O(n)操作)

IList<T>(如數組)上調用ElementAt將使用IList<T>的索引器立即獲取所需的索引。 (AnO(1)操作)

+0

Arh,所以你告訴我,ElementAt對於數組,列表,什麼都不是重載... ?? – Moberg 2010-07-05 14:10:03

+1

它沒有被重載,它使用類型轉換。 – SLaks 2010-07-05 14:15:31