c# - ¿Por qué deberíamos llamar a SuppressFinalize cuando no tenemos un destructor?
.net garbage-collection (5)
Aquí están los principales hechos.
1) Object.Finalize es lo que tu clase anula cuando tiene un Finalizer. El método del destructor de ~ TypeName () es solo una abreviatura de ''override Finalize ()'', etc.
2) Llama a GC.SuppressFinalize si está desechando recursos en su método de Disposición antes de la finalización (es decir, al salir de un bloque de uso, etc.). Si no tiene un Finalizer, entonces no necesita hacer esto. Si tiene un Finalizer, esto garantiza que el objeto se saque de la cola de Finalización (por lo que no desechamos las cosas dos veces, ya que el Finalizer generalmente también llama el método de Disposición)
3) Implementas un Finalizer como un mecanismo de "seguridad". Se garantiza que los finalizadores se ejecutarán (siempre que no se anule el CLR), por lo que le permiten asegurarse de que se limpie el código en caso de que no se haya llamado al método Dispose (tal vez el programador olvidó crear la instancia dentro de un "uso" bloque etc.
4) Los finalizadores son caros, ya que los tipos que tienen finalizadores no pueden ser recolectados en una colección de la Generación-0 (el más eficiente), y son promovidos a la Generación-1 con una referencia a ellos en la cola de F-Reachable, por lo que representan un Raíz GC. No es hasta que el GC realiza una recopilación de la Generación-1 que se llama al finalizador, y se liberan los recursos, por lo tanto, implemente los finalizadores solo cuando sea muy importante, y asegúrese de que los objetos que requieren la Finalización sean lo más pequeños posible, porque todos los objetos que pueden ser alcanzado por su objeto finalizable será promovido a la Generación-1 también.
Tengo pocas preguntas para las cuales no puedo obtener una respuesta adecuada.
1) ¿Por qué deberíamos llamar a SuppressFinalize en la función de Disposición cuando no tenemos un destructor?
2) La disposición y finalización se utilizan para liberar recursos antes de que se recoja la basura del objeto. Ya sea que se trate de un recurso administrado o no administrado, necesitamos liberarlo, entonces, ¿por qué necesitamos una condición dentro de la función de disposición, que dice "verdadero" cuando llamamos a esta función anulada desde IDisposable: desechamos y pasamos a falso cuando se llama desde una finalización?
Vea el siguiente código que copié de la red.
class Test : IDisposable
{
private bool isDisposed = false;
~Test()
{
Dispose(false);
}
protected void Dispose(bool disposing)
{
if (disposing)
{
// Code to dispose the managed resources of the class
}
// Code to dispose the un-managed resources of the class
isDisposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
¿Qué sucede si elimino la función de Desecho protegida booleana e implemento la siguiente manera?
class Test : IDisposable
{
private bool isDisposed = false;
~Test()
{
Dispose();
}
public void Dispose()
{
// Code to dispose the managed resources of the class
// Code to dispose the un-managed resources of the class
isDisposed = true;
// Call this since we have a destructor . what if , if we don''t have one
GC.SuppressFinalize(this);
}
}
Mantenga la primera versión, es más segura y es la implementación correcta del patrón de disposición.
Al llamar a
SuppressFinalize
le dice al GC que ha hecho toda la destrucción / eliminación (de los recursos en poder de su clase) y que no es necesario que llame al destructor.Necesita la prueba en caso de que el código que usa su clase ya haya llamado a desechar y no debe decirle al GC que vuelva a desechar.
Consulte this documento de MSDN (los métodos de eliminación deben llamar a SuppressFinalize).
Me estoy arriesgando, pero ... la mayoría de las personas no necesitan el patrón de desecho en toda regla. Está diseñado para ser sólido ante el acceso directo a recursos no administrados (generalmente a través de IntPtr
) y ante la herencia. La mayoría de las veces, ninguno de estos es realmente requerido.
Si solo tiene una referencia a otra cosa que implementa IDisposable
, es casi seguro que no necesita un finalizador; lo que sea que contenga el recurso directamente es responsable de lidiar con eso. Te puedes conformar con algo como esto:
public sealed class Foo : IDisposable
{
private bool disposed;
private FileStream stream;
// Other code
public void Dispose()
{
if (disposed)
{
return;
}
stream.Dispose();
disposed = true;
}
}
Tenga en cuenta que esto no es seguro para subprocesos, pero que probablemente no será un problema.
Al no tener que preocuparse por la posibilidad de que las subclases tengan recursos directamente, no es necesario suprimir el finalizador (porque no hay uno), y tampoco es necesario que las subclases personalicen la eliminación. La vida es más simple sin herencia.
Si necesita permitir una herencia incontrolada (es decir, no está dispuesto a apostar a que las subclases tendrán necesidades muy particulares), entonces debe elegir el patrón completo.
Tenga en cuenta que con SafeHandle
desde .NET 2.0, es aún más raro que necesite su propio finalizador que en .NET 1.1.
Para abordar su punto sobre por qué hay una bandera de disposing
en primer lugar: si está ejecutando dentro de un finalizador, es posible que otros objetos a los que se refiere ya se hayan finalizado. Debe dejar que se limpien a sí mismos, y solo debe limpiar los recursos que posee directamente .
Siempre debe llamar a SuppressFinalize () porque podría tener (o en el futuro) una clase derivada que implementa un Finalizer, en cuyo caso lo necesita.
Digamos que tiene una clase base que no tiene un Finalizer y decidió no llamar a SuppressFinalize (). Luego, 3 meses después, agrega una clase derivada que agrega un Finalizer. Es probable que olvide ir a la clase base y agregar una llamada a SuppressFinalize (). No hay daño en llamarlo si no hay finalizador.
Mi patrón IDisponible sugerido se publica aquí: Cómo implementar correctamente el Patrón de Disposición
1. Responde a la primera pregunta
Básicamente, no tiene que llamar al método SuppressFinalize si su clase no tiene un método de finalización (Destructor). Creo que la gente llama SupressFinalize incluso cuando no hay un método de finalización debido a la falta de conocimiento.
2. Responde la segunda pregunta
El propósito del método Finalize es liberar recursos no administrados. Lo más importante que se debe entender es que se llama al método Finalizar cuando el objeto está en la cola de finalización. El recolector de basura recoge todos los objetos que pueden ser destruidos. El recolector de basura agrega objetos que tienen finalización a la cola de finalización antes de destruir. Hay otro proceso en segundo plano .net para llamar al método de finalización de los objetos que están en la cola de finalización. En el momento en que el proceso en segundo plano ejecute el método finalize, la otra referencia administrada de ese objeto en particular puede haber sido destruida. Porque no hay un orden específico cuando se trata de la ejecución de finalización. Por lo tanto, el Patrón de Disposición quiere asegurarse de que el método de finalización no intente acceder a los objetos administrados. Es por eso que los objetos administrados van dentro de la cláusula "if (disposing)", que es inaccesible para el método de finalización.