En .NET, ¿en qué circunstancias debo usar GC.SuppressFinalize()
?
¿Qué ventaja (s) me da el uso de este método?
En .NET, ¿en qué circunstancias debo usar GC.SuppressFinalize()
?
¿Qué ventaja (s) me da el uso de este método?
Respuestas:
SuppressFinalize
solo debe ser llamado por una clase que tenga un finalizador. Informa al recolector de basura (GC) que el this
objeto se limpió por completo.
El IDisposable
patrón recomendado cuando tienes un finalizador es:
public class MyClass : IDisposable
{
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
// called via myClass.Dispose().
// OK to use any private object references
}
// Release unmanaged resources.
// Set large fields to null.
disposed = true;
}
}
public void Dispose() // Implement IDisposable
{
Dispose(true);
GC.SuppressFinalize(this);
}
~MyClass() // the finalizer
{
Dispose(false);
}
}
Normalmente, el CLR mantiene pestañas en los objetos con un finalizador cuando se crean (haciéndolos más caros de crear). SuppressFinalize
le dice al GC que el objeto se limpió correctamente y no necesita ir a la cola del finalizador. Parece un destructor de C ++, pero no actúa como uno.
La SuppressFinalize
optimización no es trivial, ya que sus objetos pueden vivir mucho tiempo esperando en la cola del finalizador. No caigas en la tentación de recurrir SuppressFinalize
a otros objetos. Ese es un defecto grave esperando a suceder.
Las pautas de diseño nos informan que un finalizador no es necesario si su objeto se implementa IDisposable
, pero si tiene un finalizador, debe implementarlo IDisposable
para permitir la limpieza determinista de su clase.
La mayoría de las veces deberías poder escaparte IDisposable
para limpiar recursos. Solo debe necesitar un finalizador cuando su objeto retiene recursos no administrados y debe garantizar que esos recursos se limpien.
Nota: A veces los codificadores agregarán un finalizador para depurar compilaciones de sus propias IDisposable
clases para probar que el código ha dispuesto su IDisposable
objeto correctamente.
public void Dispose() // Implement IDisposable
{
Dispose(true);
#if DEBUG
GC.SuppressFinalize(this);
#endif
}
#if DEBUG
~MyClass() // the finalizer
{
Dispose(false);
}
#endif
IDisposable
no es sealed
, entonces debe incluir la llamada a GC.SuppressFinalize(this)
incluso si no incluye un finalizador definido por el usuario . Esto es necesario para garantizar una semántica adecuada para los tipos derivados que agregan un finalizador definido por el usuario pero solo anulan el Dispose(bool)
método protegido .
sealed
como lo menciona @SamHarwell es importante, para las clases derivadas. CodeAnalysis da como resultado ca1816 + ca1063 cuando la clase no está sellada, pero las clases selladas están bien sin ella SuppressFinalize
.
SupressFinalize
le dice al sistema que cualquier trabajo que se haya realizado en el finalizador ya se ha realizado, por lo que no es necesario llamar al finalizador. De los documentos de .NET:
Los objetos que implementan la interfaz IDisposable pueden llamar a este método desde el método IDisposable.Dispose para evitar que el recolector de basura llame a Object.Finalize en un objeto que no lo requiere.
En general, la mayoría de los Dispose()
métodos deberían poder llamar GC.SupressFinalize()
, ya que deberían limpiar todo lo que se limpiaría en el finalizador.
SupressFinalize
es solo algo que proporciona una optimización que permite al sistema no molestarse en poner el objeto en el subproceso finalizador. Un Dispose()
finalizador / escrito correctamente debería funcionar correctamente con o sin una llamada a GC.SupressFinalize()
.
Ese método debe invocarse en el Dispose
método de los objetos que implementan IDisposable
, de esta manera el GC no llamaría al finalizador otra vez si alguien llama al Dispose
método.
Dispose(true);
GC.SuppressFinalize(this);
Si el objeto tiene finalizador, .net coloca una referencia en la cola de finalización.
Como tenemos una llamada Dispose(ture)
, borra el objeto, por lo que no necesitamos la cola de finalización para hacer este trabajo.
Entonces llame a GC.SuppressFinalize(this)
eliminar referencia en la cola de finalización.
Si una clase, o cualquier derivado de él, podrían sostener la última referencia en vivo a un objeto con un finalizador, a continuación, ya sea GC.SuppressFinalize(this)
o GC.KeepAlive(this)
debería ser llamado en el objeto después de cualquier operación que pueda estar afectado adversamente por que finalizador, asegurando así que el won finalizador No ejecute hasta después de que se complete esa operación.
El costo de GC.KeepAlive()
y GC.SuppressFinalize(this)
es esencialmente el mismo en cualquier clase que no tenga un finalizador, y las clases que sí tienen finalizadores generalmente deberían llamar GC.SuppressFinalize(this)
, por lo que usar la última función como el último paso Dispose()
puede no ser siempre necesario, pero no lo será. estar equivocado.