2008-08-05 6 views
170

¿Debería establecer todos los objetos en null (Nothing en VB.NET) una vez que haya terminado con ellos?Configuración de objetos a nulo/nada después de su uso en .NET

Entiendo que en .NET es esencial disponer de instancias de objetos que implementen la interfaz IDisposable para liberar algunos recursos, aunque el objeto todavía puede ser algo después de que se elimine (de ahí la propiedad isDisposed en formularios), por lo Supongo que todavía puede residir en la memoria o al menos en parte?

También sé que cuando un objeto se sale del alcance, se marca para la recolección listo para la siguiente pasada del recolector de basura (aunque esto puede llevar tiempo).

Así que con esto en mente, configurarlo en null ¿acelerar el sistema liberando la memoria, ya que no tiene que resolverse que ya no está en el alcance y son malos efectos secundarios?

artículos de MSDN no hacen esto en los ejemplos y en la actualidad lo hacen como yo no puede ver el daño. Sin embargo, he encontrado una mezcla de opiniones, por lo que cualquier comentario es útil.

+3

+1 gran pregunta. ¿Alguien sabe una circunstancia bajo la cual el compilador optimizará la tarea por completo? es decir, si alguien miró a MSIL bajo circunstancias diferentes y anotó IL para establecer un objeto como nulo (o la falta del mismo). –

Respuesta

66

Karl es absolutamente correcto, no hay necesidad de configurar los objetos a nulo después de su uso. Si un objeto implementa IDisposable, basta con que se llama a IDisposable.Dispose() cuando haya terminado con ese objeto (envuelto en un try .. finally, o bien, un bloque using()). Pero incluso si no recuerda llamar al Dispose(), el método del finalizador en el objeto debería llamarle al Dispose().

Me pareció que era un buen trato:

Digging into IDisposable

y esto

Understanding IDisposable

No hay ninguna razón para tratar de adivinar cuál sería la GC y sus estrategias de gerencia, porque es autoajustable y opaco. Había una buena discusión sobre el funcionamiento interno con Jeffrey Richter en rocas Dot Net aquí: Jeffrey Richter on the Windows Memory Model y Richter libro CLR via C# capítulo 20 tiene un gran tratamiento:

+5

La regla sobre no establecer como nulo no es "difícil y rápido" ... si el objeto se pone en el montón de objetos grandes (tamaño es> 85K) ayudará al GC si establece el objeto como nulo cuando está hecho de usarlo –

+0

Acepto de forma limitada, pero a menos que esté empezando a experimentar la presión de la memoria, no veo la necesidad de 'optimizar prematuramente' al poner los objetos a nulo después de su uso. – Kev

+18

Todo este asunto de "no optimizar prematuramente" suena más como "Prefiera la lentitud y no se preocupe porque las CPU son cada vez más rápidas y las aplicaciones CRUD no necesitan velocidad de todos modos". Sin embargo, puedo ser yo. :) – BobbyShaftoe

-1

Algunos objetos suponen que el método .dispose() obliga al recurso a eliminarse de la memoria.

+10

No, no lo hace; Dispose() does * not * collect the object - se usa para realizar una limpieza determinista, normalmente liberando recursos no administrados. –

+1

Teniendo en cuenta que el determinismo se aplica solo a los recursos administrados, no a los no administrados (es decir, a la memoria) – nicodemus13

4

La única vez que debe establecer que una variable sea nula es cuando la variable no sale del ámbito y ya no necesita los datos asociados a ella. De lo contrario, no hay necesidad.

+2

Eso es cierto, pero también significa que probablemente deba refactorizar su código. No creo que alguna vez haya necesitado declarar una variable fuera del alcance previsto. –

+1

Si se entiende que "variable" incluye campos de objeto, entonces esta respuesta tiene mucho sentido. En el caso en que "variable" significa solo "variable local" (de un método), entonces probablemente estamos hablando de casos de nicho aquí (por ejemplo, un método que se ejecuta durante un lapso de tiempo mucho más prolongado de lo habitual). – stakx

7

también:

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

hay algunos casos donde tiene sentido para anular las referencias. Por ejemplo, cuando está escribiendo una colección, como una cola de prioridad, y según su contrato, no debe mantener esos objetos vivos para el cliente una vez que el cliente los haya eliminado de la cola.

Pero este tipo de cosas solo importa en colecciones de larga duración. Si la cola no va a sobrevivir al final de la función en la que se creó, entonces importa mucho menos.

En general, realmente no debería molestarse. Deje que el compilador y GC hagan su trabajo para que pueda hacer el suyo.

8

En general, no hay necesidad para anular objetos después de su uso, pero en algunos casos, creo que es una buena práctica.

