Pergunta

Eu tenho uma classe abstrata que implementa IDisposable, assim:

public abstract class ConnectionAccessor : IDisposable
{
    public abstract void Dispose();
}

Em 2008 Team System Visual Studio, eu corri de análise de código no meu projeto e um dos avisos que surgiu foi o seguinte:

Microsoft.Design: Modificar 'ConnectionAccessor.Dispose ()' para que ele chama Dispose (true), em seguida, chama GC.SuppressFinalize na instância atual do objeto ( 'isto' ou 'Me' no Visual Basic), e, em seguida, retorna .

É apenas sendo boba, me dizendo para modificar o corpo de um método abstrato, ou eu deveria fazer algo mais em qualquer instância derivada de Dispose?

Foi útil?

Solução

Você deve seguir o padrão convencional para a implementação Dispose. Fazendo Dispose() virtual é considerado má prática, porque o padrão convencional enfatiza a reutilização de código na "limpeza gerido" (cliente API Dispose() chamando diretamente ou através using) e "limpeza não gerenciado" (GC chamar finalizador). Para lembrar, o padrão é o seguinte:

public class Base
{
    ~Base()
    {
        Dispose(false);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this); // so that Dispose(false) isn't called later
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
             // Dispose all owned managed objects
        }

        // Release unmanaged resources
    }
}

Key aqui é que não há duplicação entre finalizador e Dispose para limpeza não gerenciado, e ainda qualquer classe derivada pode estender tanto a limpeza gerenciados e não gerenciados.

Para o seu caso, o que você deve fazer é esta:

protected abstract void Dispose(bool disposing)

e deixar tudo como está. Mesmo que seja de valor duvidoso, já que você está aplicando suas classes derivadas para implementar Dispose agora - e como você sabe que todos eles precisam? Se a sua classe base não tem nada a dispor, mas a maioria classes derivadas provável fazer (com algumas exceções, talvez), então é só fornecer uma implementação vazia. É o que System.IO.Stream (si abstract) faz, então há um precedente.

Outras dicas

O aviso basicamente diz-lhe para implementar o padrão Dispose em sua classe.

O código resultante deve ser semelhante a este:

public abstract class ConnectionAccessor : IDisposable
{
    ~ConnectionAccessor()
    {
        Dispose(false);
    }

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

    protected virtual void Dispose(bool disposing)
    {
    }
}

A única queixa que eu tenho com as respostas dadas até agora é que todos eles assumem que você necessidade para ter um finalizador, o que não é necessariamente o caso. Há um desempenho bastante significativo sobrecarga associada a finalização, o que eu não gostaria de impor em todos os meus classes derivadas se que não era necessário.

Consulte este blog por Joe Duffy, que explica quando você pode ou não pode precisar de um finalizador, e como implementar corretamente o padrão Dispose em ambos os casos.
post resumindo Joe, a menos que você está fazendo algo bastante baixo nível lidar com memória não gerenciada, você não deve implementar um finalizador. Como regra geral, se sua classe só contém referências a tipos gerenciados que implementam IDisposable-se, você não precisa o finalizador (mas deve implementar IDisposable e dispor desses recursos). Se você está alocando recursos não gerenciados diretamente do seu código (PInvoke?) E esses recursos devem ser liberados, você precisa de um. Uma classe derivada pode sempre adicionar um finalizador se ele realmente precisa dele, mas forçando todas as classes derivadas para ter um finalizador, colocando-o na classe base faz com que todas as classes derivadas de ser impactado pelo hit de objetos finalizable desempenho quando essa sobrecarga pode não ser necessário.

Enquanto isso parece um pouco como nit-picking, o conselho é válido. Você já está indicando que você espera qualquer sub-tipos de ConnectionAccessor terão algo que eles precisam se desfazer. Portanto, parece melhor para garantir que a limpeza adequada é feito (em termos da chamada GC.SuppressFinalize) pela classe base, em vez de depender de cada subtipo de fazê-lo.

Eu uso o padrão de descarte mencionado no Bruce Wagners reservar Effective C # que é basicamente:

public class BaseClass : IDisposable
{
    private bool _disposed = false;
    ~BaseClass()
    {
        Dispose(false);
    }

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

    protected virtual void Dispose(bool disposing)
    {
        if (_disposed)
            return;

        if (disposing)
        {
            //release managed resources
        }

        //release unmanaged resources

        _disposed = true;
    }
}

public void Derived : BaseClass
{
    private bool _disposed = false;

    protected override void Dispose(bool disposing)
    {
        if (_disposed) 
            return;

        if (disposing)
        {
            //release managed resources
        }

        //release unmanaged resources

        base.Dispose(disposing);
        _disposed = true;
    }

O aviso é, porém interessante. Eric Lippert, um dos desenhadores # C, blogou sobre o porquê de mensagens de erro deve ser "Diagnostic, mas não prescritiva: descrever o problema, não a solução". Leia aqui .

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