Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

18 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
Mostrando entradas con la etiqueta vs2008. Mostrar todas las entradas
Mostrando entradas con la etiqueta vs2008. Mostrar todas las entradas
martes, 13 de julio de 2010
Elegir elementos para la caja de herramientasHacía tiempo que Visual Studio 2008 me estaba dando problemas al intentar añadir controles a la caja de herramientas. Concretamente, tras pulsar el botón derecho del ratón sobre esta zona y seleccionar la opción “Elegir elementos”, el entorno se cerraba bruscamente sin dar ningún tipo de explicación.

Aunque hasta ahora he podido convivir pacíficamente con este problema (básicamente, no añadiendo controles al Toolbox ;-)), hace unos días decidí dedicar un rato a solucionarlo.

En primer lugar, dado que el casque no mostraba ningún mensaje de error previo, he acudido al visor de eventos de Windows, encontrándome con el siguiente mensaje:
.NET Runtime version 2.0.50727.3603 - Error grave de motor de ejecución (7A036050) (80131506)
Instalando el hotfixAfortunadamente, es una pista suficiente para dar con la solución. Googleando un poco los códigos de error 7A036050 y 80131506, he dado con el hotfix KB963676, que es posible descargar desde Microsoft Connect.

Una vez instalado el software apropiado para nuestra plataforma (x86, x64…) y reiniciado el equipo, todo ha vuelto a la normalidad.

Y es que a veces bastan unos minutos para eliminar esos pequeños y molestos inconvenientes que nos hacen menos productivos. Lo que es la procrastinación ;-)

Publicado en: Variable not found
Twitteado en: twitter.com/jmaguilar
Facebookeado en: facebook.com/variablenotfound
jueves, 27 de mayo de 2010
ASP.NET MVC La mejor forma de entender y conocer los entresijos de cualquier código es ver sus interioridades en funcionamiento: poder introducir puntos de ruptura, ejecutar paso a paso, ver los valores de parámetros, de retornos… incluso retocarlo para ver lo que ocurre.

En sistemas relativamente complejos como el framework MVC, puede sernos de mucha utilidad además para entender qué está pasando en determinadas circunstancias, y ayudarnos a decidir la solución óptima a un problema.

Una posibilidad es configurar Visual Studio para utilizar los servidores de símbolos de Microsoft (aquí hay un buen artículo que explica paso a paso cómo conseguirlo). Esto nos permitirá realizar el seguimiento sobre todo el framework ASP.NET, MVC incluido.

Sin embargo, dado que el código fuente de ASP.NET MVC está disponible en Codeplex, es realmente sencillo estudiar su funcionamiento de esta forma. Sólo seguir estos pasos:
  • en primer lugar, descargamos el código fuente desde Codeplex y descomprimimos el archivo en cualquier carpeta,
  • abrimos la solución con Visual Studio y la compilamos en modo depuración,
  • abrimos (o creamos) con Visual Studio un proyecto MVC,
  • eliminamos en éste las referencias que introducen las plantillas por defecto hacia el ensamblado System.Web.Mvc (del GAC),
  • añadimos la referencia hacia el ensamblado System.Web.Mvc que encontraremos en el directorio bin de la carpeta del código fuente del framework.
Y con estas sencillas operaciones, ya podemos comenzar a disfrutar de inolvidables sesiones de depuración y experimentación ;-)

Punto de ruptura en ASP.NET MVC

Publicado en: Variable not found
Hey, ¡estoy en twitter!
martes, 16 de marzo de 2010
Como ya he comentado extensamente por aquí, T4MVC es una herramienta imprescindible a la hora de evitar “cadenas mágicas” cuando desarrollamos sistemas con el framework MVC.

A pesar de sus innumerables virtudes, desde sus comienzos este proyecto arrastra una molesta y pesada carga, asociada a la forma en que Visual Studio utiliza las T4: en principio sólo se ejecutan, y generan código por lo tanto, cuando el archivo de la plantilla es modificado.

El archivo .tt, siempre modificadoDavid Ebbo, el creado de T4MVC, aportó una solución transitoria al problema incluyendo en la propia plantilla el código para engañar al IDE, haciéndole creer que el archivo .tt estaba siendo modificado continuamente, y forzando así el proceso de generación de código. Aunque válida, resultaba un poco molesto tener el archivo siempre abierto para que este proceso funcionara.

Hace unos días, David comenta en su post “A better way to auto-run T4MVC when you build” la solución propuesta  por Joachim Lykke Andersen y que describe en su entrada “How to run T4MVC on build”, consistente en la creación de una macro de Visual Studio capaz de tomar el control antes del inicio de la compilación, e invocar la herramienta personalizada de generación de código.

Y de hecho, tanto ha convencido esta nueva solución a David, el padre de la criatura, que está considerando seriamente la eliminación de la solución de mantener el archivo continuamente modificado en favor de ésta, mucho más limpia.EnvironmentEvents

El procedimiento es el siguiente. En primer lugar, accedemos al entorno de desarrollo de macros, pulsando ALT y F11, y seleccionamos a continuación MyMacros > EnvironmentEvents.

Aparecerá un módulo VB sobre cuyo cuerpo pegamos la siguiente porción de código que, como se puede observar simplemente introduce una lógica al iniciar la compilación mediante la cual se localiza el archivo de plantilla “T4MVC.tt” y se invoca a su herramienta personalizada, en este caso, el generador de código T4:

Public Sub OnBuildBegin(ByVal Scope As EnvDTE.vsBuildScope, _
                        ByVal Action As EnvDTE.vsBuildAction) _
                        Handles BuildEvents.OnBuildBegin
 
    If Scope = vsBuildScope.vsBuildScopeSolution _
       Or Scope = vsBuildScope.vsBuildScopeProject Then
 
        Dim projectItem As VSProjectItem _
               = DTE.Solution.FindProjectItem("T4MVC.tt").Object
 
        If Not projectItem Is Nothing Then
            projectItem.RunCustomTool()
        End If
 
    End If
End Sub

Si aparece un error indicando que no existe la clase VSProjectItem, basta con incluir la referencia apropiada, utilizando la solución ofrecida por el propio entorno:

Importar VSLangProj 
Una vez finalizado, salvamos la macro y listo. A partir de ese momento, el generador será ejecutado en cada compilación de proyectos y soluciones.

De esta forma tenemos una solución que nos permite, una vez configurada la macro, olvidarnos del proceso de generación, delegándolo al entorno de desarrollo, que para eso está ;-). En breve, además, se espera la aparición de un plugin para Visual Studio que permita automatizar aún más el proceso, y la posibilidad de activar y desactivar este comportamiento; hay que tener en cuenta que al tratarse de una macro a nivel de entorno, se ejecutará siempre al compilar cualquier tipo de proyecto, sea MVC o no.
En definitiva, es una prueba más de que a veces los problemas tienen soluciones mucho más simples de lo que en principio puede parecer. Y por cierto, la idea es aplicable a cualquier tipo de plantilla T4, simplemente retocándolo un poco ;-)
Editado 17/03/2010: ha aparecido un plugin para VS2008 que realiza esta misma tarea.

Publicado en: Variable not found
martes, 26 de enero de 2010
“Error al conectarse al administrador de deshacer del archivo de código fuente c:\blahblah\archivo.designer.cs” es un mensaje con el que Visual Studio (tanto la versión 2005 como 2008) me ha abofeteado en numerosas ocasiones cuando estoy desarrollando sitios web ASP.NET.

Y la verdad, es uno de esos casos en los que no sabes qué hacer: recompilas, limpias la solución, abres y cierras el IDE… pero nada, cuando le da por ahí, no hay forma de hacerlo entrar en razón:

