Configurando Objetos para Nulo / Nada depois de usar no .NET

Você deve definir todos os objects como null ( Nothing in VB.NET) depois de ter terminado com eles?

Eu entendo que no .net é essencial para eliminar quaisquer instâncias de objects que implementam a interface IDisposable para liberar alguns resources, embora o object ainda pode ser algo depois que ele é descartado (daí a propriedade isDisposed em formulários), então eu suponho que pode ainda reside na memory ou pelo menos em parte?

Eu também sei que quando um object sai do escopo, ele é então marcado para coleta pronto para a próxima passagem do coletor de lixo (embora isso possa levar tempo).

Então, com isso em mente, a configuração será null para acelerar o sistema, liberando a memory, já que não precisa descobrir que ela não está mais no escopo e se há algum efeito colateral ruim?

Artigos do MSDN nunca fazem isso em exemplos e atualmente eu faço isso porque não consigo ver o dano. No entanto, me deparei com uma mistura de opiniões para que qualquer comentário seja útil.

   

Karl está absolutamente correto, não há necessidade de definir objects para null após o uso. Se um object implementa IDisposable , apenas certifique-se de chamar IDisposable.Dispose() quando terminar com esse object (envolto em uma tryfinally , ou, um bloco using() ). Mas mesmo que você não se lembre de chamar Dispose() , o método finalizador no object deve estar chamando Dispose() para você.

Eu pensei que isso era um bom tratamento:

Cavando em IDisposable

e isto

Entendendo IDisposable

Não há sentido em tentar adivinhar o GC e suas estratégias de gerenciamento porque ele é auto-ajustável e opaco. Houve uma boa discussão sobre o funcionamento interno com Jeffrey Richter no Dot Net Rocks aqui: Jeffrey Richter no Windows Memory Model e o livro Richters CLR via C # capítulo 20 tem um ótimo tratamento:

Outro motivo para evitar definir objects como null quando você os conclui é que eles podem mantê-los vivos por mais tempo.

