Impostazione degli oggetti su Null / Nothing dopo l’uso in .NET

Dovresti impostare tutti gli oggetti su null ( Nothing in VB.NET) una volta che hai finito con loro?

Comprendo che in .NET è essenziale disporre di qualsiasi istanza di oggetti che implementano l’interfaccia IDisposable per rilasciare alcune risorse sebbene l’object possa essere ancora qualcosa dopo che è stato disposto (da qui la proprietà isDisposed nei moduli), quindi presumo che possa risiedi ancora nella memoria o almeno in parte?

So anche che quando un object esce dal campo di applicazione viene quindi contrassegnato per la raccolta pronta per il prossimo passaggio del garbage collector (anche se ciò potrebbe richiedere del tempo).

Quindi con questo in mente lo imposterà a velocizzare il sistema liberando la memoria in quanto non deve capire che non è più nel campo di applicazione e sono eventuali effetti collaterali?

Gli articoli MSDN non fanno mai questo negli esempi e al momento lo faccio perché non riesco a vedere il danno. Tuttavia mi sono imbattuto in un misto di opinioni, quindi ogni commento è utile.

Karl è assolutamente corretto, non è necessario impostare gli oggetti su null dopo l’uso. Se un object implementa IDisposable , assicurati di chiamare IDisposable.Dispose() quando hai finito con quell’object (racchiuso in un try .. finally , o, using() block). Ma anche se non ti ricordi di chiamare Dispose() , il metodo finalizzatore sull’object dovrebbe chiamare Dispose() per te.

Ho pensato che fosse un buon trattamento:

Scavando in IDisposable

e questo

Capire IDisposable

Non ha senso tentare di indovinare il GC e le sue strategie di gestione perché è auto-regolante e opaco. C’è stata una buona discussione sul funzionamento interno con Jeffrey Richter su Dot Net Rocks qui: Jeffrey Richter sul modello di memoria di Windows e il libro di Richters CLR tramite C # capitolo 20 ha un ottimo trattamento:

Un altro motivo per evitare di impostare oggetti su null quando hai finito con loro è che può effettivamente mantenerli in vita più a lungo.