Error al conectarse al administrador de deshacer del archivo de código fuente

Como ya habría corregido el problema en el momento de escribir este post y no pude reproducirlo para capturar la ventana, os muestro cómo luce en su versión en inglés ;-)

imageBuscando un poco de información, parece ser que se trata de un problema que aparece al editar el archivo .aspx mientras se está depurando la página, siguiendo una secuencia determinada.

Una de las formas de solucionarlo es un poco bestia, pero funciona (al menos en VS 2008): basta con eliminar el archivo xx.designer.cs indicado por el error (previo backup, por si acaso, aunque hasta ahora no me ha hecho falta nunca ;-)) y forzar al entorno a que lo genere de nuevo, pulsando el botón derecho del ratón sobre el archivo .aspx y seleccionando la opción “Convertir en aplicación web” que habrá aparecido en el mismo.

De momento no le he visto contraindicaciones, y estoy de lo más contento :-)

Publicado en: Variable not found.
domingo, 24 de mayo de 2009

Yellow Screen of Death El código introducido en las vistas ASP.NET MVC pueden ser fuente de problemas en tiempo de ejecución debido a que, por defecto, no se compilan con el proyecto, sino cuando el sistema necesita mostrarlas por primera vez. Es decir, es perfectamente posible que errores de codificación en un archivo .ASPX no sean detectados hasta el momento de su ejecución, provocando un YSOD de los de toda la vida.

Un ejemplo. Imaginemos una vista tipada (en un archivo .ASPX) en la que hemos incluido el siguiente código, perfectamente válido…

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" 
Inherits
="System.Web.Mvc.ViewPage<Persona>" %>
[...]
<h2>Datos personales</h2>
Nombre:
<%= this.Model.Nombre %>

[...]
… perfectamente válido mientras la clase Persona tenga una propiedad que se llame Nombre. Si eliminamos esta propiedad o simplemente cambiamos su nombre, la refactorización automática del IDE no se realizará sobre el código de la vista, y se generará un error en ejecución en cuanto intentemos acceder a ella, es decir, estaremos llevando a tiempo de ejecución problemas propios del tiempo de compilación.

Afortunadamente, ASP.NET MVC framework incluye un mecanismo que nos permite forzar la compilación de las vistas y, así, detectar estos problemas de forma temprana, antes de poner el sitio en producción.

Para activarlo, lo primero que hay que hacer es abrir el archivo del proyecto (.csproj o .vbproj) con cualquier editor de textos, como el block de notas, localizar en las primeras líneas el comienzo de la sección PropertyGroup, e insertar justo después la declaración que habilita la compilación de las vistas:

<PropertyGroup>
<MvcBuildViews>true</MvcBuildViews>
[...]
</PropertyGroup>

Ojo a un detalle: es posible que en algún punto del archivo del proyecto exista una declaración que anule la anterior así: <MvcBuildViews>false</MvcBuildViews>. Debéis eliminar esa línea, pues de lo contrario no se activará esta característica.

Acto seguido, debemos asegurarnos de que exista el siguiente código cerca del final del archivo; en mi caso ya existía, pero si no es así debemos insertarlo, por ejemplo, justo antes del cierre del documento (en la línea anterior a </Projects>).

<Target Name="AfterBuild" Condition="'$(MvcBuildViews)'=='true'">
<AspNetCompiler VirtualPath="temp"
PhysicalPath
="$(ProjectDir)\..\$(ProjectName)" />
</Target>

Una vez guardado el archivo, recargamos la solución en Visual Studio, y listo. Eso sí, a partir de este momento tendremos que esperar un poco más durante la generación del proyecto, pero la seguridad que aporta bien vale la pena.

Fuente: Compile Views in ASP.NET MVC - Stack Overflow

Publicado en: Variable not found.

lunes, 11 de mayo de 2009

Joe Cartano, perteneciente al Visual Web Development Team de Microsoft, ha publicado en el blog oficial (Visual Web Developer Team Blog) un post anunciando que ha actualizado la plantilla que permite crear directamente proyectos ASP.NET MVC con tests unitarios NUnit.

Así, una vez instalado, al crear un proyecto de tipo ASP.NET MVC nos aparecerá un cuadro de diálogo como el siguiente, en el que se nos brindará la oportunidad de crear en la solución un proyecto de pruebas unitarias utilizando NUnit:

Crear proyecto de pruebas unitarias

Para los poseedores de algunas de las versiones profesionales de Visual Studio 2008, esta ventana no es nueva, puesto que por defecto ya se incluyen los proyectos de tests propios de Microsoft, pero sí lo es para la versión express del IDE, que no incluye el framework de pruebas y en el que había que crear el proyecto a mano.

El proyecto, que se crea automáticamente, realiza 27 tests sobre la plantilla original de proyectos ASP.NET MVC, y pueden servir como base para seguir creando nuestro propio conjunto de pruebas:

¡La plantilla ASP.NET MVC valida!

La plantilla se instala descargando el archivo .zip cuyo enlace encontraréis en el post original y ejecutando, si es necesario con un usuario con privilegios de administrador, el script installNUnit.cmd disponible en el raíz del directorio donde lo hayáis descomprimido.

Para que todo vaya bien, debéis contar con NUnit previamente instalado en vuestro equipo. Pero ojo, las plantillas están preparadas para la versión 2.4.8.0 y la versión actual es la 2.5.0.9122, por lo que puede que no os funcionen bien si estáis a la última.

En este caso, es necesario tocar a mano los archivos de plantillas. Podéis seguir estos pasos:

  1. Descargad el archivo de plantillas desde el blog del Visual Web Developer Team.
  2. Descomprimidlo sobre cualquier carpeta.
  3. En los subdirectorios CSharp y Visual Basic encontraréis un archivo llamado MvcApplication.NUnit.Tests.zip. Son las plantillas para cada uno de esos lenguajes.
  4. El interior de dicho zip está el archivo de proyecto (MvcApplication.NUnit.Test.vbproj en el caso de Visual Basic, MvcApplication.NUnit.Test.csproj en C#). Descomprimid sólo estos archivos y buscar en ambos la cadena “2.4.8.0” y sustituirla por la versión de NUnit que tengáis instalada (en mi caso, “2.5.0.9122”). Al acabar, actualizad de nuevo el .zip con el archivo que acabáis de editar.
  5. Ejecutad el script de instalación installNUnit.cmd.

Publicado en: Variable not found.

miércoles, 1 de abril de 2009
LambdaEn el primer post intentamos describir qué eran las expresiones lambda, resumiendo muy brevemente su utilidad, así como los aspectos sintácticos de este tipo de construcciones del lenguaje. El segundo lo enfocamos a explicar el papel de las lambda como funciones anónimas, su estrecha relación con los delegados, y su forma de utilización.

En esta tercera y última entrega de la serie, vamos a centrarnos en otra de las grandes utilidades de las expresiones lambda en .NET framework: la definición de árboles de expresión.

Las lambda como árboles de expresión

Árbol de expresiónLos compiladores de C# y VB.NET pueden, bajo determinadas circunstancias, utilizar las lambdas para crear un árbol de expresión, una estructura en memoria que representa en forma de árbol las operaciones a realizar, y en el orden que hay que hacerlo, para lograr un objetivo. Si lo queréis ver más claro, observad el diagrama adjunto, en el que se muestra el árbol de expresión correspondiente a una función lambda que obtiene la media de dos números.

Esto es muy diferente al caso anterior, donde hablábamos de las lambda como funciones ejecutables que podían ser referenciadas por delegados e invocadas de forma directa. Entonces las expresiones lambda eran transformadas en tiempo de compilación en código ejecutable (de hecho, en métodos estáticos que pueden ser consultados usando Reflector u otros desensambladores), y por ello podíamos utilizarlas de forma directa.

En el caso de los árboles de expresión, la compilación no genera código ejecutable correspondiente a las instrucciones definidas en la lambda, sino el código para crear y llenar el árbol con dicha expresión. Después, en tiempo de ejecución, será posible recorrer dicho árbol, analizarlo, seriarlo para almacenarlo o moverlo a otras capas, e incluso compilarlo para poder lanzar su ejecución.

Pero paremos aquí un momento... según lo dicho, cuando el compilador se encuentra con una expresión lambda, por ejemplo x => x * 2, puede optar por generar una función anónima o por generar el código de llenado del árbol, ¿cómo sabe lo que debe hacer?

Pues bien, el compilador elegirá la opción adecuada dependiendo del tipo de la referencia a la expresión lambda. Si se trata de un tipo de delegado como Func o Action, generará código ejecutable y las referencias a la función serán tratados como delegados; si se usa el tipo Expression, que veremos más adelante, se generará el árbol de expresión y las referencias no se considerarán delegados, sino objetos de este tipo. Por este motivo no podemos utilizar para declarar lambdas variables locales de tipo implícito: el compilador no sabría qué hacer.

var dobla = a => a * 2; // Error: no se puede asignar
// una expresión lambda a una variable
// local de tipo implícito.

 

Definición de árboles con expresiones lambda

Para definir un árbol de expresión a partir de una lambda debemos utilizar el tipo genérico Expression<TipoDelegado>, siendo TipoDelegado un delegado como Action o Func de los descritos en el post anterior. ¿Que esto os parece confuso? Pues esperad a ver el ejemplo... ;-)
  Expression<Func<int, int, int>> media = (a, b) => (a + b) / 2;
 
