Hi All,
Is there any specific reason why indexing is not allowed in IEnumerable.
I found a workaround for the problem I had, but just curious to know why it does not allow indexing.
Thanks,
Hi All,
Is there any specific reason why indexing is not allowed in IEnumerable.
I found a workaround for the problem I had, but just curious to know why it does not allow indexing.
Thanks,
Because it's not.
Indexing is covered by IList
. IEnumerable
means "I have some of the powers of IList, but not all of them."
Some collections (like a linked list), cannot be indexed in a practical way. But they can be accessed item-by-item. IEnumerable
is intended for collections like that. Note that a collection can implement both IList & IEnumerable (and many others). You generally only find IEnumerable
as a function parameter, meaning the function can accept any kind of collection, because all it needs is the simplest access meode.
The IEnumerable<T>
interface does not include an indexer, you're probably confusing it with IList<T>
If the object really is an IList<T>
(e.g. List<T>
or an array T[]
), try making the reference to it of type IList<T>
too.
Otherwise, you can use myEnumerable.ElementAt(index)
which uses the Enumerable.ElementAt extension method. This should work for all IEnumerable<T>
s .
Note that unless the (run-time) object implements IList<T>
, this will cause all of the first index + 1
items to be enumerated, with all but the last being discarded.
EDIT:
As an explanation, IEnumerable<T>
is simply an interface that represents "that which exposes an enumerator." A concrete implementation may well be some sort of in-memory list that does allow fast-access by index, or it may not. For instance, it could be a collection that cannot efficiently satisfy such a query, such as a linked-list (as mentioned by James Curran). It may even be no sort of in-memory data-structure at all, such as an iterator, where items are generated ('yielded') on demand, or by an enumerator that fetches the items from some remote data-source. Because IEnumerable<T>
must support all these cases, indexers are excluded from its definition.
One reason can be that the IEnumerable
may contain an unknown number of items. Some implementations produce the list of items as you iterate over it (see yield
for samples). That does not work very well with accessing items using an index. which would require you to know that there are at least that many items in the list.
The idea of interfaces is generally to expose a sort of base line contract by which code that performs work on an object can be guaranteed of certain functionality provided by that object. In the case of IEnumerable<T>
, that contract happens to be "you can access all of my elements one by one."
The kinds of methods that can be written based on this contract alone are many. See the Enumerable
class for tons of examples.
But to zero in on just one concrete one: think about Sum
. In order to sum up a bunch of items, what do you need? What contract would you require? The answer is quite simple: just a way to see every item, no more. Random access isn't necessary. Even a total count of all the items is not necessary.
To have added an indexer to the IEnumerable<T>
interface would have been detrimental in two ways:
IEnumerable<T>
interface, would be artificially restrictive as it could not deal with any type that did not implement an indexer, even though to deal with such a type should really be well within the capabilities of the code.LinkedList<T>
, Dictionary<TKey, TValue>
) would now have to either provide some inefficient means of simulating indexing, or else abandon the IEnumerable<T>
interface.All this being said, considering that the purpose of an interface is to provide a guarantee of the minimum required functionality in a given scenario, I really think that the IList<T>
interface is poorly designed. Or rather, the lack of an interface "between" IEnumerable<T>
and IList<T>
(random access, but no modification) is an unfortunate oversight in the BCL, in my opinion.