Nueva característica de “Orcas”: Tipos anónimos

En éstos últimos dos meses he publicado una serie de post que cubren algunas de las nuevas características que vendrán incorporadas a las release "Orcas" de Visual Studio y del .NET Framework. Aquí tenéis los enlaces a los cuatro primeros:

El post de hoy cubre la última característica en esta serie de post: Tipos anónimos.

¿Que son los tipos anónimos?

Los tipos anónimos es una característica de C# y VB que permiten a los desarrolladores definir tipos "inline" del CLR en el código, sin tener que definir explícita y formalmente una clase para el tipo.

Los tipos anónimos son particularmente útiles cuando consultamos y transformamos/projectamos/formamos datos con LINQ.

Ejemplo de tipo anónimo.

En el post anterior sobre sintáxis de consultas demostramos cómo podeis transformar datos con projecciones. Ésta es una característica muy poderosa de LINQ que nos permite hacer operaciones en una fuente de datos (sin importar si es una base de datos, un archivo XML, o una colección en memoria) e incrustar los resultados de los datos consultados en una estructura/formato diferente que la fuente de datos original.

En el post anterior sobre sintáxis de consultas defínimos una clase propia "MyProduct" que usamos para representar los datos de los productos transformados. Definiendo explícitamente la clase "MyProduct" tenemos un contrato formal de tipos del CLR que podemos usar para pasar los resultados de nuestros productos propios entre servicios web o entre multiples clases/assemblies en nuestra aplicación.

Sin embargo, hay veces cuando sólo queremos consultar y trabajar con datos sin tener que definir una clase explícita para representar esos datos. Aquí es donde los tipos anónimos son muy útiles ya que nos permiten definir tipos concretos dentro de nuestro código.

Por ejemplo, supongamos que usamos el diseñador relacional de LINQ to SQL que viene con "Orcas" para modelar la base de datos "Northwind" con clases de la siguiente forma:

Podemos usar el siguiente código para consultar los datos de Product en nuestra base de datos, y usar la capacidad de proyección/transformación de LINQ para incrustar los resultados en otra cosa que no sea la clase "Product" de arriba. En vez de usar una clase "MyProduct" para representar cada fila de los datos obtenidos de la base de datos, podemos usar los tipos anónimos para definir implícitamente un nuevo tipo con 4 propiedades de la siguiente forma:

En el código de arriba estamos declarando un tipo anónimo como parte de la sentencia "select" dentro de la expresión LINQ, y estamos teniendo la capacidad  del compilador de generar automáticamente el tipo anónimo con cuatro propiedades (Id, Name, UnitPrice y Total Revenue) - cuyos nombres y tipos son heredados por la forma en que construimos la consulta.

Usamos la nueva palabra reservada "var" en C# para referirnos programáticamente a la secuencia IEnumerable<T> de este tipo anónimo que se devuelve en la expresión LINQ, así como para referirnos a cada una de las instancias de ese tipo anónimo dentro de esa secuencia cuando hacemos un bucle foreach más tarde en nuestro código.

Mientras que esta sintáxis nos da la flexibilidad de un lenguaje dinámico, estamos teniendo también los beneficios de un lenguaje fuertemente tipado - incluyendo soporte para tiempo de compilación e intelisense en Visual Studio. Por ejemplo, fijáos en cómo hacemos el foreach anterior sobre la secuencia de productos devueltos y cómo tenemos soporte completo para intelisense y chequeo en tiepmo de compilación del tipo anónimo con propiedades propieas que definimos en la consulta LINQ.

Entendiendo la palabra reservada Var

C# "Orcas" introduce la nueva palabra reservada var que se usará en lugar del nombre del tipo de variable que usaremos.

La mala percepción que la gente suele tener la primera vez que ve esta palabra reservada es pensar que es un paso atrás o una referencia a una variable no tipada (por ejemplo: una referencia a Object ) Esto es incorrecto -- la palabra reservada var siempre genera una referencia a una variable de un tipo fuertemente tipada. En lugar de obligar al desarrollador a definir explícitamente el tipo de la variable, la palabra var le dice al compilador que infiera el tipo de la variable de la expresión usada para inicializarla cuando fué declarada por primera vez.

La palabra reservada var puede usarse para referirse a cualquier tipo en C# ( esto es, para referirse tanto a tipos anónimos como a tipos definidos explícitamente). De echo, la manera más facil de entender lo que la palabra reservada var es, es mirar algunos ejemplos de su uson con tipos explícitos. Por ejemplo, podemos usar var de la forma sigiente para definir tres variables:

