El código del método Main() de este programa crea un objeto de clase B pero no almacena ninguna referencia al mismo. Luego finaliza la ejecución del programa, lo que provoca la actuación del recolector de basura y la destrucción del objeto creado llamando antes a su destructor. La salida que ofrece por pantalla el programa demuestra que tras llamar al destructor de B se llama al de su clase padre, ya que es:
|
Destruido objeto de clase B
Destruido objeto de clase A
|
Nótese que aunque no se haya guardado ninguna referencia al objeto de tipo B creado y por tanto sea inaccesible para el programador, al recolector de basura no le pasa lo mismo y siempre tiene acceso a los objetos, aunque sean inútiles para el programador.
Es importante recalcar que no es válido incluir ningún modificador en la definición de un destructor, ni siquiera modificadores de acceso, ya que como nunca se le puede llamar explícitamente no tiene ningún nivel de acceso para el programador. Sin embargo, ello no implica que cuando se les llame no se tenga en cuenta el verdadero tipo de los objetos a destruir, como demuestra el siguiente ejemplo:
using System;
public class Base { public virtual void F() { Console.WriteLine("Base.F"); } ~Base() { Console.WriteLine("Destructor de Base"); this.F(); } } public class Derivada:Base { ~Derivada() { Console.WriteLine("Destructor de Derivada"); } public override void F() { Console.WriteLine("Derivada.F()"); } public static void Main() { Base b = new Derivada(); } }
|
La salida mostrada que muestra por pantalla este programa al ejecutarlo es:
|
Destructor de Derivada
Destructor de Base
Derivada.F()
|
Como se ve, aunque el objeto creado se almacene en una variable de tipo Base, su verdadero tipo es Derivada y por ello se llama al destructor de esta clase al destruirlo. Tras ejecutarse dicho destructor se llama al destructor de su clase padre siguiéndose la cadena de llamadas a destructores. En este constructor padre hay una llamada al método virtual F(), que como nuevamente el objeto que se está destruyendo es de tipo Derivada, la versión de F() a la que se llamará es a la de la dicha clase.
Nótese que una llamada a un método virtual dentro de un destructor como la que se hace en el ejemplo anterior puede dar lugar a errores difíciles de detectar, pues cuando se llama al método virtual ya se ha destruido la parte del objeto correspondiente al tipo donde se definió el método ejecutado. Así, en el ejemplo anterior se ha ejecutado Derivada.F() tras Derivada.~F(), por lo que si en Derivada.F() se usase algún campo destruido en Derivada.~F() podrían producirse errores difíciles de detectar.