Si un objeto implementa IDisposable y se almacena en un campo, creo que es bueno anularlo, solo para evitar el uso del objeto eliminado. Los errores de la siguiente clase pueden ser dolorosos:

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

Es bueno para anular el campo después de la eliminación, y obtener una NullPtrEx a la derecha en la línea donde el campo se utiliza de nuevo. De lo contrario, podría encontrarse con algún error críptico en la línea (dependiendo de lo que DoSomething haga).

+8

Bueno, un objeto eliminado debería arrojar ObjectDisposedException si ya ha sido eliminado. Esto, hasta donde yo sé, requiere un código estándar por todas partes, pero de todos modos, Disposed es un paradigma mal pensado de todos modos. – nicodemus13

+2

Ctrl + F para '.Dispose()'. Si lo encuentra, no está usando IDisposable correctamente. El único uso para un objeto desechable debe estar en los límites de un bloque de uso. Y después del bloque de uso, ya no tienes acceso a 'myField'. Y dentro del bloque de uso, no es necesario configurar 'nulo', el bloque de uso eliminará el objeto por usted. – Suamere

6

Es probable que su código no esté lo suficientemente estructurado si siente la necesidad de null variables.

Hay un número de maneras de limitar el alcance de una variable:

Como se ha mencionado por Steve Tranby

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

Del mismo modo, puede simplemente usar llaves:

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

Me parece que el uso de corchetes sin ningún "encabezado" para realmente limpiar el código y ayudar a que sea más comprensible.

+0

Intenté usar ámbitos locales personalizados una vez (la mayoría es una smarta $$). La compañía estalló. – Suamere

+0

En otra nota: Esto se debe a que el compilador de C# encontrará variables de ámbito local que implementen IDisposable, y llamará .Dispose (MOST Of the time) cuando termine su alcance. Sin embargo ... Las conexiones SQL son un gran momento cuando .Dispose() nunca está optimizado. Hay algunos tipos que requieren atención explícita, por lo que siempre hago cosas explícitamente para no ser mordido. – Suamere

33

Otra razón para evitar el establecimiento de objetos a nulo cuando haya terminado con ellos es que realmente puede mantenerlos vivos durante más tiempo.

p. Ej.

void foo() 
{ 
    var someType = new SomeType(); 
    someType.DoSomething(); 
    // someType is now eligible for garbage collection   

    // ... rest of method not using 'someType' ... 
} 

permitirá que el objeto referido por SomeType que se GC'd después de la llamada a "HacerAlgo", pero

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; 
} 

veces puede mantener el objeto con vida hasta el final del método. El JIT will usually optimized away the assignment to null, por lo que ambos bits de código terminan siendo el mismo.

+0

Ese es un punto interesante. Siempre pensé que los objetos no saldrían del alcance hasta que se completara el método en el que se encuentran. A menos, por supuesto, que el objeto tenga un alcance dentro de un bloque de Uso o esté explícitamente establecido en Nada o nulo. –

+1

La forma preferida de asegurarse de que permanezcan con vida es usar 'GC.KeepAlive (someType);' Ver http://ericlippert.com/2013/06/10/construction-destruction/ – NotMe

1

Tome un vistazo a este artículo así: http://www.codeproject.com/KB/cs/idisposable.aspx

En su mayor parte, el establecimiento de un objeto que no tiene ningún efecto nulo. La única vez que debe asegurarse de hacerlo es si está trabajando con un "objeto grande", que es uno de tamaño superior a 84K (como mapas de bits).

3

este tipo de "no hay necesidad de establecer objetos para anular después de su uso" no es del todo exacto. Hay ocasiones en que necesita NULL la variable después de eliminarla.

Sí, SIEMPRE debe llamar al .Dispose() o .Close() en cualquier cosa que la tenga cuando haya terminado. Ya sean manejadores de archivos, conexiones de bases de datos u objetos desechables.

Separado de ese es el patrón muy práctico de LazyLoad.

Di que tengo y instanciado ObjA de class A. Class A tiene una propiedad pública llamada PropB de class B.

Internamente, PropB usa la variable privada de _B y se predetermina a nulo. Cuando se utiliza PropB.Get(), comprueba si _PropB es nulo y, si lo está, abre los recursos necesarios para crear una instancia de B en _PropB. A continuación, devuelve _PropB.

Para mi experiencia, este es un truco realmente útil.

Cuando la necesidad de nula viene en es si reinicia o cambiar un de alguna manera que el contenido de _PropB eran el niño de los valores anteriores de A, necesitará para disponer y nulos a cabo _PropB por lo LazyLoad puede restablecer a busque el valor correcto SI el código lo requiere.

