Implementazione di IDisposable su una classe sigillata
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.
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.