En realidad, aparte de lo difícil de leer que es una declaración de este tipo, la idea es bastante sencilla. Lo que estamos indicando al compilador es que queremos montar un árbol de una expresión de la que conocemos el tipo de los parámetros de entrada y del valor de retorno, datos que indicamos mediante el delegado que utilizamos como argumento genérico de la clase Expression, la porción que he marcado de amarillo en el ejemplo anterior. Además, este delegado lo utilizaremos cuando queramos convertir el árbol de expresión en código ejecutable, más adelante veremos cómo.

Para que quede claro, ahí van algunos ejemplos más:
  // Árbol de expresión de una función que determina
// si el parámetro que le llega es par

Expression<Func<int, bool>> esPar = a => a%2==0;

// Árbol de expresión de una función
// que retorna el máximo de dos números.

Expression<Func<int, int, int>> maximo =
(a, b) => a > b ? a:b;

// Árbol de expresión de una función que
// retorna un string transformado

Expression<Func<string, string>> TrimMays =
a => a.Trim().ToUpper();

// Árbol de expresión de una acción
// sin parámetros que escribe por consola la fecha y hora.

Expression<Action> accion =
() => Console.WriteLine(DateTime.Now);
 
Fijaos que el tipo de delegado Action o Func, que son los parámetros genéricos de la expresión, son los que definen los tipos de parámetros y retornos de la lambda. En el primer caso, el Func<int, bool> define que la función recibe un entero y retorna un booleano; en el último caso, ni se envía ni se recibe nada, de ahí que utilicemos un delegado Action.

Un detalle importante, antes de que se me olvide comentarlo. Los árboles de expresión sirven, como su nombre indica, para almacenar expresiones. Esto limita el tipo de construcciones de código que podemos usar en las lambdas que los definen: no está permitido utilizar asignaciones, bloques con llaves { }, ni bucles... debe tratarse de una expresión que pueda representarse en una jerarquía de árbol. Si intentamos saltarnos estas restricciones, el compilador generará un error, aunque el tipo de delegado sea correcto y la función lambda también, como en el siguiente ejemplo:
  // Suma los n primeros naturales
Expression<Func<int, int>> expr =
n => {
int t = 0;
for (int i = 1; i <= n; t += i++) ;
return t;
};


// Error CS0834: Una expresión lambda con cuerpo no puede ser
// convertida a árbol de expresión

 
Árbol de expresiónVolvemos un poco atrás ahora para recordar cuando comentaba que la compilación de una lambda no genera código ejecutable correspondiente a las instrucciones definidas en la expresión, sino el código para llenar el árbol de expresión que la representa.

¿Y cómo hace eso? Muy sencillo. El compilador analiza la expresión, genera la secuencia de instrucciones que compone el árbol, las compila, e introduce el resultado en el ensamblado. El siguiente ejemplo muestra cómo crear un árbol de expresión usando una lambda, y una aproximación al código equivalente que genera el compilador, para que nos podamos hacer una idea del trabajo que nos ahorra esta característica del lenguaje:

// Árbol definido con una lambda:
Expression<Func<int, bool>> esPar = a => a % 2 == 0;

// Y el mismo árbol definido a mano,
// lo que genera el compilador automáticamente:
ParameterExpression param = Expression.Parameter(typeof(int), "a");
ConstantExpression dos = Expression.Constant(2, typeof(int));
ConstantExpression cero = Expression.Constant(0, typeof(int));
BinaryExpression modulo = Expression.Modulo(param, dos);
BinaryExpression comparacion = Expression.Equal(modulo, cero);
Expression<Func<int, bool>> esPar2 =
Expression.Lambda<Func<int, bool>>(comparacion, param);
 

Uso de los árboles de expresión

Ya sabemos qué son los árboles de expresión y cómo podemos definirlos, pero aún no le hemos visto sentido a su existencia. Pero lo tiene, vaya si lo tiene ;-)

En primer lugar, el hecho de poder definir el árbol partiendo de una expresión lambda, además de comodidad a la hora de codificar, nos permite aprovechar el tipado fuerte y la potencia del intellisense para evitar errores. En el ejemplo anterior, las probabilidades de que nos equivoquemos creando el árbol de forma manual son muy superiores a que ocurra si utilizamos la sintaxis lambda.

Segundo, fijaos que en ningún momento se está generando código IL o compilando la expresión representada por la lambda. Estamos creando una estructura en memoria. Esto quiere decir que después podemos procesar esta información como estimemos conveniente; podemos, por ejemplo, analizar su contenido, recorrerlo, seriarlo, o transformarlo, en función de nuestras necesidades. Es ideal, por tanto, cuando tengamos interés en interpretar una expresión para realizar alguna acción con ella.

Vamos a ver ahora varios ejemplos para ilustrar el uso de las lambdas y árboles de expresión en el mundo real.

Linq

En el caso de Linq aplicado a un proveedor externo de datos relacional (por ejemplo, las tecnologías Linq to SQL, o Entity Framework), no tiene interés alguno el código ejecutable asociado a una expresión, sino su estructura, pues al final va a ser traducida al lenguaje o tecnología del almacén de información. Supongamos la siguiente consulta para obtener los productos que comienzan por "S":
  // Usando una consulta Linq:
var datos = from p in productos
where p.Nombre.StartsWith("S")
select p;

// O su equivalente usando
// operadores de consulta:

var datos = productos.Where(p=>p.Nombre.StartsWith("S"));

 
La lambda resaltada servirá para crear un árbol de expresión con las condiciones indicadas, pues el método de extensión Where aplicado acepta un predicado de tipo Expression. No se genera ningún método anónimo para la expresión lambda, ni se traduce a IL su contenido: sólo interesa para definir la expresión que será introducida en el árbol. Más adelante, en el momento de extraer realmente la información desde el almacén correspondiente, el componente proveedor de datos recorrerá y analizará la estructura en memoria, generando su equivalente en SQL, que es lo que lanzará al SGBD para obtener los datos.

