Una de las novedades de .NET Framework 4.0 es la presentación de la clase System.Lazy<T>. En programación, una inicialización Lazy (el término Lazy significa, literalmente, gandul o retardado) es un método de inicialización que permite que el objeto no se instancie hasta que se llame por primera vez, es decir hasta que sea utilizado. En el ciclo de vida de una aplicación, los métodos y propiedades de una clase que componen existen, es decir se instancian en memoria y permanecen ahí desde la creación hasta que la destrucción de la clase.
Una clase Customer dónde el constructor carga una lista genérica de Order –inicializa un objecto List<Order> – éste permanece en memoria pese a que incluso es posible que ni siquiera lo necesitemos. Hasta .NET Framework 4.0 la implantación de este tipo de inicializaciones se realiza mediante programación retrasando la inicialización de un objeto hasta que realmente se necesitara; en el caso de la clase Customer, por ejemplo, instanciaríamos el objeto List<Order> en un método LoadOrders() o GetOrders(), por ejemplo. En el caso de multithreading, la cosa se complica aún más y a veces el planteamiento o diseño de la clase no es suficiente.
Como novedad esta característica ya forma parte en .NET Framework 4.0 a través de System.Lazy<T> y aporta soporte Thread-Safety y una política de propagación de excepciones consistente para dar soporte al multithreading.
A partir de ahora nos olvidaremos de las técnicas anteriores y utilizaremos System.Lazy<T> para inicializaciones perezosas. Veamos un ejemplo:
1: static void Main(string[] args)
2: {
3: Lazy<string> cadena = new Lazy<string>(delegate
4: {
5: return "Ejemplo Lazy";
6: });
7:
8: Console.WriteLine(string.Format("isValueCreated: {0}", cadena.IsValueCreated));
9: //podemos ejecutar cualquier acción y no se ejecutará hasta...
10: Console.WriteLine(string.Format("Value calling: {0}", cadena.Value));
11: //... que no llamemos al valor
12: Console.WriteLine(string.Format("isValueCreated: {0}", cadena.IsValueCreated));
13:
14: Console.Read();
15: }
El constructor de System.Lazy<T> soporta las siguientes sobrecargas:
1: public Lazy();
2: public Lazy(Func<T> valueFactory);
3: public Lazy(bool isThreadSafe);
4: public Lazy(Func<T> valueFactory, bool isThreadSafe);
El parámetro isThreadSafe se establecerá a true cuando necesitemos utilizar el objeto de forma concurrente por multiples threads, con valor predeterminado a false.
El parámetro valueFactory es un delegado del tipo Func<T> y será el encargado de inicializar el valor del objeto Lazy. Un ejemplo, además del expuesto en el ejemplo anterior, podría ser:
1: public class Order {}
2:
3: public class OrderDb
4: {
5: public static List<Order> GetOrdersBy(string idCustomer)
6: {
7: return new List<Order>
8: {
9: new Order(),
10: new Order(),
11: new Order()
12: };
13: }
14: }
15:
16: public class Customer
17: {
18: public Lazy<List<Order>> _orders;
19:
20: public Customer(string id)
21: {
22: //inicialización Lazy
23: _orders =new Lazy<List<Order>>(() => OrderDb.GetOrdersBy(id) );
24: }
25: }
Con el siguiente programa de consola:
1: class Program
2: {
3: static void Main(string[] args)
4: {
5: Customer customer = new Customer("10");
6:
7: //pese a que customer se ha incializado la Lista de Orders aún no:
8: Console.WriteLine(string.Format("isValueCreated: {0}", customer._orders.IsValueCreated));
9: //Solicitamos las ordenes
10: Console.WriteLine(string.Format("calling Value.Count: {0}", customer._orders.Value.Count));
11: //Ahora si se han instanciado
12: Console.WriteLine(string.Format("isValueCreated: {0}", customer._orders.IsValueCreated));
13:
14: Console.Read();
15: }
16: }
Cuya salida será:

Es importante tener en mente este tipo de inicializaciones sobre todo en procesos de mucho consumo de recursos, por ejemplo objetos de la capa de datos que almacenan datos binarios – imagen, video,- y que muchas veces no necesitamos que consuman memoria si no se van a utilizar. Como este ejemplo, muchos más…