LINQ básico
LINQ (Language Integrated Query) es una característica fundamental de C# que permite realizar consultas directamente sobre colecciones utilizando una sintaxis similar a SQL. Esta tecnología integra las capacidades de consulta directamente en el lenguaje, eliminando la necesidad de aprender sintaxis específicas para diferentes fuentes de datos.
LINQ transforma la manera en que trabajamos con datos en C#, ofreciendo dos sintaxis principales: la sintaxis de consulta (similar a SQL) y la sintaxis de métodos (basada en expresiones lambda). Ambas proporcionan las mismas funcionalidades, pero se adaptan a diferentes estilos de programación y preferencias del desarrollador.
Sintaxis de consulta
La sintaxis de consulta utiliza palabras clave específicas que resultan familiares para quienes conocen SQL. Esta aproximación hace que las consultas sean más legibles y expresivas, especialmente para operaciones complejas.
using System;
using System.Linq;
using System.Collections.Generic;
List<int> numeros = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// Consulta para obtener números pares
var numerosPares = from numero in numeros
where numero % 2 == 0
select numero;
foreach (int par in numerosPares)
{
Console.WriteLine(par); // 2, 4, 6, 8, 10
}
La estructura básica de una consulta LINQ sigue el patrón: from variable in colección where condición select resultado. Esta sintaxis permite expresar consultas complejas de manera clara y concisa.
List<string> nombres = new List<string> { "Ana", "Carlos", "Beatriz", "David", "Elena" };
// Consulta para nombres que empiecen por 'A' o 'B', ordenados alfabéticamente
var nombresSeleccionados = from nombre in nombres
where nombre.StartsWith("A") || nombre.StartsWith("B")
orderby nombre
select nombre.ToUpper();
foreach (string nombre in nombresSeleccionados)
{
Console.WriteLine(nombre); // ANA, BEATRIZ
}
Sintaxis de métodos
La sintaxis de métodos utiliza métodos de extensión y expresiones lambda para crear consultas más compactas. Esta aproximación es especialmente útil para consultas simples y se integra naturalmente con el estilo de programación funcional.
List<int> numeros = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// Misma consulta usando sintaxis de métodos
var numerosPares = numeros.Where(n => n % 2 == 0);
foreach (int par in numerosPares)
{
Console.WriteLine(par); // 2, 4, 6, 8, 10
}
Los métodos de extensión más comunes en LINQ proporcionan funcionalidades específicas que se pueden encadenar para crear consultas complejas:
List<string> palabras = new List<string> { "programación", "LINQ", "consultas", "datos", "C#" };
// Encadenamiento de métodos LINQ
var resultado = palabras
.Where(p => p.Length > 4)
.OrderBy(p => p.Length)
.Select(p => p.ToUpper())
.ToList();
foreach (string palabra in resultado)
{
Console.WriteLine(palabra); // DATOS, CONSULTAS, PROGRAMACIÓN
}
Operaciones de filtrado
El filtrado es una de las operaciones más fundamentales en LINQ, permitiendo seleccionar elementos que cumplan criterios específicos. El método Where acepta una expresión lambda que debe devolver un valor booleano.
List<int> edades = new List<int> { 15, 22, 35, 18, 41, 28, 19 };
// Filtrar personas mayores de edad
var mayoresDeEdad = edades.Where(edad => edad >= 18);
Console.WriteLine($"Mayores de edad: {string.Join(", ", mayoresDeEdad)}");
// Mayores de edad: 22, 35, 18, 41, 28, 19
Las condiciones complejas se pueden expresar combinando operadores lógicos dentro de las expresiones lambda:
List<double> precios = new List<double> { 15.99, 23.50, 8.75, 45.00, 12.25, 67.80 };
// Productos en rango de precio específico
var preciosRango = precios.Where(p => p >= 10 && p <= 50);
Console.WriteLine($"Precios en rango: {string.Join(", ", preciosRango)}");
// Precios en rango: 15.99, 23.50, 45.00, 12.25
Operaciones de transformación
La transformación permite modificar o extraer información específica de cada elemento utilizando el método Select. Este método es especialmente útil para cambiar el tipo de datos o crear nuevas estructuras.
List<string> nombres = new List<string> { "juan", "maría", "pedro", "ana" };
// Transformar nombres a formato título
var nombresFormateados = nombres.Select(n =>
char.ToUpper(n[0]) + n.Substring(1).ToLower());
Console.WriteLine($"Nombres formateados: {string.Join(", ", nombresFormateados)}");
// Nombres formateados: Juan, María, Pedro, Ana
Las transformaciones complejas pueden crear objetos anónimos o estructuras completamente nuevas:
List<string> productos = new List<string> { "Laptop", "Mouse", "Teclado" };
// Crear objetos con información adicional
var productosConInfo = productos.Select(p => new
{
Nombre = p,
Longitud = p.Length,
EsLargo = p.Length > 5
});
foreach (var producto in productosConInfo)
{
Console.WriteLine($"{producto.Nombre}: {producto.Longitud} caracteres, Es largo: {producto.EsLargo}");
}
Operaciones de ordenamiento
El ordenamiento en LINQ se realiza mediante los métodos OrderBy y OrderByDescending, que organizan los elementos según criterios específicos. Estos métodos mantienen la estabilidad del ordenamiento, preservando el orden relativo de elementos equivalentes.
List<string> ciudades = new List<string> { "Madrid", "Barcelona", "Valencia", "Sevilla", "Bilbao" };
// Ordenar alfabéticamente
var ciudadesOrdenadas = ciudades.OrderBy(c => c);
Console.WriteLine($"Alfabético: {string.Join(", ", ciudadesOrdenadas)}");
// Ordenar por longitud del nombre
var ciudadesPorLongitud = ciudades.OrderBy(c => c.Length);
Console.WriteLine($"Por longitud: {string.Join(", ", ciudadesPorLongitud)}");
El ordenamiento múltiple se consigue encadenando criterios con ThenBy y ThenByDescending:
List<int> numeros = new List<int> { 15, 3, 8, 15, 22, 3, 11 };
// Ordenar por valor, luego por frecuencia (simulada con índice)
var numerosOrdenados = numeros
.Select((valor, indice) => new { Valor = valor, Indice = indice })
.OrderBy(x => x.Valor)
.ThenBy(x => x.Indice);
foreach (var item in numerosOrdenados)
{
Console.WriteLine($"Valor: {item.Valor}, Posición original: {item.Indice}");
}
Operaciones de agregación
Las operaciones de agregación calculan valores únicos a partir de colecciones completas. Estos métodos son fundamentales para obtener estadísticas y resúmenes de datos.
List<double> ventas = new List<double> { 1200.50, 850.25, 2100.75, 975.00, 1850.25 };
// Operaciones básicas de agregación
double totalVentas = ventas.Sum();
double ventaPromedio = ventas.Average();
double ventaMaxima = ventas.Max();
double ventaMinima = ventas.Min();
int cantidadVentas = ventas.Count();
Console.WriteLine($"Total: {totalVentas:C}");
Console.WriteLine($"Promedio: {ventaPromedio:C}");
Console.WriteLine($"Máxima: {ventaMaxima:C}");
Console.WriteLine($"Mínima: {ventaMinima:C}");
Console.WriteLine($"Cantidad: {cantidadVentas}");
Las agregaciones condicionales combinan filtrado y agregación en una sola operación:
List<int> calificaciones = new List<int> { 85, 92, 78, 96, 88, 73, 91, 87 };
// Contar calificaciones aprobatorias (>=80)
int aprobados = calificaciones.Count(c => c >= 80);
// Promedio solo de calificaciones altas (>=85)
double promedioAltas = calificaciones.Where(c => c >= 85).Average();
Console.WriteLine($"Aprobados: {aprobados}");
Console.WriteLine($"Promedio calificaciones altas: {promedioAltas:F2}");
Operaciones de búsqueda
Las operaciones de búsqueda permiten localizar elementos específicos dentro de las colecciones. LINQ ofrece varios métodos según el tipo de búsqueda requerida.
List<string> colores = new List<string> { "rojo", "azul", "verde", "amarillo", "negro" };
// Buscar el primer elemento que cumple una condición
string primerColorLargo = colores.First(c => c.Length > 4);
Console.WriteLine($"Primer color largo: {primerColorLargo}"); // verde
// Buscar de forma segura (puede no existir)
string colorConZ = colores.FirstOrDefault(c => c.Contains("z"));
Console.WriteLine($"Color con 'z': {colorConZ ?? "No encontrado"}"); // azul
Los métodos de búsqueda seguros evitan excepciones cuando no se encuentran elementos:
List<int> numeros = new List<int> { 10, 25, 30, 45, 60 };
// Verificar si existe algún elemento que cumple la condición
bool hayNumerosPares = numeros.Any(n => n % 2 == 0);
Console.WriteLine($"¿Hay números pares?: {hayNumerosPares}");
// Verificar si todos los elementos cumplen la condición
bool todosMayoresQueCinco = numeros.All(n => n > 5);
Console.WriteLine($"¿Todos son mayores que 5?: {todosMayoresQueCinco}");
Trabajando con objetos personalizados
LINQ demuestra su verdadero potencial al trabajar con colecciones de objetos personalizados, permitiendo consultas complejas sobre propiedades específicas.
public class Estudiante
{
public string Nombre { get; set; }
public int Edad { get; set; }
public double Promedio { get; set; }
public string Carrera { get; set; }
}
List<Estudiante> estudiantes = new List<Estudiante>
{
new Estudiante { Nombre = "Ana", Edad = 20, Promedio = 8.5, Carrera = "Informática" },
new Estudiante { Nombre = "Carlos", Edad = 22, Promedio = 7.2, Carrera = "Matemáticas" },
new Estudiante { Nombre = "Beatriz", Edad = 19, Promedio = 9.1, Carrera = "Informática" },
new Estudiante { Nombre = "David", Edad = 21, Promedio = 6.8, Carrera = "Física" }
};
// Consulta compleja sobre objetos personalizados
var estudiantesDestacados = estudiantes
.Where(e => e.Promedio > 8.0 && e.Carrera == "Informática")
.OrderByDescending(e => e.Promedio)
.Select(e => new { e.Nombre, e.Promedio });
foreach (var estudiante in estudiantesDestacados)
{
Console.WriteLine($"{estudiante.Nombre}: {estudiante.Promedio}");
}
// Beatriz: 9.1
// Ana: 8.5
Las consultas de agrupación permiten organizar datos según características comunes:
// Agrupar estudiantes por carrera
var estudiantesPorCarrera = estudiantes
.GroupBy(e => e.Carrera)
.Select(grupo => new
{
Carrera = grupo.Key,
Cantidad = grupo.Count(),
PromedioGeneral = grupo.Average(e => e.Promedio)
});
foreach (var grupo in estudiantesPorCarrera)
{
Console.WriteLine($"{grupo.Carrera}: {grupo.Cantidad} estudiantes, promedio: {grupo.PromedioGeneral:F2}");
}
Fuentes y referencias
Documentación oficial y recursos externos para profundizar en CSharp
Documentación oficial de CSharp
Alan Sastre
Ingeniero de Software y formador, CEO en CertiDevs
Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, CSharp es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.
Más tutoriales de CSharp
Explora más contenido relacionado con CSharp y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
- Comprender qué es LINQ y su integración en C#.
- Aprender la sintaxis de consulta y la sintaxis de métodos para realizar consultas.
- Realizar operaciones básicas de filtrado, transformación, ordenamiento y agregación.
- Aplicar técnicas de búsqueda y trabajar con colecciones de objetos personalizados.
- Utilizar agrupaciones y consultas complejas para organizar y analizar datos.
Cursos que incluyen esta lección
Esta lección forma parte de los siguientes cursos estructurados con rutas de aprendizaje