Em C #, qual é a diferença entre um destruidor e um método Finalize em uma classe?

97

Qual é a diferença, se houver, entre um destruidor e um método Finalize em uma classe?

Recentemente, descobri que o Visual Studio 2008 considera um destrutor sinônimo de um método Finalize, o que significa que o Visual Studio não permitirá que você defina simultaneamente os dois métodos em uma classe.

Por exemplo, o seguinte fragmento de código:

class TestFinalize
{
    ~TestFinalize()
    {
        Finalize();
    }

    public bool Finalize()
    {
        return true;
    }
}

Fornece o seguinte erro na chamada para Finalize no destruidor:

A chamada é ambígua entre os seguintes métodos ou propriedades: 'TestFinalize. ~ TestFinalize ()' e 'TestFinalize.Finalize ()'

E se a chamada para Finalize for comentada, ocorrerá o seguinte erro:

O tipo 'ManagementConcepts.Service.TestFinalize' já define um membro chamado 'Finalize' com os mesmos tipos de parâmetro

Jeff Leonard
fonte

Respostas:

68

Um destruidor em C # substitui o System.Object.Finalizemétodo. Você precisa usar a sintaxe do destruidor para fazer isso. Substituir manualmente Finalizefornecerá uma mensagem de erro.

Basicamente, o que você está tentando fazer com a Finalizedeclaração de seu método é ocultar o método da classe base. Isso fará com que o compilador emita um aviso que pode ser silenciado usando o newmodificador (se for funcionar). O importante a notar aqui é que você não pode ambos overridee declarar um newmembro com nome idêntico ao mesmo tempo, assim que ter tanto um destruidor e um Finalizemétodo irá resultar em um erro (mas você pode , embora não seja recomendado, declarar um public new void Finalize()método se você não está declarando um destruidor).

Mehrdad Afshari
fonte
71

A Wikipedia tem uma boa discussão sobre a diferença entre um finalizador e um destruidor no artigo do finalizador .

C # realmente não tem um destruidor "verdadeiro". A sintaxe lembra um destruidor C ++, mas na verdade é um finalizador. Você escreveu corretamente na primeira parte do seu exemplo:

~ClassName() { }

O acima é um açúcar sintático para uma Finalizefunção. Ele garante que os finalizadores na base sejam executados, mas, de outra forma, é idêntico ao substituir a Finalizefunção. Isso significa que quando você escreve a sintaxe do destruidor, você está realmente escrevendo o finalizador.

De acordo com a Microsoft , o finalizador se refere à função que o coletor de lixo chama quando ele coleta ( Finalize), enquanto o destruidor é o seu pedaço de código que é executado como resultado (o açúcar sintático que se torna Finalize). Eles estão tão perto de serem a mesma coisa que a Microsoft nunca deveria ter feito a distinção.

O uso que a Microsoft faz do termo "destruidor" do C ++ é enganoso, porque no C ++ ele é executado no mesmo thread assim que o objeto é excluído ou retirado da pilha, enquanto no C # é executado em um thread separado em outro momento.

Kenzi
fonte
Eu diria que essa distinção entre o destruidor e o finalizador é importante fazer. No entanto, apenas aqueles que se preocupam com o que está acontecendo por baixo do capô se importam com essa distinção.
Kyle Baran
1
Observe também que o ECMA-334 eliminou explicitamente a ambigüidade de "destruidor" e "finalizador" formalmente, há muito tempo. Não sei por que a MS ainda insiste no termo enganoso em suas especificações.
FrankHB
Pelo menos trabalhando com Mono, C # é realmente modelado após C ++, e a maioria dos objetos C # nativos são objetos C ++. A maneira como o compilador que compilou o Mono funciona determina como esses objetos C ++ são destruídos e, da mesma forma, como a finalização do objeto C # se propaga para C ++ e chama esses destruidores. A distinção faz sentido nos bastidores, mas ainda não se aplica ao C # em si.
Kenzi
20

Encontrado aqui: http://sanjaysainitech.blogspot.com/2007/06/difference-between-destructor-dispose.html

  1. Destruidor

    Eles são métodos especiais que contêm código de limpeza para o objeto. Você não pode chamá-los explicitamente em seu código, pois eles são chamados implicitamente pelo GC. Em C #, eles têm o mesmo nome que o nome da classe precedido pelo ~sinal. Gostar-

    Class MyClass
    {
    
    ~MyClass()
    {
    .....
    }
    }

    No VB.NET, os destruidores são implementados substituindo o método Finalize da classe System.Object.

  2. Descarte

    Eles são como quaisquer outros métodos da classe e podem ser chamados explicitamente, mas têm um propósito especial de limpar o objeto. No método dispose, escrevemos código de limpeza para o objeto. É importante que liberemos todos os recursos não gerenciados no método dispose como conexão de banco de dados, arquivos etc. A classe que implementa o método dispose deve implementar a interface IDisposable. Um método Dispose deve chamar o método GC.SuppressFinalize para o objeto que está descartando se o a classe tem desturctor porque já fez o trabalho de limpeza do objeto, então não é necessário que o coletor de lixo chame o método Finalize do objeto. Referência: http://msdn2.microsoft.com/en-us/library/aa720161(VS.71).aspx

  3. Finalizar

    Um método Finalize atua como uma proteção para limpar recursos caso seu método Dispose não seja chamado. Você só deve implementar um método Finalize para limpar recursos não gerenciados. Você não deve implementar um método Finalize para objetos gerenciados, porque o coletor de lixo limpa os recursos gerenciados automaticamente. O método Finalize é chamado pelo GC implicitamente, portanto, você não pode chamá-lo de seu código.

    Nota: Em C #, o método Finalize não pode ser sobrescrito, então você deve usar o destruidor cuja implementação interna sobrescreverá o método Finalize em MSIL. Mas no VB.NET, o método Finalize pode ser sobrescrito porque ele suporta o método destruidor.

Atualização: Tópico semi-relacionado interessante aqui .

Andrew Siemer
fonte
1
You should only implement a Finalize method to clean up unmanaged resources: você coloca em Finalizar. Mesmo com Dispose?
Hqt
@hqt: Os casos em que se deve implementar Disposesuperam amplamente aqueles em que se deve implementar um finalizador. Implemente Disposese for provável que uma instância da classe ou classe derivada seja a última coisa a possuir diretamente um recurso não gerenciado, ou a última coisa a possuir diretamente um recurso não gerenciado, ou a propriedade direta a última coisa a possuir diretamente etc. Apenas implemente Finalizepara limpeza de recursos se a classe de alguém <i> diretamente </i> possuir um recurso não gerenciado <i> e quase nada mais </i> - um cenário muito mais restrito.
supercat de
@hqt: Se uma classe possuir diretamente recursos não gerenciados e também manter referências a outros objetos, os recursos não gerenciados geralmente devem ser divididos em sua própria classe finalizável (que idealmente não deve conter nenhuma referência forte a qualquer outra coisa), ou seja, a classe que contém referências a outros objetos, apenas possuiria "coisas que possuem recursos não gerenciados diretamente", em vez de possuir os próprios recursos e, portanto, não precisaria de um finalizador.
supercat de