Есть ли какая-либо конкретная причина, по которой индексирование не разрешено в IEnumerable.
Я нашел обходной путь для проблемы, которую я имел, но просто любопытно узнать, почему это не позволяет индексировать.
Спасибо,
Есть ли какая-либо конкретная причина, по которой индексирование не разрешено в IEnumerable.
Я нашел обходной путь для проблемы, которую я имел, но просто любопытно узнать, почему это не позволяет индексировать.
Спасибо,
Потому что это не так.
Индексирование распространяется на IList
. IEnumerable
означает "у меня есть некоторые полномочия IList, но не все из них".
Некоторые коллекции (например, связанный список) не могут быть проиндексированы на практике. Но они могут быть доступны по каждому элементу. IEnumerable
предназначен для подобных коллекций. Обратите внимание, что коллекция может реализовывать как IList, так и IEnumerable (и многие другие). Обычно вы находите IEnumerable
как параметр функции, то есть функция может принимать любые коллекции, потому что все, что ему нужно, - это самый простой режим доступа.
Интерфейс IEnumerable<T>
не включает indexer, вы, вероятно, путаете его с IList<T>
Если объект действительно является IList<T>
(например, List<T>
или массив T[]
), попробуйте сделать ссылку на него типа IList<T>
тоже.
В противном случае вы можете использовать myEnumerable.ElementAt(index)
, который использует метод расширения Enumerable.ElementAt. Это должно работать для всех IEnumerable<T>
.
Обратите внимание, что если объект (run-time) реализует IList<T>
, это приведет к перечислению всех первых элементов index + 1
, при этом все, кроме последнего, будут отброшены.
EDIT:
В качестве объяснения, IEnumerable<T>
- это просто интерфейс, который представляет "то, что предоставляет перечислитель". Конкретная реализация вполне может быть своего рода списком в памяти, который позволяет быстро получить доступ по индексу, а может и нет. Например, это может быть сборник, который не может эффективно удовлетворять такой запрос, например, связанный список (как упоминал Джеймс Карран). Это может быть вообще не какая-либо структура данных в памяти, например, итератор, где элементы генерируются ( "даны" ) по требованию или перечислителем, который извлекает элементы из какого-то удаленного источника данных. Поскольку IEnumerable<T>
должен поддерживать все эти случаи, индексаторы исключаются из его определения.
Одна из причин может заключаться в том, что IEnumerable
может содержать неизвестное количество элементов. В некоторых реализациях создается список элементов по мере его перебора (см. yield
для образцов). Это не очень хорошо работает с доступом к элементам с использованием индекса. что потребует от вас знать, что в списке есть по крайней мере много элементов.
вы можете использовать индексирование, если ваш перечислимый тип является строкой, как показано ниже
((string[])MyEnumerableStringList)[0]
[] -оператор разрешен к свойству доступа this[sometype index]
, с реализацией в зависимости от коллекции элементов.
Перечислимый интерфейс объявляет план того, что коллекция должна выглядеть в первую очередь.
Возьмите этот пример, чтобы продемонстрировать полезность чистого разделения интерфейса:
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
Также посмотрите на синтаксис для [] -оператора:
//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;
}
}
//...
}
Идея интерфейсов, как правило, заключается в том, чтобы разоблачить какой-то контракт базовой линии, посредством которого код, который выполняет работу над объектом, может быть гарантирован определенной функциональностью, предоставляемой этим объектом. В случае IEnumerable<T>
, этот контракт оказывается "вы можете получить доступ ко всем моим элементам один за другим".
Типы методов, которые могут быть написаны на основе этого договора, очень много. См. Enumerable
для тонн примеров.
Но к нулю только один конкретный: подумайте о Sum
. Чтобы суммировать кучу предметов, что вам нужно? Какой контракт вам нужен? Ответ довольно прост: просто способ увидеть каждый предмет, не более того. Случайный доступ не требуется. Даже общее количество всех предметов не требуется.
Добавление индексатора в интерфейс IEnumerable<T>
было бы пагубно двумя способами:
IEnumerable<T>
, был бы искусственно ограничительным, поскольку он не мог иметь дело с любым типом, который не реализовал индексатор, хотя для сделка с таким типом должна действительно хорошо соответствовать возможностям кода.LinkedList<T>
, Dictionary<TKey, TValue>
), теперь должен был либо предоставить некоторые неэффективные средства имитации индексации, либо отказаться от интерфейса IEnumerable<T>
.Все это сказано, учитывая, что цель интерфейса - обеспечить гарантию минимально необходимой функциональности в данном сценарии, я действительно думаю, что интерфейс IList<T>
плохо разработан. Вернее, отсутствие интерфейса "между" IEnumerable<T>
и IList<T>
(произвольный доступ, но без изменений) является неудачным наблюдением в BCL, в моем мнение.
Вы можете использовать ToList для преобразования в список. Например,
SomeItems.ToList()[1]