per esempio

 void foo() { var someType = new SomeType(); someType.DoSomething(); // someType is now eligible for garbage collection // ... rest of method not using 'someType' ... } 

consentirà che l’object indicato da someType sia GC’d dopo la chiamata a “DoSomething” ma

 void foo() { var someType = new SomeType(); someType.DoSomething(); // someType is NOT eligible for garbage collection yet // because that variable is used at the end of the method // ... rest of method not using 'someType' ... someType = null; } 

a volte può mantenere vivo l’object fino alla fine del metodo. Il JIT di solito ottimizza il compito su null , quindi entrambi i bit di codice finiscono per essere gli stessi.

No non oggetti null. Puoi controllare http://codebetter.com/blogs/karlseguin/archive/2008/04/27/foundations-of-programming-pt-7-back-to-basics-memory.aspx per ulteriori informazioni, ma impostare le cose a null non farà nulla, tranne sporcare il tuo codice.

Anche:

 using(SomeObject object = new SomeObject()) { // do stuff with the object } // the object will be disposed of 

In generale, non c’è bisogno di oggetti null dopo l’uso, ma in alcuni casi trovo che sia una buona pratica.

Se un object implementa IDisposable e viene memorizzato in un campo, penso che sia opportuno annullarlo, solo per evitare di utilizzare l’object disposto. I bug del seguente tipo possono essere dolorosi:

 this.myField.Dispose(); // ... at some later time this.myField.DoSomething(); 

È bene annullare il campo dopo averlo eliminato e ottenere un NullPtrEx direttamente sulla riga in cui il campo viene nuovamente utilizzato. Altrimenti, potresti incappare in qualche criptico bug (dipende esattamente da cosa fa DoSomething).

È probabile che il tuo codice non sia strutturato abbastanza strettamente se senti la necessità di variabili null .

Esistono diversi modi per limitare l’ambito di una variabile:

Come menzionato da Steve Tranby

 using(SomeObject object = new SomeObject()) { // do stuff with the object } // the object will be disposed of 

Allo stesso modo, puoi semplicemente usare parentesi graffe:

 { // Declare the variable and use it SomeObject object = new SomeObject() } // The variable is no longer available 

Trovo che l’utilizzo di parentesi graffe senza alcun “intestazione” per pulire veramente il codice e contribuire a renderlo più comprensibile.

L’unica volta che devi impostare una variabile su null è quando la variabile non esce dall’ambito e non hai più bisogno dei dati ad essa associati. Altrimenti non ce n’è bisogno.

In generale non è necessario impostare su null. Supponiamo però che tu abbia una funzionalità di ripristino nella tua class.

Quindi si potrebbe fare, perché non si desidera chiamare due volte, poiché alcuni di Dispose potrebbero non essere implementati correttamente e generare un’eccezione System.ObjectDisposed.

 private void Reset() { if(_dataset != null) { _dataset.Dispose(); _dataset = null; } //..More such member variables like oracle connection etc. _oraConnection } 

questo tipo di “non c’è bisogno di impostare oggetti a null dopo l’uso” non è del tutto preciso. A volte è necessario NULLare la variabile dopo averla eliminata.

Sì, dovresti SEMPRE chiamare .Dispose() o .Close() su tutto ciò che ha quando hai finito. Che si tratti di handle di file, connessioni al database o oggetti usa e getta.

Separato da quello è il modello molto pratico di LazyLoad.

Supponiamo che io abbia istanziato ObjA di class A Class A ha una proprietà pubblica denominata PropB di class B

Internamente, PropB utilizza la variabile privata di _B e i valori predefiniti su null. Quando viene utilizzato PropB.Get() , controlla se _PropB è nullo e, in caso _PropB , apre le risorse necessarie per _PropB un’istanza di una B in _PropB . Quindi restituisce _PropB .

Per la mia esperienza, questo è un trucco davvero utile.

Quando la necessità di null entra è se si ripristina o si modifica A in qualche modo che il contenuto di _PropB fosse figlio dei precedenti valori di A , sarà necessario Dispose AND null out _PropB modo che LazyLoad possa reimpostare per recuperare il valore corretto SE il codice lo richiede.

Se si esegue solo _PropB.Dispose() e dopo poco si prevede che il controllo Null per LazyLoad abbia esito positivo, non sarà nullo e si guarderanno i dati non aggiornati. In effetti, devi essere nullo dopo Dispose() solo per essere sicuro.

Avrei voluto che fosse altrimenti, ma ora ho il codice che esibisce questo comportamento dopo Dispose() su un _PropB e al di fuori della funzione chiamante che ha fatto il Dispose (e quindi quasi fuori portata), il puntello privato non è ancora ‘t null, e i dati obsoleti sono ancora lì.

Alla fine, la proprietà dismessa annullerà, ma non è stato deterministico dal mio punto di vista.

Il motivo principale, come dbkk allude, è che il contenitore padre ( ObjA con PropB ) sta mantenendo l’istanza di _PropB in ambito, nonostante Dispose() .

Ci sono alcuni casi in cui ha senso riferimenti null. Ad esempio, quando stai scrivendo una raccolta – come una coda di priorità – e dal tuo contratto, non dovresti mantenere quegli oggetti vivi per il client dopo che il client li ha rimossi dalla coda.

Ma questo genere di cose conta solo nelle collezioni di lunga durata. Se la coda non sopravvivrà alla fine della funzione in cui è stata creata, allora è molto meno importante.

Nel complesso, non dovresti preoccuparti di niente. Lascia che il compilatore e il GC svolgano il loro lavoro in modo che tu possa fare il tuo.

Dai un’occhiata anche a questo articolo: http://www.codeproject.com/KB/cs/idisposable.aspx

Per la maggior parte, l’impostazione di un object su null non ha alcun effetto. L’unica volta che dovresti assicurarti di farlo è se stai lavorando con un “object grande”, che è più grande di 84K (come bitmap).

Credo che in base alla progettazione degli implementatori di GC, non si possa accelerare GC con l’annullamento. Sono sicuro che preferirebbero che tu non ti preoccupassi di come / quando GC corre – trattalo come questo onnipresente Essere proteggere e sorvegliare e uscire per te … (inchina a testa bassa, solleva il pugno verso il cielo) .. .

Personalmente, ho spesso impostato in modo esplicito le variabili su null quando ho finito con loro come una forma di auto-documentazione. Non dichiaro, uso, quindi impostato su null in seguito – annullo immediatamente dopo che non sono più necessari. Sto dicendo, esplicitamente: “Sono ufficialmente finito con te … non ci sono più …”

Nulllifying necessario in una lingua GC? No. È utile per il GC? Forse sì, forse no, non lo so per certo, dal design non posso davvero controllarlo, e indipendentemente dalla risposta di oggi con questa o quella versione, le future implementazioni di GC potrebbero cambiare la risposta al di là del mio controllo. Inoltre se / quando il nullo viene ottimizzato, è un po ‘più di un commento di fantasia, se lo farai.

Immagino che se renderà più chiaro il mio impegno al prossimo povero scemo che segue le mie orme, e se “potrebbe” potenzialmente aiutare GC qualche volta, allora ne vale la pena. Per lo più mi fa sentire in ordine e chiaro, e a Mongo piace sentirsi in ordine e chiaro. 🙂

La vedo in questo modo: i linguaggi di programmazione esistono per permettere alle persone di dare ad altre persone un’idea di intento e un compilatore una richiesta di lavoro su cosa fare – il compilatore converte quella richiesta in un linguaggio diverso (a volte diversi) per una CPU – la CPU (o le CPU) potrebbe dare un assaggio di che lingua hai usato, le tue impostazioni di tabulazione, commenti, enfasi stilistiche, nomi di variabili, ecc. – una CPU è tutta relativa al stream di bit che indica ciò che registri e opcode e posizioni di memoria a twiddle. Molte cose scritte nel codice non si convertono in ciò che viene consumato dalla CPU nella sequenza che abbiamo specificato. Il nostro C, C ++, C #, Lisp, Babele, assemblatore o qualsiasi altra cosa è teoria piuttosto che realtà, scritto come una dichiarazione di lavoro. Quello che vedi non è ciò che ottieni, sì, anche nel linguaggio assemblatore.

Capisco la mentalità di “cose ​​inutili” (come le righe vuote) “non sono altro che rumore e ingombrano il codice”. Quello ero io all’inizio della mia carriera; Lo capisco davvero. In questo frangente mi chino verso ciò che rende il codice più chiaro. Non è che aggiungo anche 50 righe di “rumore” ai miei programmi: sono poche righe qua e là.

Ci sono eccezioni a qualsiasi regola. In scenari con memoria volatile, memoria statica, condizioni di gara, singleton, utilizzo di dati “stantii” e tutto quel tipo di marciume, questo è diverso: è necessario gestire la propria memoria, bloccando e annullando perché la memoria non fa parte di il GC’d Universe – si spera che tutti lo capiscano. Il resto del tempo con i linguaggi GC è una questione di stile piuttosto che di necessità o di un incremento prestazionale garantito.

Alla fine della giornata assicurati di capire cosa è idoneo per GC e cosa no; bloccare, disporre e neutralizzare in modo appropriato; cera su, cera fuori; inspirare ed espirare; e per tutto il resto dico: se ti fa sentire bene, fallo. Il tuo chilometraggio può variare … come dovrebbe …

Alcuni oggetti suppongono il metodo .dispose() che forza la rimozione della risorsa dalla memoria.