O GC desaloca todos os recursos para os quais não há mais referência, assim as variáveis locais não necessitam que removamos as referências manualmente. Os objetos de determinado escopo, delimitado pelas chaves ('{' e '}'), são coletáveis assim que o escopo de execução é encerrado, e o GC, que é executado periodicamente, desalocará os recursos na próxima iteração.
{ Object objeto = new Object(); // código objeto = null; //desnecessário } //fim do escopo e o objeto é liberado para a coleta
Porém isso não ocorrerá se um objeto com tempo de vida maior guardar uma referência para o mesmo objeto:
private KeyValuePairMas alguns objetos utilizam recursos que não são gerenciados pelo GC. Um exemplo de um recurso não gerenciado é a exclusividade de acesso a um arquivo, ou outro tipo de Handle fornecido pela WinAPI. Para estes objetos é necessário executar o método Dispose:
StreamReader arquivo = new StreamReader(@"c:\autoexec.bat"); //código arquivo.Dispose();Mas se surgir uma exceção no meio do caminho, o método "Dispose" não será invocado. Portanto é necessário envolver o código num try-finally que garantirá que o recurso será liberado mesmo havendo exceções
StreamReader arquivo = new StreamReader(@"c:\autoexec.bat"); try { //código } finally { arquivo.Dispose(); }A linguagem C# possui um atalho para tal construção que é IL-equivalente à construção acima:
using(StreamReader arquivo = new StreamReader(@"c:\autoexec.bat")) { //código }Esta função "using" aceita como parâmetro qualquer objeto que implemente a interface
IDisposable
. Por padrão todos os objetos que herdem esta interface devem ser instanciados em blocos using, direta ou indiretamente.
Quando o escopo do objeto não for terminar imediatamente para algumas classes do .NET é necessário também remover a referência. Costuma-se utilizar tal procedimento com a classe System.Threading.Timer no caso a seguir:
public class Servico : ServiceBase { private Timer timer; protected override void OnStart(string[] args) { this.timer = new Timer(delegate { }); } protected override void OnStop() { this.timer.Dispose(); this.timer = null; } }A implementação acima falhará no caso de uma exceção, pois o método
timer.Dispose()
não será invocado. Para corrigir deveríamos sobrescrever o método Dispose(bool disposing)
implementado pela classe ServiceBase
:
protected override void Dispose(bool disposing) { if (disposing) { this.timer.Dispose(); } base.Dispose(disposing); }Que é equivalente a:
protected override void Dispose(bool disposing) { if (disposing) { using(this.timer) { } } base.Dispose(disposing); }Algumas outras classes do .NET não possuem uma implementação do método
Dispose()
da forma esperada, por exemplo as classes que herdam da interface ICommunicationObject. Para elas cria-se um wrapper que corrige este algoritmo:
public sealed class CommunicationObjectWrapper : IDisposable { public ICommunicationObject CommunicationObject { get; private set; } public CommunicationObjectWrapper(ICommunicationObject communicationObject) { CommunicationObject = communicationObject; } public void Close() { if (CommunicationObject != null) { CommunicationObject.Close(); } } public void Dispose() { if (CommunicationObject != null) { try { ((IDisposable)CommunicationObject).Dispose(); } catch (CommunicationException) { CommunicationObject.Abort(); } catch (TimeoutException) { CommunicationObject.Abort(); } catch (Exception) { CommunicationObject.Abort(); throw; } } } }
Nenhum comentário:
Postar um comentário