¿Por qué implementar IEnumerable (T) si solo puedo definir ONE GetEnumerator?

Actualizar: Agradezco todos los comentarios, que esencialmente han constituido una oposición unánime. Si bien cada objeción planteada era válida, creo que el clavo definitivo en el ataúd fueLa astuta observación de Ani que, en última instancia, incluso eluno minúsculo El beneficio que esta idea aparentemente ofrecía, la eliminación del código repetitivo, se negaba por el hecho de que la idea misma requeriría supropio código repetitivo.

Así que sí, considéreme convencido: sería una mala idea.

Y solo para salvar un poco mi dignidad: podría haberlo jugado por el bien de la discusión, pero para empezar nunca me entusiasmó esta idea, simplemente curioso por escuchar lo que otros tenían que decir al respecto. Honesto.

Antes de descartar esta pregunta como absurda, le pido que considere lo siguiente:

IEnumerable<T> hereda de*IEnumerable, lo que significa que cualquier tipo que implementeIEnumerable<T> generalmente debe implementarambos IEnumerable<T>.GetEnumerator y (explícitamente)IEnumerable.GetEnumerator. Esto básicamente equivale a código repetitivo.Usted puedeforeach sobre cualquier tipo que tenga unGetEnumerator , siempre que ese método devuelva un objeto de algún tipo con unMoveNext método y unCurrent propiedad. Entonces, si tu tipo defineuno método con la firmapublic IEnumerator<T> GetEnumerator(), es legal enumerarlo usandoforeach.Claramente, existe una gran cantidad de código que requiereIEnumerable<T> interfaz, por ejemplo, básicamente todos los métodos de extensión LINQ. Afortunadamente, pasar de un tipo que puedasforeach a unIEnumerable<T> es trivial usando la generación automática de iteradores que C # suministra a través deyield palabra clave.

Entonces, al juntar todo esto, tuve una idea loca: ¿qué pasa si solo defino mi propia interfaz que se ve así?

public interface IForEachable<T>
{
    IEnumerator<T> GetEnumerator();
}

Entonces cada vez que defino un tipo que quiero enumerar, implementoesta interfaz en lugar deIEnumerable<T>, eliminando la necesidad de implementar dosGetEnumerator métodos (uno explícito). Por ejemplo:

class NaturalNumbers : IForEachable<int>
{
   public IEnumerator<int> GetEnumerator()
   {
       int i = 1;
       while (i < int.MaxValue)
       {
           yield return (i++);
       }
   }

   // Notice how I don't have to define a method like
   // IEnumerator IEnumerable.GetEnumerator().
}

Finalmente, para que este tipo sea compatible con el código quehace esperar elIEnumerable<T> interfaz, solo puedo definir un método de extensión para ir desde cualquierIForEachable<T> a unaIEnumerable<T> al igual que:

public static class ForEachableExtensions
{
    public static IEnumerable<T> AsEnumerable<T>(this IForEachable<T> source)
    {
        foreach (T item in source)
        {
            yield return item;
        }
    }
}

Me parece que hacer esto me permite diseñar tipos que se pueden utilizar en todos los sentidos como implementaciones deIEnumerable<T>, pero sin ese molesto explícitoIEnumerable.GetEnumerator implementación en cada uno.

Por ejemplo:

var numbers = new NaturalNumbers();

// I can foreach myself...
foreach (int x in numbers)
{
    if (x > 100)
        break;

    if (x % 2 != 0)
        continue;

    Console.WriteLine(x);
}

// Or I can treat this object as an IEnumerable<T> implementation
// if I want to...
var evenNumbers = from x in numbers.AsEnumerable()
                  where x % 2 == 0
                  select x;

foreach (int x in evenNumbers.TakeWhile(i => i <= 100))
{
    Console.WriteLine(x);
}

que piensan de esta idea? ¿Me estoy perdiendo alguna razón por la cual esto sería un error?

Me doy cuenta de que probablemente parezca una solución demasiado compleja para lo que no es tan importante para empezar (dudo que alguienDe Verdad se preocupa mucho por tener que definir explícitamente elIEnumerable interfaz); pero se me ocurrió y no veo ningún problema obvio que plantearía este enfoque.

En general, si puedo escribir una cantidad moderada de códigouna vez para ahorrarme la molestia de tener que escribir una pequeña cantidad de códigomuchas veces, para mí, vale la pena.

* ¿Es esa la terminología correcta para usar? Siempre dudo en decir que una interfaz "hereda" de otra, ya que eso no parece capturar adecuadamente la relación entre ellas. Pero tal vez sea correcto.

Respuestas a la pregunta(6)

Su respuesta a la pregunta