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.

Foi útil?

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.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top