Bueno... con este post empezamos una (pequeña) serie de posts para desgranar las novedades de C# 3.0 (o sea, el C# de VS 2008). Veremos las novedades del lenguaje y como estas se combinan para dar soporte a LINQ... y para empezar, que mejor que tipos anónimos?
Ahora en C# podemos hacer algo como:
var i = 0;
Cuando la gente ve esto, generalmente tiene dos tipos de reacciones:
- A los desarrolladores con experiencia en Javascript, o otros lenguajes dinámicamente tipados. se les humedecen los ojos de la emoción de pensar que por fin C# es un lenguaje dinámicamente tipado.
- A los desarrolladores con experiencia en VB6 también se les humedecen los ojos, pero en este ocasión es de pánico, al pensar que una cosa parecida a Variant vuelve...
Pues no... ni C# es tipado dinámicamente, ni mucho menos, algo parecido a Variant vuelve para recordarnos nuestras peores pesadillas...
... pues entonces, que significa la palabra clave
var?
En C# var significa: declara la variable del tipo correspondiente al tipo de la expresión que se halle a la derecha de la asignación...
O dicho de otro modo:
var i = 0;
es equivalente a:
int i = 0;
Y en este caso equivalente significa lo mismo, exactamente lo mismo.
Si no os lo creeis, probad a compilar esto:
var i = 0;
i = "hola";
Vereis como el compilador se quejará con un claro:
Cannot implicitly convert type 'string' to 'int'
Ok, ahora que tenemos claro que significa
var, para que lo han añadido al lenguaje? Uno puede pensar que es para evitar que tecleemos tanto... cierto que de var a int no van muchos carácteres, pero de
var a
Dictionary<MiClase, MiOtroPedazoClase>
, pues ya van unos cuantos... Pero no, nos los tiros no van por ahí... los caminos de Redmond son mucho más elevados. La incorporación de la palabra clave var viene dada por la aparición de los tipos anónimos.
Y que es un tipo anónimo? Pues como su nombre indica, una clase... sin nombre. Y como puede no tener nombre una clase, si necesito su nombre para declararla usando class? Pues porque los tipos anónimos se declaran a la vez que se construye un objeto de este tipo. Algo parecido a los métodos anónimos que los declaramos a la vez que construimos un delegado.
Para declarar un tipo anónimo y construir a la vez un objeto de este tipo de usa la palabra clave new (eso tiene al menos lógica, no? xD).
Observad el siguiente código:
string data = "Francesc Eiximenis";
var eiximenis = new { Nombre = data.Split(' ')[0], Apellido = data.Split(' ')[1] };
Estamos creando un tipo anónimo, con dos propiedades (Nombre y Apellido) y
a la vez estamos creando (e inicializando) un objeto de este tipo, y asignándolo a la variable
eiximenis. Fijaos en el uso de
var para declarar la variable... puesto que si no seria imposible hacerlo (no hay ninguna clase
Eiximenis!).
El uso clásico de los tipos anónimos consiste en casos como este: queremos agrupar dos o tres datos en un objeto, pero nos da pereza crear una clase nueva... Pues nada... solucionado!
Pero vamos a hacer cosillas un poco... más divertidas y es que aunque
Eiximenis por si sólo ya da para mucho, que pasa si quiero crear pongamos una lista de objetos de este tipo y luego recorrerla? Vamos a verlo...
List<string> _strs = new List<string>();
_strs.Add("Francesc Eiximenis");
_strs.Add("Ramon Llull");
_strs.Add("Bernat Metge");
ArrayList escritores = new ArrayList();
foreach (string s in _strs)
{
string[] tokens = s.Split(' ');
escritores.Add(new { Nombre = tokens[0], Apellido = tokens[1] });
}
Con este código creo tres objetos de mi tipo anónimo y los añado a un ArrayList... Y como se que tengo tres objetos de un tipo anónimo, y no tres objetos de
tres tipos anónimos (con las mismas propiedades?)... pues porque el compilador de C# no es tonto del todo y es capaz de
reaprovechar tipos anónimos si declaramos dos o más objetos
anónimos con las mismas propiedades... cosa que veremos que es realmente interesante.
Ok... pues eso: tengo el ArrayList
escritores con tres objetos, y puedo asegurar que los tres son del mismo tipo anónimo... y ahora quiero recorrer este ArrayList...
foreach (var escritor in escritores)
{
Console.WriteLine("Nombre: " + escritor.Nombre);
}
Ajá!!! Listos habreis pensado... pues no :( Este código que acabo de poner...
no compila. Y por que? Pues porque
var se resuelve en tiempo de compilación, no en tiempo de ejecución... y aunque la ArrayList
escritores contenga objetos de mi tipo anónimo, la clase ArrayList define que su enumerador devuelve Object, así que la variable escritor es una referencia de tipo object. Encima como el tipo es anónimo no puedo hacer un cast de escritor al tipo real porque no tiene nombre!
Estamos en un callejón sin salida? Por los pelos... pero no.
La solución pasa por generar una clase que me defina su enumerador al tipo anónimo... en definitiva en lugar de una ArrayList, usar una List<T>, siendo el parámetro genérico T nuestro tipo anónimo...
---- un incómodo silencio va aquí ----
... valeeeeeeeeeeeeee... me habeis pillado!!! Si no tenemos nombre de clase, como vamos a hacer:
List<T> escritores; // Que pongo en lugar de T si no tengo nombre de clase?
Pues por suerte tenemos una posibilidad de hacerlo, aprovechandonos del hecho de que C# es capaz de inferir los tipos genéricos de un método.
Recordad que si tengo el siguiente método:
public static Type TypeOf(T type)
{
return type.GetType();
}
Entonces, gracias a la inferencia de tipos genéricos, estas dos llamadas son equivalentes:
TypeOf<int>(10);
TypeOf(10);
La primera llamada es la tradicional (pasamos el tipo genérico). En la segunda llamada el tipo genérico se infiere a partir del tipo del parámetro.
Entonces si definimos este método:
public static List<T> ListOfType(T type)
{
return new List<T>();
}
Este método, dado un parámetro de un tipo nos devuelve una referencia a una List
preparada para contener referncias de este tipo:
List<string> s = ListOfType<string>(string.Empty);
Y aprovechandonos de la inferenncia de tipos genéricos:
List<string> s = ListOfType(string.Empty);
Y si usamos var:
var s = ListOfType(string.Empty);
Todas estas llamadas son equivalentes, y en los tres casos s es una List<string>
. Nos vamos acercando, porque entonces...
var escritores = ListOfType(new { Nombre = string.Empty, Apellido = string.Empty });
foreach (string s in _strs)
{
string[] tokens = s.Split(' ');
escritores.Add(new { Nombre = tokens[0], Apellido = tokens[1] });
}
foreach (var escritor in escritores)
{
Console.WriteLine("Nombre: " + escritor.Nombre);
}
Ya lo tenemos!!!! Fijaos que el truco es declarar la variable escritores, pasándole un objeto de mi tipo anónimo... y eso funciona porque, si os acordáis, os comenté que
el compilador de C# reaprovecha el tipo anónimo. Por suerte, porque si cada vez me crease un tipo anónimo distinto esto no funcionaria (el primer Add sobre
escritores me petaría con un error de conversión).
Saludos!!!