2013-01-07 25 views
20

Mam pytanie dotyczące odroczonego wykonania i usuwania danych.Podczas korzystania z wydajności w ramach instrukcji "using", kiedy pojawia się Dispose?

Rozważmy następujący przykład:

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++; 
     } 
    } 
} 

Czy oświadczenie break natychmiast spowodować, że czytelnik w ParseFile zbyć lub jest nadal rozpatrywana w kontekście i zablokuje plik otwarty aż sam program jest zamknięty?

+2

Napisz szybką aplikację konsolową i dowiedz się :) – jjxtra

+0

Och notatką boczną, zamiast używać 'break' gdy licznik trafi dwa, po prostu dodaj' Take (2) 'na końcu' ParseFile'. – Servy

+1

Odbierz książkę Jona Skeeta "C# in Depth", jeśli chcesz naprawdę dobrze wyjaśnić, co dzieje się w blokach iteratora, lub po prostu przejrzeć specyfikację językową, jak często sugeruje. –

Odpowiedz

16

Mamy tutaj kilka osobnych spraw.

Po pierwsze, zajmowanie się using w bloku iteratora. IEnumerator rozszerza IDisposable. Kod, który generuje bloki iteratora, jest na tyle wytrzymały, że wszelkie bloki try/finally (using powoduje utworzenie bloku try/finally) powoduje wywołanie bloku finally w metodzie enumeratora, jeśli nie był on wywoływany. już zadzwoniłem. Tak długo, jak moduł wyliczający jest usuwany, nie wycieknie StreamReader.

Tak więc teraz zadajemy sobie pytanie, czy moduł wyliczający jest zbyty. Wszystkie instrukcje foreach będą wywoływać Dispose w module wyliczającym (w przypadku implementacji IDisposable). Czynią tak, nawet jeśli wychodzisz, używając instrukcji break lub return, a także po jej zakończeniu.

Dzięki temu można mieć pewność, że w każdych okolicznościach zasób nie wycieknie, z wyjątkiem przypadków, w których nic nie wycieknie (np. Ktoś odbierający maszynę) od elementu.

+0

To trochę bardziej skomplikowane. W przypadku OP iterator będzie absolutnie dysponować ze względu na instrukcję break. Jednak wywołanie programu ParseFile generuje odwołanie do obiektu StreamReader wewnątrz "lokalnego użytku", który obecnie wykracza poza zakres. Myślę, że to kolejkuje go w kolejce finalizatora i usuwane po zebraniu? – n8wrl

+3

@ n8wrl Nie, to nieprawda. Kiedy 'IEnumerator' zostanie usunięty przez pętlę' foreach', wywoła funkcję dispose na 'StreamReaderze' (zakładając, że był wewnątrz' use' w tym momencie). W ten sposób zaimplementowano bloki iteracyjne; aby było to możliwe. – Servy

+0

Sądzę, że powinniśmy wziąć radę PsychoDada i wypróbować, ponieważ byłoby to bardzo fajne, jeśli tak to działa! – n8wrl