Por cierto, existen en la actualidad una gran cantidad de proveedores de Linq, capaces de transformar los árboles de expresión en consultas a casi cualquier tipo de almacén. Fijaos que la posibilidad de separar la codificación lambda de la interpretación de la expresión hace posible su utilización en una gran variedad de ámbitos.

Cálculo simbólico

Otro ejemplo que ilustra muy bien las posibilidades de los árboles de expresión, de mano del maestro Octavio Hernández. Se trata del artículo Cálculo simbólico en C# 3.0, publicado en la web de El Guille a principios de 2007.

A lo largo del artículo se realiza la implementación básica de un sistema de cálculo de derivadas de funciones matemáticas partiendo de un árbol de expresiones. El proceso, que el autor va explicando paso a paso, consiste en analizar el árbol, e ir generando otro árbol con el resultado de la derivación de cada expresión encontrada. A continuación se muestra la porción de código donde se realiza la derivación de una operación de suma, utilizando recursividad para derivar además cada uno de los sumandos:
  private static Expression Derive(this Expression e, string paramName)
{
switch (e.NodeType)
{
[...]
// sum rule
case ExpressionType.Add:
{
Expression dleft =
((BinaryExpression) e).Left.Derive(paramName);
Expression dright =
((BinaryExpression) e).Right.Derive(paramName);
return Expression.Add(dleft, dright);
}
[...]
}
}
 
Al final, fijaos que de nuevo no nos interesa en absoluto la lambda como función anónima ni delegado, sino la estructura de la propia expresión, de forma que podamos recorrerla y transformarla en otra expresión, en este caso la función derivada de la original. El siguiente ejemplo muestra el uso de esta clase:
   Expression<Func<double, double>> 
funcion = x => x*x; // f(x)=x^2

Expression<Func<double, double>>
derivada = funcion.Derive(); // f'(x)=2*x

Console.WriteLine(derivada); // Muestra la función derivada:
// x => ((x * 1) + (1 * x))

 
Creedme, vale la pena echarle un vistazo.

ASP.NET MVC

Un último ejemplo, que demuestra la versatilidad del uso de lambdas y árboles de expresión en multitud de escenarios. En el framework ASP.NET MVC, es posible crear enlaces hacia acciones desde dentro de las vistas (páginas .ASPX), que no son sino métodos dentro de unas clases concretas llamadas "controladores". El caso es que para hacer referencia a un método del controlador, pueden utilizarse estas dos vías, de resultado idéntico:
  // Obtiene un enlace al método "ChangePassword"
// de la clase "AccountController":

Html.ActionLink("Cambiar clave", "ChangePassword", "Account")

// Lo mismo, pero usando un árbol de expresión:
Html.ActionLink<AccountController>
(acc=>acc.ChangePassword(), "Cambiar clave")
 
Aunque el resultado es el mismo, la segunda usa un árbol de expresión para realizar la referencia al método ChangePassword de la clase AccountController. La implementación del método ActionLink recorre el árbol generado desde la expresión lambda para obtener el nombre del controlador y del método, por lo que es equivalente a primera fórmula, pero beneficiándose de las ventajas del tipado fuerte y del intellisense en la edición.

Los árboles de expresión como código ejecutable

Hasta ahora siempre me he referido a los árboles de expresión como entidades de almacenamiento. De alguna u otra forma, estaba equiparando su utilidad a la cualquier estructura de datos que permitiera guardar y procesar información, lo cual es cierto pero sólo parcialmente.

Los árboles de expresión aportan una característica adicional: se pueden convertir en código ejecutable. Es decir, es posible compilar un árbol de expresión en tiempo de ejecución, dando lugar a una función anónima a la que podemos tener acceso, es decir, invocarla, a través de sus delegados.

Veámoslo con un caso concreto. Retomando el ejemplo de obtención de derivadas, sería perfectamente posible obtener el valor de la función derivada en un punto ampliando ligeramente el código visto anteriormente:
  Expression<Func<double, double>> 
funcion = x => x*x; // f(x)=x^2

Expression<Func<double, double>>
derivada = funcion.Derive(); // f'(x)=2*x

// Compilamos la función derivada
// y obtenemos un delegado a la misma:

Func<double, double> funcDerivada = derivada.Compile();

double result = funcDerivada(6); // Obtenemos el valor de la función
// invocando al delegado con x=6

Console.WriteLine(result); // Muestra "12"
 
Como se puede observar en el código, la llamada al método Compile() devuelve un delegado del tipo Func indicado en el parámetro genérico de la expresión, que apunta hacia la función anónima creada "al vuelo" a partir de las expresiones contenidas en el árbol. Es decir, desde la definición simbólica contenida en el árbol ¡obtenemos código ejecutable!

Aunque espectacular, en realidad no hay nada mágico en esta característica: se trata de ir recorriendo el árbol y emitiendo el código IL correspondiente a cada expresión, que está perfectamente tipificada y definida. En el namespace System.Linq.Expressions existe una bonita clase interna llamada ExpressionCompiler que se dedica exclusivamente a ello, utilizando herramientas suministradas por System.Reflection.Emit, como los generadores de lenguaje intermedio ILGenerator.

¡Y hasta aquí hemos llegado!

A lo largo de estos tres posts hemos recorrido las principales características y utilidades de las expresiones lambda. Obviamente, han quedado cosas por detrás; no era objetivo de esta serie profundizar demasiado, sino ofrecer una visión suficiente para animar a los desarrolladores a utilizar esta potente característica que nos ofrece C# (y VB.NET).

Espero que os haya sido una lectura útil, al menos tanto como me ha resultado a mí su escritura. Ah, y para consultas, sugerencias o puntualizaciones, por aquí me tenéis.

Publicado en: www.variablenotfound.com.
LambdaEn el post anterior intentamos realizar una primera aproximación a las expresiones lambda, centrándonos en obtener una definición lo suficientemente cercana, que nos permitiera conocer a grandes rasgos qué son, así como en describir su forma general y sus particularidades sintácticas.

En esta segunda entrega vamos a profundizar un poco en el papel de las expresiones lambda como vía para definir muy rápidamente funciones anónimas y los tipos de delegados con los que podemos referenciarlas, y por tanto, invocarlas.

Ya en el tercer post describiremos el papel de las expresiones lambda como herramienta de generación de árboles de expresión.

Las lambdas como funciones anónimas

Como habíamos insinuado anteriormente, uno de los usos de las expresiones lambda es permitir la definición "en línea" de funciones anónimas. De hecho, en tiempo de compilación las expresiones lambda son convertidas en métodos a los que el compilador establece un nombre único autogenerado, como los ejemplos mostrados a continuación:

Transformación de lamdas en métodos
Las referencias a estas funciones anónimas son transformadas en delegados (punteros) a las mismas, lo que nos permitirá, por ejemplo, invocarlas desde el código. En la práctica esto quiere decir que podemos asignar una lambda a una variable y ejecutarla como muestra el siguiente pseudocódigo:

delegado duplica = x => x * 2;
escribe duplica(2); // Escribe un 4
 
En este primer acercamiento, fijaos que duplica es el nombre del delegado, la función definida en forma de expresión lambda no tiene nombre, será el compilador el que se asigne uno.

Veamos cómo se concreta esta idea en C#. En el siguiente código, la variable duplica apunta hacia una función anónima definida a través de la expresión lambda en cuya implementación lo único que se hace es retornar el doble del valor que le llega como parámetro. Vemos también cómo podemos utilizarla de forma directa:
  Func<int, int> duplica = x => x * 2;
int result = duplica(7); // result vale 14
 
