2010-01-02 13 views

Respuesta

17

Pregúntate a ti mismo "imagina si esto fuera cierto".

Si todos los métodos de enumeración estuvieran en una única interfaz, ¿cómo podrían dos usuarios enumerar la misma lista al mismo tiempo?

Hay dos interfaces porque una dice: "Puede enumerarme", mientras que la otra dice: "aquí hay un objeto que realiza un seguimiento de una tarea de enumeración determinada".

La interfaz IEnumerable es una fábrica que crea tantos objetos IEnumerator como desee. Cómo y cuándo se utilizan esos enumeradores depende del consumidor.

+1

1. Este es un buen ejemplo de por qué las interfaces son independientes y discretas. Otra razón es que la misma colección de tipos podría enumerarse de diferentes maneras. Imagine un árbol binario: puede realizar recorridos de prefijo, infijo, posfijo y ancho en primer lugar (solo por nombrar algunos). Mantener las interfaces separadas permite realizar diferentes tipos de recorridos en la misma estructura a través de una única interfaz simple. – LBushkin

+0

¡Gracias por su respuesta! Esto es más claro para mí ahora. –

7

IEnumerable implica que el objeto es una colección o fuente de datos que pueden ser iterado sobre de una forma lineal. IEnumerator es la interfaz para la implementación real que realiza la iteración.

4

Porque "IEnumerable" dice "ven, enumerame" (y luego dices -cómo, dame el Enumerador), sin embargo, "IEnumerator" dice "puedo enumerar tu colección". y ya lo tienes, no necesitas obtener más.

0

Porque a menudo lo que hace la enumeración es solo tangencial (si es que lo está) relacionado con lo enumerado. Si IEnumerable e IEnumerator eran la misma interfaz, no podrían compartirse, o tendría que tener algún argumento sin tipo en GetEnumerator que le permitiera pasar el objeto para ser enumerado. Dividirlos permite que la infraestructura de enumeración se comparta potencialmente entre diferentes tipos.

2

Tienes muy buenas respuestas aquí. Solo un poco de énfasis. Un enumerador mantiene el estado, realiza un seguimiento del objeto actual en la colección que se enumera. Disponible a través de IEnumerator.Current. Y sabe cómo cambiar ese estado, IEnumerator.MoveNext(). Mantener el estado requiere un objeto separado que almacene el estado. Ese estado no se puede almacenar dentro del objeto de colección fácilmente porque solo hay un objeto de colección, pero puede haber más de un enumerador.

Utilicé la frase "fácilmente" porque en realidad es posible que una colección realice un seguimiento de sus enumeradores. Después de todo, era una llamada al método GetEnumerator() de la clase de recopilación que devolvía el iterador. Hay una clase de colección en el marco .NET que hace esto, Microsoft.VisualBasic.Collection. Necesitaba implementar un contrato VB6 para su clase Collection y ese contrato especifica que el cambio de una colección mientras se está enumerando es legal. Lo que significa que cuando se modifique la Colección, debe ser razonable con todos los objetos del iterador que se crearon.

Se les ocurrió un truco muy bueno, WeakReference podría haberse inspirado en esto. Echa un vistazo al código mostrado por Reflector. Cosas inspiradoras Cave un poco más y encuentre "versión" en las clases de colección. Truco impresionante

+0

Me pregunto por qué no ha habido una colección más limpia disponible (al menos antes de la red 4.0) que podría hacer eso? La enumeración a través de una colección y la eliminación selectiva de elementos parecería una operación común. – supercat

+0

@super: porque se está rompiendo fundamentalmente. Es posible que pueda compensar iteradores por una sola mutación de la colección. Pero algo como quitar un elemento e insertarlo en otra posición va a romper algo. El iterador verá un elemento dos veces o no aparecerá. Más allá de eso, hacer un seguimiento de todas las instancias activas de iteradores es bastante caro. –

+0

Puedo entender que las colecciones 'normales' no admitirían tales características, pero la Colección logra tener una semántica útil que sería aún más útil si se implementaran en una clase sin su extravagancia. Uno no querría esa semántica en todas partes, pero a veces son agradables. En realidad, me hubiera gustado haber visto más sabores de iEnumerator; las aplicaciones podrían seleccionar una basada en la semántica requerida (algunas agregarían métodos, otras solo agregarían garantías). Una herramienta útil sería iPurgeEnumerator: llamar a un predicado con cada elemento y eliminar si es verdadero. – supercat

0

Después de leer la publicación de Bart De Smet en minlinq Ya no estoy de acuerdo con que sea estrictamente necesario dividir las dos interfaces.

Por ejemplo - en el siguiente enlace, IEnumerable/IEnumerator se collaped a una única interfaz de método

Func<Func<Option<T>>> 

y algunas implementaciones básicas

public static class FEnumerable 
{ 
    public static Func<Func<Option<T>>> Empty<T>() 
    { 
     return() =>() => new Option<T>.None(); 
    } 

    public static Func<Func<Option<T>>> Return<T>(T value) 
    { 
     return() => 
     { 
      int i = 0; 
      return() => 
       i++ == 0 
       ? (Option<T>)new Option<T>.Some(value) 
       : (Option<T>)new Option<T>.None(); 
     }; 
    } 

    ... 

} 

public static Func<Func<Option<T>>> Where<T>(this Func<Func<Option<T>>> source, Func<T, bool> filter) 
{ 
    return source.Bind(t => filter(t) ? FEnumerable.Return(t) : FEnumerable.Empty<T>()); 
} 
Cuestiones relacionadas