2009-09-26 9 views
8

Posso credere che un oggetto sia stato distrutto e che il suo distruttore venga chiamato immediatamente quando esce dallo scope in C#?C# - Gli oggetti vengono immediatamente distrutti quando si esce dal campo di applicazione?

Penso che molte pratiche di codifica comuni (ad esempio gli oggetti di transazione) si basino su questo comportamento, ma non sono molto abituato a lavorare con la garbage collection e ho poche informazioni su come questi linguaggi si comportano di solito.

Grazie.

+0

@RA - scusate il ritardo, ha avuto un po' Faccende IRL da fare :) Aggiunti alcuni esempi ora, spero che questo chiarisca ulteriormente. –

risposta

24

Nope, .Net e hense C# fa affidamento su una gestione della memoria di garbage collection. Quindi i distruttori (che in .Net sono chiamati finalizzatori) non vengono chiamati fino a quando GC non riterrà opportuno distruggere gli oggetti.

Inoltre: la maggior parte degli oggetti "regolari" in C# non ha distruttori. Se è necessario il modello di distruttore, è necessario implementare lo IDisposable interface con Dispose Pattern. Su oggetti usa e getta si dovrebbe anche assicurarsi che venga chiamato il metodo Dispose, con lo using keyword o chiamando direttamente il metodo.

Per chiarire ulteriormente (si spera): lo smaltimento deterministico è utile in .Net per es. quando è necessario liberare esplicitamente risorse che non sono gestite dal runtime .Net. Esempi di tali risorse sono gli handle di file, le connessioni al database, ecc. Di solito è importante liberare queste risorse non appena non sono più necessarie. Quindi non possiamo permetterci di aspettare che il GC li liberi.