Sólo con objeto de que podáis entender el código anterior, os adelantaré que la porción Func<int, int> es una forma rápida de tipificar el delegado, indicando que duplica apunta a una función que espera un entero como parámetro de entrada, y que su valor de retorno será otro entero. Esto lo veremos dentro de un momento.

De la misma forma que asignamos la expresión lambda a una variable, podemos hacerlo también para indicar el valor de un parámetro a un método que acepte un delegado concreto. Por ejemplo, el siguiente código muestra un método llamado calcula que recibe un valor entero y una referencia a una función, retornando el resultado de efectuar dicha operación sobre el entero proporcionado:
  // Es método ejecuta la función indicada por
// el parámetro operacion, enviándole el valor especificado,
// y retorna el resultado obtenido de la misma.

public int calcula(int valor, Func<int, int> operacion)
{
return operacion(valor); // retorna el resultado de aplicar la
// expresión indicada al valor.

}

// Usos posibles:
int i = calcula(4, x => x / 2); // Le pasamos una referencia a la
// función que estamos definiendo sobre
// la marcha. El resultado es que i=2.


int j = calcula(4, duplica); // Le pasamos la variable "duplica",
// que es una referencia a la lambda
// definida anteriormente. J valdrá 8.

 
<HistoriaDelAbuelete>
Seguro que a los más viejos del lugar esto le recuerda a los Codeblocks que utilizábamos en Clipper a principios de los 90 (uuf, cómo pasa el tiempo...). ¿Todavía reconocéis el siguiente código?
  bDuplica := { |n| n*2 }
? EVAL(bDuplica, 7) // Muestra un 14
 </HistoriaDelAbuelete>

Una consecuencia directa de que las expresiones lambdas sean referenciadas a través de delegados es que podemos utilizarlas en cualquier sitio donde se acepte un delegado, con la única precaución de escribirla teniendo en cuenta el tipo de su retorno y los parámetros que recibe. Un ejemplo claro lo tenemos en la suscripción a eventos, donde la técnica habitual consiste en utilizar un delegado a un método en el que se implementa la lógica del tratamiento de los mismos, algo como:
  // Nos suscribimos al evento MouseMove:
this.MouseMove += new MouseEventHandler(this.Form1_MouseMove);
[...]

// Tratamiento del evento MouseMove:
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
this.Text = e.X + "," + e.Y;
}
 
Como sabemos, podemos suscribirnos al evento MouseMove añadiéndole delegados del tipo MouseEventHandler, definido en System.Windows.Forms, cuya firma indica que recibe un parámetro de tipo object, otro de tipo MouseEventArgs y no retorna ningún valor, exactamente igual que sería un delegado anónimo (C# 2.0) escrito así:
  this.MouseMove += delegate(object sender, MouseEventArgs args)
{
this.Text = args.X + "," + args.Y;
};
 
Y dado que las lambdas pueden sustituir de forma directa a cualquier delegado, podemos utilizarlas para conseguir un código más compacto:
  this.MouseMove += (sender, args) => {
this.Text = args.X + "," + args.Y;
};
 
Llegados a este punto es conveniente aclarar que las expresiones lambda son características introducidas en los lenguajes, y por tanto en sus compiladores, pero no en la plataforma de ejecución (CLR) en sí. Por tanto, todo lo descrito hasta el momento era posible realizarlo antes que las lambda aparecieran por el horizonte, aunque de forma un poco más tediosa, utilizando mecanismos que la versión 2.0 del framework ponía a nuestra disposición, como los delegados y métodos anónimos. En este sentido, el uso de expresiones lambda aportan mucha simplicidad, elegancia y legibilidad al código.

Esto explica, además, que Visual Studio 2008 sea capaz de generar código para .NET 2.0 a partir de código fuente C# 3.0.

Tipos de delegados de expresiones lambda

Antes ya había adelantado que la definición Func<int, int> era simplemente una forma de indicar el tipo del parámetro que recibía la función lambda, así como el tipo del valor de retorno. En realidad, lo único que estábamos haciendo era definir, de forma muy sencilla y rápida, el delegado hacia la función.

Vamos a concretar esto un poco más, pero antes de continuar, una cosa: si para tí un genérico es un tipo de medicamento, mejor que leas algo sobre el tema antes de continuar, pues en caso contrario es posible que te pierdas un poco ;-). Pues probar leyendo una introducción a los generics en c#, o la Guía de programación de C#.

.NET Framework ofrece en el espacio de nombres System un conjunto de definiciones de genéricas de delegados para que podamos utilizarlos para "apuntar" hacia las funciones definidas mediante expresiones lambda, llamados Action y Func.

Utilizaremos los tipos Func para definir referencias a expresiones lambda que retornen un valor, o sea, funciones. De ahí su nombre. Los tipos Action, en cambio, están destinados a referenciar a lambdas que realicen acciones y que no retornen ningún valor. De ahí su nombre también. ;-)

Una de estas definiciones es la que habíamos usado en un ejemplo anterior:
  Func<int, int> duplica = x => x * 2;
 
Como se puede observar, al tratarse de una referencia a una función que retorna un valor, hemos utilizado un tipo Func con dos parámetros genéricos, que corresponde con la siguiente declaración existente en el espacio de nombres System:
  public delegate TResult Func<T, TResult>(T arg);
 
Por ello, cuando declarábamos que la variable duplica era del tipo Func<int, int>, lo que indicábamos era, en primer lugar que el parámetro que necesitaba la lambda era un int, y que ésta nos devolvería también un int, es decir, lo mismo que si hubiéramos definido duplica así, utilizando métodos anónimos de C# 2.0:
  // En el área de declaraciones:
public delegate int Duplicador(int arg);
...
// En el código:
Duplicador duplica = delegate(int k) { return k*2 };
 
Obviamente, la sintaxis lambda es mucho más compacta y expresiva.

En la práctica, lo único que tenemos que tener claro a la hora de referenciar una función lambda es el tipo de cada uno de los parámetros que usa, y el tipo de retorno. Estos se introducen, en ese orden, en los parámetros genéricos de la clase Func y listo. Como esto debe quedar claro, ahí van unos ejemplos de definición y uso:
  // Recibe un entero y retorna un booleano:
Func<int, bool> esPar = x => x%2==0;
Console.WriteLine(esPar(2)); // Muestra "True"

// Recibe dos enteros, retorna otro entero:
Func<int, int, int> suma = (a,b) => a+b;
Console.WriteLine(suma(2,3)); // Muestra "5"

// No recibe nada, retorna un texto:
Func<string> hora = () => "Son las "
+ DateTime.Now.ToShortTimeString();
Console.WriteLine(hora()); // Muestra "Son las 14:21:10"

 
Es importante saber que en el framework están definidos los delegados Func<tipo1, tipo2..., tipoResult> para funciones de hasta cuatro parámetros. Si necesitamos más deberemos definir los delegados a mano, aunque esto es realmente sencillo utilizando una de las declaraciones existentes y añadiéndole el número de parámetros que deseemos. Por ejemplo, para seis parámetros la definición del genérico sería algo así como:
  public delegate 
TResult Func<T1, T2, T3, T4, T5, T6, TResult>
(T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6);
 
Pero ahora aparece un pequeño problema: las funciones sin retorno no pueden referenciarse con delegados de tipo Func, puesto que el framework .NET no soporta la instanciación de tipos genéricos utilizando parámetros void (ECMA 335, sección 9.4, pág. 153). Por tanto, no podríamos declarar un delegado como Func<int, void> para apuntar hacia una función que recibe un entero y no devuelve nada. Si lo pensáis un poco, este es el motivo de que no exista ninguna sobrecarga de la clase Func sin parámetros genéricos, pues como mínimo debemos indicar el tipo del valor de retorno.

