2013-01-07 13 views
20

Ho una domanda riguardante l'esecuzione differita e lo smaltimento dei dati.Quando si utilizza il rendimento all'interno di un'istruzione "using", quando si verifica Dispose?

consideri il seguente esempio:

private IEnumerable<string> ParseFile(string fileName) 
{ 
    using(StreamReader sr = new StreamReader(fileName)) 
    { 
     string line; 
     while((line = sr.ReadLine()) != null) 
     { 
      yield return line; 
     } 
    } 
} 

private void LineReader(string fileName) 
{ 
    int counter = 0; 

    foreach(string line in ParseFile(fileName)) 
    { 
     if(counter == 2) 
     { 
      break; // will this cause a dispose on the StreamReader? 
     } else 
     { 
      Console.WriteLine(line); 
      counter++; 
     } 
    } 
} 

disposta la dichiarazione break immediatamente causare il lettore in ParseFile di disporre o è ancora considerata nel contesto e si blocca il file aperto fino a quando il programma stesso è chiuso?

+2

Scrivi un'app per console rapida e scopri :) – jjxtra

+0

Oh nota a margine, piuttosto che usare 'break' quando il contatore colpisce due, basta aggiungere un 'Take (2)' alla fine di 'ParseFile'. – Servy

+1

Raccogli il libro di Jon Skeet "C# in Depth" se vuoi una spiegazione veramente buona di cosa succede nei blocchi iteratori, o semplicemente guarda le specifiche del linguaggio, come spesso suggerisce. –

risposta

16

Quindi abbiamo diversi problemi separati in corso qui.

Prima di tutto, si occupa dello using nel blocco iteratore. IEnumerator estende IDisposable. Il codice che genera i blocchi iteratore è in realtà abbastanza robusto che qualsiasi blocco try/finally (a using viene creato un blocco try/finally) il contenuto del blocco finally viene chiamato nel metodo Dispose dell'enumeratore, se non è stato già chiamato. Finché l'enumeratore è disposto, non perderà lo StreamReader.

Così ora ci chiediamo se l'enumeratore è disposto. Tutte le istruzioni foreach chiameranno Dispose sull'enumeratore (dovrebbe implementare IDisposable). Lo fanno anche se si esce usando una dichiarazione break o return, così come quando finisce normalmente.

Quindi si può essere sicuri che in tutte le circostanze la risorsa non sarà trapelata, salvo i casi in cui non si può impedire a nessuna perdita (ad esempio, qualcuno che scollega la macchina).

+0

È un po 'più complicato di così. Nel caso dell'OP, l'iteratore si sbarazzerà in modo assoluto in base all'istruzione break. Ma la chiamata a ParseFile produce un riferimento a StreamReader all'interno di un 'local-using' che ora è andato fuori portata. Penso che questo lo accoda nella coda del finalizzatore e venga eliminato quando viene raccolto? – n8wrl

+3

@ n8wrl No, non è vero. Quando 'IEnumerator' è eliminato dal ciclo' foreach' chiamerà dispose sullo 'StreamReader' (supponendo che fosse all'interno dell'uso di quel punto). È il modo in cui sono stati implementati i blocchi iteratori; per rendere possibile tutto questo – Servy

+0

Suppongo che dovremmo prendere il consiglio di PsychoDad e provarlo perché sarebbe molto bello se è così che funziona! – n8wrl

Problemi correlati