无法将带[]的索引应用于“ System.Collections.Generic.IEnumable < >”类型的表达式

是否有任何特定的原因不允许在 IEnumable 中进行索引。

我找到了一个解决问题的方法,但只是好奇为什么它不允许索引。

谢谢,

130305 次浏览

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.

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:

  1. 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.
  2. 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 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" ABC1 and IList<T> (random access, but no modification) is an unfortunate oversight in the BCL, in my opinion.

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;
}
}
//...
}

you can use indexing if your enumerable type is string like below

((string[])MyEnumerableStringList)[0]

You can use ToList to convert to a list. For example,

SomeItems.ToList()[1]

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

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);
}