IGrouping
supports the ElementAt
method to index into the grouping's collection. So why doesn't the square bracket operator work?
I can do something like
list.GroupBy(expr).Select(group => group.ElementAt(0)....)
but not
list.GroupBy(expr).Select(group => group[0]....)
I'm guessing this is because the IGrouping interface doesn't overload the square bracket operator. Is there a good reason why IGrouping didn't overload the square bracket operator to do the same thing as ElementAt
?
That's a bit back to front, all enumerables are supported by (rather than supports, as it's an extension method provided from the outside) ElementAt()
but only some are of a type that also support []
, such as List<T>
or anything that implements IList<T>
.
Grouping
certainly could implement []
easily enough, but then it would have to always do so, as the API would be a promise it would have to keep on keeping, or it would break code written to the old way if it did break it.
ElementAt()
takes a test-and-use approach in that if something supports IList<T>
it will use []
but otherwise it counts the appropriate number along. Since you can count-along with any sequence, it can therefore support any enumerable.
It so happens that Grouping
does support IList<T>
but as an explicit interface, so the following works:
//Bad code for demonstration purpose, do not use:
((IList<int>)Enumerable.Range(0, 50).GroupBy(i => i / 5).First())[3]
But because it's explicit it doesn't have to keep supporting it if there was ever an advantage found in another approach.
The test-and-use approach of ElementAt
:
public static TSource ElementAt<TSource>(this IEnumerable<TSource> source, int index)
{
if (source == null) throw Error.ArgumentNull("source");
IList<TSource> list = source as IList<TSource>;
if (list != null) return list[index];
if (index < 0) throw Error.ArgumentOutOfRange("index");
using (IEnumerator<TSource> e = source.GetEnumerator())
{
while (true)
{
if (!e.MoveNext()) throw Error.ArgumentOutOfRange("index");
if (index == 0) return e.Current;
index--;
}
}
}
Therefore gets the optimal O(1) behaviour out of it, rather than the O(n) behaviour otherwise, but without restricting Grouping
to making a promise the designers might later regret making.