martes, 10 de junio de 2008

C# es fácil (iv): Expresiones lambda

La inclusión de las expresiones lambda en C#, ha hecho que algunos empiecen a tener escalofríos y sudores fríos, recordando sus tiempos de estudiante de ingeniero informático y el cálculo lambda... Cuando Microsoft anunció, la incorporación de expresiones lambda en C# muchos se empezaron a preguntar si esto era el renacimiento de C# como lenguaje funcional. Al final, como suele suceder, no ha sido para tanto...

En definitiva podemos decir que las expresiones lambda son una sintaxis alternativa para los métodos anónimos (que aparecieron en C# 2.0, o sea Visual Studio 2005). Imaginemos el caso que queremos ordenar una lista, usando el método Sort. El mètodo Sort espera un delegate de tipo Comparison. Este delegate es un delegate genérico que espera dos argumentos de tipo T, y devuelve un int:

public delegate int Comparison<T>(T x,T y);

Si queremos ordenar una lista, usando un método anónimo, haríamos algo parecido a esto:

List<string> lista = new List<string>() { "Perro", "Gato", "Zorro" };

lista.Sort (delegate (string s1, string s2)

{

return s1.CompareTo(s2);

} );

Dentro de la llamada del método Sort, creamos el método anónimo (de tipo Comparison<string>).

Bien, pues básicamente las expresiones lambda son una sintaxis alternativa (muuucho más compacta), para hacer exactamente lo mismo. Fijaros como quedaría el código anterior usando expresiones lambda:

List<string> lista = new List<string>() { "Perro", "Gato", "Zorro" };

lista.Sort ( (x,y) => x.CompareTo(y));

Es una sintaxis mucho más compacta que "la clásica" de métodos anónimos, verdad??? ;-) Pues vamos a comentarla rápidamente...

El operador => es el operador lambda, y la sintaxis és param-list => valor_retorno siendo param-list una lista de parámetros (separados por comas), y valor_retorno una expresión que se evaluará y será el resultado de la expresión lambda.

Veamos otro ejemplo de como las expresiones lambda sustituyen (sintacticamente) a un método anónimo. Imaginemos el delegate:

public delegate T Updater<T> (T value);

Un delegate que toma un objeto de tipo T y devuelve otro del mismo tipo.

En la sintaxis estándard (C# 1.0 sin métodos anónimos) haríamos algo como:

Updater<int> pFoo = new Updater<int>(SumaUno);

donde SumaUno, seria un método definido como algo parecido a:

int SumaUno(int x) { return x + 1; }

Usando métodos anónimos (C# 2.0) la cosa nos queda como:

Updater<int> pFooAnonimo = delegate(int x) { return x + 10; };

Lo cual ya es más compacto que la sintaxis anterior... y usando las nuevas expresiones lambda, la cosa nos queda como:

Updater<int> pFooLambda = x => x + 50;

Más compacto... imposible, no??

Bueno... una cosilla debeis tener presente con las expresiones lambda... C# es un lenguaje fuertemente tipado, y por ello las expresiones lambda tienen tipo... Que nosotros no pongamos tipo en los parámetros de una expresión lambda, no significa que lo tengan, significa que el compilador debe poder deducirlos por el contexto.En este ejemplo que acabo de poner, el compilador deduce que "x" es de tipo int, porque estamos asignando la expresión lambda a un delegate que espera un parámetro de tipo int (y una expresión lambda siempre se asigna a un delegate, y es este delegate quien nos define el tipo de los parámetros). En el otro ejemplo que he expuesto antes (el de la llamada a Sort), el compilador puede deducir que los parámetros x e y son de tipo string, porque estoy llamando a Sort de una List<string> que por lo tanto me espera un delegate de tipo Comparison<string> que define dos parámetros de tipo string.

En resumen: las expresiones lambda son una nueva sintaxis (eso sí, mucho más compacta) para una funcionalidad que ya teníamos: los métodos anónimos en C#.