Introducción rápida a LINQ con C#: manejar información en memoria nunca fue tan sencillo

21/08/2019Artículo original

Como programadores, es muy habitual tener que trabajar sobre colecciones de datos por un motivo u otro, seleccionar datos, agruparlos, sumarlos …

Una manera muy socorrida de trabajar con este tipo de datos es recorrer la colección. De hecho, es la solución para la gran mayoría de los lenguajes y la que mejor rendimiento nos ofrece.

Suponiendo que tenemos una lista de enteros, si queremos sumarlos podríamos hacer algo así:

var valores = new List<int> {1,2,3,4,5,6,7,8,9};var suma = 0;foreach (var valor in valores){ suma += valor;}

O si por ejemplo, queremos buscar los números que sean pares:

var valores = new List<int> {1,2,3,4,5,6,7,8,9};var pares = new List<int>();foreach (var valor in valores){ if (valor % 2 == 0) { pares.Add(valor); }}

La lista de ejemplos es infinita, y siempre vamos a poder encontrar una solución iterando la colección, pero… ¿Es la mejor opción?

¿Qué es LINQ?

Dicho de manera sencilla, LINQ (Language Integrated Query) es un conjunto de extensiones integradas en el lenguaje C#, que nos permite trabajar de manera cómoda y rápida con colecciones de datos, como si de una base de datos se tratase. Es decir, podemos llevar a cabo inserciones, selecciones y borrados, así como operaciones sobre sus elementos.

Todas estas operaciones las vamos a conseguir muy fácilmente gracias a los métodos de extensión para colecciones que nos ofrece el espacio de nombres “System.Linq” y a las expresiones lambda. Sin ir más lejos, los dos ejemplos anteriores se podrían hacer así:

var valores = new List<int> {1,2,3,4,5,6,7,8,9};var suma = valores.Sum();var pares = valores.Where(x => x % 2 == 0).ToList();

En el ejemplo anterior, vemos que LINQ puede devolvernos valores de operaciones, o devolvernos colecciones.

Seguramente te hayas fijado en el ToList() del segundo caso. Esto es porque LINQ siempre nos va a devolver un objeto de tipo IEnumerable, el cual debemos iterar. Hasta que no lo iteremos, la consulta no se ha ejecutado todavía, y solo tenemos una expresión sobre una colección, por eso invocamos ToList() para forzar la ejecución de la consulta.

  Tutorial SQL #6: Agrupaciones y funciones de agregación

Vamos a ver un resumen de algunas de sus herramientas más útiles.

Para los ejemplos, vamos a suponer una clase como esta:

public class Alumno{ public string Nombre { get; set; } public int Nota { get; set; }}

Y partamos de la base de que tenemos una colección de alumnos como esta:

var alumnos = new List<Alumno>{ new Alumno {Nombre = "Pedro",Nota = 5}, new Alumno {Nombre = "Jorge",Nota = 8}, new Alumno {Nombre = "Andres",Nota = 3}};

Ahora veamos algunas de las operaciones que podemos llevar a cabo con LINQ sobre esta:

Select

Nos va a permitir hacer una selección sobre la colección de datos, ya sea seleccionándolos todos, solo una parte o transformándolos:

var nombresAlumnos = alumnos.Select(x => x.Nombre).ToList();

Where

Nos permite seleccionar una colección a partir de otra con los objetos que cumplan las condiciones especificadas:

var alumnosAprobados = alumnos.Where(x => x.Nota >= 5).ToList();

First/Last

Esta extensión nos va a permitir obtener respectivamente el primer y el último objeto de la colección. Esto es especialmente útil si la colección está ordenada.

var primero = alumnos.First();var ultimo = alumnos.Last();

OrderBy

Gracias a este método, vamos a poder ordenar la colección en base a un criterio de ordenación que le indicamos mediante una expresión lambda. Análogamente, también existe OrderByDescending, el cual va a ordenar la colección de manera inversa según el criterio:

var ordenadoMenorAMayor = alumnos.OrderBy(x => x.Nota).ToList();var ordenadoMayorAMenos = alumnos.OrderByDescending(x => x.Nota).ToList();

Sum

Como hemos visto más arriba, nos va a permitir sumar la colección:

var sumaNotas = alumnos.Sum(x => x.Nota);

Max/Min

Gracias a esta extensión, vamos a poder obtener los valores máximo y mínimo de la colección:

var notaMaxima = alumnos.Max(x => x.Nota);var notaMinima = alumnos.Min(x => x.Nota);

Average

Este método nos va a devolver la media aritmética de los valores (numéricos) de los elementos que le indiquemos de la colección:

var media = alumnos.Average(x => x.Nota);

All/Any

Con este último operador, vamos a poder comprobar si todos o alguno de los valores de la colección cumplen el criterio que le indiquemos:

var todosAprobados = alumnos.All(x => x.Nota >= 5);var algunAprobado = alumnos.Any(x => x.Nota >= 5);

Sintaxis integrada

Aunque en los ejemplos anteriores hemos visto el uso directo de los métodos de extensión, otra de las grandes ventajas que tiene LINQ es que permite crear expresiones directamente en el código, de manera similar a si escribiésemos SQL directamente en C#. Por ejemplo:

var resultado = from alumno in alumnos where alumno.Nota >= 5 orderby alumno.Nota select alumno;

nos devolverá la lista de alumnos que tienen una nota superior a o igual a 5, ordenados por nota ascendentemente.

  Frameworks para desarrollo de aplicaciones móviles híbridas

¿No es algo casi mágico?

Ventajas y desventajas

Ahora que hemos visto un poco por dónde pisamos, es hora de que hablemos sobre las ventajas y desventajas que nos puede aportar utilizar LINQ en vez de iterar las colecciones.

La principal y única desventaja que tiene, es que es un poco más lenta que si utilizásemos bucles for o foreach para iterar la colección y hacer la operación. Por supuesto esto no es apreciable en prácticamente ninguna situación convencional, pero en entornos donde cada milisegundo cuenta, debes conocer que tiene un impacto.

Por otro lado, las ventajas que nos aporta LINQ son principalmente que el código es más legible, ya que utiliza una sintaxis muy declarativa de lo que está haciendo, y sobre todo, nos ofrece una manera unificada de acceder a datos, sean el tipo que sean, y tengan el origen que tengan. Por ejemplo, podemos utilizar LINQ para trabajar con bases de datos, con XML, con Excel, con objetos en memoria, ¡y hasta con Twitter!

  Todo sobre los archivos .PDB: qué son y por qué se generan siempre al compilar una aplicación .NET

Resumiendo

Pese a que en esta entrada solo hemos hecho una pequeña introducción con un resumen reducido de las extensiones más frecuentes que nos aporta LINQ (créeme que muy pequeño… te recomiendo mirar el espacio de nombres y ver todas sus opciones), es una herramienta muy potente. Tanto, que otros lenguajes la han implementado también.

Si bien es cierto que existe una merma de rendimiento respecto a iterar el bucle directamente, el rendimiento perdido en el 99,99% de los casos se compensa con el beneficio que aporta tener un código claro, legible y mantenible.

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Contiene enlaces a sitios web de terceros con políticas de privacidad ajenas que podrás aceptar o no cuando accedas a ellos. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Más información
Privacidad