He visto algunas preguntas sobre finalizadores e IDisposable, stackoverflow también debería tener algo sobre GC.SupressFinalize y referencias débiles
Sam Saffron
No creo que las referencias débiles hagan mucho con respecto a los finalizadores, tal vez debería publicar una pregunta más directa sobre ellos.
Michael Burr
Yerp, tenía la intención de publicar una pregunta por separado sobre las referencias débiles, todo esto puede unirse cuando construyes grupos de objetos. También debería hacer una pregunta sobre la recuperación de objetos ala ReRegisterForFinalize
Sam Saffron
Respuestas:
296
SuppressFinalizesolo debe ser llamado por una clase que tenga un finalizador. Informa al recolector de basura (GC) que el thisobjeto se limpió por completo.
El IDisposablepatrón recomendado cuando tienes un finalizador es:
publicclassMyClass:IDisposable{privatebool disposed =false;protectedvirtualvoidDispose(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;}}publicvoidDispose()// 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). SuppressFinalizele 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 SuppressFinalizeoptimizació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 SuppressFinalizea 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 IDisposablepara permitir la limpieza determinista de su clase.
La mayoría de las veces deberías poder escaparte IDisposablepara 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 IDisposableclases para probar que el código ha dispuesto su IDisposableobjeto correctamente.
publicvoidDispose()// Implement IDisposable{Dispose(true);#if DEBUG
GC.SuppressFinalize(this);#endif}#if DEBUG~MyClass()// the finalizer{Dispose(false);}#endif
En el primer fragmento de código, solo publico cómo se ve el patrón recomendado de IDisposable + finalizador. El código de depuración es bueno, pero puede distraer. .. Solo puedo recomendar evitar finalizadores, excepto para las clases que tienen recursos no administrados. Escribir código finalizador seguro no es trivial.
Robert Paulson
1
Hola, ¿por qué necesitamos llamar a dispose con false como parámetro del finalizador? ¿Qué pasa si nunca se llamó a la eliminación y luego no se eliminará? ¿Qué pasa si solo verificamos si el objeto ha sido eliminado o no y hacemos la limpieza real?
Dreamer
3
@Dreamer: depende de su implementación. En general, desea saber si el finalizador llama a Dispose versus la implementación IDisposable.Dispose (). Si se llama desde el finalizador, debe asumir que las referencias privadas ya no son válidas y que realmente no puede hacer mucho. Sin embargo, si se llama desde IDisposable.Dispose (), sabrá que las referencias siguen siendo válidas.
Robert Paulson
32
Si la implementación de la clase IDisposableno 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 .
Sam Harwell
1
No ser sealedcomo 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.
guiones el
38
SupressFinalizele 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.
SupressFinalizees 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 Disposemétodo de los objetos que implementan IDisposable, de esta manera el GC no llamaría al finalizador otra vez si alguien llama al Disposemétodo.
Creo que "Debe" está mal, ni siquiera "debería". Es solo que en algunos escenarios, puede eliminar la sobrecarga de poner en cola / finalizar el objeto.
Básico
1
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.
Respuestas:
SuppressFinalize
solo debe ser llamado por una clase que tenga un finalizador. Informa al recolector de basura (GC) que elthis
objeto se limpió por completo.El
IDisposable
patrón recomendado cuando tienes un finalizador es: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 recurrirSuppressFinalize
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 implementarloIDisposable
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 suIDisposable
objeto correctamente.fuente
IDisposable
no essealed
, entonces debe incluir la llamada aGC.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 elDispose(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 ellaSuppressFinalize
.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:En general, la mayoría de los
Dispose()
métodos deberían poder llamarGC.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. UnDispose()
finalizador / escrito correctamente debería funcionar correctamente con o sin una llamada aGC.SupressFinalize()
.fuente
Ese método debe invocarse en el
Dispose
método de los objetos que implementanIDisposable
, de esta manera el GC no llamaría al finalizador otra vez si alguien llama alDispose
método.Ver: Método GC.SuppressFinalize (Object) - Microsoft Docs
fuente
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.fuente
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)
oGC.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()
yGC.SuppressFinalize(this)
es esencialmente el mismo en cualquier clase que no tenga un finalizador, y las clases que sí tienen finalizadores generalmente deberían llamarGC.SuppressFinalize(this)
, por lo que usar la última función como el último pasoDispose()
puede no ser siempre necesario, pero no lo será. estar equivocado.fuente