Почему операторы foreach принимают объекты, реализующие шаблон «Коллекция», а не принимают только объекты, реализующие IEnumerable?

Многие люди спрашивают меня, почему, и у меня нет для них хорошего ответа.

Очевидно, есть веская причина. Кто-нибудь это знает?

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


person Rafael Romão    schedule 25.12.2008    source источник


Ответы (2)


Предположим, вам нужен эквивалент IEnumerable<int>, но вы используете C# 1.0. Вы могли реализовать IEnumerable, но это потребовало бы упаковки и распаковки на каждой итерации. Используя своего рода утиную версию foreach, вы можете обойтись без бокса. Во многих случаях бокс на самом деле не так вреден (я склонен считать, что снижение производительности преувеличено), но все равно выглядит неэлегантно.

Я сильно, сильно подозреваю, что если бы дженерики существовали в C# 1.0, foreach было бы ограничено IEnumerable<T>.

person Jon Skeet    schedule 25.12.2008
comment
Обобщения были БОЛЬШОЙ НЕДОСТАЮЩЕЙ ФУНКЦИЕЙ (BMF) версии 1.0, и этот факт отразился на фреймворке и языках. Это всего лишь один пример того, что было спроектировано по-другому и теперь не может быть исправлено. - person Jay Bazuzi; 25.12.2008
comment
@Джей: Согласен. Конечно, такие вещи будут всегда, но лично я бы предпочел, чтобы они отложили выпуск .NET 1.0 на год (или больше) и добавили дженерики. Большая маркетинговая головная боль. К счастью, эта особая сложность в C# на самом деле не наносит большого ущерба. Есть хуже :( - person Jon Skeet; 26.12.2008

Есть еще пара преимуществ foreach с утиным типом по сравнению с IEnumerable:

  1. There are some things which satisfy enough of the "IEnumerable" contract to be usable with "foreach", but which don't fully satisfy the contract, especially the ability to repeatedly get independent enumerators. With regard this point, alloing "foreach" to accept either IEnumerator [generic or not] or IEnumerable [likewise] would be an alternative solution.
  2. Although some implementations of IEnumerable.GetEnumerator return a struct that implements IEnumerator, and although this is useful when using duck typing, the behavior can be problematic in some contexts. The fact that foreach uses duck typing allows compilers to have a public GetEnumerator() function return a struct-type enumerator (which does not implement any interface) and have compilers use that, while having IEnumerable.GetEnumerator return a class.

Второе преимущество действительно может быть достигнуто только с помощью утиного набора текста.

person supercat    schedule 15.11.2011