La clave para cubrir estos casos se encuentra en el tipo Action. Como comentaba unas líneas más arriba, el objeto de estos tipos de delegados es apuntar a expresiones lambda que realicen acciones y que no retornen ningún valor, por lo que sus parámetros genéricos describirán exclusivamente los tipos de los parámetros de la función. En este caso, como es obvio, sí existe una clase no parametrizada Action para apuntar a funciones sin parámetros, además de disponer de genéricos que cubren las acciones de hasta cuatro parámetros. Veamos unos ejemplos:
  // Acción sin parámetros (no genérica):
Action saluda = () => Console.WriteLine("hola");
saluda(); // Muestra "hola";

// Acción que recibe un string
Action<string> apaga = motivo => {
log(motivo);
shutdown();
};
apaga("mantenimiento"); // Apaga el sistema
 
Por último, me parece interesante recordar algo que había comentado en el post anterior, que en las expresiones lambda no era necesario indicar el tipo de los parámetros ni del retorno porque el compilador los infería del contexto. Como podemos ver, lo tiene bastante fácil, puesto que simplemente debe tomar la definición del delegado para conocerlos; por eso no es necesario introducir redundancias como las siguientes:
  Func<int, int> duplica = (int a) => (int)(a * 2); // ¡Redundante!

// Forma más cómoda:

Func<int, int> duplica = a => a * 2; // Ok!
 
En cualquier caso, si por algún motivo es necesario utilizar la forma explícita, sabed que no se permite hacerlo de forma parcial, es decir, o le ponéis los tipos a todo, o no se los ponéis a nada.

Y hasta aquí esta segunda entrega. En el siguiente post, el último de la serie, estudiaremos el uso de las lambda como herramientas de definición de árboles de expresión.

Por supuesto, para cualquier duda o sugerencia, ya sabéis dónde encontrarme. :-)

Publicado en: www.variablenotfound.com.
LambdaEntre las múltiples novedades aparecidas con C# 3.0 y VB.NET 9.0, las expresiones lambda son sin duda una de las que en principio pueden parecer más complejas, probablemente por su relación con conceptos no demasiado asimilables como los delegados, inferencia de tipado, métodos anónimos, o tipos genéricos, entre otros.

Sin embargo, esa aparente dificultad desaparece en cuanto se les presta un poco de atención, y una vez comprendidas aportan a los desarrolladores una potencia y agilidad difíciles de lograr con las herramientas disponibles hasta el momento. Sólo hay que ver su amplia utilización dentro del propio .NET framework, LINQ, y nuevas plataformas como ASP.NET MVC, para darse cuenta de su importancia. Y por si fuera poco, según cuentan los expertos, su uso "engancha".

A lo largo de esta serie de tres posts intentaré describir las expresiones lambda desde un punto de vista práctico, con la única pretensión de aportar algo de luz a los que todavía no han sucumbido a su poder. ;-)

El objetivo de este primer post es puramente introductorio, y trataré conceptos y nociones básicas para poder abordar los siguientes. En el segundo post de la serie trataremos las expresiones lambda como funciones anónimas, dejando para el tercero los misteriosos árboles de expresión.

Introducción a las lambda

Según la definición en la Referencia del lenguaje C# de MSDN:
"Una expresión lambda es una función anónima que puede contener expresiones e instrucciones y se puede utilizar para crear delegados o tipos de árboles de expresión"
En la Guía de programación de Visual Basic 9 encontramos otra definición, muy simple y pragmática:
"Una expresión lambda es una función sin nombre que calcula y devuelve un solo valor. Se pueden utilizar las expresiones lambda dondequiera que un tipo de delegado sea válido"
ScottGu también aportó su granito de arena para hacer el concepto más cercano a los desarrolladores; como siempre, al grano:
"Las Expresiones Lambda aportan una sintaxis más concisa y funcional para escribir métodos anónimos."
[...]
"La forma más sencilla para conceptualizar las expresiones lambda es pensar en ellas como formas de escribir métodos breves en una línea."
Partiendo de estas definiciones, y de otras muchas aportadas por Google ;-), está claro que las lambda son funciones, es decir, un conjunto de intrucciones capaces de retornar un valor partiendo de los parámetros que se les suministra, aunque en determinados casos es posible que no reciba ningún parámetro, o que realicen una acción sin retornar nada. Igual que una función tradicional, vaya. Y de hecho, en el cuerpo de una expresión lambda puede haber casi de todo: llamadas a otras funciones, expresiones, bucles, declaraciones de variables...

Sin embargo, a diferencia de los métodos o funciones habituales, las lambdas no necesitan de un identificador, puesto que se declaran in situ, justo en el momento en que van a asignarse a una variable o a utilizarse como parámetro de una función, pasando el destinatario de esta asignación a actuar como delegado, o puntero, hacia la misma, o a ser el contenedor del árbol de expresión que la representa. Ein? Chino, eh? No pasa nada, dentro de poco estudiaremos estos dos usos en profundidad, pero antes vamos a ver cómo se definen las expresiones lambda a nivel de código.

Forma de las expresiones lambda

Las expresiones lambda en C# se escriben según el patrón descrito a continuación, al que le siguen algunos ejemplos que lo ilustran e introducen algunas particularidades.
Forma general: parámetros => expresión, donde:
- parámetros: lista de parámetros separados por comas
- "=>" : separador.
- expresión: implementación de las operaciones a realizar

num => num * 2 // Lambda con un parámetro que retorna
// el doble del valor que se le pasa.


(a, b) => a + b // Lambda con dos parámetros que retorna
// la suma de ambos.


num => { // Lambda con cuerpo que recibe un
int x = new Random().Next(); // entero, y retorna la suma de éste
return num+x; // con un número aleatorio.
}

() => DateTime.Now // Lambda que no recibe parámetros
// y retorna la fecha y hora del sistema.


msg => Console.WriteLine(msg); // Recibe un parámetro, realiza una
// acción y no retorna nada.

 
Como se puede observar, cuando sólo existe un parámetro no es necesario utilizar paréntesis en el lado izquierdo de la expresión, mientras que hay que hacerlo en todos los demás casos. También es interesante destacar que las lambda con cuerpo deben utilizar return para retornar el valor deseado, cuando esto sea necesario.

Y un último dato: fijaos que ni los parámetros ni el retorno de la función tienen indicado un tipo. Aunque puede hacerse, normalmente no será necesario puesto que el compilador podrá inferir (deducir) el tipo a partir de su contexto, más adelante veremos cómo es esto posible. Por tanto, no es necesario escribir código tan extenso como:
  (int a, int b) => (int)(a+b)
 
Y hasta aquí este primer post introductorio. En el siguiente trataremos de explicar el papel de las expresiones lambda como funciones anónimas y facilitadoras del trabajo con delegados.

Publicado en: www.variablenotfound.com.
domingo, 11 de enero de 2009
Logo de Visual Studio
Un detalle que llama la atención al añadir una página maestra a un proyecto ASP.NET desde Visual Studio 2008 es que, por defecto, añade dos secciones de contenido (ContentPlaceHolder) a la estructura de la página.

Una de ellas es la habitual, en el cuerpo de <form runat="server">, que es donde las páginas basadas en esa MasterPage introducirán sus contenidos y controles. La otra, sin embargo, es una novedad respecto a versiones anteriores del IDE: en la sección <head> del documento:
<head runat="server">
<title></title>
<asp:ContentPlaceHolder ID="head" runat="server">
</asp:ContentPlaceHolder>

</head>
<body>
<form id="form1" runat="server">
<div>
<asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server">
</asp:ContentPlaceHolder>
</div>
</form>
</body>
</html>
 
