Menú de navegaciónMenú
Categorías

La mejor forma de Aprender Programación online y en español www.campusmvp.es

?id=da1bbe12-8e4d-4e74-955b-f256e633fce3

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

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.

Language-Integrated Query (LINQ) es el nombre de un conjunto de tecnologías basadas en la integración de capacidades de consulta directamente en el lenguaje C#

Fuente: MSDN

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();

Como se puede comprobar, la lectura es mucho más clara, por lo que ganamos en mantenibilidad del código.

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<T>, 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.

Sobre la ejecución diferida de consultas se puede hablar largo y tendido ya que es una materia en sí misma. Para más información puedes consultar este enlace.

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.

¿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!

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.

Jorge Turrado Jorge lleva en el mundo de la programación desde los tiempos de .Net Framework 3.0. Experto en la plataforma .NET, .NET Core y en técnicas de integración continua, trabaja desde hace varios años en el desarrollo de sistemas comerciales de visión artificial. Microsoft lo ha reconocido como MVP en tecnologías de desarrollo en 2018. Puedes seguirlo en Twitter: @JorgeTurrado o en su blog FixedBuffer Ver todos los posts de Jorge Turrado
Archivado en: Lenguajes y plataformas

¿Te ha gustado este post?
Pues espera a ver nuestro boletín mensual...

Suscríbete a la newsletter

La mejor formación online para desarrolladores como tú

Comentarios (10) -

Hola Jorge gracias por tu aporte.

Cuando se utilizan los métodos de extensión, que es lo que hace realmente C#? como recorre los registros? "por debajo" lo que hace es hacer un for o foreach?

Si fuera así la ventaja seria como dices que el código se vuelve más legible y realizar filtros de manera más fácil.

Responder

Jorge Turrado
Jorge Turrado

Buenas tardes David,
Muchas gracias por tu comentario!!
Depende un poco de la extensión, por ejemplo, si vemos el código de la extensión "Max" en GitHub github.com/.../Max.cs#L28
No esta haciendo ni uno ni otro del todo, esta utilizando el GetEnumerator y Next(), pero podríamos decir que eso es el "equivalente" a utilizar un foreach.
La implementación de "Last", comprueba si la colección es "indexable", y si es indexable accede al último indice, y sino lo es la itera (con el mismo método que el anterior) para quedarse con el final:
github.com/.../Last.cs#L55

Cada método de extensión trabaja de manera un poco diferente ya que para optimizarlos no se puede generalizar. Como el código es público, puedes echarle un ojo al funcionamiento de Linq:
github.com/.../System.Linq

Y sí, la gran ventaja de Linq es la facilidad de uso y la legibilidad que aporta.

Responder

Marcelo Pfannkuche
Marcelo Pfannkuche

LINQ para mí todavía es un "cuco", cosas de la edad... pero lo has explicado tan bien y sencillo que tal vez me anime a ahondar más profundo en el tema. Exelente aporte!

(Marcelo PF)

Saludos

Responder

Hola Jorge, excelente explicación! voy a entrar en .net, mvc, ... y ahora veo claro el uso de LINQ. Solo una pregunta: voy a desarrollar en .net bajo framework 4.5 con MVC. Pero me recomiendan empezar ya con net.core (¿opinas igual?). LINQ ¿funciona igual en net. core? Es exactamente la misma sintaxis , formas, etc.?
Gracias!
Álvaro

Responder

Jorge Turrado
Jorge Turrado

Buenas Álvaro,
Gracias por tu comentario!
En primer lugar respondo a la pregunta sobre Linq, un rotundo SÍ (en mayúsculas ademas :) ). Linq se usa exactamente igual en .Net Framework y en .Net Core, la sintaxis no cambia para nada y el uso tampoco.
Respecto a si usar .Net Core o .Net Framework, Microsoft recomiendó hace unos meses utilizar siempre .Net Core siempre y solo utilizar .Net Framework cuando se añadan cosas a proyecto "legacy".
Mi recomendación es siempre que se pueda utilizar .Net Core para ejecutables y .Net Standard para bibliotecas. Son a mi modo de verlo ahora mismo el tandem ganador. Las librerías en .Net Standard se pueden consumir desde cualquier implementación de .Net (moderna). Los ejecutables aprovechan las muchísimas mejoras que tiene  .Net Core respecto a .Net Framework como una mejora brutal de rendimiento y encima son multiplataforma.
En resumen, yo usaría .Net Standard y .Net Core siempre que se pueda y no tienes que preocuparte los más minimo por Linq porque el uso es exactamente el mismo.

Responder

Jorge,
Muchas gracias por tu rapidísima respuesta. Sobre LINQ, con tu respuesta ya no me quedan dudas, gracias!
Sobre .Net Core, también me has resuelto este gran dilema que tenía. Y era muy importante para mí.
Gracias de nuevo!

Álvaro
PD: y gracias por tu aportación con este tipo de temas, nos aclaran dudas y a veces nos dan el empujón que necesitamos

Responder

Jesus Gomez
Jesus Gomez

Muy buen aporte.

Responder

Hola Jorge muchas gracias por tu explicación:

Tengo una duda, me pudieras decir que diferencia existe entre utilizar LINQ Query vs Métodos de extensión, ejemplo:

LINQ Query
            var corredores = from r in Formula1.ObtenCorredoresGanadores()
                         where r.CarrerasGanadas > 10 && (r.Pais == "Brasil" || r.Pais == "Alemania")
                         select r;

            foreach (var r in corredores)
            {
                Console.WriteLine($"{r}");
            }

Métodos de extensión:

            var corredores = Formula1.ObtenCorredoresGanadores()
                .Where(r => r.CarrerasGanadas > 10 && (r.Pais == "Brasil" || r.Pais == "Alemania"));

            foreach (var r in corredores)
            {
                Console.WriteLine($"{r}");
            }

Cual es mejor, ya que obtengo el mismo resultado, te pregunto ya que soy nuevo en C#.
Gracias por tu atención.
Saludos...

Responder

Jorge Turrado
Jorge Turrado

Hola Isaac,
Gracias por tu comentario!
Funcionalmente no hay ninguna diferencia, son exactamente iguales (por lo que es normal que obtengas el mismo resultado). La única diferencia que existe entre las dos sintaxis es eso, la sintaxis.
Que escribas las consultas utilizando una u otra no afecta en nada ni al rendimiento ni al resultado, simplemente usa la que te resulte más cómoda. Si que es cierto que en algunas empresas si que esta definido cual se debe usar pero solo es por motivos de coherencia en el código (imagina que cada programador que toca un proyecto hace las cosas con su propio estilo...)
Espero haber resulto tu duda :)

Responder

Jorge, muchas gracias por tu ayuda.
Saludos...

Responder

Agregar comentario

Los datos anteriores se utilizarán exclusivamente para permitirte hacer el comentario y, si lo seleccionas, notificarte de nuevos comentarios en este artículo, pero no se procesarán ni se utilizarán para ningún otro propósito. Lee nuestra política de privacidad.