Consider this code:
int size = 100 * 1000 * 1000;
var emu = Enumerable.Range(0, size);
var arr = Enumerable.Range(0, size).ToArray();
when I call emu.ElementAt(size-10) and arr.ElementAt(size-10) and measure the time the arr is much faster (the array is 0.0002s compared to IEnumerable 0.59s).
As I understand it, the extention method ElementAt() have the signature
public static TSource ElementAt<TSource>(this IEnumerable<TSource> source, int index)
and since the 'source' is a IEnumerable the logic carried out would be similar - opposed to what I see where the array is accessed directly.
Could someone please explain this :)
Calling ElementAt
on an IEnumerable<T>
will loop through the items until it reaches the desired index. (An O(n) operation)
Calling ElementAt
on an IList<T>
(such as an array) will use the IList<T>
's indexer to immediately get the desired index. (An O(1) operation)
This is an optimization performed at execution-time. Although the call isn't overloaded, it is able to check (using is
or as
) whether the source is actually an IList<T>
. If it is, it's able to go directly to the right element.
Various other calls do this - notable Count()
which is optimised for ICollection<T>
and (as of .NET 4) the nongeneric ICollection
interface.
One of the downsides of extension methods is that all these optimizations have to be performed by the implementation itself - types can't override anything to "opt in" to optimizing extension methods. That means the optimizations have to all be known by the original implementor :(
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With