Como habréis deducido, esto nos permite introducir contenidos personalizados en la sección <head> de una página concreto, como meta tags, o referencias a scripts que deseamos que se carguen con la página, evitando tener que incluirlos mediante programación. Así, si por ejemplo deseamos introducir una referencia a la librería jQuery en una página, la implementación de ésta incluiría los dos placeholders, tal y como sigue:
<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
<script type="text/javascript" src="/scripts/jquery.js"></script>
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
<!-- contenidos del webform -->
</asp:Content>
 
La buena noticia para los que todavía trabajamos con Visual Studio 2005 es que podemos aplicar esta misma técnica desde esta versión. Aunque por defecto el IDE no incluye el ContentPlaceHolder en el encabezado, podemos añadirlo a mano para que quede como en el código mostrado anteriormente, y el efecto será idéntico.

Eso sí, a cambio tendremos que aguantar que el entorno nos avise de que la etiqueta no es correcta en ese punto, puesto que sólo VS2008 es capaz de interpretarlo correctamente, pero se trata sólo de un molesto aviso, que no influirá en la compilación o ejecución de las páginas.


Publicado en: www.variablenotfound.com.
lunes, 8 de diciembre de 2008
Popurrí de tipos de gráficas permitidasPues tiene una pinta excelente el control para la generación de gráficas estadísticas Chart Control para ASP.NET 3.5, recientemente presentado en sociedad por Scottgu (con la habitual traducción en Thinking in .net).

Se trata de un componente con una versión específica para ASP.NET, válida para WebForms y MVC framework, y otra para Windows Forms, que permite generar gráficas estadísticas prácticamente de cualquier tipo, visualmente muy atractivas, realmente fáciles de utilizar en nuestas aplicaciones y, además, de forma gratuita.

Enumero características interesantes, o que me han llamado la atención (ambas cosas no están necesariamente unidas ;-)), del control para ASP.NET:
  • El control se renderiza en cliente con una etiqueta <img>.
  • Se puede forzar al control a generar las imágenes al vuelo o a almacenarlas físicamente en una carpeta.
  • Las imágenes generadas pueden ser cacheadas para mejorar el rendimiento.
  • Genera BMPs, JPGs, PNGs o EMFs.
  • Permite también usarlo con aplicaciones no ASP.NET 3.5 a través del modo "binary streaming", que fuerza a que el control elimine toda la salida HTML de la página donde se encuentra y retorne únicamente la imagen como resultado, de forma dicha página puede ser utilizada como source de un tag <img> en otro sitio.
  • Soporta eventos del tipo "PrePaint" y "PostPaint" para poder hacer retoques a mano sobre los resultados, como:
    void Chart1_PostPaint(object sender, ChartPaintEventArgs e)
    {
    e.ChartGraphics.Graphics.DrawString("Hola",
    new Font("Arial", 12f),
    Brushes.Black, 10, 10);
    }
     
  • 25 tipos de gráficas, muchas de ellas con vistas en tres dimensiones, en las que se puede modificar prácticamente todo: rotación, inclinación, sombras, etc.
  • Podemos crear imágenes con múltiples gráficas distintas, utilizar en ellas todas las series de datos que deseemos, con un número ilimitado de puntos.
  • Control total sobre los ejes en cuanto a escalado, visualización o etiquetado.
  • Posibilidad de añadir anotaciones, leyendas y otros elementos "extra".
  • Permite establecer datos enlazando el control a fuentes (binding), o de forma manual sobre el mismo utilizando los diseñadores o etiquetas ASP.NET.
  • Soporta mapeo de imágenes, posibilidad de capturar clicks sobre áreas para establecer comportamientos personalizados, o combinarlo con Ajax para enriquecer la experiencia de usuario.


Instalación

Antes de instalar, asegúrate que cumples el requisito previo básico, tener instalado Microsoft .NET Framework 3.5 SP1. Si no lo has hecho antes, ya sabes por dónde empezar ;-)

Una vez asegurado este punto, el siguiente paso es descargar Microsoft Chart Control, que incluye controles tanto para ASP.NET como para Windows Forms. Existe también, como descarga opcional, el paquete de idioma para Microsoft Chart Control, que contiene la localización del producto para otros idiomas.

Después, es una buena idea instalar el Add-on para Visual Studio 2008 que os facilitará el trabajo con el control desde este entorno de desarrollo, a base de diseñadores integrados. No olvidéis también bajaros también la documentación si váis a necesitar información detallada de las librerías incluidas.

Y, por último, para tomar conciencia del tipo de resultados que se pueden obtener con este control, el ideal es descargar los proyectos de demostración, que os permitirán ver y tocar una auténtica batería de ejemplos seguro muy útiles a la hora de usarlo en vuestros desarrollos, tanto ASP.NET como Winforms.

Prueba del Chart Control

Publicado en: www.variablenotfound.com.
martes, 2 de diciembre de 2008
Código fuenteAl escribir el post "Métodos genéricos en C#", estuve pensando en tratar este tema también en VB.NET de forma simultánea, pero al final preferí limitarme a C# para no hacer la entrada más extensa de lo que ya iba a resultar de por sí.

Esto, unido a un comentario de Julio sobre el propio post en el que preguntaba si existía algo parecido en Visual Basic .NET, ha hecho que reedite el mismo, pero centrándome esta vez en dicho lenguaje.

Los métodos genéricos son interesantes herramientas que están con nosotros desde los tiempos del .NET Framework 2.0 y pueden resultarnos muy útiles de cara a la construcción de frameworks o librerías reutilizables.

Podríamos considerar que un método genérico es a un método tradicional lo que una clase genérica a una tradicional; por tanto, se trata de un mecanismo de definición de métodos con tipos parametrizados, que nos ofrece la potencia del tipado fuerte en sus parámetros y devoluciones aun sin conocer los tipos concretos que utilizaremos al invocarlos.

Vamos a profundizar en el tema desarrollando un ejemplo, a través del cual podremos comprender por qué los métodos genéricos pueden sernos muy útiles para solucionar determinado tipo de problemas, y describiremos ciertos aspectos, como las restricciones o la inferencia, que nos ayudarán a sacarles mucho jugo.

Escenario de partida

Como sabemos, los métodos tradicionales trabajan con parámetros y retornos fuertemente tipados, es decir, en todo momento conocemos los tipos concretos de los argumentos que recibimos y de los valores que devolvemos. Por ejemplo, en el siguiente código, vemos que el método Maximo, cuya misión es obvia, recibe dos valores Integer y retorna un valor del mismo tipo:
  Function Maximo(ByVal uno As Integer, ByVal otro As Integer) _
As Integer
If uno > otro Then Return uno
Return otro
End Function
 
Hasta ahí, todo correcto. Sin embargo, está claro que retornar el máximo de dos valores es una operación que podría ser aplicada a más tipos, prácticamente a todos los que pudieran ser comparados. Si quisiéramos generalizar este método y hacerlo accesible para otros tipos, se nos podrían ocurrir al menos dos formas de hacerlo.

La primera sería realizar un buen puñado de sobrecargas del método para intentar cubrir todos los casos que se nos puedan dar:

Function Maximo(ByVal uno As Integer, ByVal otro As Integer) _
As Integer
' ...
End Function
Function Maximo(ByVal uno As Long, ByVal otro As Long) _
As Long
' ...
End Function
Function Maximo(ByVal uno As Decimal, ByVal otro As Decimal) _
As Decimal
' ...
End Function

' Y así hasta que te aburras...
 
Obviamente, sería un trabajo demasiado duro para nosotros, desarrolladores perezosos como somos. Además, según Murphy, por más sobrecargas que creáramos seguro que siempre nos faltaría al menos una: justo la que vamos a necesitar ;-).