Si solo hace _PropB.Dispose() y poco después espera que la verificación nula de LazyLoad sea exitosa, no será nulo, y estará buscando datos obsoletos. En efecto, debe anularlo después de Dispose() solo para estar seguro.

seguro de que me gustaría que fuera de otra forma, pero tengo ahora mismo código que exhiben este comportamiento después de una Dispose() en un _PropB y fuera de la función de llamada que hizo el Desechar (y por lo tanto casi fuera de alcance), el puntal privada todavía no es nulo, y los datos obsoletos todavía están allí.

Eventualmente, la propiedad eliminada se anulará, pero eso no ha sido determinista desde mi punto de vista.

La razón de la base, como dbkk alude es que el contenedor principal (ObjA con PropB) es mantener la instancia de _PropB en su alcance, a pesar de la Dispose().

+0

Buen ejemplo que muestra cómo configurar null manualmente significa un error más fatal para la persona que llama que es algo bueno. – rolls

5

En general, no es necesario establecer el valor nulo. Pero supongamos que tiene una funcionalidad de reinicio en su clase.

Entonces puede que lo haga, porque no desea llamar a disponer dos veces, ya que parte del Dispose puede no implementarse correctamente y arrojar la excepción System.ObjectDisposed.

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

creo que por el diseño de los ejecutores de GC, no se puede velocidad GC con anulación. Estoy seguro de que preferirían que no te preocupes por cómo/cuando se ejecuta GC - trátalo como este omnipresente Siendo protegiéndolo y mirándolo por todas partes ... (inclina la cabeza hacia abajo, levanta el puño hacia el cielo) ...

Personalmente, a menudo establezco explícitamente las variables como nulas cuando termino con ellas como una forma de auto-documentación. No declaro, uso, y luego configuro nulo más tarde; anulo inmediatamente después de que ya no se necesitan. Estoy diciendo, explícitamente, "Ya terminé oficialmente contigo ... se habrá ido ..."

¿Es necesario anular en un lenguaje GC? No. ¿Es útil para el GC? Tal vez sí, tal vez no, no estoy seguro, por diseño realmente no puedo controlarlo, y sin importar la respuesta de hoy con esta versión o esa, las futuras implementaciones de GC podrían cambiar la respuesta más allá de mi control. Además, si/nulling se optimiza, es un poco más que un comentario de lujo si se quiere.

Me imagino que si mi intención es más clara para el siguiente tonto pobre que sigue mis pasos, y si "podría" potencialmente ayudar a GC a veces, entonces vale la pena para mí. En general, me hace sentir limpio y ordenado, y a Mongo le gusta sentirse ordenado y claro. :)

Lo veo así: los lenguajes de programación existen para permitir que las personas den a otras personas una idea de intento y un compilador una solicitud de trabajo de qué hacer; el compilador convierte esa solicitud a un idioma diferente (a veces varios) para una CPU: la (s) CPU (s) podría (n) indicar el lenguaje que usó, la configuración de pestañas, comentarios, énfasis estilístico, nombres de variables, etc. - una CPU tiene que ver con el flujo de bits que le dice qué registros y códigos de operación y ubicaciones de memoria para twiddle. Muchas cosas escritas en el código no se convierten en lo que consume la CPU en la secuencia que especificamos. Nuestro C, C++, C#, Lisp, Babel, ensamblador o lo que sea es teoría en lugar de realidad, escrito como una declaración de trabajo. Lo que ves no es lo que obtienes, sí, incluso en lenguaje ensamblador.

Entiendo la mentalidad de "cosas innecesarias" (como líneas en blanco) "no son más que ruido y desordenar el código". Ese fui yo al principio de mi carrera; Lo entiendo totalmente. En esta coyuntura me inclino hacia lo que hace que el código sea más claro. No es como si estuviera agregando incluso 50 líneas de "ruido" a mis programas, son algunas líneas aquí o allá.

Existen excepciones a cualquier regla. En escenarios con memoria volátil, memoria estática, condiciones de carrera, singletons, uso de datos "obsoletos" y todo ese tipo de podredumbre, eso es diferente: NECESITA administrar su propia memoria, bloqueando y anulando como apropos porque la memoria no es parte de el Universo GC'd - ojalá todos entiendan eso. El resto del tiempo con los lenguajes de GC es una cuestión de estilo en lugar de necesidad o un aumento de rendimiento garantizado.

Al final del día, asegúrese de entender qué es elegible para GC y qué no; bloquear, desechar y anular apropiadamente; encerar, encerar; inhala exhala; y para todo lo demás, digo: si se siente bien, hazlo. Su kilometraje puede variar ... como debería ...

Cuestiones relacionadas