Pregunta

Actualizar : Agradezco todos los comentarios, que han formado esencialmente oposición unánime. Mientras que cada objeción es válida, siento que el clavo final en el ataúd fue astuta observación de Ani que, en última instancia, incluso el strong> un minúsculo ventaja propia código repetitivo

Así que sí, me consideran convencido:. Sería una mala idea

Y sólo para una especie de rescate mi dignidad tanto: yo podría haber jugado para bien del argumento, pero nunca fue vendido en esta idea para empezar - simplemente curiosidad por escuchar lo que otros tienen que decir al respecto. Honesto.


Antes de despedir a esta pregunta tan absurda, te pido que considerar lo siguiente:

  1. hereda IEnumerable<T> de * IEnumerable, lo que significa que cualquier tipo que implementos IEnumerable<T> generalmente debe implementar ambos IEnumerable<T>.GetEnumerator y (explícitamente) IEnumerable.GetEnumerator. Esto equivale básicamente al código repetitivo.
  2. Puede foreach sobre cualquier tipo que tiene un método GetEnumerator, siempre y cuando el método devuelve un objeto de algún tipo con un método y una propiedad MoveNext Current. Así que si su tipo define un método con el public IEnumerator<T> GetEnumerator() firma, es legal para enumerar sobre él, usando foreach.
  3. Es evidente que hay una gran cantidad de código por ahí que requiere la interfaz IEnumerable<T> - por ejemplo, básicamente todos los métodos de extensión de LINQ. Por suerte, para pasar de un tipo que puede foreach a un IEnumerable<T> es trivial mediante la generación automática de iterador que C # suministros a través de la palabra clave yield.

Por lo tanto, poner todo esto junto, yo tenía esta idea loca: ¿y si acabo de definir mi propia interfaz que es similar al siguiente:

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

Entonces cada vez que definir un tipo que yo quiero ser enumerables, implemento este interfaz en lugar de IEnumerable<T>, lo que elimina la necesidad de implementar dos métodos GetEnumerator (uno explícita) . 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().
}

último , con el fin de hacer este tipo compatible con el código que hace esperar que la interfaz IEnumerable<T>, sólo puede definir un método de extensión para ir desde cualquier IForEachable<T> a un IEnumerable<T> de esta manera:

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 al hacer esto me permite diseñar tipos que se pueden utilizar en todos los sentidos como implementaciones de IEnumerable<T>, pero sin que la aplicación explícita IEnumerable.GetEnumerator molestos 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);
}

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

Me doy cuenta de que probablemente parece una solución demasiado difícil de lo que no es tan grande de un acuerdo para empezar es (dudo que nadie realmente cuidados que mucho de tener que definir explícitamente la interfaz IEnumerable) ; pero simplemente me vino a la cabeza y no aparece ningún problema obvio que este enfoque plantearía.

En general, si se puede escribir una cantidad moderada de código una vez para salvarme la molestia de tener que escribir una pequeña cantidad de código muchas veces , para mí , vale la pena.

* ¿Es que la terminología derecho de uso? Siempre soy reacio a decir "hereda de" una interfaz de otra, como eso no parece captar adecuadamente la relación entre ellos. Pero tal vez es justo.

¿Fue útil?

Solución

¿No estás solo moviendo el texto modelo en otro lugar - desde la escritura del IEnumerable.GetEnumeratormethod en cada clase para llamar a su extensión AsEnumerable cada vez que se espera un IEnumerable<T>? Por lo general, yo esperaría un tipo enumerable que se utiliza para consultar el momento más veces de lo que está escrito (que es exactamente una vez). Esto significaría que este patrón conducirá a más repetitivo, en promedio.

Otros consejos

Te falta una cosa enorme -

Si implementa su propia interfaz en lugar de IEnumerable<T>, su clase no funciona con los métodos marco que esperan IEnumerable<T> - sobre todo, que será totalmente incapaz de utilizar LINQ, o usar su clase para construir un List<T>, o muchas otras abstracciones útiles .

Se puede lograr esto, como usted menciona, a través de un método de extensión por separado - sin embargo, esto tiene un costo. Mediante el uso de un método de extensión para convertir a un IEnumerable<T>, va a añadir otro nivel de abstracción requerido con el fin de utilizar su clase (que se va a hacer mucha más frecuencia que la autoría de la clase), y disminuir el rendimiento (el método de extensión se en efecto, generar una nueva implementación de la clase interna, que es realmente innecesario). Lo más importante, cualquier otro usuario de su clase (o más tarde) tendrá que aprender un nuevo API que no logra nada - usted está haciendo su clase más difícil de usar al no utilizar interfaces estándar, ya que viola las expectativas del usuario

Tienes razón:. Que hace parece una solución demasiado compleja para un problema bastante fácil

También introduce un nivel adicional de indirección para cada paso de la iteración. Probablemente no es un problema de rendimiento, pero todavía algo innecesario cuando no creo que realmente está ganando algo muy significativo.

Además, aunque el método de extensión le permite convertir cualquier IForEachable<T> en un IEnumerable<T>, significa que su propio tipo no satisfacer una limitación genérica de esta manera:

public void Foo<T>(T collection) where T : IEnumerable

o similares. Básicamente, al tener que realizar la conversión, que está perdiendo la capacidad de tratar un solo objeto como ambos una implementación de IEnumerable<T> y el tipo concreto real.

Además, al no implementar IEnumerable, usted está contando usted mismo fuera de inicializadores de colección. (A veces de forma explícita implemento IEnumerable solo para habilitar la opción para la inicialización de recogida, una excepción de GetEnumerator().)

Ah, y que haya también introdujo un poco más de la infraestructura, que no es familiar para todos los demás en el mundo, en comparación con las vastas hordas de desarrolladores de C # que ya saben acerca IEnumerable<T>.

En nuestra base de código es probable que haya más de 100 casos de este fragmento exacta:

IEnumerator IEnumerable.GetEnumerator()
{
    return this.GetEnumerator();
}

Y estoy muy de acuerdo con eso. Es un precio pequeño a pagar por la total compatibilidad con cualquier otra pieza de código .NET que se ha escrito;)

Su solución propuesta requiere esencialmente todos los consumidores / persona que llama de su nueva interfaz para recordar también que llamar a un método de extensión especial en ella antes de que sea utilidad .

Es mucho más fácil de usar IEnumerable para la reflexión. Intentar invocar interfaces genéricas a través de la reflexión es tal dolor a. La pena de boxeo a través de IEnumerable se pierde por la cabeza de la reflexión en sí ¿por qué preocuparse usando una interfaz genérica? Como un ejemplo, la serialización viene a la mente.

Creo que todos ya se ha mencionado las razones técnicas para no hacer esto, así que voy a añadir a la mezcla: exigir su usuario llamar AsEnumerable () en su colección para poder utilizar extensiones enumerables violaría el principio de menor sorpresa.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top