5

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

Cœur
  • 37,241
  • 25
  • 195
  • 267
Moberg
  • 783
  • 1
  • 8
  • 17

2 Answers2

12

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
  • 868,454
  • 176
  • 1,908
  • 1,964
5

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
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • You can somewhat indirectly opt-in to the optimizations by having your custom collection type implement `IList`, but implement it explicitly and/or only ever expose your collection publicly as an `IEnumerable`. 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 Jul 05 '10 at 17:12