Olá Fellipe.
Tudo bem?
Essa é uma duvida muito comum.
Embora funcione para um exemplo didático, em código de produção geralmente não é uma boa prática implementar IEnumerable<T> e IEnumerator<T> na mesma classe.
O principal problema está no método:
public IEnumerator<string> GetEnumerator()
{
return this;
}
Ao retornar a própria instância como enumerador, você cria um objeto que é simultaneamente a coleção e o iterador. Isso traz algumas limitações:
- O estado da iteração (
posicao) fica armazenado na própria coleção. - Duas iterações simultâneas podem gerar comportamento incorreto.
- Um segundo
foreach pode começar de uma posição inesperada caso o enumerador não tenha sido reiniciado. - A classe passa a ter duas responsabilidades: armazenar os dados e controlar a navegação pelos dados.
Por exemplo:
var dias = new DiasSemana();
foreach(var dia1 in dias)
{
foreach(var dia2 in dias)
{
// Pode apresentar comportamento inesperado
}
}
O padrão mais comum é que a coleção implemente apenas IEnumerable<T> e retorne um novo enumerador a cada chamada de GetEnumerator().
Uma implementação moderna seria:
public class DiasSemana : IEnumerable<string>
{
private readonly string[] dias =
[
"Segunda", "Terça", "Quarta",
"Quinta", "Sexta", "Sábado", "Domingo"
];
public IEnumerator<string> GetEnumerator()
{
foreach (var dia in dias)
yield return dia;
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Ou até mais simples:
public IEnumerator<string> GetEnumerator() => ((IEnumerable<string>)dias).GetEnumerator();
Eu diria que:
- Para aprendizado, implementar as duas interfaces na mesma classe ajuda a entender como o mecanismo de enumeração funciona internamente.
- Para código real, prefira separar as responsabilidades ou utilizar
yield return, que é a solução mais idiomática e legível.
A coleção deve representar os dados; o enumerador deve representar o estado da iteração. Separar essas responsabilidades torna o código mais seguro, reutilizável e aderente ao padrão.
Avise qualquer duvida.
Bons estudos.