generate - params comments c#
Eliminar, ¿cuándo se llama? (7)
Quiero escribir una clase que sea sencilla y muy fácil de usar, para asegurarme de que se limpien todos los recursos posibles. No quiero responsabilizar al usuario de mi clase.
No puedes hacer eso. La administración de la memoria simplemente no está diseñada para acomodar a desarrolladores desorientados.
El patrón IDisposable está destinado a los desarrolladores como una forma de contar un objeto cuando ya terminaron, en lugar de tener que tratar de resolverlo utilizando elementos como el recuento de referencias.
Puede utilizar el Finalizador como alternativa para los usuarios que no eliminan los objetos correctamente, pero no funciona bien como el método principal para limpiar objetos. Para trabajar sin problemas, los objetos se deben desechar de forma adecuada, de modo que no sea necesario llamar al Finalizer, que es más costoso.
Considera el siguiente código:
namespace DisposeTest
{
using System;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Calling Test");
Test();
Console.WriteLine("Call to Test done");
}
static void Test()
{
DisposeImplementation di = new DisposeImplementation();
}
}
internal class DisposeImplementation : IDisposable
{
~DisposeImplementation()
{
Console.WriteLine("~ in DisposeImplementation instance called");
}
public void Dispose()
{
Console.WriteLine("Dispose in DisposeImplementation instance called");
}
}
}
El Dispose simplemente nunca se llama, incluso si pongo un bucle de espera después de la Test();
invocación. Así que eso apesta. Quiero escribir una clase que sea sencilla y muy fácil de usar, para asegurarme de que se limpien todos los recursos posibles. No quiero responsabilizar al usuario de mi clase.
Posible solución: usar using
, o llamar a Deshacerme (básicamente lo mismo). ¿Puedo forzar al usuario a usar un uso? ¿O puedo forzar la disposición a ser llamado?
Llamar a GC.Collect();
después de la Test();
tampoco funciona
Poner di
a null
no invoca a Dispose. El Deconstructor FUNCIONA, por lo que el objeto se deconstruye cuando sale de Test()
Ok chicos, está claro ahora!
¡Gracias a todos por sus respuestas o comentarios! ¡Agregaré una advertencia en el comentario!
Dispose no se llama automáticamente. Debe usar una cláusula de uso para ajustar el uso o llamarlo manualmente.
Ver http://msdn.microsoft.com/en-us/library/aa664736%28VS.71%29.aspx
Y solo para apropiarse de otra idea que pueda tener: no puede llamar a dispose
del destructor ... Intenté esto hace algún tiempo en un proyecto.
Se deben hacer un par de puntos importantes para abordar la pregunta del OP:
- .NET GC no es determinista (es decir, nunca se sabe ni se debe depender de cuándo sucede)
- Dispose nunca es llamado por .NET Framework; debe llamarlo manualmente, preferiblemente envolviendo su creación en un bloque
using()
. - Establecer explícitamente un objeto desechable como nulo sin llamar a Dispose () sobre él es algo malo de hacer. Lo que sucede es que establece explícitamente los objetos "raíz de referencia" en nulo. Esto significa que no puede llamar a Dispose más tarde Y, lo que es más importante, envía el objeto a GC Finalization Queue for Finalization. Causar la finalización por malas prácticas de programación debe evitarse a toda costa.
Finalizador: algunos desarrolladores se refieren a él como un destructor. Y, de hecho, incluso se lo denomina Destructor en la especificación de lenguaje C # 4.0 (sección 1.6.7.6) y en versiones anteriores de la especificación ECMA-334 actual . Afortunadamente, la 4ª Edición (junio de 2006) define correctamente los Finalizadores en la Sección 8.7.9 e intenta aclarar la confusión entre los dos en la Sección 17.12. Cabe señalar que existen diferencias internas importantes (no es necesario entrar en detalles sangrientos) entre lo que tradicionalmente se conoce como destructor y Destructor / Finalizer en .NET Framework.
- Si hay un Finalizer presente, el .NET Framework lo
GC.SuppressFinalize()
si y solo si no se llama aGC.SuppressFinalize()
. - NUNCA debes llamar explícitamente a un finalizador. Afortunadamente, C # no permitirá explícitamente esto (no sé sobre otros idiomas); aunque puede forzarse llamando a
GC.Collect(2)
para la 2ª generación del GC.
Finalización: la finalización es la forma del .NET Framework de lidiar con la limpieza y la liberación de recursos "elegantes".
- Solo ocurre cuando hay objetos en la cola de finalización.
- Solo ocurre cuando se produce una recolección de basura para Gen2 (que es aproximadamente 1 de cada 100 colecciones para una aplicación .NET bien escrita).
- Hasta e incluyendo .NET 4, hay una única cadena de finalización. Si este hilo se bloquea por alguna razón, su aplicación está jodida.
- Escribir el código de finalización correcto y seguro no es trivial y los errores pueden hacerse con bastante facilidad (es decir, accidentalmente permitiendo que se arrojen excepciones desde el Finalizer, permitiendo dependencias de otros objetos que ya podrían estar finalizados, etc.)
Si bien esta es, sin duda, más información que usted solicitó, proporciona información general sobre cómo funcionan las cosas y por qué funcionan de la manera en que lo hacen. Algunas personas argumentarán que no deberían tener que preocuparse por administrar la memoria y los recursos en .NET, pero eso no cambia el hecho de que debe hacerse, y no veo que eso desaparezca en el futuro cercano.
Se supone que debes deshacerte de ti mismo, ya sea llamando al método Dispose
o usando using
. Recuerde, ¡no es un deconstructor!
Si no puede confiar en que los usuarios de su clase se deshagan de los recursos de forma adecuada, es probable que se equivoquen de otras maneras.
Tendrá que llamar a Dispose
explícitamente o envolviendo el objeto en una instrucción using
. Ejemplo:
using (var di = new DisposeImplementation())
{
}
Posible solución: usar usando, o llamar a Deshacerme (básicamente lo mismo).
Usar es lo mismo que llamar a Dispose
dentro de un bloque finally
.
Todas las respuestas son (más o menos) correctas, he aquí un ejemplo:
static void Test()
{
using (DisposeImplementation di = new DisposeImplementation())
{
// Do stuff with di
}
}
Llamar manualmente a Dispose
también funcionará, pero la ventaja de using
enunciado es que el objeto también se eliminará cuando salgas del bloque de control porque se produce una excepción.
Podría agregar un finalizador que maneje el recurso dispuesto en caso de que alguien "olvide" usar la interfaz IDisposable:
public class DisposeImplementation : IDisposable
{
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// get rid of managed resources
}
// get rid of unmanaged resources
}
~DisposeImplementation()
{
Dispose(false);
}
}
Vea esta pregunta para obtener información adicional. Sin embargo, esto es solo una compensación para las personas que no usan tu clase correctamente :) Te sugiero que agregues una gran llamada Debug.Fail()
al finalizador, para advertir al desarrollador de su error.
Si elige implementar el patrón, verá que GC.Collect()
activará la eliminación.
Use esto como un patrón / plantilla para sus clases
public class MyClass : IDisposable
{
private bool disposed = false;
// Implement IDisposable.
// Do not make this method virtual.
// A derived class should not be able to override this method.
public void Dispose()
{
Dispose(true);
// This object will be cleaned up by the Dispose method.
// Therefore, you should call GC.SupressFinalize to
// take this object off the finalization queue
// and prevent finalization code for this object
// from executing a second time.
GC.SuppressFinalize(this);
}
// Dispose(bool disposing) executes in two distinct scenarios.
// If disposing equals true, the method has been called directly
// or indirectly by a user''s code. Managed and unmanaged resources
// can be disposed.
// If disposing equals false, the method has been called by the
// runtime from inside the finalizer and you should not reference
// other objects. Only unmanaged resources can be disposed.
private void Dispose(bool disposing)
{
// Check to see if Dispose has already been called.
if (!this.disposed)
{
// If disposing equals true, dispose all managed
// and unmanaged resources.
if (disposing)
{
// Dispose managed resources.
......
}
// Call the appropriate methods to clean up
// unmanaged resources here.
// If disposing is false,
// only the following code is executed.
...........................
// Note disposing has been done.
disposed = true;
}
}
// Use C# destructor syntax for finalization code.
// This destructor will run only if the Dispose method
// does not get called.
// It gives your base class the opportunity to finalize.
// Do not provide destructors in types derived from this class.
~MyClass()
{
// Do not re-create Dispose clean-up code here.
// Calling Dispose(false) is optimal in terms of
// readability and maintainability.
Dispose(false);
}
}
Y, por supuesto, como lo mencionaron otros, no se olvide de using(...){}
bloque.