Расширение поддержки GetEnumerator
для циклов foreach
.
Заметка
Эта статья является спецификацией компонентов. Спецификация служит проектным документом для функции. Она содержит предлагаемые изменения спецификации, а также информацию, необходимую во время проектирования и разработки функции. Эти статьи публикуются до тех пор, пока предложенные изменения спецификации не будут завершены и включены в текущую спецификацию ECMA.
Может возникнуть некоторое несоответствие между спецификацией компонентов и завершенной реализацией. Эти различия фиксируются в соответствующих совещаниях по проектированию языка (LDM) .
Дополнительные сведения о процессе внедрения спецификаций функций в стандарт языка C# см. в статье о спецификациях .
Проблема чемпиона: https://github.com/dotnet/csharplang/issues/3194
Сводка
Разрешите циклам foreach
распознавать метод расширения GetEnumerator
, который иначе соответствует шаблону foreach, и выполняться по выражению, если в противном случае это вызовет ошибку.
Мотивация
Это приведет foreach
в соответствие с реализацией других функций в C#, включая асинхронный и деконструкцию на основе шаблонов.
Подробный дизайн
Изменение спецификации относительно просто. Мы изменим раздел The foreach statement
§13.9.5 на этот текст:
Обработка инструкции foreach во время компиляции сначала определяет тип коллекции , тип перечислителя и тип элемента выражения. Это определение осуществляется следующим образом:
Если тип
выражения является типом массива, то существует неявное преобразование ссылок из в интерфейс (так как реализует этот интерфейс). Тип коллекции является интерфейсом IEnumerable
, тип перечислителя является интерфейсомIEnumerator
, а тип элемента является типом элемента типа массиваX
.Если тип
X
выраженияdynamic
то есть неявное преобразование из выражения в интерфейсIEnumerable
(§10.2.10). Коллекция типа является интерфейсомIEnumerable
, а перечислитель типа является интерфейсомIEnumerator
. Если идентификаторvar
задан как тип локальной переменной, являетсяdynamic
, в противном случае -object
.В противном случае определите, имеет ли тип
X
соответствующий методGetEnumerator
:
- Выполните поиск члена для типа
X
с идентификаторомGetEnumerator
и без аргументов типа. Если поиск элемента не создает совпадение или создает неоднозначность или создает совпадение, которое не является группой методов, проверьте наличие перечисленного интерфейса, как описано ниже. Рекомендуется выдать предупреждение, если поиск элемента создает что-либо, кроме группы методов или отсутствия совпадения.- Выполните разрешение перегрузки с помощью результирующей группы методов и пустого списка аргументов. Если разрешение перегрузки не приводит к применению применимых методов, приводит к неоднозначности или приводит к одному лучшему методу, но этот метод является статическим или не общедоступным, проверьте наличие перечисленного интерфейса, как описано ниже. Рекомендуется выдавать предупреждение, если разрешение перегрузки не приводит к однозначному общедоступному методу экземпляра или не найдено применимых методов.
- Если возвращаемый тип
E
методаGetEnumerator
не является классом, структурой или типом интерфейса, создается ошибка и дальнейшие действия не выполняются.- Выполняется поиск элемента на
E
с идентификаторомCurrent
без аргументов типа. Если поиск элемента не соответствует, результатом является ошибка, или результатом является что-либо, кроме свойства общедоступного экземпляра, которое разрешает чтение, создается ошибка, и дальнейшие действия не выполняются.- Выполняется поиск элемента на
E
с идентификаторомMoveNext
без аргументов типа. Если в результате поиска участника не находится совпадение, или если результат является чем-либо, кроме группы методов, возникает ошибка, и дальнейшие шаги не предпринимаются.- Разрешение перегрузки выполняется в группе методов с пустым списком аргументов. Если разрешение перегрузки не приводит к применению применимых методов, приводит к неоднозначности или приводит к одному лучшему методу, но этот метод является статическим или не общедоступным, или его тип возвращаемого значения не
bool
, возникает ошибка и дальнейшие шаги не выполняются.- Тип коллекции равен
X
, тип перечислителя равенE
, и тип элемента является типом свойстваCurrent
.В противном случае проверьте наличие перечисленного интерфейса:
- Если среди всех типов
, для которых существует неявное преобразование из в , существует уникальный тип таким образом, что не и для всех остальных существует неявное преобразование из в , то является интерфейсом тип коллекции . Тип перечислителя — это интерфейса, а типа элемента . - В противном случае, если существует несколько таких
T
типа, возникает ошибка и дальнейшие действия не выполняются.- В противном случае, если существует неявное преобразование из
X
в интерфейсSystem.Collections.IEnumerable
, то тип коллекции является этим интерфейсом, тип перечислителя является интерфейсомSystem.Collections.IEnumerator
, а тип элемента являетсяobject
.В противном случае определите, имеет ли тип X соответствующий метод расширения
GetEnumerator
:
- Выполните поиск метода расширения для типа
X
с идентификаторомGetEnumerator
. Если поиск элемента не создает совпадение или создает неоднозначность или создает совпадение, которое не является группой методов, создается ошибка, и дальнейшие действия не выполняются. Рекомендуется выдавать предупреждение, если поиск элемента выдает что-либо, кроме группы методов или отсутствия совпадения.- Выполните разрешение перегрузки с помощью результирующей группы методов и одного аргумента типа
X
. Если разрешение перегрузки не создает применимых методов, приводит к неоднозначности или приводит к одному лучшему методу, но этот метод недоступен, возникает ошибка, и никакие дальнейшие действия не предпринимаются.
- Это разрешение позволяет передавать первый аргумент по ссылке, если
X
является типом структуры, а вид ссылки -in
.- Если возвращаемый тип
E
методаGetEnumerator
не является классом, структурой или типом интерфейса, создается ошибка и дальнейшие действия не выполняются.- Выполняется поиск элемента на
E
с идентификаторомCurrent
без аргументов типа. Если поиск элемента не соответствует, результатом является ошибка, или результатом является что-либо, кроме свойства общедоступного экземпляра, которое разрешает чтение, создается ошибка, и дальнейшие действия не выполняются.- Выполняется поиск элемента на
E
с идентификаторомMoveNext
без аргументов типа. Если в результате поиска участника не находится совпадение, или если результат является чем-либо, кроме группы методов, возникает ошибка, и дальнейшие шаги не предпринимаются.- Разрешение перегрузки выполняется в группе методов с пустым списком аргументов. Если разрешение перегрузки не приводит к применению применимых методов, приводит к неоднозначности или приводит к одному лучшему методу, но этот метод является статическим или не общедоступным, или его тип возвращаемого значения не
bool
, возникает ошибка и дальнейшие шаги не выполняются.- Тип коллекции равен
X
, тип перечислителя равенE
, и тип элемента является типом свойстваCurrent
.В противном случае возникает ошибка, и дальнейшие действия не выполняются.
Для await foreach
правила изменяются аналогичным образом. Единственное изменение, необходимое для этой спецификации, заключается в удалении строки Extension methods do not contribute.
из описания, так как остальная часть этой спецификации основана на приведенных выше правилах с различными именами, замененными методами шаблона.
Недостатки
Каждое изменение добавляет языку дополнительную сложность, и это потенциально допускает ситуации, когда вещи, которые не предназначены для foreach
быть foreach
, например, Range
.
Альтернативы
Ничего не делая.
Неразрешенные вопросы
Нет на этом этапе.
C# feature specifications