2010-07-05 55 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