Otra posibilidad sería intentar generalizar utilizando las propiedades de la herencia. Es decir, si asumimos que tanto los valores de entrada del método como su retorno son del tipo base Object, aparentemente tendríamos el tema resuelto. Lamentablemente, al finalizar nuestra implementación nos daríamos cuenta de que no es posible hacer comparaciones entre dos Object's, por lo que, o bien incluimos en el cuerpo del método código para comprobar que ambos sean comparables (consultando si implementan IComparable), o bien elevamos el listón de entrada a nuestro método, así:
  Function Maximo(ByVal uno As IComparable, ByVal otro As Object) As Object
If uno.CompareTo(otro) > 0 Then Return uno
Return otro
End Function
 
Pero efectivamente, como ya habréis notado, esto tampoco sería una solución válida para nuestro caso. En primer lugar, el hecho de que ambos parámetros sean Object o IComparable no asegura en ningún momento que sean del mismo tipo, por lo que podría invocar el método enviándole, por ejemplo, un String y un Integer, lo que provocaría un error en tiempo de ejecución. Y aunque es cierto que podríamos incluir código que comprobara que ambos tipos son compatibles, ¿no tendríais la sensación de estar llevando a tiempo de ejecución problemática de tipado que bien podría solucionarse en compilación?

El método genérico

Fijaos que lo que andamos buscando es simplemente alguna forma de representar en el código una idea conceptualmente tan sencilla como: "mi método va a recibir dos objetos de un tipo cualquiera T, que implemente IComparable, y va a retornar el que sea mayor de ellos". En este momento es cuando los métodos genéricos acuden en nuestro auxilio, permitiendo definir ese concepto como sigue:
  Function Maximo(Of T As IComparable) _
(ByVal uno As T, ByVal otro As T) As T
If uno.CompareTo(otro) > 0 Then Return uno
Return otro
End Function
 
En el código anterior, podemos distinguir una porción de código que aparece resaltada justo después del nombre del método, y antes de comenzar a definir sus parámetros. Es la forma de indicar que Maximo es un método genérico y operará sobre un tipo cualquiera al que llamaremos T, y mediante una restricción estamos indicando que deberá implementar obligatoriamenter el interfaz IComparable (más adelante trataremos esto en profundidad).

A continuación, podemos observar que los dos parámetros de entrada son del tipo T, así como el retorno de la función. Si no lo ves claro, sustituye mentalmente la letra T por Integer (por ejemplo) y seguro que mejora la cosa.

Lógicamente, estos métodos pueden presentar un número indeterminado de parámetros genéricos, como en el siguiente ejemplo. Observad que la palabra clave Of sólo se indica al principio:
  Function MiMetodo(Of T1, T2, TResult) _
(ByVal par1 As T1, ByVal par2 As T2) As TResult
 
Y una aclaración antes de continuar: lo de usar la letra T para identificar el tipo es pura convención, podríamos llamarlo de cualquier forma (por ejemplo Maximo(Of MiTipo)(ByVal uno as MiTipo, ByVal otro as MiTipo) As MiTipo), aunque ceñirse a las convenciones de codificación es normalmente una buena idea.

Restricciones en parámetros genéricos

Retomemos un momento el código de nuestro método genérico:
  Function Maximo(Of T As IComparable) _
(ByVal uno As T, ByVal otro As T) As T
If uno.CompareTo(otro) > 0 Then Return uno
Return otro
End Function
 
Antes había comentado que en este caso estabamos creando un método que podría actuar sobre cualquier tipo, aunque mediante una restricción forzábamos a que éste implementara, obligatoriamente, el interfaz IComparable, lo que nos permitiría realizar la operación de comparación que necesitamos.

Obviamente, las restricciones no son obligatorias; de hecho, sólo debemos utilizarlas cuando necesitemos limitar de alguna forma los tipos permitidos como parámetros genéricos, como en el ejemplo anterior. Está permitida la utilización de las siguientes reglas:
  • As Structure, indica que el argumento debe ser un tipo valor.
  • As Class, indica que T debe ser un tipo referencia.
  • As New, fuerza a que el tipo T disponga de un constructor público sin parámetros; es útil cuando desde dentro del método se pretende instanciar un objeto del mismo.
  • As nombredeclase, indica que el argumento debe heredar o ser de dicho tipo.
  • As nombredeinterfaz, el argumento deberá implementar el interfaz indicado.
  • As nombredetipogenérico, indica que el argumento al que se aplica debe ser igual o heredar del tipo, también argumento del método, indicado por nombredetipogenérico (observad en el siguiente ejemplo el parámetro T2).
Un último detalle relativo a esto: a un mismo parámetro se pueden aplicar varias restricciones, en cuyo caso se introducirán entre llaves ("{" y "}") separadas por comas, como aparece en el siguiente ejemplo:
  Function MiMetodo(Of T1 As {Class, IEnumerable}, _
T2 As {T1, New}, _
TResult As New) _
(ByVal par1 As T1, ByVal par2 As T2) As TResult
' ... Cuerpo del método
End Function
 

Uso de métodos genéricos

A estas alturas ya sabemos, más o menos, cómo se define un método genérico, pero nos falta aún conocer cómo podemos consumirlos, es decir, invocarlos desde nuestras aplicaciones. Aunque puede intuirse, la llamada a los métodos genéricos debe incluir tanto la tradicional lista de parámetros del método como los tipos que lo concretan. Vemos unos ejemplos:
  Dim mazinger As String = Maximo(Of String)("Mazinger", "Afrodita")
Dim i99 As Integer = Maximo(Of Integer)(2, 99)
 
Una interesantísima característica de la invocación de estos métodos es la capacidad del compilador para inferir, en muchos casos, los tipos que debe utilizar como parámetros genéricos, evitándonos tener que indicarlos de forma expresa. El siguiente código, totalmente equivalente al anterior, aprovecha esta característica:
  Dim mazinger As String = Maximo("Mazinger", "Afrodita")
Dim i99 As Integer = Maximo(2, 99)
 
El compilador deduce el tipo del método genérico a partir de los que estamos utilizando en la lista de parámetros. Por ejemplo, en el primer caso, dado que los dos parámetros son String, puede llegar a la conclusión de que el método tiene una signatura que coincide con la definición del genérico, utilizando String como tipo parametrizado.

Otro ejemplo de método genérico

Veamos un ejemplo un poco más complejo. El método CreaLista, aplicable a cualquier clase, retorna una lista genérica (List(Of T)) del tipo parametrizado del método, que rellena inicialmente con los argumentos (variables) que se le suministra:
  Function CreaLista(Of T)(ByVal ParamArray pars() As T) As List(Of T)
Dim list As New List(Of T)
For Each elem As T In pars
list.Add(elem)
Next
Return list
End Function

' ...
' Uso:

Dim nums = CreaLista(Of Integer)(1, 2, 3, 4, 5, 6, 7)
Dim noms = CreaLista(Of String)("Pepe", "Juan", "Luis")
 
Otros ejemplos de uso, ahora beneficiándonos de la inferencia de tipos:
  Dim nums = CreaLista(1, 2, 3, 4, 5, 6, 7)
Dim noms = CreaLista("Pepe", "Juan", "Luis")

' Incluso con tipos anónimos de VB.NET 9
Dim v = CreaLista( _
New With {.X = 1, .Y = 2}, _
New With {.X = 3, .Y = 4} _
)
Console.WriteLine(v(1).Y) ' Muestra "4"
 
En resumen, se trata de una característica de la plataforma .NET, reflejada en lenguajes como C# y VB.Net, que está siendo ampliamente utilizada en las últimas incorporaciones al framework, y a la que hay que habituarse para poder trabajar eficientemente con ellas.

Publicado en: www.variablenotfound.com.