Importante

Você está vendo a versão anterior da nova experiência da Alura que estamos preparando para você. Em breve, ela ganha uma identidade visual novinha totalmente pensada em potencializar seus estudos!

1
resposta

Dúvida sobre Enumerator e Enumerable

Seria uma boa prática colocar as 2 interfaces na mesma classe ?

public class DiasSemana : IEnumerable<string>, IEnumerator<string>
{
    private readonly string[] dias = ["Segunda", "Terça", "Quarta", "Quinta", "Sexta", "Sábado", "Domingo"];

    private int posicao = -1;

    public string Current => dias[posicao];

    object IEnumerator.Current => Current;

    public void Dispose(){ }

    public IEnumerator<string> GetEnumerator()
    {
        return this;
    }

    public bool MoveNext()
    {
        posicao++;
        return posicao < dias.Length;
    }

    public void Reset()
    {
        posicao = -1;
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}
1 resposta

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.