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 deX
para a interfaceIEnumerable
(uma vez 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
da expressão fordynamic
então existe 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
é 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 deGetEnumerator
apropriado:
- Efetuar a procura de membros 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é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étodoGetEnumerator
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 identificadorCurrent
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 identificadorMoveNext
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 é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>
, há um tipo únicoT
tal queT
não édynamic
e para todos os outrosTi
há uma conversão implícita deIEnumerable<T>
paraIEnumerable<Ti>
, então o tipo de coleção é a interfaceIEnumerable<T>
, O tipo de enumerador é oIEnumerator<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 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
que seja adequado.
- Realizar a pesquisa de método de extensão no tipo
X
com identificadorGetEnumerator
. 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 forin
.- Se o tipo de retorno
E
do métodoGetEnumerator
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 identificadorCurrent
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 identificadorMoveNext
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 éE
e o tipo de elemento é o tipo da propriedadeCurrent
.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 foreach
possam ser foreach
, como Range
.
Alternativas
Não fazer nada.
Questões por resolver
Nenhum neste momento.
C# feature specifications