Идея интерфейсов, как правило, заключается в том, чтобы раскрыть своего рода контракт базовой линии, с помощью которого код, выполняющий работу над объектом, может быть гарантирован для определенных функций, предоставляемых этим объектом. В случае IEnumerable<T>
этот контракт «вы можете получить доступ ко всем моим элементам один за другим».
Существует множество методов, которые могут быть написаны только на основе этого контракта. См. Enumerable
класс для тонн примеров.
Но сосредоточимся только на одном конкретном: подумайте о Sum
. Для того, чтобы подвести итог кучу предметов, что вам нужно? Какой контракт вы бы потребовали? Ответ довольно прост: просто способ увидеть каждый предмет, не более. Произвольный доступ не требуется. Даже общее количество всех предметов не требуется.
Добавление индексатора к интерфейсу IEnumerable<T>
было бы вредно для двух причин:
- Код, который требует контракт, описанный выше (доступ к последовательности элементов), если для него требуется интерфейс
IEnumerable<T>
, будет искусственно ограничительным, поскольку он не может иметь дело с любым типом, который не реализует индексатор, даже если он иметь дело с таким типом действительно должно быть в пределах возможностей кода.
- Любой тип, который хотел бы представить последовательность элементов, но не был соответствующим образом оборудован для обеспечения произвольного доступа по индексу (например,
LinkedList<T>
, Dictionary<TKey, TValue>
), теперь должен был бы обеспечить некоторые неэффективные средства имитация индексирование или отказ от интерфейса IEnumerable<T>
.
При этом, учитывая, что целью интерфейса является обеспечение минимальной требуемой функциональности в данном сценарии, я действительно считаю, что интерфейс IList<T>
плохо спроектирован. Или, скорее, отсутствие интерфейса «между» IEnumerable<T>
и IList<T>
(произвольный доступ, но без изменений), на мой взгляд, является неудачным упущением в BCL.