Implementare il pattern Dispose del .NET Framework

di Cristian Civera, in .NET Framework,

Il .NET Framework ha la principale caratteristica di offrire un mondo managed dove gli oggetti e il loro ciclo di vita sono gestiti autonomamente e non richiedeno un particolare interesse da parte del programmatore per il rilascio della memoria, perché il tutto è gestito dal Garbage Collector.

Alcune classi però, contenute nel framework o che il programmatore sviluppa, possono ricorrere all'uso di risorse non gestite, principalmente legate al mondo unmanaged come handle kernel, gdi o win32 le quali vanno esplicitamente rilasciate. Per non lasciare nulla al caso, il .NET Framework stesso e le linee guida indicano di usare pattern Dispose nelle classi per poter essere riconosciute dallo sviluppatore e per fornire un modo unico per consumare e rilasciare una classe che fa uso di risorse unmanaged. Nel caso in cui queste operazioni di rilascio non vengano effettuate, se la classe usa direttamente questi handle, il tutto verrà demandato alla gestione da parte del sistema operativo. Se questi sono di tipo kernel, moriranno solo al termine del processo, ma se questi sono di tipo gdi resteranno allocati fino allo spegnimento della macchina.

In generale, le classi che fanno uso del pattern si riconoscono dal fatto che implementano l'interfaccia IDisposable, il quale dispone di un unico metodo Dispose per indicare in modo esplicito di rilasciare le risorse. Inoltre, i linguaggi VB e C# permettono di sfruttare la parola chiave using per gestire in modo automatico generando del codice che sfrutti il costrutto try/finally per chiamare tale metodo sulla variabile passata.

Il pattern prevede inoltre che si implementi il distruttore della classe la cui implementazione deve rilasciare le risorse. In questo modo, nel caso il programmatore si dimentichi di chiamare il Dispose, il CLR provvederà poi in un secondo momento. La differenza è che questa operazione non è determinata e potrebbe non avvenire subito ed inoltre sovraccarica il CLR che deve collezionare prima le classi che si possono rilasciare, guardare quali implementano il distruttore e poi un secondo momento, con un thread secondario, chiamare il distruttore. Solo in un secondo ciclo del GC verrà finalmente rilasciata la risorsa.
Riassumendo quindi, ecco come implementare il pattern in una classe:

public class MiaClasse : IDisposable
{

  public void Dispose()
  {
    this.Dispose(true);
    GC.SuppressFinalize(this);
  }

  ~MiaClasse()
  {
    this.Dispose(false);
  }
  
  protected virtual void Dispose(bool disposing)
  {
    if (disposing)
    {
    }
  }
}

Come si può vedere, si implementa sia l'interfaccia, sia il distruttore andando a chiamare il metodo Dispose differenziando tramite un boolean la situazione che si presenta. Se disposing è true la chiamata è esplicita e quindi solitamente si annullano campi privati e si effettua il Dispose anche di eventuali classe interne che si usano a loro volta. In ogni caso comunque, indipendentemente da disposing, si chiudono le risorse unmanaged interne.
Il metodo GC.SuppressFinalize istruisce il Garbage Collector di togliere dalla finalization list la classe, evitando il doppio giro di rilascio descritto in precedenza, così da essere trattata come una normale.

In alternativa all'implementazione completa descritta in precedenza, è possibile ereditare la classe da System.ComponentModel.Component e semplicemente sovrascrivere il metodo protected Dispose.

Commenti

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Approfondimenti

I più letti di oggi