BT

Disseminando conhecimento e inovação em desenvolvimento de software corporativo.

Contribuir

Tópicos

Escolha a região

Início Notícias Uma proposta para o IDisposable de análise estática: O atributo DisposeUnused

Uma proposta para o IDisposable de análise estática: O atributo DisposeUnused

Quando o .NET foi criado, havia incertezas sobre como o IDisposable deveria ser utilizado. Como resultado, o IDisposable foi aplicado de uma maneira excessivamente agressiva, com muitas categorias de classes fazendo requisições de métodos Dispose vazios. Isso levou a problemas com as ferramentas de análise estática, que não são capazes de separar casos reais dos Disposecalls que são vazios e provenientes de falsos positivos.

Para entender o motivo, precisamos voltar ao histórico inicial do CLR e ao funcionamento da coleta do lixo. Originalmente, o CLR deveria ser o novo tempo de execução do Visual Basic, que no final dos anos 90 era baseado no COM. Sob o modelo COM, os objetos têm uma contagem de referência. À medida que as referências são criadas e destruídas, a contagem é atualizada e, se a contagem chegar a zero, o objeto será desalocado. Isso cria um modelo determinístico de coleta de lixo, no qual é possível saber exatamente quando os recursos serão limpos.

A desvantagem mais significativa deste modelo de coleta de lixo para coleta de referência é que pode facilmente vazar a memória. Isso acontece quando temos uma série de objetos fazendo referência uns aos outros em looping, onde cada objeto impede que a contagem de referência dos outros chegue a zero. Também há problemas de desempenho em ambientes multi-thread porque os bloqueios são necessários ao ajustar a contagem de referência.

No início do desenvolvimento, a Microsoft decidiu que o CLR evitaria esses problemas escolhendo um coletor de lixo de marcação e varredura. Isso já estava bem estabelecido na comunidade em geral por meio da popularidade do Java. Mas esse estilo de GC não libera deterministicamente os recursos, tornando-o inadequado para conexões com bancos de dados, identificadores de arquivos e outros recursos altamente limitados. Daí a criação de IDisposable.

Ao mesmo tempo, a Microsoft estava experimentando o conceito de "componentes". A ideia de um componente nunca foi bem definida. Há uma classe Component, juntamente com interfaces como IComponent, IContainer e ISite. Depois de quase duas décadas, a documentação ainda possui um comentário extremamente vago sobre "compartilhamento de objetos entre aplicações". Presumivelmente, o pensamento era que os componentes seriam iguais ao COM, onde uma aplicação pode interagir diretamente com objetos em outro programa. Mas isso realmente não deu certo, por isso a história ficou esquecida.

Enquanto isso, no Windows Forms, havia uma noção diferente de "componente" que realmente significava "algo que pode ser colocado em um formulário ou janela". Além dos elementos reais da interface do usuário, como caixas de texto, isso incluiria objetos que adicionavam recursos como temporizadores. Isso vem da era da programação do VB 6, onde quase tudo que desejamos usar deve ser colocado no próprio formulário. Até a conexão e os comandos do banco de dados podem ser colocados diretamente no formulário.

E foi assim que acabamos com objetos aparentemente sem sentido sendo marcados como IDisposable. Classes como DataTable e SqlCommand não têm recursos não gerenciados para descartar. Mas, porque no passado pensamos de maneira equivocada que seria interessante colocá-los dentro do formulário, herdaram a classe Component. E, novamente, o Component é descartável, para que possamos escolher quando fechar os objetos proxy.

Análise estática

À medida que a análise estática continua, lentamente, a passar de uma ferramenta avançada para algo que todos devem usar, os avisos sobre objetos descartáveis se tornam, cada vez mais, um problema. Não é tão ruim para objetos de vida curta, como SqlCommand, pois é fácil envolvê-lo em uma declaração de uso sem pensar no fato de que a declaração na verdade, não faz nada.

É mais difícil para coisas como DataTable. Esse é um objeto que tem vida útil prolongada e pode ser usado fora do local que foi criado. A menos que esteja suprimido ou desabilitado, as ferramentas de análise estática reportarão avisos e erros sobre o DataTable e sobre os objetos semelhantes que não estão sendo descartados.

Proposta DisposeUnusedAttribute

A "melhor" solução seria simplesmente remover todos os métodos Dispose não utilizados. Mas isso não é uma opção viável atualmente, porque isso quebraria a compatibilidade com as versões anteriores.

Edward Brey propôs uma solução simples, mas elegante, sugerindo a criação de um atributo DisposeUnused que silencie as ferramentas de análise estática. As subclasses não herdariam esse atributo.

Este design não é a solução perfeita. Uma vez que DisposeUnused é aplicado a uma classe, seria uma mudança de última hora removê-lo. Para o DataTable, isso não é uma preocupação, mas Stephen A. Imhoff oferece um exemplo contrário, onde poderia ocorrer o problema:

Na verdade, é pior, porque agora temos algo que diz "sim, ignore este contrato que afirmo precisar" e, se um tipo de repente tiver que dispor de um recurso (por exemplo, o MemoryStream começará a alocar uma matriz nativa para tamanhos realmente grandes), os sistemas precisam começar a limpar, mas nós informamos anteriormente que não podem…

Outro problema é que nem sempre sabemos o que há dentro de uma variável. Digamos que temos uma variável do tipo Component. No momento da compilação, não há como saber se o que colocamos na variável precisará ser descartado. Portanto, pode fazer mais sentido aplicar o DisposeUnused apenas nas variáveis classificadas onde as subclasses adicionais não são possíveis.

Avalie esse artigo

Relevância
Estilo/Redação

Conteúdo educacional

BT