C# 3.0. Nuevas características III

Introducción

Seguimos con un repaso a las características nuevas de C# 3.0, en la primera parte vimos el uso de de propiedades automáticas e inicializadores de objetos y colecciones. En la segunda parte vimos una introducción al uso de extensiones, que nos proporionan una forma no intrusiva de ampliar la funcionalidad de una clase.

En esta última parte vamos a ver la expresiones lambda y su integración con Linq así como algunas de las funciones que nos proporciona este último.

Expresiones Lambda

En C# 2.0 se introdujo el concepto de métodos anónimos. Estos métodos mejoraban la forma de escribir código de forma que nos ahorrabamos la necesidad de escribir un método para un delegado, simplificando conseguían una mejora de este estilo:

public delegate void MsgDelegate(string texto);

public void MuestraMensaje(string texto)
{
   MessageBox.Show(texto);
}

public void Logger(string texto)
{
   Logger.Add(texto);
}

public void Principal()
{
   miFuncion(Logger);
   miFuncion(MuestraMensaje);
}
public delegate void MsgDelegate(string texto);

public void Principal()
{
  mifuncion(delegate (string t) { Logger.Add(t); });
  mifuncion(delegate (string t) { MessageBox.Show(t); });
}

Como vemos con la implementación utilizando métodos anónimos nos ahorramos mucho código, puesto que evitamos la declaración de las funciones de los delegados. Esto es muy útil cuando no se trata de funciones que se vayan a reutilizar sino de una operación puntual que necesitamos realizar.

Las expresiones lambda vienen a simplificar aún más los métodos anónimos mediante un cambio en la sintaxis que los define. Las expresiones lambda están inspiradas en las expresiones del mismo nombre de los lenguajes funcionales y constituyen una de las bases de LinQ (en cuanto a utilidad). Vamos a ver el ejemplo anterior:

public void Principal()
{
  mifuncion( t => Logger.Add(t) );
  mifuncion( t => MessageBox.Show(t) );
}

Dado que el tipo del delegado está bien especificado, el compilador es capaz de extraer que tipo tiene t y actuar en consecuencia, por lo que nos ahorramos la declaración de t. La sintaxis de las expresiones lambda es difícil de explicar formalmente porque se comprende mucho más fácil con ejemplos

  expr = (param1, param2..., paramN) => código de la función

Por ejemplo, supongamos que vamos a definir una expresion lambda de tipo Func que es un delegado que nos proporciona .NET y que define un tipo de funcion que recibe dos enteros (los dos primeros int especificados) y devuelve un string.

Func<int,int,string> lambdaExpression = (x,y) => (x*y).ToString();

Por tanto vemos que las expresiones lambda suponen un reemplazo que mejora los métodos anónimos en cuanto a legibilidad y extensión. Ahora bien, las expresiones lambda, pese a ser útiles, donde realmente demuestran su utilidad es en la combinación con Linq.

LinQ

¿Qué es LinQ? LinQ significa Language Integrated Query (Querys integradas en el lenguaje) y nos permite realizar querys similares a SQL directamente en código sobre distintos tipos de colecciones.

En realidad LinQ son una serie de métodos de extensión para el interfaz IEnumerable principalmente que añaden cierta funcionalidad sobre ellas. Así LinQ define funciones tales como Select, Order by, Where o Cast por ejemplo. Así mismo LinQ define los principales delegados que actuarán sobre distintas funciones (el anterior Func que hemos visto está definido en LinQ) ... vamos a ver algunos ejemplos y una pequeña descripción de algunos de los métodos

  • Select: La función select es el equivalente a la función map de los lenguajes funcionales. Nos permíte aplicar una función de mapeado a cada uno de los valores de la lista y devolver otro tipo de lista si queremos (o la misma). Por ejemplo:

// Obtener el cuadrado de todos los elementos de la lista
// list<int> lista
lista.Select(x = x => x*x);

  • Where: Realiza una función de filtrado sobre la lista, se proporciona un delegado de tipo Func que quiere decir, un delegado cuya entrada es genérica y devuelve un boolean, y basado en dicho delegado comprueba, para cada elemento de la lista si lo cumple o no, devolviendo solo aquellos que lo cumplen. Por ejemplo:

// Obtener solo los elementos de lista menores que 5
// list<int> lista
List<int> menoresQCinco = lista.Where(x => x < 5);
// Obtener los elementos pares
lista.Where(x => x % 2 == 0

  • Order By: Ordena los elementos de la lista. Muy fácil de ver con un ejemplo

class Persona
{
  public string Nombre { get; set;}
  public string Edad { get; set; }
}

public void Test()
{
  List<Persona> personas = new List<Persona>();
  RellenaPersonas(personas);
  List<Persona> ordenada = personas.OrderBy(p => p.Edad);
}

  • Max/Min: Devuelve el máximo/mínimo elemento en una lista de enteros, decimales, doubles, etc.
  • Join: Realiza un join entre dos listas dadas

Query syntax

Otra de las formas de utilizar LinQ es la llamada query sintax. Básicamente es una forma equivalente al uso de métodos de extensión LinQ pero mediante una sintaxis diferente. Vamos a ver un ejemplo de uso y compararlo con el equivalente en métodos de extensión:

// Esto está sacado de la documentación de MSDN
string sentence = "the quick brown fox jumps over the lazy dog";
// Split the string into individual words to create a collection.
string[] words = sentence.Split(' ');

// Using query expression syntax.
var query = from word in words
            group word.ToUpper() by word.Length into gr
            orderby gr.Key
            select new { Length = gr.Key, Words = gr };
// Using method-based query syntax.
var query2 = words.
    GroupBy(w => w.Length, w => w.ToUpper()).
    Select(g => new { Length = g.Key, Words = g }).
    OrderBy(o => o.Length);

LinQ to SQL

LinQ to SQL es un mundo aparte. Formalmente la idea es la siguiente: como ya estamos utilizando un lenguaje con una sintaxis razonablemente parecida a la de SQL (y para realizar exactamente lo mismo) ¿por qué no ampliar el ámbito de dichas querys de LinQ para poder utilizarlas sobre DataSets, es decir, porque no utilizar esa forma de realizar querys para poder realizar querys directamente sobre un objeto de datos de .NET.

No obstante, aunque la idea es buena, de momento el único proveedor que soporta LinQ To SQL es SQL Server y por otro lado, por algunos comentarios que he leido por ahí, la traducción automática no siempre es buena (al final LinQ acaba traduciendo las querys en código a querys en SQL).

7
Average: 7 (1 vote)
Your rating: None