views:

88

answers:

2

Hi

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 :)

+10  A: 

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)

SLaks
Arh, so you are telling me that ElementAt is overloaded for Arrays, Lists, Whatnot..??
Moberg
It's not overloaded. It uses a type-cast.
SLaks
+3  A: 

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 :(

Jon Skeet
You can somewhat indirectly opt-in to the optimizations by having your custom collection type implement `IList<T>`, but implement it explicitly and/or only ever expose your collection publicly as an `IEnumerable<T>`. It's not ideal, but basically these are the optimization 'hooks'. If you wanted to, you could provide a custom interface for each custom extension method that allows a class writer to override the extension method behavior, though this might get a bit cluttered. Something like `WidgetExtensions.ToggleWidget(this Widget widget)` and `WidgetExtensions.IToggleWidget`.
Dan Bryant