El compilador inferirá el tipo de las variables "name", "age" y "male" basándose en el tipo de su asignación inicial (en este caso un string, un entero y un booleano). Esto significa que generará el mismo código IL si las hubiéramos declarado de la siguiente forma:

El CLR no sabrá en realidad que la palabra var está siendo usada - desde su punto de vista no hay diferencia entre los dos códigos anteriores. La primera versión es simplemente una forma que el compilador tiene de ahorrarle al desarrollador unas cuantas pulsaciones de teclas, y es el compilador el que hace el trabajo de inferir y declarar el nombre del tipo.

Además de usar la palabra var para tipos base, podemos usar cualquier tipo que hayamos hecho nosotros. Por ejemplo, podríamos irnos a la consulta de proyección de LINQ que hicimos en el post anterior en el que usábamos un tipo explícito "MyProduct" para la incursaticón y adaptarla para que use la palabra var:

Importante: aunque estemos usando la palabra var en el código de arriba, NO estamos usándola con un tipo anónimo. La consulta LINQ sigue usando la clase "MyProduct" para devolver los datos - lo que implica que "var products" es una colección "IEnumerable<Product> products". De esta forma la variable "var p" que usamos en el foreach es lo mismo que "MyProduct p".

Regla importante sobre la palabra var

Debido a que la palabra var  produce un tipo fuertemente tipado en la declaración, el compilador necesita ser capaz de inferir el tipo basándose en su uso. Esto es, tenemos que asignarle un valor inicial al declarala. El compilador nos mostrará un error en caso contrario:

Declarando tipos anónimos

Ahora que hemos introducido la palabra var, podemos empezar a usarala para referirnos a tipos anóniimos.

Los tipos anónimos en C# son definidos usando la misma sintaxis de inicializacón de objetos de la que hablamos en el primer post de esta serie. La diferencia es que en lugar de declarar el nombre del tipo como parte de la inicialización gramatical, cuando instanciamos tipos anónimos dejamos en blanco la parte que viene despues de la palabra new:

