Domanda

Non credo che questa domanda sia stata posta prima. Sono un po 'confuso sul modo migliore di implementare IDisposable su una classe sigillata, in particolare una classe sigillata che non eredita da una classe base. (Cioè, una "classe sigillata pura" che è il mio termine inventato.)

Forse alcuni di voi sono d'accordo con me sul fatto che le linee guida per l'implementazione di IDisposable sono molto confuse. Detto questo, voglio sapere che il modo in cui intendo implementare IDisposable è sufficiente e sicuro.

Sto facendo del codice P / Invoke che alloca un IntPtr attraverso Marshal.AllocHGlobal e, naturalmente, desidero smaltire in modo pulito la memoria non gestita che ho creato . Quindi sto pensando a qualcosa del genere

using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential)]
public sealed class MemBlock : IDisposable
{
     IntPtr ptr;
     int length;

     MemBlock(int size)
     {
           ptr = Marshal.AllocHGlobal(size);
           length = size;
     }

     public void Dispose()
     {
          if (ptr != IntPtr.Zero)
          {
               Marshal.FreeHGlobal(ptr);
               ptr = IntPtr.Zero;
               GC.SuppressFinalize(this);
          }
     }

     ~MemBlock()
     {
           Dispose();
     }    
}

Suppongo che, poiché MemBlock è completamente sigillato e non deriva mai da un'altra classe che non è necessaria l'implementazione di un smaltimento protetto virtuale (eliminazione bool)) .

Inoltre, il finalizzatore è strettamente necessario? Tutti i pensieri sono ben accetti.

È stato utile?

Soluzione

Il finalizzatore è necessario come meccanismo di fallback per liberare eventualmente risorse non gestite se hai dimenticato di chiamare Dispose .

No, non dovresti dichiarare un metodo virtuale in una classe sigillata . Non si compilerebbe affatto. Inoltre, non è consigliabile dichiarare nuovi membri protetti nelle classi sigillate .

Altri suggerimenti

Un'aggiunta minore; nel caso generale , un modello comune è avere un metodo Dispose (bool disposing) , in modo da sapere se ci si trova in Dispose ( dove sono disponibili più cose) rispetto al finalizzatore (dove non dovresti davvero toccare altri oggetti gestiti collegati).

Ad esempio:

 public void Dispose() { Dispose(true); }
 ~MemBlock() { Dispose(false); }
 void Dispose(bool disposing) { // would be protected virtual if not sealed 
     if(disposing) { // only run this logic when Dispose is called
         GC.SuppressFinalize(this);
         // and anything else that touches managed objects
     }
     if (ptr != IntPtr.Zero) {
          Marshal.FreeHGlobal(ptr);
          ptr = IntPtr.Zero;
     }
 }

Da Weblog di Joe Duffy :

  

Per le classi sigillate, questo schema è necessario   non essere seguito, nel senso che dovresti   implementa semplicemente il tuo Finalizer e   Smaltire con i metodi semplici (ad es.   ~ T () (Finalizza) e Dispose () in C #).   Quando scegli l'ultimo percorso, il tuo   il codice dovrebbe comunque aderire al   linee guida di seguito riguardanti   attuazione della finalizzazione e   disporre la logica.

Quindi sì, dovresti essere bravo.

Hai bisogno del finalizzatore come menzionato da Mehrdad. Se vuoi evitarlo, puoi dare un'occhiata a SafeHandle. Non ho abbastanza esperienza con P / Invoke per suggerire l'uso corretto.

Non puoi dichiarare metodi virtuali in una classe sigillata. Anche la dichiarazione di membri protetti in una classe sigillata ti dà un avvertimento del compilatore. Quindi l'hai implementato correttamente. Chiamare GC.SuppressFinalize (questo) dall'interno del finalizzatore non è necessario per ovvi motivi, ma non può nuocere.

Avere un finalizzatore è essenziale quando si ha a che fare con risorse non gestite, poiché non vengono liberate automaticamente, è necessario farlo nel finalizzatore con viene chiamato automaticamente dopo che l'oggetto è stato spazzato via.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top