Partilhar via


Suporte de extensão GetEnumerator para loops foreach.

Observação

Este artigo é uma especificação de recurso. A especificação serve como o documento de design para o recurso. Ele inclui mudanças de especificação propostas, juntamente com as informações necessárias durante o design e desenvolvimento do recurso. Estes artigos são publicados até que as alterações de especificações propostas sejam finalizadas e incorporadas na especificação ECMA atual.

Pode haver algumas discrepâncias entre a especificação do recurso e a implementação concluída. Essas diferenças são capturadas nas notas pertinentes da Language Design Meeting (LDM).

Você pode saber mais sobre o processo de adoção de especificações de recursos no padrão de linguagem C# no artigo sobre as especificações .

Questão campeã: https://github.com/dotnet/csharplang/issues/3194

Resumo

Permita que os loops foreach reconheçam um método de extensão GetEnumerator que, de outra forma, satisfaça o padrão de foreach e realizem um loop sobre a expressão, caso contrário seria um erro.

Motivação

Isso alinhará o foreach com a forma como outros recursos em C# são implementados, incluindo assíncrono e desconstrução baseada em padrões.

Projeto detalhado

A mudança de especificação é relativamente simples. Modificamos a seção The foreach statement§13.9.5 para este texto:

O processamento em tempo de compilação de uma instrução foreach primeiro determina o tipo de coleção , o tipo de enumerador e o tipo de elemento da expressão. Esta determinação procede da seguinte forma:

  • Se o tipo X de expressão é um tipo de matriz, então há uma conversão de referência implícita de X para a interface IEnumerable (uma vez que System.Array implementa essa interface). O tipo de coleção é a interface IEnumerable, o tipo de enumerador é a interface IEnumerator e o tipo de elemento é o tipo de elemento do tipo de matriz X.

  • Se o tipo X da expressão for dynamic então existe uma conversão implícita da expressão para a interface IEnumerable (§10.2.10). O tipo de coleção é a interface IEnumerable e o tipo de enumerador é a interface IEnumerator. Se o identificador var é dado como o local_variable_type então o tipo de elemento é dynamic, caso contrário, é object.

  • Caso contrário, determine se o tipo X tem um método de GetEnumerator apropriado:

    • Efetuar a procura de membros no tipo X com o identificador GetEnumerator e sem argumentos de tipo. Se a pesquisa de membro não produzir uma correspondência, ou produzir uma ambiguidade, ou produzir uma correspondência que não seja um grupo de método, verifique se há uma interface enumerável conforme descrito abaixo. Recomenda-se que um aviso seja emitido se a pesquisa de membros resultar em algo que não seja um grupo de métodos ou nenhuma correspondência encontrada.
    • Execute a resolução de sobrecarga usando o grupo de métodos resultante e uma lista de argumentos vazia. Se a resolução de sobrecarga não resultar em nenhum método aplicável, resultar em uma ambiguidade ou resultar em um único melhor método, mas esse método for estático ou não público, verifique se há uma interface enumerável conforme descrito abaixo. Recomenda-se que seja emitido um aviso caso a resolução de sobrecarga produza algo diferente de um método de instância pública inequívoco ou caso não haja métodos aplicáveis.
    • Se o tipo de retorno E do método GetEnumerator não for um tipo de classe, struct ou interface, um erro será produzido e nenhuma outra etapa será executada.
    • A pesquisa de membros é realizada em E com o identificador Current e sem argumentos de tipo. Se a pesquisa de membro não produzir nenhuma correspondência, resultar num erro, ou retornar algo que não seja uma propriedade de instância pública que permita leitura, um erro será produzido e nenhuma outra etapa será executada.
    • A pesquisa de membros é realizada em E com o identificador MoveNext e sem argumentos de tipo. Se a pesquisa de membro não produzir nenhuma correspondência, o resultado for um erro ou o resultado for qualquer coisa, exceto um grupo de métodos, um erro será produzido e nenhuma outra etapa será executada.
    • A resolução de sobrecarga é executada no grupo de métodos com uma lista de argumentos vazia. Se a resolução de sobrecarga não resultar em nenhum método aplicável, resultar em uma ambiguidade ou resultar em um único melhor método, mas esse método for estático ou não público, ou seu tipo de retorno não for bool, um erro será produzido e nenhuma outra etapa será tomada.
    • O tipo de coleção é X, o tipo de enumerador é Ee o tipo de elemento é o tipo da propriedade Current.
  • Caso contrário, verifique se há uma interface enumerável:

    • Se entre todos os tipos Ti para os quais há uma conversão implícita de X para IEnumerable<Ti>, há um tipo único T tal que T não é dynamic e para todos os outros Ti há uma conversão implícita de IEnumerable<T> para IEnumerable<Ti>, então o tipo de coleção é a interface IEnumerable<T>, O tipo de enumerador é o IEnumerator<T>de interface e o tipo de elemento é T.
    • Caso contrário, se houver mais de um tipo T, então ocorre um erro e nenhuma outra ação é realizada.
    • Caso contrário, se houver uma conversão implícita de X para a interface System.Collections.IEnumerable, o tipo de coleção é essa interface, o tipo de enumerador é a interface System.Collections.IEnumeratore o tipo de elemento é object.
  • Caso contrário, determine se o tipo «X» tem um método de extensão GetEnumerator que seja adequado.

    • Realizar a pesquisa de método de extensão no tipo X com identificador GetEnumerator. Se a pesquisa de membro não produzir uma correspondência, ou produzir uma ambiguidade, ou produzir uma correspondência que não seja um grupo de métodos, será apresentado um erro e nenhuma outra ação será tomada. Recomenda-se que um aviso seja emitido se a pesquisa de membros resultar em qualquer coisa, exceto um grupo de métodos ou nenhuma correspondência.
    • Execute a resolução de sobrecarga usando o grupo de métodos resultante e um único argumento do tipo X. Se a resolução de sobrecarga não produzir métodos aplicáveis, resultar em uma ambiguidade ou resultar em um único melhor método, mas esse método não estiver acessível, um erro será produzido e nenhuma outra etapa será tomada.
      • Esta resolução permite que o primeiro argumento seja passado por ref se X for um tipo struct e o tipo de ref for in.
    • Se o tipo de retorno E do método GetEnumerator não for um tipo de classe, struct ou interface, um erro será produzido e nenhuma outra etapa será executada.
    • A pesquisa de membros é realizada em E com o identificador Current e sem argumentos de tipo. Se a pesquisa de membro não produzir nenhuma correspondência, resultar num erro, ou retornar algo que não seja uma propriedade de instância pública que permita leitura, um erro será produzido e nenhuma outra etapa será executada.
    • A pesquisa de membros é realizada em E com o identificador MoveNext e sem argumentos de tipo. Se a pesquisa de membro não produzir nenhuma correspondência, o resultado for um erro ou o resultado for qualquer coisa, exceto um grupo de métodos, um erro será produzido e nenhuma outra etapa será executada.
    • A resolução de sobrecarga é executada no grupo de métodos com uma lista de argumentos vazia. Se a resolução de sobrecarga não resultar em nenhum método aplicável, resultar em uma ambiguidade ou resultar em um único melhor método, mas esse método for estático ou não público, ou seu tipo de retorno não for bool, um erro será produzido e nenhuma outra etapa será tomada.
    • O tipo de coleção é X, o tipo de enumerador é Ee o tipo de elemento é o tipo da propriedade Current.
  • Caso contrário, é produzido um erro e não são tomadas mais medidas.

No caso de await foreach, as regras são modificadas de forma semelhante. A única alteração necessária para essa especificação é remover a linha Extension methods do not contribute. da descrição, já que o restante dessa especificação é baseado nas regras acima com nomes diferentes substituídos pelos métodos de padrão.

Desvantagens

Cada alteração adiciona mais complexidade à linguagem, e isso potencialmente permite que coisas que não foram projetadas para serem foreachpossam ser foreach, como Range.

Alternativas

Não fazer nada.

Questões por resolver

Nenhum neste momento.