Cosa succede se ritorni prima della fine dell'istruzione using? Sarà chiamato lo smaltimento?


115

Ho il seguente codice

using(MemoryStream ms = new MemoryStream())
{
     //code
     return 0;
}

Il dispose()metodo viene chiamato alla fine delle usingparentesi graffe dell'istruzione, }giusto? Dato che io returnprima della fine della usingdichiarazione, l' MemoryStreamoggetto verrà smaltito correttamente? Che succede qui?


4
@ JonH: trova il duplicato esatto, quindi vota per chiudere in tal caso, per favore.
Noldorin

@Noldorin: sono andato a cercare un duplicato su questo, perché ho pensato che doveva essere stato chiesto prima, ma non sono riuscito a trovarne uno. Immagino che ci siano ancora domande facili là fuori. :)
Randolpho

@JonH e @Noldorin - i duplicati sarebbero stati presentati quando la domanda è stata formata, cerca "domande simili", una funzione che le persone sembrano non usare abbastanza.
Adam Houldsworth

@Adam: prova tu stesso. Copia / incolla il titolo e guarda quali duplicati vengono presentati dal sistema. Ti do un suggerimento: la risposta è nessuna. Idem se cerchi tramite Google o la ricerca di SO. Sembra che questa domanda non sia stata posta prima.
Randolpho

Aaap ... me lo riprendo. Ho appena trovato un duplicato vicino, dopo una ricerca molto dedicata: stackoverflow.com/questions/2641692/… Ora, la domanda viene posta in modo completamente diverso, ma l'ultima domanda è praticamente la stessa. Suppongo che dopo tutto possiamo considerarlo uno stupido.
Randolpho

Risposte:


167

Sì, Disposeverrà chiamato. Viene chiamata non appena l'esecuzione lascia l'ambito del usingblocco, indipendentemente dal mezzo impiegato per abbandonare il blocco, che si tratti della fine dell'esecuzione del blocco, di returnun'istruzione o di un'eccezione.

Come sottolinea correttamente @Noldorin, l'utilizzo di un usingblocco nel codice viene compilato in try/ finally, con Disposeessere chiamato nel finallyblocco. Ad esempio il codice seguente:

using(MemoryStream ms = new MemoryStream())
{
     //code
     return 0;
}

diventa effettivamente:

MemoryStream ms = new MemoryStream();
try
{
    // code
    return 0;
}
finally
{
    ms.Dispose();
}

Quindi, poiché finallyè garantita l'esecuzione dopo che il tryblocco ha terminato l'esecuzione, indipendentemente dal suo percorso di esecuzione, Disposeè garantito che venga chiamato, non importa cosa.

Per ulteriori informazioni, vedere questo articolo di MSDN .

Addendum:
Solo un piccolo avvertimento da aggiungere: poiché Disposeè garantito che venga chiamato, è quasi sempre una buona idea assicurarsi che Disposenon generi mai un'eccezione quando si implementa IDisposable. Purtroppo, ci sono alcune classi nella libreria di base che fanno un tiro in determinate circostanze, quando Disposeviene chiamato - Sto guardando voi, WCF Service Reference / Proxy Client! - e quando ciò accade può essere molto difficile rintracciare l'eccezione originale se è Disposestata chiamata durante uno svolgimento dello stack di eccezioni, poiché l'eccezione originale viene inghiottita a favore della nuova eccezione generata dalla Disposechiamata. Può essere frustrante in modo esasperante. O è frustrantemente esasperante? Uno dei due. Forse entrambi.


4
Penso che scoprirai che è effettivamente compilato in un blocco di prova finale con una chiamata a Disposefinalmente, quindi sta funzionando efficacemente sull'implementazione di finally, come descrivi.
Noldorin

@ Noldorin: esattamente. Anche se suppongo di poter essere esplicito al riguardo. Modifica imminente ...
Randolpho

1
Si noti inoltre che ci sono alcune circostanze in cui non è garantita l'esecuzione del blocco finale, come l'utilizzo di Environment.FailFast e se si verifica un'eccezione StackOverFlowException.
Christopher McAtackney

@ C.McAtackney: anche un buon punto. Inoltre, IIRC, OutOfMemoryException; fondamentalmente se non riesci a catturare l'eccezione perché si tratta di un errore di esecuzione critico, Dispose non verrà chiamato. Ovviamente, in tal caso è garantito che il programma si arresti in modo anomalo, insieme a tutta la memoria allocata, quindi nel 99,9% dei casi non è un problema, a meno che tu non stia facendo cose instabili come scrivere su un file nel tuo metodo dispose . A parte il catastrofico crash del programma, ovviamente.
Randolpho

Non dovresti mai usare l'istruzione 'using ()' con WCF - fai riferimento a questo articolo per maggiori informazioni. Ecco uno snippet che utilizzo per i proxy WCF: 'WCFProxy variableName = null; prova {variableName = new WCFProxy (); // TODO code here variableName.Proxy.Close (); variableName.Dispose (); } catch (Eccezione) {if (variableName! = null && variableName.Proxy! = null) {variableName.Proxy.Abort (); } gettare; } "
Dave Black

18

usingle istruzioni si comportano esattamente come i try ... finallyblocchi, quindi verranno sempre eseguite su qualsiasi percorso di uscita del codice. Tuttavia, credo che siano soggetti alle pochissime e rare situazioni in cui i finallyblocchi non vengono chiamati. Un esempio che posso ricordare è se il thread in primo piano esce mentre i thread in background sono attivi: tutti i thread tranne il GC vengono sospesi, il che significa che i finallyblocchi non vengono eseguiti.

Modifica ovvia: si comportano allo stesso modo a parte la logica che consente loro di gestire oggetti IDisposable, d'oh.

Contenuto bonus: possono essere impilati (dove i tipi differiscono):

using (SqlConnection conn = new SqlConnection("string"))
using (SqlCommand comm = new SqlCommand("", conn))
{

}

E anche delimitato da virgole (dove i tipi sono gli stessi):

using (SqlCommand comm = new SqlCommand("", conn), 
       SqlCommand comm2 = new SqlCommand("", conn))
{

}

4

Il tuo oggetto MemoryStream verrà smaltito correttamente, non devi preoccuparti di questo.



0

Dai un'occhiata al tuo codice in reflector dopo averlo compilato. Scoprirai che il compilatore refactoring del codice per garantire che dispose venga chiamato nello stream.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.