Mi viene chiesto di implementare IDisposable su oggetti che sono risorse gestite al 100%, che non contengono stream e risorse di grandi dimensioni.
Capisco limportanza di disporre correttamente di grandi risorse e risorse non gestite, ma per quanto riguarda laltro lato dellequazione?
Per un oggetto che non beneficia dellimplementazione di IDisposable (piccoli oggetti completamente gestiti) , quale impatto negativo può avere GC.SuppressFinalize, se ce ne sono?
Commenti
Rispondi
Lunico obiettivo di GC.SuppressFinalize
è:
impedire al finalizzatore di rilasciare risorse non gestite che sono già state liberate dallimplementazione IDisposable.Dispose.
Una volta eliminato loggetto, dovresti effettivamente chiamare GC.SuppressFinalize(this);
, come mostrato in una risposta alla domanda ” Quando dovrei usare GC.SuppressFinalize ()? “. Anche la regola di analisi del codice “ CA1816 è utile.
Ora, implementando IDisposable
su gli oggetti che non hanno risorse non gestite sembrano abbastanza strani e discutibili. Poiché lunico motivo per cui la persona ti ha dato è stato: “è proprio il modo in cui” lo abbiamo sempre fatto, fallo anche tu “, invece di fornire dati di profilazione / benchmarking che mostrano che laggiunta di IDisposable
migliorerà qualcosa in un caso specifico, non ci sono ragioni reali per farlo.
Provocherebbe problemi di prestazioni? Difficile a dirsi: dipenderà da un caso particolare.
causa altri problemi? Ovviamente. Senza contare il fatto che IDisposable
è doloroso da implementare, se questa pratica viene usata troppo, il codice diventa illeggibile e impossibile da mantenere:
public int ComputePrice(Rebate rebate) { using (var currentCurrency = this.User.FindCurrency()) { using (var priceWithoutRebate = this.GetPurePrice()) { using (var canApplyRebate = rebate.CanApplyTo(this)) { if (!canApplyRebate) { return priceWithoutRebate; } using (var priceWithRebate = priceWithoutRebate.Apply(rebate)) { return priceWithRebate; } } } } }
Commenti
- Beh, credo che la storia sia che molto tempo fa avevano qualche connessione / problemi di perdita di flusso, probabilmente a causa della perdita di connessione / comando del codice ADO.NET, e questo ha causato enormi perdite di risorse. Sapevano che limplementazione di IDisposable su tutto avrebbe risolto il problema, quindi lhanno fatto, e ha funzionato. ust implementarlo sulla maggior parte degli oggetti.
- @AndrewHoffman: sembra una spiegazione plausibile. Ma ancora non ' giustifica la pratica corrente.
Risposta
Non è necessario utilizzare il pattern Dispose + Finalizer pesante (“painful”) su una classe IDisposable, if la classe è sigillata e la classe non ha risorse non gestite.
In questa situazione, puoi utilizzare un sottoinsieme del pattern Dispose + Finalizer, che ha solo public Dispose()
, non ha finalizzatore e la classe è sigillata.
La ragione per sigillare la classe è evitare i problemi di “E se una classe figlia avesse risorse non gestite?” e “E se una classe figlia in futuro avesse risorse non gestite?”
Joe Duffy, ex Microsoft, ha un lungo articolo (oltre 30 pagine) che spiega come implementare correttamente IDisposable
e tutte le implementazioni dei sottoinsiemi di permutazioni.
I collaboratori dellarticolo includono Herb Sutter, Brian Grunkemeyer, Jeff Richter e altri luminari di C #. Larticolo è molto più approfondito di quanto presentato su MSDN.
Ahimè, il nuovo motore di blog di Joe Duffy non ha fatto un buon lavoro nel preservare larticolo originale del 2005, quindi sembra un po alterato. Ecco il link: http://joeduffyblog.com/2005/04/08/dg-update-dispose-finalization-and-resource-management/
Vorrei poter allegare il mio documento MS-Word che contiene lintero articolo di Joe Duffy in una migliore formato. 🙁
IDisposable
in questo caso migliora limpronta della memoria?IDisposable
senza scrivere un distruttore / finalizzatore. Non ' non so se la persona che ti ha chiesto di implementareIDisposable
significava che dovresti creare anche un distruttore. Ovviamente, se la classe non ha un distruttore, non ha sensoGC.SuppressFinalize
. Se la classe ha un distruttore, assicurati di direGC.SuppressFinalize(this)
quando il tuo metodoDispose
è stato eseguito. In caso contrario, listanza non verrà facilmente raccolta in modo indesiderato, verrà accodata su una coda del finalizzatore e verrà raccolta solo in una raccolta di seconda generazione.