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 deX
para a interfaceIEnumerable
(já queSystem.Array
implementa essa interface). O tipo de coleção é a interfaceIEnumerable
, o tipo de enumerador é a interfaceIEnumerator
e o tipo de elemento é o tipo de elemento do tipo de matrizX
.Se o tipo
X
de expressão fordynamic
, haverá uma conversão implícita da expressão para a interfaceIEnumerable
(§10.2.10). O tipo de coleção é a interfaceIEnumerable
e o tipo de enumerador é a interfaceIEnumerator
. Se o identificadorvar
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étodoGetEnumerator
apropriado:
- Realize a pesquisa de membro no tipo
X
com o identificadorGetEnumerator
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étodoGetEnumerator
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 identificadorCurrent
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 identificadorMoveNext
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 éE
e o tipo de elemento é o tipo da propriedadeCurrent
.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 deX
paraIEnumerable<Ti>
, existe um tipo únicoT
de modo queT
não sejadynamic
e para todos os outrosTi
há uma conversão implícita deIEnumerable<T>
paraIEnumerable<Ti>
, o tipo de coleção é a interfaceIEnumerable<T>
, o tipo de enumerador é a interfaceIEnumerator<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 interfaceSystem.Collections.IEnumerable
, o tipo de coleção é essa interface, o tipo de enumerador é a interfaceSystem.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 identificadorGetEnumerator
. 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 forin
.- Se o tipo de retorno
E
do métodoGetEnumerator
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 identificadorCurrent
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 identificadorMoveNext
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 éE
e o tipo de elemento é o tipo da propriedadeCurrent
.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.
C# feature specifications