C # 2008
Estou trabalhando nisso há algum tempo e ainda estou confuso sobre o uso dos métodos finalize e descarte no código. Minhas perguntas estão abaixo:
Eu sei que precisamos apenas de um finalizador enquanto dispomos de recursos não gerenciados. No entanto, se houver recursos gerenciados que fazem chamadas para recursos não gerenciados, ainda seria necessário implementar um finalizador?
No entanto, se eu desenvolver uma classe que não use nenhum recurso não gerenciado - direta ou indiretamente, devo implementar o
IDisposable
para permitir que os clientes dessa classe usem a 'declaração de uso'?Seria viável implementar IDisposable apenas para permitir que os clientes de sua classe usassem a instrução using?
using(myClass objClass = new myClass()) { // Do stuff here }
Desenvolvi este código simples abaixo para demonstrar o uso Finalizar / descartar:
public class NoGateway : IDisposable { private WebClient wc = null; public NoGateway() { wc = new WebClient(); wc.DownloadStringCompleted += wc_DownloadStringCompleted; } // Start the Async call to find if NoGateway is true or false public void NoGatewayStatus() { // Start the Async's download // Do other work here wc.DownloadStringAsync(new Uri(www.xxxx.xxx)); } private void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) { // Do work here } // Dispose of the NoGateway object public void Dispose() { wc.DownloadStringCompleted -= wc_DownloadStringCompleted; wc.Dispose(); GC.SuppressFinalize(this); } }
Pergunta sobre o código fonte:
Aqui eu não adicionei o finalizador, e normalmente o finalizador será chamado pelo GC, e o finalizador chamará Dispose. Como não tenho o finalizador, quando chamo o método Dispose? É o cliente da classe que precisa chamá-lo?
Portanto, minha classe no exemplo é chamada NoGateway e o cliente pode usar e descartar a classe assim:
using(NoGateway objNoGateway = new NoGateway()) { // Do stuff here }
O método Dispose seria chamado automaticamente quando a execução chegasse ao final do bloco using ou o cliente precisa chamar manualmente o método de descarte? ie
NoGateway objNoGateway = new NoGateway(); // Do stuff with object objNoGateway.Dispose(); // finished with it
Eu estou usando a
WebClient
classe na minhaNoGateway
classe. ComoWebClient
implementa aIDisposable
interface, isso significa queWebClient
indiretamente usa recursos não gerenciados? Existe uma regra rígida e rápida para seguir isso? Como sei que uma classe usa recursos não gerenciados?
fonte
Respostas:
O padrão IDisposable recomendado está aqui . Ao programar uma classe que usa IDisposable, geralmente você deve usar dois padrões:
Ao implementar uma classe selada que não usa recursos não gerenciados, você simplesmente implementa um método Dispose como nas implementações normais da interface:
Ao implementar uma classe não lacrada, faça o seguinte:
Observe que eu não declarei um finalizador em
B
; você só deve implementar um finalizador se tiver recursos não gerenciados reais para descartar. O CLR lida com objetos finalizáveis de maneira diferente de objetos não finalizáveis, mesmo seSuppressFinalize
for chamado.Portanto, você não deve declarar um finalizador, a menos que seja necessário, mas concede aos herdeiros um gancho para chamá-lo
Dispose
e implementá-lo se eles usarem recursos não gerenciados diretamente:Se você não estiver usando recursos não gerenciados diretamente (
SafeHandle
e os amigos não contarem, pois declaram seus próprios finalizadores), não implemente um finalizador, pois o GC lida com classes finalizáveis de maneira diferente, mesmo se você suprimir o finalizador posteriormente. Observe também que, emboraB
não tenha um finalizador, ele ainda chamaSuppressFinalize
para lidar corretamente com qualquer subclasse que implementa um finalizador.Quando uma classe implementa a interface IDisposable, significa que em algum lugar existem alguns recursos não gerenciados que devem ser eliminados quando você terminar de usar a classe. Os recursos reais são encapsulados dentro das classes; você não precisa excluí-los explicitamente. Simplesmente chamar
Dispose()
ou agrupar a classe emusing(...) {}
garantirá que todos os recursos não gerenciados sejam eliminados conforme necessário.fonte
IDisposable
, é provável que demore um pouco. Você está salvando o CLR do esforço de copiá-lo de Gen0 -> Gen1 -> Gen2O padrão oficial a ser implementado
IDisposable
é difícil de entender. Eu acredito que este é melhor :Uma solução ainda melhor é ter uma regra na qual você sempre deve criar uma classe de wrapper para qualquer recurso não gerenciado que você precise manipular:
Com
SafeHandle
e seus derivados, essas classes devem ser muito raras .O resultado para classes descartáveis que não lidam diretamente com recursos não gerenciados, mesmo na presença de herança, é poderoso: elas não precisam mais se preocupar com recursos não gerenciados . Eles serão simples de implementar e entender:
fonte
disposed
bandeira e verificar em conformidade.disposed
bandeira, verifique-a antes de descartar e defina-a após descartar. Veja aqui a ideia. Você também deve verificar o sinalizador antes de qualquer método da classe. Faz sentido? É complicado?Observe que qualquer implementação IDisposable deve seguir o padrão abaixo (IMHO). Desenvolvi esse padrão com base nas informações de vários "deuses" excelentes do .NET Framework Guidelines do .NET Framework (observe que o MSDN não segue isso por algum motivo!). As diretrizes de design do .NET Framework foram escritas por Krzysztof Cwalina (arquiteto do CLR na época) e Brad Abrams (acredito que o CLR Program Manager na época) e Bill Wagner ([C # efetivo] e [C # mais eficaz] (apenas faça uma procure-os no Amazon.com:
Observe que NUNCA deve implementar um Finalizador, a menos que sua classe contenha diretamente (não herda) recursos NÃO gerenciados. Depois de implementar um Finalizador em uma classe, mesmo que nunca seja chamado, é garantido que você terá uma coleção extra. Ele é automaticamente colocado na fila de finalização (que é executada em um único encadeamento). Além disso, uma observação muito importante ... todo o código executado dentro de um Finalizador (caso seja necessário implementar uma) DEVE ser seguro para threads E seguro para exceções! Coisas ruins acontecerão de outra forma ... (ou seja, comportamento indeterminado e, no caso de uma exceção, uma falha fatal irrecuperável do aplicativo).
O padrão que eu montei (e escrevi um trecho de código) é o seguinte:
Aqui está o código para implementar IDisposable em uma classe derivada. Observe que você não precisa listar explicitamente a herança de IDisposable na definição da classe derivada.
Publiquei esta implementação no meu blog em: Como implementar corretamente o padrão de descarte
fonte
Interlocked.Exchange
número inteiroIsDisposed
na função de invólucro não virtual seria mais seguro.Concordo com o pm100 (e deveria ter dito isso explicitamente no meu post anterior).
Você nunca deve implementar IDisposable em uma classe, a menos que precise. Para ser muito específico, há cerca de 5 vezes em que você precisa / deve implementar o IDisposable:
Sua classe contém explicitamente (ou seja, não por herança) quaisquer recursos gerenciados que implementam IDisposable e devem ser limpos assim que sua classe não for mais usada. Por exemplo, se sua classe contiver uma instância de um Stream, DbCommand, DataTable etc.
Sua classe contém explicitamente quaisquer recursos gerenciados que implementam um método Close () - por exemplo, IDataReader, IDbConnection, etc. Observe que algumas dessas classes implementam IDisposable por ter Dispose () e um método Close ().
Sua classe contém explicitamente um recurso não gerenciado - por exemplo, um objeto COM, ponteiros (sim, você pode usar ponteiros em C # gerenciado, mas eles devem ser declarados em blocos 'não seguros' etc.) No caso de recursos não gerenciados, verifique também se chame System.Runtime.InteropServices.Marshal.ReleaseComObject () no RCW Embora o RCW seja, em teoria, um invólucro gerenciado, ainda há contagem de referência em andamento.
Se sua classe se inscrever em eventos usando referências fortes. Você precisa cancelar o registro / desanexar-se dos eventos. Sempre verifique se eles não são nulos antes de tentar cancelar o registro / desanexação deles !.
Sua classe contém qualquer combinação dos itens acima ...
Uma alternativa recomendada para trabalhar com objetos COM e ter que usar Marshal.ReleaseComObject () é usar a classe System.Runtime.InteropServices.SafeHandle.
A BCL (Equipe da Biblioteca de Classes Base) tem uma boa publicação no blog aqui http://blogs.msdn.com/bclteam/archive/2005/03/16/396900.aspx
Uma observação muito importante a ser feita é que, se você estiver trabalhando com o WCF e limpando recursos, SEMPRE SEMPRE evite o bloco 'using'. Existem muitas postagens no blog e algumas no MSDN sobre por que essa é uma má idéia. Também publiquei aqui - Não use 'using ()' com um proxy WCF
fonte
Usando lambdas em vez de IDisposable.
Eu nunca fiquei empolgado com a ideia usando / IDisposable. O problema é que exige que o chamador:
Meu novo método preferido é usar um método de fábrica e um lambda
Imagine que eu quero fazer algo com um SqlConnection (algo que deve ser envolvido em um uso). Classicamente você faria
Nova maneira
No primeiro caso, o chamador simplesmente não pôde usar a sintaxe de uso. No segundo caso, o usuário não tem escolha. Não existe um método que crie um objeto SqlConnection, o chamador deve chamar DoWithConnection.
DoWithConnection se parece com isso
MakeConnection
agora é privadofonte
DoForAll(Action<T>) where T:IComparable<T>
, chamando o delegado indicado em cada registro. Dado dois desses objetos, os quais retornarão dados em ordem classificada, como um produziria todos os itens que existem em uma coleção, mas não no outro? Se os tipos forem implementadosIEnumerable<T>
, é possível executar uma operação de mesclagem, mas isso não funcionaráDoForAll
.DoForAll
coleções sem precisar primeiro copiar uma, em sua totalidade, em alguma outra estrutura, seria usar dois threads, o que seria um pouco mais hoggish de recursos do que simplesmente usar alguns IEnumerable e ter cuidado para liberá-los.ninguém respondeu à pergunta sobre se você deve implementar o IDisposable, mesmo que não seja necessário.
Resposta curta: Não
Resposta longa:
Isso permitiria que um consumidor da sua classe usasse 'using'. A pergunta que eu faria é - por que eles fariam isso? A maioria dos desenvolvedores não usará 'using' a menos que saibam que precisam - e como sabem. Ou
Portanto, ao implementar o IDisposable, você está dizendo aos desenvolvedores (pelo menos alguns) que essa classe encerra algo que deve ser lançado. Eles usarão 'using' - mas há outros casos em que o uso não é possível (o escopo do objeto não é local); e eles terão que começar a se preocupar com a vida útil dos objetos nesses outros casos - eu me preocuparia com certeza. Mas isso não é necessário
Você implementa Idisposable para permitir que eles usem usando, mas eles não usarão, a menos que você peça.
Então não faça isso
fonte
Se você estiver usando outros objetos gerenciados que estão usando recursos não gerenciados, não é sua responsabilidade garantir que eles sejam finalizados. Sua responsabilidade é chamar Dispose nesses objetos quando Dispose é chamado no seu objeto e para por aí.
Se sua classe não usa recursos escassos, não entendo por que você faria sua classe implementar IDisposable. Você só deve fazer isso se estiver:
Sim, o código que usa seu código deve chamar o método Dispose do seu objeto. E sim, o código que usa seu objeto pode usar
using
como você mostrou.(2 novamente?) É provável que o WebClient use recursos não gerenciados ou outros recursos gerenciados que implementam IDisposable. A razão exata, no entanto, não é importante. O importante é que ele implemente o IDisposable e, portanto, você deve agir de acordo com esse conhecimento, descartando o objeto quando terminar, mesmo que o WebClient não utilize outros recursos.
fonte
Descarte o padrão:
Exemplo de herança:
fonte
Alguns aspectos de outra resposta estão levemente incorretos por 2 razões:
Primeiro,
na verdade é equivalente a:
Isso pode parecer ridículo, pois o operador 'new' nunca deve retornar 'null', a menos que você tenha uma exceção OutOfMemory. Mas considere os seguintes casos: 1. Você chama um FactoryClass que retorna um recurso IDisposable ou 2. Se você possui um tipo que pode ou não herdar de IDisposable, dependendo de sua implementação - lembre-se de que eu vi o padrão IDisposable implementado incorretamente vezes em muitos clientes em que os desenvolvedores apenas adicionam um método Dispose () sem herdar de IDisposable (ruim, ruim, ruim). Você também pode ter o caso de um recurso IDisposable ser retornado de uma propriedade ou método (novamente ruim, ruim, ruim - não 'entregue seus recursos IDisposable)
Se o operador 'as' retornar nulo (ou propriedade ou método que retorna o recurso), e seu código no bloco 'using' proteger contra 'null', seu código não explodirá ao tentar chamar Dispose em um objeto nulo por causa de a verificação nula 'incorporada'.
A segunda razão pela qual sua resposta não é precisa é o seguinte stmt:
Primeiro, a finalização (assim como o próprio GC) não é determinística. O CLR determina quando chamará um finalizador. ou seja, o desenvolvedor / código não tem idéia. Se o padrão IDisposable for implementado corretamente (como postamos acima) e o GC.SuppressFinalize () tiver sido chamado, o Finalizador NÃO será chamado. Esse é um dos grandes motivos para implementar corretamente o padrão corretamente. Como existe apenas um encadeamento do Finalizer por processo gerenciado, independentemente do número de processadores lógicos, é possível degradar facilmente o desempenho fazendo backup ou até interrompendo o encadeamento do Finalizer, esquecendo de chamar GC.SuppressFinalize ().
Publiquei uma implementação correta do Dispose Pattern no meu blog: Como implementar corretamente o Dispose Pattern
fonte
NoGateway = new NoGateway();
eNoGateway != null
?1) O WebClient é um tipo gerenciado, portanto você não precisa de um finalizador. O finalizador é necessário no caso de seus usuários não Dispose () da sua classe NoGateway e o tipo nativo (que não é coletado pelo GC) precisa ser limpo depois. Nesse caso, se o usuário não chamar Dispose (), o WebClient contido será descartado pelo GC logo após o NoGateway.
2) Indiretamente sim, mas você não deve se preocupar com isso. Seu código está correto como está e você não pode impedir que seus usuários se esqueçam de Dispose () com muita facilidade.
fonte
Padrão do msdn
fonte
é equivalente a
Um finalizador é chamado para o GC destruindo seu objeto. Isso pode ocorrer em um momento totalmente diferente do que quando você sai do seu método. O Dispose of IDisposable é chamado imediatamente depois que você sai do bloco using. Portanto, o padrão geralmente é usar o recurso para liberar recursos imediatamente após você não precisar mais deles.
fonte
Pelo que sei, é altamente recomendável NÃO usar o Finalizador / Destrutor:
Principalmente, isso se deve ao não saber quando ou se será chamado. O método de descarte é muito melhor, especialmente se você estiver usando ou descartado diretamente.
usando é bom. use-o :)
fonte