考虑以下代码:
int size = 100 * 1000 * 1000;
var emu = Enumerable.Range(0, size);
var arr = Enumerable.Range(0, size).ToArray();
当我调用emu.ElementAt(size-10)和arr.ElementAt(size-10)并测量arr的时间要快得多(与IEnumerable 0.59s相比,数组为0.0002s)。
据我了解,扩展方法ElementAt()具有签名
public static TSource ElementAt<TSource>(this IEnumerable<TSource> source, int index)
由于'source'是一个IEnumerable,所执行的逻辑将是相似的 - 与我所看到的直接访问数组的地方相反。
有人可以解释一下:)
答案 0 :(得分:12)
在ElementAt
上调用IEnumerable<T>
将遍历项目,直到达到所需的索引。 (一个O(n)操作)
在ElementAt
(例如数组)上调用IList<T>
将使用IList<T>
的索引器立即获得所需的索引。 (一个O(1)操作)
答案 1 :(得分:5)
这是在执行时间执行的优化。虽然调用未重载,但可以检查(使用is
或as
)源是否实际为IList<T>
。如果是,它就能直接进入正确的元素。
其他各种调用都是这样做的 - 值得注意的是Count()
,它针对ICollection<T>
和(从.NET 4开始)非通用ICollection
接口进行了优化。
扩展方法的一个缺点是所有这些优化都必须由实现本身执行 - 类型不能覆盖任何“选择”优化扩展方法。这意味着原始实现者必须知道优化:(