El compilador parseará la sintaxis anterior y definirá automáticamente un tipo del CLR con cuatro propiedades. El tipo de cada una de esas cuatro propiedades estan determinadas por el tipo de inicialización que tengan cada una (por ejemplo, en el ejemplo anterior a la propiedad "Id" se le asigna un entero - así que el compilador generará la propiedad el mismo patrón IL que hubiera generado si la hubiesemos declarado explícitamente como entero.

El nombre actual del CLR para el tipo anónimo será generado por el compilador de C#. En realidad el CLR no sabe la diferencia entre un tipo anónimo y tipo no anónimo - así que la semántica de los dos casos son exáctamente iguales. Bart De Smet tiene un gran post donde cuenta los detalles de esto si queréis ver exactamente el patrón del nombre de la clase y el IL generado.

Fijáos arriba cuando escribimos "product." en el tipo anónimo, seguimos teniendo soporte para el intelisense y chequeo en tiempo de compilación en Visual Studio. También vemos cómo el intellisense nos dice que es un "Anonymous Type" - pero sigue teniendo información completa de las propiedades (el texto rodeado con un círculo rojo).

Uso de tipos anónimos para jerarquías

Uno de los problemas que hacen que los tipos anónimos sean perfectos para usar es la habilidad para construir projecciones jerárquicas de datos con una cantidad mínima de código.

Por ejemplo, podríamos escribir la siguiente expresion LINQ para consultar todos los productos de la base de datos de Northwind cuyos precios sean mayores de 50$ e incrustar los resultados en una estructura jerárquica ordenada por el nivel de stock de Product (usando la sentencia "group into" proporcionada por LINQ)

Cuando el código anterior se ejecuta en ASP.NET, tendremos la siguiente salida en nuestro navegador:

De todas formas podríamos hacer un incrustado jerárquico basandonos en JOIN. Por ejemplo, el código siguiente crea un nuevo tipo anónimo con algunas columnas de productos como propiedades, así como una propiedad con una subcolección jerárquica que contiene los detalles de pedidos de los 5 pedidos más recientes de ese producto en particular:

Fijáos con qué facilidad podemos navegar sobre los datos jerárquicos. Arriba estamos haciendo un bucle sobre la consulta de productos, y luego llenamos la colección con los últimos cinco pedidos de esos productos. Como podemos ver, tengo soporte para el intellisense y chequeo en tiempo de compilación en cualquier parte (incluso en las propiedades de objetos de la subcolecció de los detalles de productos en el tipo anónimo).

Data Binding con tipos anónimos.

Como ya hemos dicho ántes, no hay ninguna diferencia desde el punto de vista del CLr entre tipos anónimos y tipos definidos. Los tipos anónimos y la palabra var son un dulce para evitar que escribamos código - la semántica en el runtime es la misma que si definiéramos los tipos correspondientes.

Entre otras cosas, esto significa que todas las capacidades de reflexión de los tipos estándar de .NET funcionan también con los tipos anónimos - lo que implica que las capacidades de databinding para controles de interfaz de usuario (UI controls) funcionan bien. Por ejemplo, si queremos mostrar lso resultados de la consulta jerárquica anterior de LINQ, podemos definir un <asp:gridview> en un aspx de la siguiente forma:

El .aspx anterior contiene un gridview con 2 columnas estándar con botones y una columna con un template que contiene un control <asp:BulletedList> que suaremos para mostrar la jerarquía de detalles de los pedidos.

Podráimos escribir el código LINQ para construir la consulta jerárquica contra la base de datos y enlacar los datos a nuestro Grid-View:

Como el GridView soporta el enlace con cualquier secuencia IEnumerable<T>, y usa refelxión para obtener las propiedades de los valores, funcionará perfectamente con los tipos anónimos que usamos arriba.

En tiempo de ejecución el código anterior muestra un grid simple de detalles de productos con una lista jerárquica con las cantitades más recientes de los pedidos:

Obviamente podemos hacer este grid mucho más bonito - pero espero que hayáis capatado la idea de lo fácil que es ahora construir consultas jerárquicas sobre bases de datos, incrustarlas los resultados en donde queramos, y trabajar sobre esos resultados en nuestro código o enlazarlos con controles de interfaz de usuario.

Resumen

Los tipos anónimos son una característica que permite a los desarrolladores definir los tipos en el código, sin tener que declarar una clase explícita. Aunque se pueden usar en un monton de escenarios, son particularmente útiles cuando consultamos y transformamos/incrustamos datos con LINQ.

Este post concluye la seríe de 5 post sobre "Orcas". Haré más post sobre LINQ que demostrarán cómo aprovechar todas estas nuevas características para realizar acceso a datos (definiendo modelos de datos, consultando, actualizando, usando procedimientos almacenados, validaciones, etc). Quería terminar esta serie de cinco post primero, para que tuviéseis una forma fácil de entender todo lo que hay construido debajo del lenguaje a medida que nos vayamos metiendo en algunos escenarios nuevos en próximos post.

Espero que sirva.

Scott

Traducido por: Juan María Laó Ramos. Microsoft Student Partner

8 pensamientos en “Nueva característica de “Orcas”: Tipos anónimos

  1. Pingback: LINQ to SQL (3ª Parte - Consultando la base de datos) « Thinking in .NET

  2. Pingback: Usando LINQ to XML (y como crear un lector de RSS con él) « Thinking in .NET

  3. gabo

    ¿El uso de VAR va encaminado a que en un futuro no tengamos que hacer uso de definir explícitamente del tipo de variables (int, String, etc) y sea sólo como una opción?

    Ya que esto puede ser adictivo y abusar del uso de VAR para todo en todos lados, ya que el compilador lo hará por mi.

    Responder
  4. Vio

    Hola Gabo:

    Los tipos anónimos están pensados para ahorrarnos trabajo en ciertos casos, no es aconsejable usarlos siempre. En los ejemplos en los que hemos trabajado, el compilador crea un tipo con las propiedades y métodos que estime necesarios. Pero esto sólo lo usaremos cuando no sea necesario crear una clase completa, es decir, si sólo vamos a usar algunos campos de la tabla Products, y sólo va a ser en el escenario de una página, quizás no sea conveniente esto. En el caso de que necesitar una clase en varias partes del código ya si sería aconsejable crear una clase para ello.
    En resúmen, esta característica, como tantas otras, hay que usarlas cuando sea necesario.
    Un saludo.

    Responder
  5. Pingback: ASP.NET MVC Framework (Primera parte) « Thinking in .NET

  6. Pingback: ASP.NET MVC Framework (2ª Parte): URL Routing « Thinking in .NET

  7. jorge

    Hola, buenas tardes, una pregunta como podria hacer una funcion donde el llamado a los catalogos sea dinamica es decir:

    var mcatalogo = from mcata in @cata
    select mcata;

    en donde tengo que llenar el dropdownlist con el resultado del query, tengo creado mi contexto en las regas del negocio.
    Alguna idea, gracias de antemano

    Responder
  8. Pingback: LINQ to XML (como crear rss reader) | Un Mundo Interesante ....

Deja un comentario