Inicialización diferida en CSharp

En varios proyectos en los que he trabajado he visto la necesidad de diferir la inicialización de objetos porque su sola instanciación es un proceso muy pesado, como es el caso de los sistemas de información donde se controlan proveedores y pagos a proveedores, es decir, proveedor tiene una colección de pagos que crecerá y crecerá como pagos le hagamos a ese proveedor. Para este ejemplo usare dos entidades con unos pocos campos Padre e Hijo.

El primer approach y que se ve mucho en muchos proyectos es empleando una técnica de la forma:

    public class Padre
    {
        public Padre(int id)
        {
            Id = id;
        }

        public int Id { get; private set; }

        public string Nombre { get; set; }

        private List<Hijo> _hijos;
        public List<Hijo> Hijos
        {
            get
            {
                if (_hijos == null)
                    _hijos = ObtenerHijos(Id);
                return _hijos;
            }
        }

        private List<Hijo> ObtenerHijos(int padreId)
        {
            Thread.Sleep(10000);
            return new List<Hijo> { new Hijo { Id = 1, Nombre = "Pepillo" } };
        }

    }

    public class Hijo
    {
        public int Id { get; set; }

        public string Nombre { get; set; }

    }

Si realizamos una instancia de Padre veremos que no se realiza la espera bloqueante del Thread.Sleep() hasta que se acude a la propiedad Hijos, [NOTA: He puesto ese Thread.Sleep para simular una largo proceso contra una base de datos pro ejemplo]

        private static void Main(string[] args)
        {
            var padre = new Padre(1);
            var hijos = padre.Hijos;
        }

Lazy<T>

Hasta la versión 3.5 del Framework debíamos realizar este tipo de tareas y ocuparnos nosotros mismos de la seguridad en subprocesos y es ahí donde la tarea ya no es tan divertida, pues debemos asegurarnos que una vez inicializada esta propiedad esté disponible para todas las demás además de esto ocuparnos de excepciones y con multithread ya suena engorroso, por fortuna en la versión 4 del Framework llego Lazy<T>

Lazy nos proporciona la ventaja de estas cargas diferidas con ThreadSafe y proporcionan control en propagación de excepciones y su implementación no debería suponer muchos cambios sobre nuestro código, algo como:

    public class Padre
    {
        public Padre(int id)
        {
            Id = id;
            _hijos = new Lazy<List<Hijo>>(() => ObtenerHijos(id));
        }

        public int Id { get; private set; }

        public string Nombre { get; set; }

        private readonly Lazy<List<Hijo>> _hijos;
        public List<Hijo> Hijos
        {
            get { return _hijos.Value; }
        }

        private List<Hijo> ObtenerHijos(int padreId)
        {
            Thread.Sleep(10000);
            return new List<Hijo> { new Hijo { Id = 1, Nombre = "Pepillo" } };
        }
    }

Como vemos el uso de Lazy es supremamente sencillo, el campo _hijos es del tipo Lazy<List<Hijos>> y para acceder a la carga diferida simplemente acudimos a la propiedad Value. Además de esto podemos pasar al constructor del Lazy un delegado con el código a ejecutar una vez solicitemos el valor de la propiedad.

LazyInitializer

Una segunda opción es usar LazyInitializer, esta es una clase estática que brinda un poco más de performance a este tipo de tareas de inicialización diferida, el código de este queda de la forma:

    public class Padre
    {
        public Padre(int id)
        {
            Id = id;

        }

        public int Id { get; private set; }

        public string Nombre { get; set; }

        private List<Hijo> _hijos;
        public List<Hijo> Hijos
        {
            get
            {
                if(_hijos == null)
                    LazyInitializer.EnsureInitialized<List<Hijo>>(ref _hijos, () => ObtenerHijos(Id));
                return _hijos;
            }
        }

        private List<Hijo> ObtenerHijos(int padreId)
        {
            Thread.Sleep(10000);
            return new List<Hijo> { new Hijo { Id = 1, Nombre = "Pepillo" } };
        }
    }

Como vemos, el framework nos brinda herramientas para realizar este trabajo de una forma un poco más sencilla, facilitándonos aún más el trabajo.

Hasta el próximo post.

Anuncios
Inicialización diferida en CSharp

2 comentarios en “Inicialización diferida en CSharp

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s