Невозможно применить индексирование с помощью [] к выражению типа 'System.Collections.Generic.IEnumerable <> - PullRequest
56 голосов
/ 31 августа 2010

Существует ли какая-либо конкретная причина, по которой индексация не разрешена в IEnumerable.

Я нашел обходной путь для возникшей проблемы, но мне просто интересно узнать, почему он не позволяет индексировать.

Спасибо,

Ответы [ 7 ]

63 голосов
/ 01 сентября 2010

Потому что это не так.

Индексация покрывается IList.IEnumerable означает «У меня есть некоторые возможности IList, но не все из них».

Некоторые коллекции (например, связанный список) не могут быть проиндексированы на практике.Но они могут быть доступны по пунктам.IEnumerable предназначен для таких коллекций.Обратите внимание, что коллекция может реализовывать как IList, так и IEnumerable (и многие другие).Как правило, в качестве параметра функции вы найдете только IEnumerable, что означает, что функция может принимать любую коллекцию, потому что все, что ей нужно, - это самый простой режим доступа.

44 голосов
/ 01 сентября 2010

Интерфейс IEnumerable<T> не включает индексатор , вы, вероятно, путаете его с IList<T>

Если объект действительно является IList<T> (например, List<T> или массивом T[]), попробуйте сделать ссылку на него также типа IList<T>.

В противном случае вы можете использовать myEnumerable.ElementAt(index), который использует метод расширения Enumerable.ElementAt . Это должно работать для всех IEnumerable<T> с. Обратите внимание, что если (во время выполнения) объект не реализует IList<T>, это приведет к перечислению всех первых index + 1 элементов со всеми, кроме последнего, отбрасываемыми.

EDIT: В качестве объяснения, IEnumerable<T> - это просто интерфейс, представляющий «то, что предоставляет перечислитель». Конкретная реализация вполне может быть своего рода списком в памяти, который делает разрешающим быстрый доступ по индексу, или он может не разрешать. Например, это может быть коллекция, которая не может эффективно удовлетворить такой запрос, например, связанный список (как упомянуто Джеймсом Керраном). Это может быть даже никакой сортировки структуры данных в памяти вообще, такой как итератор, где элементы генерируются («выдаются») по требованию, или перечислитель, который выбирает элементы из некоторого удаленного источник данных. Поскольку IEnumerable<T> должен поддерживать все эти случаи, индексаторы исключаются из его определения.

3 голосов
/ 14 июля 2016

вы можете использовать индексирование, если ваш перечисляемый тип - строка, как показано ниже

((string[])MyEnumerableStringList)[0]
3 голосов
/ 01 сентября 2010

Одной из причин может быть то, что IEnumerable может содержать неизвестное количество предметов. В некоторых реализациях список элементов создается при итерации по нему (примеры см. yield). Это не очень хорошо работает с доступом к элементам с помощью индекса. что потребовало бы, чтобы вы знали, что в списке есть хотя бы столько элементов.

2 голосов
/ 25 сентября 2013

Оператор [] разрешается в свойстве доступа this[sometype index], реализация которого зависит от Element- Collection .

. Enumerable-Interface объявляет план как должна выглядеть коллекция в первую очередь .

Этот пример демонстрирует полезность чистого разделения интерфейсов :

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;
     }
  }
//...
}
1 голос
/ 16 мая 2019

Вы можете использовать ToList для преобразования в список. Например,

SomeItems.ToList()[1]
1 голос
/ 01 сентября 2010

Идея интерфейсов, как правило, заключается в том, чтобы раскрыть своего рода контракт базовой линии, с помощью которого код, выполняющий работу над объектом, может быть гарантирован для определенных функций, предоставляемых этим объектом. В случае IEnumerable<T> этот контракт «вы можете получить доступ ко всем моим элементам один за другим».

Существует множество методов, которые могут быть написаны только на основе этого контракта. См. Enumerable класс для тонн примеров.

Но сосредоточимся только на одном конкретном: подумайте о Sum. Для того, чтобы подвести итог кучу предметов, что вам нужно? Какой контракт вы бы потребовали? Ответ довольно прост: просто способ увидеть каждый предмет, не более. Произвольный доступ не требуется. Даже общее количество всех предметов не требуется.

Добавление индексатора к интерфейсу IEnumerable<T> было бы вредно для двух причин:

  1. Код, который требует контракт, описанный выше (доступ к последовательности элементов), если для него требуется интерфейс IEnumerable<T>, будет искусственно ограничительным, поскольку он не может иметь дело с любым типом, который не реализует индексатор, даже если он иметь дело с таким типом действительно должно быть в пределах возможностей кода.
  2. Любой тип, который хотел бы представить последовательность элементов, но не был соответствующим образом оборудован для обеспечения произвольного доступа по индексу (например, LinkedList<T>, Dictionary<TKey, TValue>), теперь должен был бы обеспечить некоторые неэффективные средства имитация индексирование или отказ от интерфейса IEnumerable<T>.

При этом, учитывая, что целью интерфейса является обеспечение минимальной требуемой функциональности в данном сценарии, я действительно считаю, что интерфейс IList<T> плохо спроектирован. Или, скорее, отсутствие интерфейса «между» IEnumerable<T> и IList<T> (произвольный доступ, но без изменений), на мой взгляд, является неудачным упущением в BCL.

...