Per ottenere uno smaltimento deterministico (simile al comportamento dell'ambito di C++) nel mondo non deterministico del GC .Net, le classi .Net si basano sull'interfaccia IDisposable. Prendendo in prestito dal Dispose Pattern, ecco alcuni esempi:

In primo luogo, istanziare una risorsa usa e getta e poi lasciare che l'oggetto andare fuori portata, lascerà fino al GC di smaltire l'oggetto:

1. { 
2.  var dr = new DisposableResource(); 
3. } 

Per risolviamo questo problema possiamo esplicitamente disporre dell'oggetto:

1. { 
2.  var dr = new DisposableResource(); 
3. 
4.  ... 
5. 
6.  dr.Dispose(); 
7. } 

Ma cosa succede se qualcosa va storto tra le linee 2 e 6? Smaltire non sarà chiamato. Per garantire, inoltre, che lo smaltimento sarà finalmente chiamato indipendentemente da eventuali eccezioni che possiamo fare la seguente:

1. var dr = new DisposableResource(); 
2. try 
3. { 
4.  ... 
5. } 
6. finally 
7. { 
8.  dr.Dispose(); 
9. } 

Dal momento che questo modello è spesso necessaria, C# include la parola chiave usando per semplificare le cose. Il seguente esempio è equivalente a quanto sopra:

1. using (var dr = new DisposableResource()) 
2. { 
3.  ... 
4. } 
+1

Il richiedente domanda non conosce le sottigliezze di ciò che è un distruttore C#, quindi non penso che dovresti chiamare un distruttore un finalizzatore e farlo con esso. I distruttori C++ e i distruttori C# sono * cose molto diverse. * – Joren

+1

Ricorda inoltre che .NET Garbage Collection è basato su "generazione", quindi un oggetto può potenzialmente essere garbage collection da una raccolta di 0, ma non è stato completamente "distrutto" "o" rimosso "dalla memoria. Pensa anche a cose come Referenze deboli con le quali un oggetto può essere raccolto, ma comunque accessibile dal codice dell'applicazione! Fondamentalmente, un oggetto durerà fino a quando il runtime lo vorrà. Non hai voce in capitolo! ;) (http://www.simple-talk.com/dotnet/.net-framework/understanding-garbage-collection-in-.net/) – CraigTP

+0

@Peter: Grazie per la risposta. Stai suggerendo che IDisposable offra un modo per raggiungere il comportamento dell'oscilloscopio visto ad esempio in C++? Prenderesti in considerazione la possibilità di elaborarlo e magari aggiungere del codice? – sharkin

0

Non penso che si debba fare affidamento sui netturbini in questo modo. Anche se si deduce il modo in cui operano, potrebbe essere che nella prossima versione lo abbiano reimplementato.

In ogni caso, gli oggetti non vengono raccolti automaticamente nel momento in cui vengono annullati. Generalmente vengono raccolti fino a quando non viene raggiunta una soglia e quindi vengono rilasciati.

Soprattutto nei programmi java questo è molto evidente quando si osserva il consumo di memoria sul task manager. Cresce e cresce e tutto ad un tratto ogni minuto cade di nuovo.

4

Non c'è nulla di simile a un distruttore simile a C++ in C#.(Esiste un concetto diverso di distruttore in C#, chiamato anche finalizzatore, che utilizza la stessa sintassi dei distruttori C++, ma non sono correlati alla distruzione di oggetti. Sono intesi a fornire un meccanismo di pulizia per le risorse non gestite.) The immondizia Il raccoglitore pulirà gli oggetti qualche volta dopo che non saranno più referenziati. Non immediatamente, e non c'è modo di garantire neanche questo.

Fortunatamente non vi è alcun motivo reale per cui si desideri garantire questo. Se hai bisogno della memoria, allora il GC lo recupererà. Se non lo fai, perché preoccuparsi se c'è ancora qualche oggetto spazzatura in giro? Non è una perdita di memoria: il GC può ancora trovarlo e ripulirlo in qualsiasi momento.

4

No, questo non è garantito. Simile a linguaggi come Java, in C# il garbage collector viene eseguito quando è necessario (ad esempio quando l'heap diventa troppo pieno). Tuttavia, quando gli oggetti implementano IDisposable, i. e. hanno un metodo Dispose() e deve essere chiamato, allora si può sfruttare la parola chiave using:

using (var foo = new DisposableObject()) { 
    // do something with that 
} 

In questo modo Dispose() saranno chiamati immediatamente al momento di lasciare che using blocco.

Nota: IDisposable si trova in molti tipi, in particolare GDI + ma anche connessioni di database, transazioni, ecc., Quindi potrebbe essere proprio lo schema giusto qui.

Nota 2: Dietro le quinte di cui sopra blocco avranno tradotto in un blocco try/finally:

var foo = new DisposableObject(); 
try 
{ 
    // do something with that 
} 
finally 
{ 
    foo.Dispose(); 
} 

Ma che la traduzione è fatta dal compilatore e molto comodo per non dimenticare di chiamare Dispose().

0

No. Se si fa riferimento a specifiche CLI (p. 8.9.6.7 su finalizzatori) http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-335.pdf è possibile trovare il seguente

CLI dovrebbe garantire che finalizzatori sono chiamati subito dopo l'istanza diventa inaccessibile . Mentre si basa sulla pressione di memoria per grilletto finalizzazione è accettabile, implementatori dovrebbero prendere in considerazione l'uso di ulteriori metriche

ma non deve.

7

No. Un oggetto in realtà non va "fuori ambito", il riferimento ad esso (ad esempio la variabile che si utilizza per accedervi) lo fa.

Una volta che non vi sono più riferimenti a un determinato oggetto, tale oggetto diventa idoneo per la raccolta dati inutili (GC) in caso di necessità.. Ogni volta che il GC decide di reclamare lo spazio dell'oggetto non più a lungo referenziato, allora verrà chiamato il finalizzatore degli oggetti.

Se il proprio oggetto è una risorsa (ad esempio un handle di file, una connessione al database), deve implementare l'interfaccia IDisposable (che obbliga l'oggetto a implementare un metodo Dispose() per eliminare eventuali connessioni aperte, ecc.).La migliore pratica per te in questo caso sarebbe quella di creare l'oggetto come parte di un blocco using, così che quando questo blocco sarà completato, la tua applicazione chiamerà automaticamente il metodo Dispose(), che si occuperà di chiudere la tua connessione file/db /qualunque cosa.

ad es.


using (var conn = new DbConnection()) 
{ 
    // do stuff with conn 
} // conn.Dispose() is automatically called here. 

Il blocco using è solo un po 'di zucchero sintattico che avvolge le interazioni con l'oggetto conn in un blocco try, insieme a un blocco finally che richiede solo conn.Dispose()

Problemi correlati