Cannot apply indexing with [] to an expression of type 'System.Collections.Generic.IEnumerable<>
Is there any specific reason why indexing is not allowed in IEnumerable.
I found a 开发者_如何学Cworkaround 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 mode.
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.
You can use ToList to convert to a list. For example,
SomeItems.ToList()[1]
you can use indexing if your enumerable type is string like below
((string[])MyEnumerableStringList)[0]
The []-operator is resolved to the access property this[sometype index]
, with implementation depending upon the Element-Collection.
An Enumerable-Interface declares a blueprint of what a Collection should look like in the first place.
Take this example to demonstrate the usefulness of clean Interface separation:
var ienu = "13;37".Split(';').Select(int.Parse);
//provides an WhereSelectArrayIterator
var inta = "13;37".Split(';').Select(int.Parse).ToArray()[0];
//>13
//inta.GetType(): System.Int32
Also look at the syntax of the []-operator:
//example
public class SomeCollection{
public SomeCollection(){}
private bool[] bools;
public bool this[int index] {
get {
if ( index < 0 || index >= bools.Length ){
//... Out of range index Exception
}
return bools[index];
}
set {
bools[index] = value;
}
}
//...
}
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:
- Code that requires the contract described above (access to a sequence of elements), if it required the
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. - Any type that wanted to expose a sequence of elements but was not appropriately equipped to provide random access by index (e.g.,
LinkedList<T>
,Dictionary<TKey, TValue>
) would now have to either provide some inefficient means of simulating indexing, or else abandon theIEnumerable<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.
In my case I solved it by add .ToList() at the end of LINQ statement :
public ActionResult Statistics(int programId )
{
int[] testids = { 51, 52, 54, 55, 56, 57, 60, 1125, 3161 };
var stat = (from programs_tests in _context.programs_tests
join labTest in _context.LabTests
on programs_tests.testid equals labTest.TestId
where programs_tests.program_id == programId
&& !testids.Contains(labTest.TestId)
select programs_tests).ToList();
return View(stat);
}
I had a column that did not allow nulls and I was inserting a null value.
You can use IEnumerable
in conjunction with this
:
public interface IWorksheets : IEnumerable
{
IWorksheet this[int index] { get; }
IWorksheet this[string name] { get; }
}
Then you can use foreach
and indexing:
IWorksheet worksheet= excelWorkbook.Worksheets["Sheet1"]
foreach (IWorksheet worksheet in excelWorkbook.Worksheets)
{
精彩评论