2009-12-21 10 views
7

Considere el siguiente código, el primero demuestra que la "limpieza" se ejecuta cuando terminamos de iterar sobre el IEnumerable de cadenas. El segundo pase es lo que me está causando dolor. Necesito poder abandonar IEnumerable antes de llegar al final y luego ejecutar el código de limpieza. Pero si ejecuta esto, verá que en el segundo paso la limpieza nunca se dispara.¿Cómo puedo abandonar un IEnumerator sin iterar hasta el final?

¿Cuál es la forma preferida de abandonar un IEnumerable como este?

static void Main(string[] args) 
{ 
    // first pass 
    foreach (String color in readColors()) 
     Console.WriteLine(color); 

    // second pass 
    IEnumerator<string> reader = readColors().GetEnumerator(); 
    if (reader.MoveNext()) 
    { 
     Console.WriteLine(reader.Current); 
     reader.Dispose(); 
    } 
} 
static IEnumerable<string> readColors() 
{ 
    string[] colors = { "red", "green", "blue" }; 
    for (int i = 0; i < colors.Length; i++) 
     yield return colors[i]; 

    Console.WriteLine("Cleanup goes here"); 
} 
+1

me pueden faltar algo, o que su ejemplo es demasiado simplista ... pero lo que la limpieza Qué Ened a ejecutar al final de la enumeración en readColors? Parece que el GC debería ser capaz de manejar las cosas solo por sí mismo. –

+0

Si tiene una colección personalizada, también puede implementar la IDisposable?! – serhio

Respuesta

7

Es necesario poner la parte principal de su método de iterador en un try..finally, con el código de limpieza en el último:

public IEnumerable<string> readColors() 
    { 
     try 
     { 
      string[] colors = { "red", "green", "blue" }; 
      for (int i = 0; i < colors.Length; i++) 
       yield return colors[i]; 
     } 
     finally 
     { 
      Console.WriteLine("Cleanup goes here"); 
     } 
    } 

Recuerde que bajo el capó un método iterador provoca una clase separada que ser creado, que implementa IEnumerable y IEnumerator. Al poner su limpieza en el bloque finally, termina en el método de clase generado 'Dispose.

[Editar : (como se ha señalado en otras respuestas) prefieren un comunicado using sobre su enfoque de llamar Dispose manualmente. Asumía que lo había hecho así solo para resaltar el tema en discusión, pero vale la pena señalar de todos modos]

+0

@Luke: Sí, lo es. Esa es la línea 'reader.Dispose();'. – jason

+0

Esta es la respuesta correcta, una vez que hace esto, puede simplificar su segunda línea para seguir usando "foreach" en lugar del código más complicado que tiene ahora. Solo hazlo: foreach (color de cadena en readColors()) {Console.WriteLine (color); descanso; } – StarPacker

4

Esa es una forma de abandonarlo. La razón por la que usted no está viendo

Cleanup goes here 

impresa en la consola se debe a que el bucle for (int i = 0; i < colors.Length; i++) nunca se ejecuta hasta el final. Vea a continuación cómo forzar la ejecución del código de limpieza.

Aquí hay otra manera. Este es el patrón preferido para usar objetos IDisposable en C#. Esto se prefiere porque esto hará que se llame a IEnumerator.Dispose incluso si se produce una excepción.

using (IEnumerator<string> reader = readColors().GetEnumerator()) { 
    reader.MoveNext(); 
    Console.WriteLine(reader.Current); 
} 

Como para forzar el código de limpieza tiene que ejecutar se puede hacer lo siguiente:

static IEnumerable<string> readColors() { 
    string[] colors = { "red", "green", "blue" }; 
    try { 
     for (int i = 0; i < colors.Length; i++) { 
      yield return colors[i]; 
     } 
    } 
    finally { 
     Console.WriteLine("Cleanup goes here"); 
    } 
} 
+0

@Downvoter: Da una razón. – jason

+0

Cuando di el voto negativo, solo había un par de líneas de respuesta que no parecían tener mucho sentido. Ahora que ha aclarado, lo he eliminado. –

+0

@Rob Levine: hice clic en enviar un poco antes de tiempo, lo suficientemente justo. Gracias por regresar para comentar. – jason

1

Creo que la forma preferida de hacer la limpieza es usando IDisposable. En este caso, será mejor que implemente su propio IEnumerable<string> con un IEnumerator<string> específico y utilizando el método Dispose normal. Usted obtiene disponer de gratis cuando usa foreach.

class MyEnumerator : IEnumerator<string> 
    { 
     // ... 
     #region IDisposable Members 

     public void Dispose() 
     { 
      // do your cleanup here 
      throw new NotImplementedException(); 
     } 

     #endregion 
     // ... 
    } 
+0

¿Por qué el voto a favor? –

+1

No necesita implementar el suyo propio; el compilador lo hará por usted siempre y cuando ponga la limpieza en un bloque finally. –

+0

Probablemente sea correcto, y para casos simples creo que su respuesta es mejor. –

0
try { 

    string[] colors = { "red", "green", "blue" }; 
    for (int i = 0; i < colors.Length; i++) { 
    if(condition == true) 
     break; 
    yield return colors[i]; 
    } 
} 
finally { 
    Console.WriteLine("Cleanup goes here"); 
} 
+0

@Luke: Sí, lo es. Esa es la línea 'reader.Dispose();'. – jason

Cuestiones relacionadas