2010-10-16 18 views
8

vamos a tener este código:¿Cuándo se llama al método IEnumerator.Reset()?

class MyList : IEnumerable, IEnumerator 
{ 
    int[] A = { 1, 2, 3, 4, 5 }; 
    int i = -1; 

    #region IEnumerator Members 

    public object Current 
    { 
     get { return A[i]; } 
    } 

    public bool MoveNext() 
    { 
     i++; 
     return i < 5; 
    } 

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

    #endregion 

    #region IEnumerable Members 

    public IEnumerator GetEnumerator() 
    { 
     return (IEnumerator)this; 
    } 

    #endregion 
} 

Y En el Método principal:

MyList list = new MyList(); 
foreach (int i in list) 
{ 
    Console.WriteLine(i); 
} 

foreach (int i in list) 
{ 
    Console.WriteLine(i); 
} 

Por qué la segunda foerach no funciona ?? y la "i" no se inicializa de nuevo?

Es cierto: ¿El método de reinicio debe invocarse automáticamente antes de que se ejecute foreach?

¿por qué no llama aquí?

+2

¿No hay solución? :( –

+1

Creo que solo existe para la interoperabilidad con COM. El código .net normal no lo usa, y casi todas las implementaciones lanzan una excepción. – CodesInChaos

Respuesta

6

IEnumerable e IEnumerator generalmente deben ser clases separadas, y excepto en el caso de los enumeradores que siempre devuelven vacío o siempre devuelven el mismo elemento, el método GetEnumerator siempre debe devolver una nueva instancia de un IEnumerator.

No hay mucho sentido para IEnumerator.Reset; for-each loops no lo usa, y los consumidores de un IEnumerable/IEnumerator no pueden usarlo a menos que sepan cuál es el tipo enumerable, en cuyo caso podrían usar el tipo real en lugar de la interfaz.

+0

Y si alguien implementa el método MoveNext de tal manera que cuando se trata de regresar con falso, antes de esta llamada Restablecer es una mala práctica? Y si es así, ¿por qué ponen el Restablecimiento en la interfaz? – jannagy02

+0

@ jannagy02: Hay una serie de aspectos de .NET que deben considerarse productos residuales de evolución/o compromiso.Algunos nunca fueron bien pensados; otros pueden haber sido partes sensibles y útiles en una etapa del diseño, pero se vuelven inútiles cuando se reelaboran otras partes del diseño. A veces las cosas evolucionan a través del proceso de diseño, incluso aquellos involucrados en un diseño podrían no recordar qué partes se pensaron bien y cuáles se juntaron. – supercat

3

Restablecer no se llama por foreach. Mirar su método Principal en Reflector lo confirma.

Las clases de .NET, como ArrayList, realmente devuelven una nueva instancia de una clase que implementa IEnumerator.

Por ejemplo ArrayList implementa IEnumerable, y su método GetEnumerator se parece a esto:

public virtual IEnumerator GetEnumerator() 
{ 
    return new ArrayListEnumeratorSimple(this); 
} 

lo que no hay necesidad de preocuparse acerca de llamar Restablecer ya que cada foreach utiliza una nueva instancia del empadronador.

Para obtener un ejemplo completo que muestra la implementación de IEnumerable y una clase separada que implementa IEnumerator, puede consultar la documentación para IEnumerable.

9

Restablecer es redundante; tanto que es un requisito en la especificación de idioma para bloques de iteradores para lanzar una excepción en Restablecer. Lo correcto es simplemente deshacerse y liberar el antiguo iterador y llamar a GetEnumerator nuevamente. O mejor: evite teniendo para leerlo dos veces, ya que no todos los datos son repetibles.

+1

¿Tiene un enlace a donde dice que debe lanzarse una excepción? Me gustaría leer sobre eso. – GiddyUpHorsey

+0

Hmmm ... encontrado [esto] (http://msdn.microsoft.com/en-us/library/system.collections.ienumerator.reset.aspx). Aparentemente 'Reset' solo existe para la interoperabilidad COM. Me parece que nunca debería haberse definido en la interfaz en primer lugar. – GiddyUpHorsey

+0

El reinicio no sería redundante si estuviera mejor definido. Si existiera un IEnumerable 'general' y un IMultipassEnumerable que heredara de él y devolviera un IMultipassEnumerator, que implementó Restablecer, entonces podría tener la semántica de que las enumeraciones repetidas que usan el mismo enumerador garantizarían la obtención de los mismos resultados, incluso si el la colección fue cambiada; por el contrario, si uno intenta usar dos enumeradores separados una vez cada uno, bien podrían devolver resultados diferentes. – supercat

2

Esto también funciona:

public bool MoveNext() 
{ 
     if(i < 5) 
     { 
      i++; 
      return true; 
     } 
     else 
     { 
      i = -1; 
      return false; 
     } 
} 
-1

Hay un muchos casos en los que tengo que hacer esto, así que lo que hago se restablece la llamada en mi método GetEnumerator. Aquí hay un ejemplo:

public IEnumerator GetEnumerator() 
{ 
    this.Reset(); // Reset each time you get the Enumerator 
    return (IEnumerator)this; 
} 
+0

La respuesta aceptada es mucho mejor; simplemente no debería tener su 'IEnumerable' devolviendo la misma instancia de' IEnumerator' cada vez. Al utilizar este enfoque, no puedes * nunca * tener dos iteradores diferentes que iteren en la misma lista. Usar una implementación adecuada que tenga múltiples iteradores no es un problema en absoluto. – Servy

+0

Ese es un buen punto. Mi respuesta fue más directa a la pregunta original que iteraba la colección dos veces por separado. – user2880159

Cuestiones relacionadas