¿Cuándo debo crear un destructor?


185

Por ejemplo:

public class Person
{
    public Person()
    {
    }

    ~Person()
    {
    }
}

¿Cuándo debo crear manualmente un destructor? ¿Cuándo has necesitado crear un destructor?


1
El lenguaje C # llama a estos "destructores", pero la mayoría de la gente los llama "finalizadores" ya que ese es su nombre .NET y reduce la confusión con los destructores de C ++ (que son bastante diferentes). Cómo implementar IDisposable y Finalizadores: 3 reglas fáciles
Stephen Cleary

55
Cuando te sientes imprudente.
capdragon

32
Creo que el teclado de TomTom no funcionaba bien. El bloqueo de mayúsculas se activaba y desactivaba esporádicamente. Extraño.
Jeff LaFay


Terminé usando un destructor como ayuda para la depuración basada en la sugerencia de Greg Beech: stackoverflow.com/questions/3832911/…
Brian

Respuestas:


237

ACTUALIZACIÓN: Esta pregunta fue el tema de mi blog en mayo de 2015 . Gracias por la gran pregunta! Consulte el blog para obtener una larga lista de falsedades que la gente comúnmente cree acerca de la finalización.

¿Cuándo debo crear manualmente un destructor?

Casi nunca.

Por lo general, uno solo crea un destructor cuando su clase retiene algún recurso costoso no administrado que debe limpiarse cuando el objeto desaparece. Es mejor usar el patrón desechable para garantizar que el recurso se limpie. Un destructor es esencialmente una garantía de que si el consumidor de su objeto olvida deshacerse de él, el recurso aún se limpia eventualmente. (Tal vez.)

Si crea un destructor, tenga mucho cuidado y comprenda cómo funciona el recolector de basura . Los destructores son realmente raros :

  • No corren en tu hilo; corren en su propio hilo. ¡No provoques callejones sin salida!
  • Una excepción no controlada lanzada desde un destructor es una mala noticia. Está en su propio hilo; quien va a atraparlo?
  • Se puede llamar a un destructor en un objeto después de que el constructor comience pero antes de que el constructor termine. Un destructor escrito correctamente no dependerá de invariantes establecidos en el constructor.
  • Un destructor puede "resucitar" un objeto, haciendo que un objeto muerto vuelva a estar vivo. Eso es realmente extraño. No lo hagas
  • Un destructor podría nunca ejecutarse; no puede confiar en que el objeto esté programado para su finalización. Es probable será, pero eso no es una garantía.

Casi nada de lo que es normalmente cierto es cierto en un destructor. Ten mucho, mucho cuidado. Escribir un destructor correcto es muy difícil.

¿Cuándo has necesitado crear un destructor?

Al probar la parte del compilador que maneja los destructores. Nunca he necesitado hacerlo en el código de producción. Raramente escribo objetos que manipulen recursos no administrados.


"Se puede llamar a un destructor en un objeto después de que el constructor comience pero antes de que el constructor termine". Pero puedo confiar en que los inicializadores de campo se hayan ejecutado, ¿verdad?
configurador

13
@configurator: No. Supongamos que el tercer inicializador de campo de un objeto con un finalizador se llama método estático y provoca una excepción. ¿Cuándo se ejecutará el cuarto inicializador de campo? Nunca. Pero el objeto todavía está asignado y debe finalizarse. Diablos, ni siquiera tienes una garantía de que los campos de tipo doble se inicializaron completamente cuando se ejecuta el dtor. Podría haber habido un aborto de hilos a mitad de la escritura del doble y ahora el finalizador tiene que lidiar con un doble medio cero inicializado a medias.
Eric Lippert

1
Excelente publicación, pero debería haber dicho "debería crearse cuando su clase se aferra a algún objeto costoso no administrado o hace que exista un gran número de objetos no administrados" - Para un ejemplo concreto, tengo una clase de matriz en C # que utiliza un C ++ nativo subyacente clase de matriz para hacer mucho trabajo pesado - Hago muchas matrices - un "destructor" es muy superior a IDisposable en este caso específico, porque mantiene los lados administrados y no administrados de la casa en mejor sincronización
Mark Mullin

1
pythonnet usa destructor para liberar GIL en CPython no administrado
denfromufa

