2009-07-06 22 views
22

Estaba pasando por IEnumerable e IEnumerator, pero no pude obtener un punto claro ... si tenemos foreach, entonces ¿por qué necesitamos estas dos interfaces? ¿Hay algún escenario en el que tengamos que usar interfaces? En caso afirmativo, ¿alguien puede explicarlo con un ejemplo? Cualquier sugerencia y comentario son bienvenidos. Gracias.IEnumerable, IEnumerator vs foreach, cuándo usar lo

Respuesta

46

foreachusa las interfaces en muchos casos. Necesita las interfaces si desea implementar una secuencia que foreach puede usar. (Bloques Iterator suelen hacer esta tarea aplicación muy simple sin embargo.)

Sin embargo, sólo de vez en cuando puede ser útil el uso de los iteradores directamente. Un buen ejemplo es cuando intentamos "emparejar" dos secuencias diferentes. Por ejemplo, supongamos que recibe dos secuencias: una de nombres, otra de edades, y desea imprimir las dos juntas. Es posible escribir:

static void PrintNamesAndAges(IEnumerable<string> names, IEnumerable<int> ages) 
{ 
    using (IEnumerator<int> ageIterator = ages.GetEnumerator()) 
    { 
     foreach (string name in names) 
     { 
      if (!ageIterator.MoveNext()) 
      { 
       throw new ArgumentException("Not enough ages"); 
      } 
      Console.WriteLine("{0} is {1} years old", name, ageIterator.Current); 
     } 
     if (ageIterator.MoveNext()) 
     { 
      throw new ArgumentException("Not enough names"); 
     } 

    } 
} 

Del mismo modo, puede ser útil utilizar el iterador si desea tratar (por ejemplo) el primer elemento de forma diferente al resto:

public T Max<T>(IEnumerable<T> items) 
{ 
    Comparer<T> comparer = Comparer<T>.Default; 

    using (IEnumerator<T> iterator = items.GetEnumerator()) 
    { 
     if (!iterator.MoveNext()) 
     { 
      throw new InvalidOperationException("No elements"); 
     } 
     T currentMax = iterator.Current; 

     // Now we've got an initial value, loop over the rest 
     while (iterator.MoveNext()) 
     { 
      T candidate = iterator.Current; 
      if (comparer.Compare(candidate, currentMax) > 0) 
      { 
       currentMax = candidate; 
      } 
     } 
     return currentMax; 
    } 
} 

Ahora, si está interesado en la diferencia entre IEnumerator<T> y IEnumerable<T>, puede pensar en términos de la base de datos: piense en IEnumerable<T> como una tabla, y IEnumerator<T> como cursor. Puede pedirle a una tabla que le proporcione un cursor nuevo, y puede tener varios cursores sobre la misma tabla al mismo tiempo.

Puede tomar un tiempo realmente asimilar esta diferencia, pero solo recordar que una lista (o matriz, o lo que sea) no tiene ningún concepto de "dónde se encuentra en la lista" sino un iterador sobre esa lista/array/lo que tiene tiene ese poco de estado es útil.

+0

Gracias a Jon por toda su ayuda. Lo tengo ahora y estudiaré más para dejarlo en claro. También encuentro el siguiente enlace muy útil: http://stackoverflow.com/questions/558304/can-anyone-explain-ienumerable-and-ienumerator-to-me – Wondering

+0

Este es un ejemplo de dónde estás atascado usando IEnumerator solo para mantenerse eficiente http://stackoverflow.com/questions/1018407/what-is-the-most-elegant-way-to-get-a-set-of-items-by-index-from-a-collection/1025137#1025137 –

+0

@Jon Skeet Me gustó mucho la forma en que codificaste el método Max . Tengo una pregunta. Usaste usando construir con IEnumberator. Sé usar usando lo que viene dentro(). ¿Por qué lo usaste en IEnumberator? –

8

Lo que dijo Jon.

  • IEnumerable o IEnumerable<T>: mediante la implementación de este objeto establece que se le puede dar un iterador que se puede utilizar para recorrer más de la secuencia/colección/set
  • IEnumerator o IEnumerator<T>: si se llama al método GetEnumerator definido en la interfaz anterior, obtienes un objeto iterador como referencia de IEnumerator. Esto le permite llamar a MoveNext() y obtener el objeto actual.
  • foreach: es una construcción/fachada C# en cierto sentido, en el sentido de que no necesita saber cómo funciona debajo del capó. Obtiene internamente el iterador y llama a los métodos correctos para que se concentre en lo que quiere hacer con cada elemento (el contenido del bloque foreach). La mayoría de las veces, solo necesitarías foreach a menos que estés implementando tu propio tipo o iteración personalizada, en cuyo caso necesitarías conocer las primeras 2 interfaces.
+0

Gracias Gishu por dejarlo más claro para mí. También encuentro que este enlace es muy útil: http://stackoverflow.com/questions/558304/can-anyone-explain-ienumerable-and-ienumerator-to-me – Wondering

6

Tenga en cuenta que no tienen para implementar IEnumerator y varients del mismo para usar foreach - IIRC, todo lo que necesita es un método GetEnumerator() que devuelve un objeto que tiene un método MoveNext() volver un bool y una propiedad Current que devuelve un objeto. No es necesario utilizar IEnumerator e IEnumerable, aunque en general es una muy buena idea hacerlo. Ver here para más información.

+0

Es una vieja bestia extraña, esa - usted puede solo implemente los dos métodos. El gran problema al hacer eso es que el sistema de tipos no sabe que la cosa es enumerable, así que imagino que tendría problemas para usarla, digamos, Linq to Objects. –

+3

Sí, es extraño. La razón por la que admitimos esa característica es para que pueda escribir una colección de números enteros en C# 1.0 y enumerarlos sin incluir el boxeo. Ahora que tenemos IE , ya no es necesario el enfoque de "patrón". –

+1

@Eric Lippert: hay otra ventaja de esta característica: permite evitar asignaciones de montón para los enumeradores de estructuras, aunque sugeriría que las clases cuyos métodos GetEnumerator devuelvan una estructura deberían tener métodos IEnumerable.GetEnumerator que devuelven clases. – supercat

Cuestiones relacionadas