Constructores y Finalizadores

Dentro de la orientación a objetos nos encontramos dos tipos de métodos especiales, el constructor y el destructor. En .NET hay dos tipos de constructores de instancia (ctor) y de tipo (cctor) también llamado constructor estático.

Constructores

Cuando un tipo es accedido por primera vez, se ejecuta el constructor estático bajo un doble lock (Como implementar un Singleton concurrente) para asegurar que solamente se llama una vez, si además se está creando una instancia del objeto se llamará al constructor del objeto para inicializarlo. También se llama a todos los constructores base hasta que se llega a System.Object.

Hay que tener en cuenta que el constructor de un objeto no es más que un método que puede aceptar parámetros y que devuelve una referencia sí mismo a this. Como programadores no tenemos que manejar esta situación puesto que el propio compilador la realiza por nosotros.

Hay un caso excepcional en el que podemos crear un objeto pero no obtener una referencia a el nunca, si en el constructor se lanza una excepción. En este caso, el objeto se inicializa, se llama a los constructores base y se aborta el método en el punto en el que se lance la excepción, pero ya se ha reservado memoria para el objeto y nunca podremos acceder a esa referencia de ese objeto a medio construir.

public class Foo
{
    public DateTime DateTime { get; set; }
    public string file { get; set; }
    public Foo()
    {
        DateTime = DateTime.Now;
        string[] v = file.Split(',');
    }
}

Aquí tenemos un ejemplo de una clase que en el constructor lanzará una excepción de tipo NullReferenceException, realmente si se ha creado el objeto solo que si hacemos algo como:

Foo f = new Foo();

Nunca llegaremos a obtener la referencia del objeto hay creado y además tenemos que tener en cuenta que se tendrá que recolectar el objeto para liberar memoria en la siguiente recolección de basura.

Es por eso que hay que tener mucho cuidado con lo que se pone en el constructor porque podemos dejar a nuestro tipo inutilizado.

La situación se agrava si se lanza una excepción en el constructor de tipo, ya que no podremos acceder a ninguna variable estática ni crear instancias de este tipo.

Destructores o finalizadores

Los destructores (o finalizadores) son los métodos que se llamarán cuando el objeto ya no es usado y se reclama la memoria usada. En este caso es el CLR el que se encarga de llamar a este finalizador pues nosotros no tenemos ningún mecanismo para liberar un objeto.

Un finalizador se define de esta manera:

~Foo()
{
   Console.WriteLine("Adiossss !!");
}

Eso significa que cuando esa instancia del tipo Foo sea recolectada se ejecutará este método. Si miramos al IL generado por este programa realmente el destructor ha pasado a llamarse Finalize() y está marcado como virtual.

Además de definir un destructor de clase hay otro mecanismo para poder definir un destructor, implementando la interfaz IDisposable. Este es el único mecanismo que tiene un programador de .NET para poder definir un destructor de objeto y además poder llamarlo el mismo de codigo de usuario.

Para evitar que este método sea llamado otra vez por el CLR existe el método GC.SupressFinalize.

¿Cómo es llamado este método?

Cada vez que se realiza una recolección de basura el GC examina todos los objetos y si encuentra alguno que es finalizable, es decir, tiene destructor o implementa IDisposable, entonces ese objeto que está listo para ser recolectado pasa a la cola de finalización.

Cuando los objetos están en esa cola de finalización un Thread de alta prioridad se encarga de sacar un elemento de la cola, ejecutar su finalizador y marcarlo como listo para ser recolectado.

Consideraciones a tener en cuenta:

· Cuando el objeto está listo para ser finalizado se lleva a la cola de ReadyToFinalize y además todos los objeto de su grafo son marcados. Eso significa que si ese grafo de objeto normalmente moriría en esta recolección, ahora es promovida a la siguiente, simplemente porque su objeto raíz es finalizable.

· Las generaciones más viejas son recolectadas menos que las generaciones más jóvenes lo que indica que nuestro grafo de objetos puede permanecer vivo durante mucho tiempo hasta que es completamente finalizado y recolectado.

· Al utiliza un único thread, aunque está en prioridad alta, puede darse el caso de que el si tenemos una maquina con 32 procesadores y solamente tenemos una finalizado objetos la cola pueda crecer mucho.

· Hay que tener mucho cuidado con lo que se ejecuta en el finalizador porque si se produce una excepción el proceso entero se descarga. No estamos hablando de que se descargue el dominio de aplicación sino todo el proceso.

Garantías de finalización:

· Durante la ejecución normal de la aplicación y durante cada recolección de basura, se llamara a los finalizadores de los objetos que haya en la cola.

· Si el proceso termina sin la coordinación del CLR, llamando a TerminateProcess o ExitProcess, el CLR tendrá su primera noticia de que el proceso se está cerrando cuando se envíe el mensaje DLL_PROCESS_DETACH en el DllMain. Así que el CLR no tiene tiempo de hacer nada y lo único que puede pasar es que termine los finalizadores críticos (CriticalFinalizerObject).

· Si el proceso es cerrado con el pleno conocimiento del CLR, llamado a Exit o Enviroment.Exit, se finalizarán los objetos que están en la cola de finalización y los finalizadores críticos. Pero los posibles objetos que salgan de una recolección de basura listos para ser finalizados se quedarán sin ser finalizados.

· Si se descarga el dominio de aplicación entonces solamente se finalizarán los objetos que estuvieran en la cola de finalización y los finalizadores criticos.

El codigo del ejemplo: http://www.luisguerrero.net/downloads/ctor.zip

Saludos. Luis.

Un comentario en “Constructores y Finalizadores”

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *