Implementando IDisposable em uma classe selada
Pergunta
Eu não acho que essa pergunta foi feita antes. Estou um pouco confuso sobre a melhor maneira de implementar IDisposable
em um selado classe especificamente, uma classe selada que não herda de uma classe base. (Ou seja, uma "classe selada pura", que é o meu composta prazo.)
Talvez alguns de vocês concordam comigo em que as diretrizes para a implementação IDisposable
são muito confusas. Dito isto, eu quero saber que a maneira que eu pretendo implementar IDisposable
é suficiente e seguro.
Eu estou fazendo algum / code P Invoke que aloca um IntPtr
através Marshal.AllocHGlobal
e, naturalmente, quero dispor de forma limpa da memória não gerenciada eu criei. Então, eu estou pensando em algo como isto
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();
}
}
Estou assumindo que porque MemBlock
é completamente selado e nunca deriva de outra classe que implementar um virtual protected Dispose(bool disposing)
não é necessário.
Além disso, é o finalizador estritamente necessário? Todos os pensamentos bem-vindos.
Solução
O finalizador é necessário como um mecanismo de retorno de recursos não gerenciados finalmente livres, se você se esqueceu de chamar Dispose
.
Não, você não deve declarar um método virtual
em uma classe sealed
. Ele não compilar em tudo. Além disso, não é recomendado para declarar novos membros protected
nas aulas sealed
.
Outras dicas
Uma adição menor; no Geral caso, um padrão comum é ter um método Dispose(bool disposing)
, para que você saiba se você está em Dispose
(onde mais coisas estão disponíveis) vs o finalizador (onde você realmente não deve tocar em qualquer outros objetos gerenciados conectado).
Por exemplo:
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;
}
}
A partir de Joe Duffy Weblog :
Para as classes selados, esta necessidade padrão não ser seguido, o que significa que você deve simplesmente implementar o seu finalizador e Elimine com os métodos simples (i.e. ~ T () (Finalizar) e dispor () em C #). Ao escolher o último percurso, o código ainda devem aderir ao orientações abaixo sobre implementação de finalização e lógica dispor.
Então, sim, você deve ser bom.
Você precisa fazer o finalizador como Mehrdad mencionado. Se você quiser evitar isso, você pode dar uma olhada em SafeHandle. Eu não tenho experiência suficiente com P / Invoke para sugerir o uso correto.
Você não pode declarar métodos virtuais em uma classe selada. Também declarar membros protegidos em uma classe selada dá-lhe um aviso do compilador. Então você implementou-lo corretamente. Chamando GC.SuppressFinalize (this) de dentro do finalizador não é necessário por razões óbvias, mas não pode prejudicar.
Ter um finalizador é essencial quando se lida com recursos não gerenciados, porque eles não são liberados automaticamente, você tem que fazê-lo no finalizador com é chamado automaticamente após o objeto foi lixo coletado.