Como algunos ya sabeis, Visual Studio 2008 viene con el .NET Framework 3.5, que incluye una buena cantidad de mejoras y novedades con respecto al framework 2.0 que viene con Visual Studio 2005. Algunas de esas características forman parte del trabajo realizado para Windows Vista, pero hay muchas otras de igual o mayor interés para los desarrolladores.
Variables sin tipo, tipos anónimos e inicializadores de objeto:
¿Qué pasa si necesitas un sitio donde guardar información pero no sabes a priori de qué tipo? Fácil, declaras la variable sin especificar su tipo y permites que sea C# quien determine el tipo real de los datos que vas a almacenar.
Por ejemplo, si queremos tener un diccionario que indexa una lista de enteros en base a una cadena (de forma similar a los arrays asociativos que existen en PHP) podríamos hacerlo así:
// Framework 2.0: Dictionary <string , List<int>> ejemplo = new Dictionary </string><string , List<int>>(); // Framework 3.0: var ejemplo = new Dictionary </string><string , List<int>>();
Por supuesto, tenemos que inicializar la variable sin tipo en cuanto la declaremos, para que el compilador pueda decidir en tiempo de compilación qué tipo tendrá la variable, ya sea un tipo predefinido como int, una clase creada por el usuario, o un tipo anónimo (hablo sobre ellos un poco más adelante).
De la misma forma se pueden declarar arrays sin tipo, como en el siguiente ejemplo:
// En este caso se crea un array de tipo Int32 con los elementos indicados. var potenciasDeDos = new [] {2, 4, 8, 16, 31, 64, 128, 256, 512};
Los tipos o clases anónimas, por definición, no tienen nombre. Eso significa que se pueden considerar como definiciones de clase de usar y tirar. La verdadera importancia de estos «tipos anónimos» aparece cuando consideramos LINQ, Language INtegrated Queries, ya que dependiendo de la consulta que realicemos, obtendremos unos datos u otros, de forma que C# lo que hace es crear una clase anónima con los campos devueltos por la consulta LINQ para que se pueda acceder a ellos fácil y rápidamente.
Un tipo anónimo se define así:
var cosa = new { Name = "Botellín de Guiness", Price = 1.57, Description = "Un mal sustituto de una auténtica Guiness, servida de barril en varias tiradas." };
Como se puede ver, hemos usado una declaración sin tipo para inicializar un conjunto de datos con aspecto de objeto de forma que el compilador entiende que es un objeto, pero al no pertenecer a una clase definida, el compilador lo define automáticamente como objeto de tipo anónimo.
Además, se presenta otra característica interesante: hemos inicializado los valores de los atributos de clase sin necesitar un constructor. Esto es lo que se conoce como inicializadores de objeto, y básicamente permiten asignar el valor que queramos a un objeto de cualquier tipo que haya sido creado usando el constructor por defecto. A continuación veremos un par de ejemplos de cómo usar los inicializadores de objeto para simplificar la escritura del código:
//Inicialización de un objeto con C#2 Cerveza botellin = new Cerveza(); botellin.marca = "Guiness";botellin.temperatura = 5; botellin.caducidad = Datetime.Now.AddYears(2); CajaCerveza.Add(botellin); //Inicialización de un objeto con los inicializadores en C#3 Cerveza botellin = new Cerveza() { marca = "Guiness", temperatura = 5, caducidad = Datetime.Now.AddYears(2) }; CajaCerveza.Add(botellin); //O también podemos hacer esto: CajaCerveza.Add(new Cerveza() { marca = "Guiness", temperatura = 5, caducidad = Datetime.Now.AddYears(2) });
Expresiones Lambda:
Expresión Lambda suena a forma terrorífica de arte abstracto, pero en realidad es una forma de decir «función anónima». En C#3 las funciones anónimas se escriben con el operador «=>» , de forma que lo que queda a la izquierda del operador son los parámetros, y lo que queda a la derecha es el cuerpo de la función anónima. Por ejemplo:
x => x+1; () => return new SalirATomarGuiness(); (x, y) => { var suma = x + y; return suma; }
Las ventajas proporcionadas por esta sintaxis pueden no ser evidentes, a no ser que comparemos lo que pasa entre C#2 y C#3:
//Tenemos una lista de palabras var palabras = new List</string><string> { "mola", "Guiness", "tomar", "me" }; //Ordenar por longitud de palabra, al estilo C#2 Words.Sort(delegate(string a, string b) { return a.Length.CompareTo(b.Length); }); //Ordenar por longitud de palabra, al estilo C#3 palabras.Sort((a, b) => a.Length.CompareTo(b.Length)); // Show results. foreach (string palabreja in palabras) Console.Write(palabreja + " ");
La salida es «me mola tomar Guiness» en ambos casos. Como podemos ver, el uso de estas funciones anónimas es mucho más sencillo que el tradicional uso de delegados, y además, dependiendo de la situación, ¡nos podemos ahorrar el tener que declarar un delegado como atributo de clase! Además, nos fijamos en que no declaramos tipo al crear el método anónimo, ¿adivinamos por qué? (pista: mirar sección anterior 😛 )
Bueno, este post está siendo más largo de lo que me gustaría, así que dejaré el tema de LINQ (y cómo explota los tipos anónimos, expresiones Lambda y etc) para otro día.