Compartir a través de


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 de X a la interfaz IEnumerable (ya que System.Array implementa esta interfaz). El tipo de colección es la interfaz IEnumerable, el tipo de enumerador es la interfaz IEnumerator y el tipo de elemento es el tipo de elemento del tipo de matriz X.

  • Si el tipo X de expresión es dynamic entonces hay una conversión implícita de expresión a la interfaz IEnumerable (sección 10.2.10). El tipo de colección es la interfaz IEnumerable y el tipo de enumerador es la interfaz IEnumerator. Si el identificador var se da como local_variable_type, entonces el tipo del elemento es dynamic, de lo contrario es object.

  • En caso contrario, se determina si el tipo X tiene un método apropiado GetEnumerator:

    • Haga una búsqueda de miembros en el tipo X con el identificador GetEnumerator 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étodo GetEnumerator 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 identificador Current 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 identificador MoveNext 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 es E, y el tipo del elemento es el tipo de la propiedad Current.
  • En caso contrario, comprueba si existe una interfaz enumerable:

    • Si entre todos los tipos Ti para los que hay una conversión implícita de X a IEnumerable<Ti>, hay un único tipo T tal que T no es dynamic y para todos los demás Ti hay una conversión implícita de IEnumerable<T> a IEnumerable<Ti>, entonces el tipo de colección es la interfaz IEnumerable<T>, el tipo de enumerador es la interfaz IEnumerator<T>, y el tipo de elemento es T.
    • 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 interfaz System.Collections.IEnumerable, entonces el tipo de colección es esta interfaz, el tipo de enumerador es la interfaz System.Collections.IEnumerator, y el tipo de elemento es object.
  • 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 identificador GetEnumerator. 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 es in.
    • Si el tipo de retorno E del método GetEnumerator 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 identificador Current 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 identificador MoveNext 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 es E, y el tipo del elemento es el tipo de la propiedad Current.
  • 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.