Compartilhar via


Suporte à 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. Ela inclui alterações de especificação propostas, juntamente com as informações necessárias durante o design e o desenvolvimento do recurso. Esses artigos são publicados até que as alterações de especificação propostas sejam finalizadas e incorporadas na especificação ECMA atual.

Pode haver algumas divergências entre a especificação do recurso e a implementação concluída. Essas diferenças são capturadas nas notas pertinentes da reunião de design de idioma (LDM).

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

Problema do especialista: https://github.com/dotnet/csharplang/issues/3194

Resumo

Permitir que os loops foreach reconheçam um método de extensão GetEnumerator que satisfaça o padrão foreach e interajam com a expressão quando, de outra forma, resultaria em 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ão.

Projeto detalhado

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

Primeiramente, o processamento em tempo de compilação de uma instrução foreach 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 for um tipo de matriz, então há uma conversão de referência implícita de X para a interface IEnumerable (já 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 de expressão for dynamic, haverá 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 for fornecido como local_variable_type, o tipo de elemento será dynamic; caso contrário, será object.

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

    • Realize a pesquisa de membro 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étodos, verifique se há uma interface enumerável, conforme descrito abaixo. É recomendável que um aviso seja emitido se a pesquisa de membros produzir algo além de um grupo de métodos ou nenhuma correspondência.
    • 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 resultar em nenhum método aplicável, resultar em uma ambiguidade ou resultar em um único método melhor, mas esse método for estático ou não público, verifique se há uma interface enumerável conforme descrito abaixo. É recomendável que um aviso seja emitido se a resolução de sobrecarga produzir qualquer coisa, exceto um método de instância pública inequívoco ou se não houver métodos aplicáveis.
    • Se o tipo de retorno E do método GetEnumerator não for uma classe, struct ou tipo de interface, um erro será produzido e nenhuma etapa adicional será executada.
    • A consulta de membro é executada em E com o identificador Current sem argumentos de tipo. Se a pesquisa de membro não encontrar nenhuma correspondência, ou se o resultado for qualquer coisa exceto uma propriedade de instância pública que permita a leitura, um erro será produzido e nenhuma etapa adicional será executada.
    • A consulta de membro é executada em E com o identificador MoveNext sem argumentos de tipo. Se a pesquisa de membros 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 métodos aplicáveis, resultar em uma ambiguidade ou resultar em um único melhor método, mas esse método for estático, não for público ou seu tipo de retorno não puder ser bool, um erro é produzido e nenhuma outra etapa é seguida.
    • O tipo de coleção é X, o tipo 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>, existe um tipo único T de modo que T não seja dynamic e para todos os outros Ti há uma conversão implícita de IEnumerable<T> para IEnumerable<Ti>, o tipo de coleção é a interface IEnumerable<T>, o tipo de enumerador é a interface IEnumerator<T> e o tipo de elemento é T.
    • Caso contrário, se houver mais de um tipo desse tipo T, um erro é produzido e nenhuma outra etapa é executada.
    • 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.IEnumerator, e o tipo de elemento é object.
  • Caso contrário, determine se o tipo "X" tem um método de extensão GetEnumerator apropriado:

    • Execute a pesquisa de método de extensão no tipo X com o identificador GetEnumerator. Se a pesquisa de membros não produzir uma correspondência, ou produzir uma ambiguidade, ou produzir uma correspondência que não seja um grupo de métodos, um erro será produzido e nenhuma outra etapa será executada. É recomendável que um aviso seja emitido se a pesquisa de membros produzir algo além de 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 ambiguidade ou resultar em um único método melhor, mas esse método não for acessível, um erro será produzido e nenhuma etapa adicional será executada.
      • Essa resolução permite que o primeiro argumento seja passado por referência se X for um tipo de struct e o tipo ref for in.
    • Se o tipo de retorno E do método GetEnumerator não for uma classe, struct ou tipo de interface, um erro será produzido e nenhuma etapa adicional será executada.
    • A consulta de membro é executada em E com o identificador Current sem argumentos de tipo. Se a pesquisa de membro não encontrar nenhuma correspondência, ou se o resultado for qualquer coisa exceto uma propriedade de instância pública que permita a leitura, um erro será produzido e nenhuma etapa adicional será executada.
    • A consulta de membro é executada em E com o identificador MoveNext sem argumentos de tipo. Se a pesquisa de membros 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 métodos aplicáveis, resultar em uma ambiguidade ou resultar em um único melhor método, mas esse método for estático, não for público ou seu tipo de retorno não puder ser bool, um erro é produzido e nenhuma outra etapa é seguida.
    • O tipo de coleção é X, o tipo enumerador é Ee o tipo de elemento é o tipo da propriedade Current.
  • Caso contrário, um erro será produzido e nenhuma outra etapa será executada.

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

Desvantagens

Cada mudança acrescenta uma complexidade extra ao idioma, e isso potencialmente permite que coisas que não foram projetadas para serem foreach também sejam foreach, como Range.

Alternativas

Não fazer nada.

Perguntas não resolvidas

Nenhuma das opções no momento.