Respostas:
Outros já abordaram a diferença entre Dispose
e Finalize
(entre o Finalize
método ainda é chamado de destruidor na especificação da linguagem), então vou adicionar um pouco sobre os cenários em que o Finalize
método é útil.
Alguns tipos encapsulam recursos descartáveis de uma maneira em que é fácil usá-los e descartá-los em uma única ação. O uso geral costuma ser assim: abrir, ler ou escrever, fechar (Descarte). Ele se encaixa muito bem com a using
construção.
Outros são um pouco mais difíceis. WaitEventHandles
para instâncias não são usadas assim, pois são usadas para sinalizar de um encadeamento para outro. A questão então é quem deve recorrer Dispose
a eles. Como uma proteção, tipos como esses implementam um Finalize
método, que garante que os recursos sejam descartados quando a instância não for mais referenciada pelo aplicativo.
Finalize
pode ser justificada é quando há um número de objetos interessados em manter um recurso vivo, mas não há como um objeto que deixa de se interessar pelo recurso possa descobrir se é o último. Nesse caso, Finalize
normalmente será acionado apenas quando ninguém estiver interessado no objeto. O tempo de folga de Finalize
é horrível para recursos não fungíveis, como arquivos e bloqueios, mas pode ser bom para recursos fungíveis.
O método finalizador é chamado quando seu objeto é coletado de lixo e você não tem garantia de quando isso acontecerá (você pode forçá-lo, mas isso prejudicará o desempenho).
Por Dispose
outro lado, o método deve ser chamado pelo código que criou sua classe, para que você possa limpar e liberar todos os recursos que adquiriu (dados não gerenciados, conexões com o banco de dados, identificadores de arquivo, etc.) no momento em que o código é concluído. seu objeto.
A prática padrão é implementar IDisposable
e Dispose
para que você possa usar seu objeto em uma using
declaração. Tais como using(var foo = new MyObject()) { }
. E no seu finalizador, você liga Dispose
, caso o código de chamada tenha esquecido de se desfazer de você.
Finalize é o método de recuo, chamado pelo coletor de lixo ao recuperar um objeto. Dispose é o método de "limpeza determinística", chamado pelos aplicativos para liberar recursos nativos valiosos (identificadores de janela, conexões com o banco de dados etc.) quando eles não são mais necessários, em vez de deixá-los retidos indefinidamente até que o GC chegue ao objeto.
Como usuário de um objeto, você sempre usa Dispose. Finalizar é para o GC.
Como implementador de uma classe, se você possui recursos gerenciados que devem ser descartados, implementa Dispose. Se você possui recursos nativos, implementa Dispose e Finalize, e ambos chamam um método comum que libera os recursos nativos. Esses idiomas geralmente são combinados por meio de um método Dispose (descarte bool) privado, que Dispose chama com true e Finalize chama com false. Esse método sempre libera recursos nativos, verifica o parâmetro de descarte e, se for verdade, descarta os recursos gerenciados e chama GC.SuppressFinalize.
Dispose
é bom, e implementá-lo corretamente geralmente é fácil. Finalize
é mau, e implementá-lo corretamente geralmente é difícil. Entre outras coisas, como o GC garantirá que a identidade de nenhum objeto seja "reciclada" desde que exista qualquer referência a esse objeto, é fácil limpar um monte de Disposable
objetos, alguns dos quais já podem ter sido limpos. sem problemas; qualquer referência a um objeto no qual Dispose
já foi chamado permanecerá uma referência a um objeto no qual Dispose
já foi chamado.
Fred
possuir o identificador de arquivo nº 42 e fechá-lo, o sistema poderá anexar esse mesmo número a alguns identificadores de arquivo que são dados a alguma outra entidade. Nesse caso, o identificador de arquivo nº 42 não se referiria ao arquivo fechado de Fred, mas ao arquivo que estava em uso ativo por essa outra entidade; para Fred
tentar fechar o punho # 42 novamente seria desastroso. Tentar controlar 100% de forma confiável se um objeto não gerenciado ainda foi lançado é viável. Tentar acompanhar vários objetos é muito mais difícil.
Finalizar
protected
, public
ou não, private
para que o método não possa ser chamado diretamente do código do aplicativo e, ao mesmo tempo, possa fazer uma chamada para o base.Finalize
métodoDescarte
IDisposable
em todo tipo que tenha um finalizadorDispose
método. Em outras palavras, evite usar um objeto depois que o Dispose
método tiver sido chamado.Dispose
todos os IDisposable
tipos assim que terminar com elesDispose
ser chamado várias vezes sem gerar erros.Dispose
método usando o GC.SuppressFinalize
métodoDispose
métodosDispose / Finalized Pattern
Dispose
e Finalize
ao trabalhar com recursos não gerenciados. A Finalize
implementação seria executada e os recursos ainda seriam liberados quando o objeto fosse coletado como lixo, mesmo que um desenvolvedor deixasse de chamar Dispose
explicitamente o método.Finalize
método, bem como no Dispose
método. Além disso, chame o Dispose
método para quaisquer objetos .NET que você tenha como componentes nessa classe (tendo recursos não gerenciados como membros) a partir do Dispose
métodoFinalize é chamado pelo GC quando este objeto não está mais em uso.
Dispose é apenas um método normal que o usuário desta classe pode chamar para liberar quaisquer recursos.
Se o usuário esqueceu de chamar Dispose e se a classe Finalize foi implementada, o GC garantirá que seja chamado.
Existem algumas chaves do livro MCSD Certification Toolkit (exame 70-483), página 193:
destruidor ≈ (é quase igual a)base.Finalize()
, o destruidor é convertido em uma versão de substituição do método Finalize que executa o código do destruidor e depois chama o método Finalize da classe base. Então é totalmente não determinístico que você não pode saber quando será chamado, porque depende do GC.
Se uma classe não contém recursos gerenciados e não gerenciados , ela não deve implementar IDisposable
ou ter um destruidor.
Se a classe tiver apenas recursos gerenciados , ela deve ser implementada, IDisposable
mas não deve ter um destruidor. (Quando o destruidor é executado, você não pode ter certeza de que os objetos gerenciados ainda existem, portanto não pode chamar os Dispose()
métodos deles .)
Se a classe tiver apenas recursos não gerenciados , ela precisará ser implementada IDisposable
e precisará de um destruidor, caso o programa não seja chamado Dispose()
.
Dispose()
O método deve ser seguro para executar mais de uma vez. Você pode conseguir isso usando uma variável para acompanhar se ela já foi executada antes.
Dispose()
deve liberar recursos gerenciados e não gerenciados .
O destruidor deve liberar apenas recursos não gerenciados . Quando o destruidor é executado, você não pode ter certeza de que os objetos gerenciados ainda existem; portanto, você não pode chamar os métodos Dispose de qualquer maneira. Isso é obtido usando o protected void Dispose(bool disposing)
padrão canônico , onde apenas os recursos gerenciados são liberados (descartados) quando disposing == true
.
Após liberar recursos, Dispose()
deve chamarGC.SuppressFinalize
, para que o objeto possa pular a fila de finalização.
Um exemplo de implementação para uma classe com recursos não gerenciados e gerenciados:
using System;
class DisposableClass : IDisposable
{
// A name to keep track of the object.
public string Name = "";
// Free managed and unmanaged resources.
public void Dispose()
{
FreeResources(true);
// We don't need the destructor because
// our resources are already freed.
GC.SuppressFinalize(this);
}
// Destructor to clean up unmanaged resources
// but not managed resources.
~DisposableClass()
{
FreeResources(false);
}
// Keep track if whether resources are already freed.
private bool ResourcesAreFreed = false;
// Free resources.
private void FreeResources(bool freeManagedResources)
{
Console.WriteLine(Name + ": FreeResources");
if (!ResourcesAreFreed)
{
// Dispose of managed resources if appropriate.
if (freeManagedResources)
{
// Dispose of managed resources here.
Console.WriteLine(Name + ": Dispose of managed resources");
}
// Dispose of unmanaged resources here.
Console.WriteLine(Name + ": Dispose of unmanaged resources");
// Remember that we have disposed of resources.
ResourcesAreFreed = true;
}
}
}
99% do tempo, você também não deve se preocupar. :) Mas, se seus objetos mantiverem referências a recursos não gerenciados (identificadores de janela, identificadores de arquivo, por exemplo), você precisará fornecer uma maneira de seu objeto gerenciado liberar esses recursos. Finalizar fornece controle implícito sobre a liberação de recursos. É chamado pelo coletor de lixo. Dispose é uma maneira de fornecer controle explícito sobre uma liberação de recursos e pode ser chamado diretamente.
Há muito mais para aprender sobre o assunto Coleta de Lixo , mas isso é um começo.
O finalizador é para limpeza implícita - você deve usá-lo sempre que uma classe gerenciar recursos que absolutamente devem ser limpos, caso contrário você vazaria alças / memória, etc ...
A implementação correta de um finalizador é notoriamente difícil e deve ser evitada sempre que possível - a SafeHandle
classe (disponível no .Net v2.0 e posterior) agora significa que você raramente (se alguma vez) precisa implementar mais um finalizador.
A IDisposable
interface é para limpeza explícita e é muito mais usada - você deve usar isso para permitir que os usuários liberem ou limpem explicitamente recursos sempre que terminarem de usar um objeto.
Observe que, se você tiver um finalizador, também deverá implementar a IDisposable
interface para permitir que os usuários liberem explicitamente esses recursos mais cedo do que seriam se o objeto fosse coletado com lixo.
Consulte Atualização da DG: Descarte, finalização e gerenciamento de recursos para o que considero o melhor e mais completo conjunto de recomendações sobre finalizadores e IDisposable
.
O resumo é -
Além disso, outra diferença é: na implementação Dispose (), você também deve liberar recursos gerenciados , enquanto isso não deve ser feito no Finalizador. Isso ocorre porque é muito provável que os recursos gerenciados referenciados pelo objeto já tenham sido limpos antes de estarem prontos para serem finalizados.
Para uma classe que utiliza recursos não gerenciados, a melhor prática é definir ambos - o método Dispose () e o Finalizador - a serem usados como substituto no caso de um desenvolvedor esquecer de descartar explicitamente o objeto. Ambos podem usar um método compartilhado para limpar recursos gerenciados e não gerenciados:
class ClassWithDisposeAndFinalize : IDisposable
{
// Used to determine if Dispose() has already been called, so that the finalizer
// knows if it needs to clean up unmanaged resources.
private bool disposed = false;
public void Dispose()
{
// Call our shared helper method.
// Specifying "true" signifies that the object user triggered the cleanup.
CleanUp(true);
// Now suppress finalization to make sure that the Finalize method
// doesn't attempt to clean up unmanaged resources.
GC.SuppressFinalize(this);
}
private void CleanUp(bool disposing)
{
// Be sure we have not already been disposed!
if (!this.disposed)
{
// If disposing equals true i.e. if disposed explicitly, dispose all
// managed resources.
if (disposing)
{
// Dispose managed resources.
}
// Clean up unmanaged resources here.
}
disposed = true;
}
// the below is called the destructor or Finalizer
~ClassWithDisposeAndFinalize()
{
// Call our shared helper method.
// Specifying "false" signifies that the GC triggered the cleanup.
CleanUp(false);
}
O melhor exemplo que eu conheço.
public abstract class DisposableType: IDisposable
{
bool disposed = false;
~DisposableType()
{
if (!disposed)
{
disposed = true;
Dispose(false);
}
}
public void Dispose()
{
if (!disposed)
{
disposed = true;
Dispose(true);
GC.SuppressFinalize(this);
}
}
public void Close()
{
Dispose();
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// managed objects
}
// unmanaged objects and resources
}
}
Diferença entre os métodos Finalize e Dispose em C #.
O GC chama o método finalize para recuperar os recursos não gerenciados (como operação de arquivos, API do Windows, conexão de rede, conexão com o banco de dados), mas o tempo não é fixo quando a GC o chamaria. É chamado implicitamente pelo GC e significa que não temos controle de baixo nível.
Método de Disposição: Temos um controle de baixo nível, como chamamos a partir do código. podemos recuperar os recursos não gerenciados sempre que acharmos que não é utilizável. Podemos conseguir isso implementando o padrão IDisposal.
As instâncias de classe geralmente encapsulam o controle sobre recursos que não são gerenciados pelo tempo de execução, como identificadores de janela (HWND), conexões com o banco de dados e assim por diante. Portanto, você deve fornecer uma maneira explícita e implícita de liberar esses recursos. Forneça controle implícito implementando o método Finalize protegido em um objeto (sintaxe do destruidor em C # e as extensões gerenciadas para C ++). O coletor de lixo chama esse método em algum momento depois que não há mais referências válidas para o objeto. Em alguns casos, convém fornecer aos programadores que usam um objeto a capacidade de liberar explicitamente esses recursos externos antes que o coletor de lixo libere o objeto. Se um recurso externo for escasso ou caro, um melhor desempenho poderá ser alcançado se o programador liberar explicitamente os recursos quando eles não estiverem mais sendo usados. Para fornecer controle explícito, implemente o método Dispose fornecido pela Interface IDisposable. O consumidor do objeto deve chamar esse método quando terminar de usar o objeto. Dispose pode ser chamado mesmo se outras referências ao objeto estiverem ativas.
Observe que, mesmo quando você fornece controle explícito por meio de Dispose, deve fornecer limpeza implícita usando o método Finalize. Finalizar fornece um backup para impedir que os recursos vazem permanentemente se o programador falhar em Dispose.
A principal diferença entre Dispose e Finalize é que:
Dispose
geralmente é chamado pelo seu código. Os recursos são liberados instantaneamente quando você o chama. As pessoas esquecem de chamar o método, então a using() {}
declaração é inventada. Quando o seu programa terminar a execução do código dentro {}
dele, ele chamará o Dispose
método automaticamente.
Finalize
não é chamado pelo seu código. É para ser chamado pelo Garbage Collector (GC). Isso significa que o recurso poderá ser liberado a qualquer momento no futuro, sempre que o GC decidir fazê-lo. Quando o GC faz seu trabalho, ele passa por muitos métodos Finalize. Se você tiver uma lógica pesada, isso tornará o processo lento. Isso pode causar problemas de desempenho para o seu programa. Portanto, tenha cuidado com o que você coloca lá.
Eu, pessoalmente, escreveria a maior parte da lógica de destruição em Dispose. Felizmente, isso esclarece a confusão.
Como sabemos, dispor e finalizar ambos são usados para liberar recursos não gerenciados.
Para responder na primeira parte, você deve fornecer exemplos em que as pessoas usam abordagens diferentes para exatamente o mesmo objeto de classe. Caso contrário, é difícil (ou até estranho) responder.
Quanto à segunda pergunta, leia melhor primeiro este uso adequado da interface IDisposable, que afirma que
É a sua escolha! Mas escolha Dispose.
Em outras palavras: o GC conhece apenas o finalizador (se houver. Também conhecido como destruidor da Microsoft). Um bom código tentará limpar os dois (finalizador e Dispose).