3
Impresionante artículo Eric. Accesorios para esto -> "Diversión extra extra: el tiempo de ejecución utiliza una generación de código menos agresiva y una recolección de basura menos agresiva al ejecutar el programa en el depurador, porque es una mala experiencia de depuración que los objetos que estás depurando desaparezcan repentinamente aunque la variable que hace referencia al objeto está dentro del alcance. ¡Eso significa que si tiene un error en el que un objeto se está finalizando demasiado pronto, probablemente no pueda reproducir ese error en el depurador! "
Ken Palmer

17

Se llama "finalizador", y normalmente solo debe crear uno para una clase cuyo estado (es decir, campos) incluya recursos no administrados (es decir, punteros a los manejadores recuperados mediante llamadas p / invoke). Sin embargo, en .NET 2.0 y versiones posteriores, en realidad hay una mejor manera de lidiar con la limpieza de los recursos no administrados: SafeHandle . Teniendo esto en cuenta, prácticamente nunca debería necesitar escribir un finalizador nuevamente.


25
@ThomasEding - Sí, lo es . C # usa la sintaxis del destructor, pero en realidad está creando un finalizador . Una vez más .
JDB todavía recuerda a Mónica el

@JDB: La construcción lingüística se llama destructor. No me gusta el nombre, pero así se llama. El acto de declarar un destructor hace que el compilador genere un método finalizador que contiene un poco de código contenedor junto con lo que aparezca en el cuerpo del destructor.
supercat

8

No necesita uno a menos que su clase mantenga recursos no administrados como los identificadores de archivos de Windows.


55
Bueno, en realidad, se llama destructor
David Heffernan

2
Ahora estoy confundido. ¿Es finalizador o destructor?

44
La especificación de C # de hecho lo llama destructor. Algunos ven esto como un error. stackoverflow.com/questions/1872700/…
Ani

2
@ThomasEding - Sí, lo es . C # usa la sintaxis del destructor, pero en realidad está creando un finalizador .
JDB todavía recuerda a Mónica el

2
Me encantan los comentarios aquí, panto real :)
Benjol

4

Se llama destructor / finalizador, y generalmente se crea al implementar el patrón Disposed.

Es una solución alternativa cuando el usuario de su clase olvida llamar a Dispose, para asegurarse de que (eventualmente) se liberen sus recursos, pero no tiene ninguna garantía de cuándo se llama al destructor.

En esta pregunta de desbordamiento de pila , la respuesta aceptada muestra correctamente cómo implementar el patrón de disposición. Esto solo es necesario si su clase contiene recursos no controlados que el recolector de basura no logra limpiar por sí mismo.

Una buena práctica es no implementar un finalizador sin darle también al usuario de la clase la posibilidad de desechar manualmente el objeto para liberar los recursos de inmediato.


En realidad, NO se llama destructor en C # con buena razón.
TomTom

14
En realidad lo es . Gracias por darme un voto negativo porque estás equivocado. Consulte la biblioteca de MSDN con respecto a este problema específico: msdn.microsoft.com/en-us/library/66x5fx1b.aspx
Øyvind Bråthen

1
@TomTom su nombre oficial es destructor
David Heffernan

En realidad no es un método alternativo, simplemente permite que el GC administre cuando sus objetos liberan recursos no administrados, la implementación de IDisposable le permite administrar eso usted mismo.
HasaniH

3

Cuando tenga recursos no administrados y necesite asegurarse de que se limpiarán cuando su objeto desaparezca. Un buen ejemplo sería objetos COM o controladores de archivos.


2

He usado un destructor (solo para fines de depuración) para ver si un objeto se estaba purgando de la memoria en el ámbito de una aplicación WPF. No estaba seguro de si la recolección de basura realmente estaba purgando el objeto de la memoria, y esta era una buena manera de verificar.


1

Los destructores proporcionan una forma implícita de liberar recursos no administrados encapsulados en su clase, se les llama cuando el GC se acerca a ellos y se llama implícitamente al método Finalizar de la clase base. Si está utilizando muchos recursos no administrados, es mejor proporcionar una forma explícita de liberar esos recursos a través de la interfaz IDisposable. Consulte la guía de programación de C #: http://msdn.microsoft.com/en-us/library/66x5fx1b.aspx

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.