2010-07-31 23 views
8

Al trabajar con archivos en C#, estoy condicionado a pensar en liberar los recursos asociados. Por lo general, esta es una declaración de uso, a menos que sea un método de conveniencia de un trazador de líneas como File.ReadAllLines, que abrirá y cerrará el archivo por mí.Cuando File.ReadLines libera recursos

.Net 4.0 ha introducido el método de conveniencia File.ReadLines. Esto devuelve un IEnumerable y se factura como una forma más eficiente de procesar un archivo; evita almacenar todo el archivo en la memoria. Para hacer esto, supongo que hay alguna lógica de ejecución diferida en el enumerador.

Obviamente, dado que este método devuelve un IEnumerable, no y IDisposable, no puedo ir con mi reacción visceral de una declaración using.

Mi pregunta es: Teniendo esto en cuenta, ¿hay algún inconveniente en la desasignación de recursos con este método?

¿Llamar a este método significa que la liberación de los bloqueos de archivo asociados no es determinista?

Respuesta

12

IEnumerable no hereda de IDisposable, porque por lo general, la clase que lo implementa sólo le da la promesa de ser numerable, no ha hecho realmente nada todavía que la eliminación órdenes.

Sin embargo, cuando se enumeran sobre ella, primero se recupera un IEnumerator llamando al método IEnumerable.GetEnumerator, y por lo general, el objeto subyacente a volver qué implemento IDisposable.

La forma foreach se implementa es similar a esto:

var enumerator = enumerable.GetEnumerator(); 
try 
{ 
    // enumerate 
} 
finally 
{ 
    IDisposable disposable = enumerator as IDisposable; 
    if (disposable != null) 
     disposable.Dispose(); 
} 

De esta manera, si el objeto en efecto implementar IDisposable, será desechado. Para File.ReadLines, el archivo realmente no se abre hasta que empiece a enumerar sobre él, por lo que el objeto que obtiene de File.ReadLines no necesita deshacerse, pero sí el enumerador que obtiene.

Como los comentarios indican, IEnumerator no hereda de IDisposable, a pesar de que muchas implementaciones típicas hace, mientras que el genérico IEnumerator<T> no heredará IDisposable.

+0

En realidad, IEnumerator no implementa IDisposable. Pero muchas * implementaciones * de IEnumerator implementan IDisposable, y el bucle foreach, y los métodos de extensión LINQ, todos hacen un molde 'as' para ver si el enumerador implementa IDisposable, y si es así llaman a Dispose. –

+2

Corrección: el IEnumerator no genérico no implementa IDisposable, pero sí el genérico ('IEnumerator '). –

+0

Ah, tienes razón. Gah, tonto error, déjame editar la respuesta. –

2

+1 por respuesta de Lasse.

Particularmente para File.ReadLines donde el enumerador llama a .MoveNext() se desechará el TextReader interno cuando se encuentre con un EOF, o si ocurre una falla.

private bool MoveNext() 
{ 
    bool flag; 
    try 
    { 
     switch (this.<>1__state) 
     { 
      case 0: 
       this.<>1__state = -1; 
       this.<>7__wrap2 = this.reader; 
       this.<>1__state = 1; 
       this.<line>5__1 = null; 
       goto Label_005C; 

      case 2: 
       this.<>1__state = 1; 
       goto Label_005C; 

      default: 
       goto Label_0078; 
     } 
    Label_003E: 
     this.<>2__current = this.<line>5__1; 
     this.<>1__state = 2; 
     return true; 
    Label_005C: 
     if ((this.<line>5__1 = this.reader.ReadLine()) != null) 
     { 
      goto Label_003E; 
     } 
     this.<>m__Finally3(); // Disposal at end of file. 
    Label_0078: 
     flag = false; 
    } 
    fault 
    { 
     this.System.IDisposable.Dispose(); // Disposal due to fault. 
    } 
    return flag; 
} 
Cuestiones relacionadas