Compatibilidad con extensiones GetEnumerator
para bucles foreach
.
Nota:
Este artículo es una especificación de características. La especificación actúa como documento de diseño de la característica. Incluye cambios de especificación propuestos, junto con la información necesaria durante el diseño y el desarrollo de la característica. Estos artículos se publican hasta que se finalizan los cambios de especificación propuestos y se incorporan en la especificación ECMA actual.
Puede haber algunas discrepancias entre la especificación de características y la implementación completada. Esas diferencias se recogen en las notas de la reunión de diseño de lenguaje (LDM) correspondientes.
Puede obtener más información sobre el proceso de adopción de especificaciones de características en el estándar del lenguaje C#, en el artículo sobre especificaciones.
Problema del campeón: https://github.com/dotnet/csharplang/issues/3194
Resumen
Permite que los bucles foreach
reconozcan un método de extensión GetEnumerator
que, de otra manera, cumpliría con el patrón foreach, y que recorran en bucle la expresión cuando de lo contrario sería un error.
Motivación
Esto hará que foreach
se ajusta a cómo se implementan otras características en C#, incluida la deconstrucción asíncrona y basada en patrones.
Diseño detallado
El cambio de especificación es relativamente sencillo. Hemos modificado la sección The foreach statement
§13.9.5 a este texto:
El procesamiento en tiempo de compilación de una instrucción foreach primero determina el tipo de colección , el tipo de enumerador y el tipo de elemento de la expresión. La determinación procede de la siguiente manera:
Si el tipo
X
de expresión es un tipo array entonces hay una conversión de referencia implícita deX
a la interfazIEnumerable
(ya queSystem.Array
implementa esta interfaz). El tipo de colección es la interfazIEnumerable
, el tipo de enumerador es la interfazIEnumerator
y el tipo de elemento es el tipo de elemento del tipo de matrizX
.Si el tipo
X
de expresión esdynamic
entonces hay una conversión implícita de expresión a la interfazIEnumerable
(sección 10.2.10). El tipo de colección es la interfazIEnumerable
y el tipo de enumerador es la interfazIEnumerator
. Si el identificadorvar
se da como local_variable_type, entonces el tipo del elemento esdynamic
, de lo contrario esobject
.En caso contrario, se determina si el tipo
X
tiene un método apropiadoGetEnumerator
:
- Haga una búsqueda de miembros en el tipo
X
con el identificadorGetEnumerator
y sin argumentos de tipo. Si la búsqueda de miembros no produce una coincidencia, o produce una ambigüedad, o produce una coincidencia que no es un grupo de métodos, compruebe si hay una interfaz enumerable como se describe a continuación. Se recomienda emitir una advertencia si la búsqueda de miembros genera cualquier cosa excepto un grupo de métodos o ninguna coincidencia.- Realiza la resolución de sobrecarga utilizando el grupo de métodos resultante y una lista de argumentos vacía. Si la resolución de sobrecarga no da como resultado ningún método aplicable, da como resultado una ambigüedad o da como resultado un único método mejor pero ese método es estático o no es público, compruebe si hay una interfaz enumerable como se describe a continuación. Se recomienda emitir una advertencia si la resolución de sobrecarga genera cualquier cosa excepto un método de instancia inequívoco y público o ningún método aplicable.
- Si el tipo de retorno
E
del métodoGetEnumerator
no es un tipo de clase, estructura o interfaz, se produce un error y no se realizan más pasos.- La búsqueda de miembros se realiza
E
con el identificadorCurrent
y sin argumentos de tipo. Si la búsqueda de miembros no produce ninguna coincidencia, el resultado es un error, o el resultado es cualquier cosa excepto una propiedad pública de instancia que permite la lectura, se produce un error y no se realizan más pasos.- La búsqueda de miembros se realiza
E
con el identificadorMoveNext
y sin argumentos de tipo. Si la búsqueda de miembros no produce ninguna coincidencia, el resultado es un error, o el resultado es cualquier cosa excepto un grupo de métodos, se produce un error y no se toman más medidas.- La resolución de sobrecarga se realiza en el grupo de métodos con una lista de argumentos vacía. Si la resolución de sobrecargas no da como resultado ningún método aplicable, da como resultado una ambigüedad, o da como resultado un único mejor método pero ese método es estático o no es público, o su tipo de retorno no es
bool
, se produce un error y no se siguen más pasos.- El tipo de la colección es
X
, el tipo del enumerador esE
, y el tipo del elemento es el tipo de la propiedadCurrent
.En caso contrario, comprueba si existe una interfaz enumerable:
- Si entre todos los tipos
Ti
para los que hay una conversión implícita deX
aIEnumerable<Ti>
, hay un único tipoT
tal queT
no esdynamic
y para todos los demásTi
hay una conversión implícita deIEnumerable<T>
aIEnumerable<Ti>
, entonces el tipo de colección es la interfazIEnumerable<T>
, el tipo de enumerador es la interfazIEnumerator<T>
, y el tipo de elemento esT
.- En caso contrario, si hay más de un tipo
T
, se produce un error y no se realizan más pasos.- De lo contrario, si hay una conversión implícita de
X
a la interfazSystem.Collections.IEnumerable
, entonces el tipo de colección es esta interfaz, el tipo de enumerador es la interfazSystem.Collections.IEnumerator
, y el tipo de elemento esobject
.En caso contrario, determina si el tipo "X" tiene un método
GetEnumerator
de extensión apropiado:
- Realice la búsqueda de métodos de extensión en el tipo
X
con identificadorGetEnumerator
. Si la búsqueda de miembros no produce una coincidencia, o produce una ambigüedad, o produce una coincidencia que no es un grupo de métodos, se produce un error y no se dan más pasos. Se recomienda emitir una advertencia si la búsqueda de miembros produce algo distinto a un grupo de métodos o cuando no haya coincidencias.- Realice la resolución de sobrecarga utilizando el grupo de métodos resultante y un único argumento de tipo
X
. Si la resolución de sobrecarga no produce ningún método aplicable, da lugar a una ambigüedad, o da lugar a un único mejor método pero ese método no es accesible, se produce un error y no se siguen más pasos.
- Esta resolución permite que el primer argumento sea pasado por ref si
X
es un tipo struct, y el tipo ref esin
.- Si el tipo de retorno
E
del métodoGetEnumerator
no es un tipo de clase, estructura o interfaz, se produce un error y no se realizan más pasos.- La búsqueda de miembros se realiza
E
con el identificadorCurrent
y sin argumentos de tipo. Si la búsqueda de miembros no produce ninguna coincidencia, el resultado es un error, o el resultado es cualquier cosa excepto una propiedad pública de instancia que permite la lectura, se produce un error y no se realizan más pasos.- La búsqueda de miembros se realiza
E
con el identificadorMoveNext
y sin argumentos de tipo. Si la búsqueda de miembros no produce ninguna coincidencia, el resultado es un error, o el resultado es cualquier cosa excepto un grupo de métodos, se produce un error y no se toman más medidas.- La resolución de sobrecarga se realiza en el grupo de métodos con una lista de argumentos vacía. Si la resolución de sobrecargas no da como resultado ningún método aplicable, da como resultado una ambigüedad, o da como resultado un único mejor método pero ese método es estático o no es público, o su tipo de retorno no es
bool
, se produce un error y no se siguen más pasos.- El tipo de la colección es
X
, el tipo del enumerador esE
, y el tipo del elemento es el tipo de la propiedadCurrent
.En caso contrario, se produce un error y no se realizan más pasos.
Para await foreach
, las reglas se modifican de forma similar. El único cambio que se requiere en esa especificación es eliminar la línea Extension methods do not contribute.
de la descripción, ya que el resto de esa especificación se basa en las reglas anteriores con diferentes nombres sustituidos para los métodos del patrón.
Inconvenientes
Cada cambio agrega complejidad adicional al lenguaje, y esto potencialmente permite que lo que se diseñó para tener foreach
tenga foreach
, como Range
.
Alternativas
Sin hacer nada.
Preguntas sin resolver
Ninguno en este punto.
C# feature specifications