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.

Fecha de publicación:
Jorge Turrado Jorge lleva en el mundo de la programación desde los tiempos de .Net Framework 3.0. Es experto en la plataforma .NET, en Kubernetes y en técnicas de integración continua entre otras cosas. Actualmente trabaja como Staff SRE en la empresa SCRM Lidl International Hub. Microsoft lo ha reconocido como MVP en tecnologías de desarrollo, es CNCF Ambassador y maintainer oficial de KEDA, el autoescalador de Kubernetes basado en eventos. Puedes seguirlo en Twitter: @JorgeTurrado o en su blog FixedBuffer Ver todos los posts de Jorge Turrado
Archivado en: Lenguajes y plataformas

Boletín campusMVP.es

Solo cosas útiles. Una vez al mes.

🚀 Únete a miles de desarrolladores

DATE DE ALTA

x No me interesa | x Ya soy suscriptor

La mejor formación online para desarrolladores como tú

Comentarios (18) -

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

Muy buena explicitación. Sin embargo me queda una duda.  Para usar LinQ, es necesario que se realice el mapeo de la base de datos con Entity Framework ? y que pasa si se trata de una base de datos diferente a SQL Server, por ejemplo Oracle. Si es Oracle, Entity Framework sirve igual ?

Responder

Jorge Turrado
Jorge Turrado

Buenas Oscar,
Linq es un sistema para hacer consultas a colecciones, pero no necesariamente tienen que ser a bases de datos. Puedes utilizar Linq con por ejemplo un array o una lista que tengas sin necesidad de ser objetos de una base de datos. La idea de esto es utilizar el lenguaje parecido al que usariamos en una base de datos para manejar colecciones en memoria.

Por otro lado, esta Entity Framework/Entity Framework Core, que es un ORM que lo que hace es permitirnos acceder a una base de datos como si de colecciones se tratase, y hace por nosotros la labor de convertir las consultas en Linq a SQL, pero este es solo uno de los casos de uso de Linq.

Te invito a probar el código de los ejemplos y seguro que lo ves más claro, no vas a tener ninguna base de datos de por medio, pero Linq va a seguir funcionando igual :)

Responder

Hola Jorge, muy buena expliación y buen aporte para entender y aprender sobre LINQ. Casualmente necesitaba una solución que todo me apuntaba a LINQ y la encontré gracias a tu explicación. Solo tengo una duda sobre un ejercicio y quizas puedas ayudar.  En una variable string la cual contiene los siguientes datos.

string data = "Pepe,6,3.6\nPaolo,5,1.7\nJose,1.3,2.6\nAlex,12,-1.5";
var values = datos.Split('\n');

var scoreQuery = from line in values
                             let fields = line.Split(',')
                             orderby fields[0] ascending
                             select line;

La expresión está perfecta, incluso ordena ascendentemente por campo, en el ejemplo anterior me ordena por los nombre,  pero cuando incluyo un número de 2 cifras enteras ya la expresión no me sirve, no los ordena.

De antemano muchas gracias por su gran aporte que me inspiró a resolver muchos ejercicios utilizando LINQ.

Responder

José Manuel Alarcón
José Manuel Alarcón

Hola Alexeis:

¿Qué es lo que pretendes hacer exactamente con esa expresión? Tal y como la tienes ordenas las líneas por el nombre que aparece en primera posición, y eso no cambia aunque le pongas cifras de dos o más dígitos en los otros valores, pues no se usan para nada.

Si pones un ejemplo más claro de lo que pretendes hacer igual te podemos echar una mano.

Saludos.

Responder

Hola, amigo, por la primera posición la expresión si cumple los requisitos, pero cuando ordenos por la 2da posición me lo ordena así:

var scoreQuery = from line in values
                             let fields = line.Split(',')
                             orderby fields[1] ascending
                             select line;

Me lo ordena así:
string data = "nJose,1.3,2.6\nAlex,12,-1.5\nPaolo,5,1.7\Pepe,6,3.6 ";

Es decir cuando ordeno por la 2da columna el 12 me lo está tomando como si fuera un 1, así me ha pasado cuando lo he probado con otros números.

Responder

Hola José a lo mejor no me explique bien, el ejercicio consiste en ordenar por columna los datos de un excel. El método tiene como entrada un string y la posición por la cual se quiere ordenar. El método no lo pongo para ahorrar lienas

string data = "Pepe,6,3.6\nPaolo,5,1.7\nJose,1.3,2.6\nAlex,12,-1.5";
var values = datos.Split('\n');

var scoreQuery = from line in values
                             let fields = line.Split(',')
                             orderby fields[1] ascending
                             select line;

Al ordenar por la posición 1 los campos no se me ordenan como se requiere, para las otras posiciones si.

Responder

José Manuel Alarcón
José Manuel Alarcón

Es que lo que estás ordenando son cadenas de texto, no números. Por lo tanto, por ejemplo, "12" es mayor que "5" porque se comparan el "1" y el "5", y luego ya no se sigue comparando porque "1" es menor que "5" al comparar tan solo cadenas. Es como funciona.

Si quieres compararlos como números, que es lo que son, debes antes convertirlos a números. Dado que los hay enteros y con decimales, lo suyo es convertirlos primero a tipos double, pero además como el separador de decimales que tienes es el punto y no la coma, el formato por defecto de tu sistema (que es español) no te vale porque en español el separador decimal es la coma, no el punto. Por lo tanto tienes que usar una cultura que use el punto como separador de decimales (por ejemplo el inglés americano), así:

using System.Globalization;

CultureInfo cultura = CultureInfo.CreateSpecificCulture("en-US");

Y ahora usas esa cultura para convertir las cadenas a números, así:

var scoreQuery = from line in values
                 let fields = line.Split(',')
                 orderby double.Parse(fields[1], cultura) ascending
                 select line;

Esto hará exactamente lo que quieres, aunque quizá no sea la mejor forma de hacerlo, pero funciona.

Saludos.

Responder

José Manuel Alarcón
José Manuel Alarcón

Perdón, donde puse "mayor" en la primera frase, quería decir "menor", obviamente: "...por ejemplo, "12" es MENOR que "5" porque se comparan el "1" y el "5"..."

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.