En C #, ¿cuál es la diferencia entre un destructor y un método Finalize en una clase?

97

¿Cuál es la diferencia, si la hay, entre un destructor y un método Finalize en una clase?

Recientemente descubrí que Visual Studio 2008 considera un destructor sinónimo de un método Finalize, lo que significa que Visual Studio no le permitirá definir simultáneamente ambos métodos en una clase.

Por ejemplo, el siguiente fragmento de código:

class TestFinalize
{
    ~TestFinalize()
    {
        Finalize();
    }

    public bool Finalize()
    {
        return true;
    }
}

Da el siguiente error en la llamada a Finalizar en el destructor:

La llamada es ambigua entre los siguientes métodos o propiedades: 'TestFinalize. ~ TestFinalize ()' y 'TestFinalize.Finalize ()'

Y si se comenta la llamada a Finalizar, da el siguiente error:

El tipo 'ManagementConcepts.Service.TestFinalize' ya define un miembro llamado 'Finalizar' con los mismos tipos de parámetros

Jeff Leonard
fuente

Respuestas:

68

Un destructor en C # anula el System.Object.Finalizemétodo. Tienes que usar la sintaxis del destructor para hacerlo. Anular manualmente Finalizele dará un mensaje de error.

Básicamente, lo que está tratando de hacer con la Finalizedeclaración de su método es ocultar el método de la clase base. Hará que el compilador emita una advertencia que puede silenciarse usando el newmodificador (si iba a funcionar). Lo importante a destacar aquí es que usted no puede tanto overridey declarar un newmiembro con nombre idéntico al mismo tiempo, por lo que tener tanto un destructor y un Finalizemétodo dará lugar a un error (pero que puede , aunque no es recomendable, declarar un public new void Finalize()método si no está declarando un destructor).

Mehrdad Afshari
fuente
71

Wikipedia tiene una buena discusión sobre la diferencia entre un finalizador y un destructor en el artículo del finalizador .

C # realmente no tiene un destructor "verdadero". La sintaxis se parece a un destructor de C ++, pero realmente es un finalizador. Lo escribiste correctamente en la primera parte de tu ejemplo:

~ClassName() { }

Lo anterior es azúcar sintáctico para una Finalizefunción. Asegura que los finalizadores en la base estén garantizados para ejecutarse, pero por lo demás es idéntico a invalidar la Finalizefunción. Esto significa que cuando escribe la sintaxis del destructor, realmente está escribiendo el finalizador.

Según Microsoft , el finalizador se refiere a la función a la que llama el recolector de basura cuando recolecta ( Finalize), mientras que el destructor es su parte del código que se ejecuta como resultado (el azúcar sintáctico que se convierte Finalize). Están tan cerca de ser lo mismo que Microsoft nunca debería haber hecho la distinción.

El uso de Microsoft del término "destructor" de C ++ es engañoso, porque en C ++ se ejecuta en el mismo hilo tan pronto como el objeto se elimina o se saca de la pila, mientras que en C # se ejecuta en un hilo separado en otro momento.

Kenzi
fuente
Yo diría que tal distinción entre el destructor y el finalizador es importante. Sin embargo, solo aquellos que se preocupan por lo que sucede bajo el capó se preocuparían por dicha distinción.
Kyle Baran
1
También tenga en cuenta que ECMA-334 ha eliminado explícitamente la ambigüedad de "destructor" y "finalizer" formalmente, hace mucho tiempo. No sé por qué MS todavía insiste en el término engañoso en sus especificaciones.
FrankHB
Al menos por trabajar con Mono, C # está realmente modelado después de C ++, y la mayoría de los objetos nativos de C # son objetos de C ++. La forma en que funciona el compilador que compiló Mono dicta cómo se destruyen esos objetos C ++ y, de la misma forma, cómo la finalización del objeto C # se propaga a C ++ y llama a esos destructores. La distinción tiene sentido bajo el capó, pero todavía no se aplica a C # en sí.
Kenzi
20

Encontrado aquí: http://sanjaysainitech.blogspot.com/2007/06/difference-between-destructor-dispose.html

  1. Incinerador de basuras

    Son métodos especiales que contienen código de limpieza para el objeto. No puede llamarlos explícitamente en su código, ya que GC los llama implícitamente. En C # tienen el mismo nombre que el nombre de la clase precedido por el ~signo. Me gusta-

    Class MyClass
    {
    
    ~MyClass()
    {
    .....
    }
    }

    En VB.NET, los destructores se implementan anulando el método Finalize de la clase System.Object.

  2. Disponer

    Estos son como cualquier otro método de la clase y se pueden llamar explícitamente, pero tienen un propósito especial de limpiar el objeto. En el método dispose escribimos código de limpieza para el objeto. Es importante que liberemos todos los recursos no administrados en el método dispose como la conexión a la base de datos, archivos, etc. La clase que implementa el método dispose debe implementar la interfaz IDisposable. Un método Dispose debe llamar al método GC.SuppressFinalize para el objeto que está eliminando si tiene desturctor porque ya ha hecho el trabajo de limpiar el objeto, entonces no es necesario que el recolector de basura llame al método Finalize del objeto. Referencia: http://msdn2.microsoft.com/en-us/library/aa720161(VS.71).aspx

  3. Finalizar

    Un método Finalize actúa como una protección para limpiar recursos en caso de que no se llame a su método Dispose. Solo debe implementar un método Finalize para limpiar los recursos no administrados. No debe implementar un método Finalize para objetos administrados, porque el recolector de basura limpia los recursos administrados automáticamente. El método de finalización es llamado implícitamente por el GC, por lo tanto, no puede llamarlo desde su código.

    Nota: En C #, el método Finalize no se puede anular, por lo que debe usar un destructor cuya implementación interna anulará el método Finalize en MSIL, pero en VB.NET, el método Finalize se puede anular porque admite el método destructor.

Actualización: Interesante hilo semi relacionado aquí .

Andrew Siemer
fuente
1
You should only implement a Finalize method to clean up unmanaged resources: lo pones en Finalizar. ¿Lo mismo con Dispose?
hqt
@hqt: Los casos en Disposelos que uno debería implementar un finalizador superan ampliamente a aquellos en los que debería implementar un finalizador. Implementar Disposesi es probable que una instancia de la clase o una clase derivada sea lo último en poseer directamente un recurso no administrado, o poseer directamente lo último que posea directamente un recurso no administrado, o poseer directamente lo último que posea directamente etc. Solo implemente Finalizepara la limpieza de recursos si la clase de uno <i> directamente </i> posee un recurso no administrado <i> y casi nada más </i> - un escenario mucho más limitado.
supercat
@hqt: si una clase posee recursos no administrados directamente y también tiene referencias a otros objetos, los recursos no administrados generalmente deben dividirse en su propia clase finalizable (que idealmente no debe contener referencias fuertes a nada más), es decir, la clase que contiene referencias a otros objetos solo poseería "cosas que posean directamente recursos no administrados", en lugar de poseer los recursos en sí mismos, y por lo tanto no necesitaría un finalizador.
supercat