Mejores prácticas en el uso de Dispose y Finalize en .Net

Microsoft .Net Framework proporciona un recolector de basura que se ejecuta en segundo plano y libera la memoria ocupada por los objetos administrados cuando ya no se hace referencia a ellos en su código. Aunque el recolector de basura es experto en limpiar la memoria ocupada por los objetos administrados, no se garantiza que la memoria ocupada por los objetos no administrados se limpie cuando se ejecute el siguiente ciclo de GC. Si tiene recursos no administrados en su aplicación, debe asegurarse de liberar dichos recursos explícitamente cuando haya terminado de usarlos. En este artículo, destacaré las mejores prácticas que debe seguir para limpiar los recursos utilizados en su aplicación.

El GC utiliza generaciones para mantener y administrar la vida útil relativa de los objetos que se crean en la memoria. Los objetos que se crean nuevos se colocan en la generación 0. La suposición básica es que un objeto recién creado puede tener una vida más corta, mientras que un objeto que es viejo, puede tener una vida más larga. Cuando los objetos que residen en la generación 0 no se recuperan después de un ciclo de GC, se mueven a la generación 1. De manera similar, si los objetos que residen en la generación 1 sobreviven a una limpieza del GC, se mueven a la generación 2. Tenga en cuenta que el GC se ejecuta con más frecuencia en el generaciones inferiores que en las superiores. Por lo tanto, los objetos que residen en la generación 0 se limpiarían con más frecuencia en comparación con los objetos que residen en la generación 1. Entonces,Es una mejor práctica de programación asegurarse de utilizar más objetos locales que objetos en el ámbito superior para evitar que los objetos se muevan a generaciones superiores.

Tenga en cuenta que cuando tiene un destructor en su clase, el tiempo de ejecución lo trata como un método Finalize (). Como la finalización es costosa, solo debe usar destructores si es necesario, cuando tenga algunos recursos en su clase que necesitaría limpiar. Cuando tienes un finalizador en tu clase, los objetos de esas clases se mueven a la cola de finalización. Si los objetos son accesibles, se mueven a la cola "Freachable". El GC recupera la memoria ocupada por objetos que no son accesibles. Periódicamente, el GC comprueba si los objetos que residen en la cola "Freachable" son accesibles. Si no son accesibles, la memoria ocupada por esos objetos se recupera. Por lo tanto, es evidente que los objetos que residen en la cola "Freachable" necesitarían más tiempo para ser limpiados por el recolector de basura.Es una mala práctica tener destructores vacíos en su clase C # ya que los objetos para tales clases se moverían a la cola de finalización y luego a la cola "Freachable" si es necesario.

Se llama implícitamente a un finalizador cuando se reclama la memoria ocupada por el objeto. Sin embargo, no se garantiza que un finalizador sea llamado por el CG; puede o no ser llamado en absoluto. En esencia, un finalizador funciona en un modo no determinista: el tiempo de ejecución no garantiza que se llamará a un finalizador. Sin embargo, puede obligar a que se llame al finalizador, aunque no es una buena práctica, ya que hay sanciones de rendimiento asociadas. Los finalizadores siempre deben estar protegidos y siempre deben usarse para limpiar recursos administrados únicamente. Nunca debe asignar memoria dentro del finalizador, escribir código para implementar la seguridad de subprocesos o invocar métodos virtuales desde dentro de un finalizador.

El método Dispose, por otro lado, proporciona un enfoque de "limpieza determinista" hacia la limpieza de recursos en .Net. Sin embargo, el método Dispose, a diferencia del finalizador, debe llamarse explícitamente. Si tiene un método Dispose definido en una clase, debe asegurarse de que se llame. Por lo tanto, el código del cliente debe llamar explícitamente al método Dispose. Pero, ¿qué pasa si olvidas llamar al método Dispose expuesto por una clase que usa recursos no administrados? Los clientes de una instancia de una clase que implementa la interfaz IDisposable deben llamar al método Dispose explícitamente. En este caso, debe llamar a Dispose desde el finalizador. Esta estrategia de finalización determinista automática garantiza que se limpien los recursos no administrados que se utilizan en su código.

Debe implementar IDisposable en cada tipo que tenga un finalizador. Es una práctica recomendada implementar tanto Dispose como Finalize cuando tiene recursos no administrados en su clase.

El siguiente fragmento de código ilustra cómo puede implementar el patrón Dispose Finalize en C #.

Eliminación de vacío virtual protegido (eliminación de bool)

        {

            si (desechando)

            {

                // escribir código para limpiar objetos administrados

            }

            // escribir código para limpiar objetos y recursos no administrados

        }

Este método Dispose parametrizado se puede llamar automáticamente desde el destructor como se muestra en el fragmento de código a continuación.

  ~ Recursos ()

        {

            si (! dispuesto)

            {

                dispuesto = verdadero;

                Desechar (falso);

            }

        }