por exemplo

 void foo() { var someType = new SomeType(); someType.DoSomething(); // someType is now eligible for garbage collection // ... rest of method not using 'someType' ... } 

permitirá que o object referido por someType seja GC após a chamada para “DoSomething”, mas

 void foo() { var someType = new SomeType(); someType.DoSomething(); // someType is NOT eligible for garbage collection yet // because that variable is used at the end of the method // ... rest of method not using 'someType' ... someType = null; } 

às vezes pode manter o object vivo até o final do método. O JIT geralmente irá otimizar a atribuição para null , assim ambos os bits de código acabam sendo os mesmos.

Não, não anule objects. Você pode conferir http://codebetter.com/blogs/karlseguin/archive/2008/04/27/foundations-of-programming-pt-7-back-to-basics-memory.aspx para mais informações, mas definir as coisas para null não fará nada, exceto o seu código sujo.

Além disso:

 using(SomeObject object = new SomeObject()) { // do stuff with the object } // the object will be disposed of 

Em geral, não há necessidade de objects nulos após o uso, mas em alguns casos eu acho que é uma boa prática.

Se um object implementa IDisposable e é armazenado em um campo, acho bom anulá-lo, apenas para evitar o uso do object descartado. Os erros da seguinte espécie podem ser dolorosos:

 this.myField.Dispose(); // ... at some later time this.myField.DoSomething(); 

É bom anular o campo depois de descartá-lo e obter um NullPtrEx diretamente na linha onde o campo é usado novamente. Caso contrário, você pode se deparar com algum erro enigmático ao longo da linha (dependendo exatamente do que DoSomething faz).

As chances são de que o seu código não esteja estruturado de maneira suficiente se você sentir a necessidade de null variables.

Existem várias maneiras de limitar o escopo de uma variável:

Como mencionado por Steve Tranby

 using(SomeObject object = new SomeObject()) { // do stuff with the object } // the object will be disposed of 

Da mesma forma, você pode simplesmente usar chaves:

 { // Declare the variable and use it SomeObject object = new SomeObject() } // The variable is no longer available 

Eu acho que usar chaves sem qualquer “título” para realmente limpar o código e ajudar a torná-lo mais compreensível.

A única vez que você deve definir uma variável como nula é quando a variável não sai do escopo e você não precisa mais dos dados associados a ela. Caso contrário, não há necessidade.

Em geral, não há necessidade de definir como nulo. Mas suponha que você tenha uma funcionalidade de redefinição em sua class.

Então você pode fazer, porque você não quer chamar de dispor duas vezes, uma vez que alguns dos Dispose não podem ser implementados corretamente e lançar exceção System.ObjectDisposed.

 private void Reset() { if(_dataset != null) { _dataset.Dispose(); _dataset = null; } //..More such member variables like oracle connection etc. _oraConnection } 

Esse tipo de “não há necessidade de definir objects como nulo após o uso” não é totalmente preciso. Há momentos em que você precisa NULL a variável depois de descartá-lo.

Sim, você deve sempre chamar .Dispose() ou .Close() em qualquer coisa que tenha quando terminar. Seja identificadores de arquivos, conexões de database ou objects descartáveis.

Separado do que é o padrão muito prático de LazyLoad.

Digamos que eu tenha instanciado ObjA da class A Class A tem uma propriedade pública chamada PropB da class B

Internamente, o PropB usa a variável privada de _B e o padrão é null. Quando PropB.Get() é usado, ele verifica se _PropB é nulo e, se estiver, abre os resources necessários para instanciar um B em _PropB . Em seguida, retorna _PropB .

Para minha experiência, este é um truque realmente útil.

Onde a necessidade de null _PropB é se você redefinir ou alterar A de alguma forma que o conteúdo de _PropB fosse o filho dos valores anteriores de A , você precisaria Descartar AND null out _PropB para que LazyLoad pudesse redefinir para buscar o valor correto Se o código exigir isso.

Se você fizer apenas _PropB.Dispose() e logo após esperar que a verificação nula para LazyLoad seja bem-sucedida, ela não será nula e você verá dados obsoletos. De fato, você deve anulá-lo após Dispose() apenas para ter certeza.

Eu certamente gostaria que fosse de outra forma, mas eu tenho código agora exibindo esse comportamento após um Dispose() em um _PropB e fora da function de chamada que fez o Dispose (e, portanto, quase fora do escopo), o suporte privado ainda é ‘t null, e os dados obsoletos ainda estão lá.

Eventualmente, a propriedade eliminada será nula, mas isso não é determinista da minha perspectiva.

O principal motivo, como o dbkk alude, é que o contêiner pai ( ObjA com PropB ) está mantendo a instância de _PropB no escopo, apesar do Dispose() .

Existem alguns casos em que faz sentido as referências nulas. Por exemplo, quando você está escrevendo uma coleção – como uma fila de prioridades – e por seu contrato, você não deve manter esses objects vivos para o cliente depois que o cliente os remove da fila.

Mas esse tipo de coisa só importa em collections de longa duração. Se a fila não sobreviver ao final da function em que foi criada, então é muito menos importante.

Em geral, você realmente não deveria se incomodar. Deixe o compilador e o GC fazerem seus trabalhos para que você possa fazer o seu.

Dê uma olhada neste artigo também: http://www.codeproject.com/KB/cs/idisposable.aspx

Na maioria das vezes, definir um object como nulo não tem efeito. A única vez que você deve ter certeza disso é se estiver trabalhando com um “object grande”, que é maior que 84 K (como bitmaps).

Eu acredito que pelo design dos implementadores do GC, você não pode acelerar o GC com a anulação. Tenho certeza de que eles prefeririam que você não se preocupasse com a forma como / quando o GC é executado – trate-o como este Ser onipresente protegendo e vigiando por você … (arcos de cabeça baixa, levanta o punho para o céu) .. .

Pessoalmente, muitas vezes eu defino explicitamente as variables ​​como nulas quando termino com elas como uma forma de auto-documentação. Eu não declaro, uso e, em seguida, defino como nulo depois – eu anulo imediatamente depois que eles não são mais necessários. Estou dizendo, explicitamente: “Eu estou oficialmente feito com você … se foi …”

A anulação é necessária em uma linguagem GC? Não. É útil para o GC? Talvez sim, talvez não, não sei ao certo, pelo design eu realmente não posso controlá-lo, e independentemente da resposta de hoje com esta versão ou que, futuras implementações de GC poderiam mudar a resposta além do meu controle. Além disso, se / quando a anulação for otimizada, será um pouco mais do que um comentário sofisticado, se desejar.

Eu acho que se isso torna minha intenção mais clara para o próximo pobre idiota que segue meus passos, e se potencialmente ” pode ajudar GC às vezes, então vale a pena para mim. Principalmente me faz sentir limpo e arrumado, e Mongo gosta de se sentir limpo e arrumado. 🙂

Eu vejo isso da seguinte forma: Linguagens de programação existem para deixar as pessoas darem a outras pessoas uma idéia de intenção e um compilador uma solicitação de trabalho do que fazer – o compilador converte essa requisição em uma linguagem diferente (algumas vezes várias) para uma CPU – a (s) CPU (s) pode (m) dar a entender que linguagem você usou, suas configurações de tabulação, comentários, ênfases estilísticas, nomes de variables ​​etc. – uma CPU é toda sobre o stream de bits que informa o que registra e codifica. Muitas coisas escritas em código não são convertidas no que é consumido pela CPU na sequência que especificamos. Nosso C, C ++, C #, Lisp, Babel, montador ou o que quer que seja teoria e não realidade, escrito como uma declaração de trabalho. O que você vê não é o que você recebe, sim, mesmo em linguagem assembler.

Eu entendo a mentalidade de “coisas desnecessárias” (como linhas em branco) “não são nada além de ruído e confusão.” Essa foi eu mais cedo na minha carreira; Eu entendo totalmente isso. Neste momento, eu me inclino para o que torna o código mais claro. Não é como se eu estivesse adicionando 50 linhas de “ruído” aos meus programas – são algumas linhas aqui ou ali.

Existem exceções para qualquer regra. Em cenários com memory volátil, memory estática, condições de corrida, singletons, uso de dados “obsoletos” e todo esse tipo de apodrecimento, é diferente: você PRECISA gerenciar sua própria memory, bloqueando e anulando como a propósito, porque a memory não faz parte de o Universo GC – espero que todos entendam isso. O resto do tempo com linguagens GC’d é uma questão de estilo, em vez de necessidade ou um aumento de desempenho garantido.

No final do dia, certifique-se de entender o que é elegível para o GC e o que não é; bloquear, eliminar e anular adequadamente; encerar, encerar; inspire, expire; e para todo o resto eu digo: Se é bom, faça. Sua milhagem pode variar … como deveria …

Alguns objects supõem o método .dispose() que força o recurso a ser removido da memory.