Qué es LINQ#
LINQ te permite hacer consultas sobre colecciones (filtrar, ordenar, transformar) con código corto y legible, casi como una frase. Necesita using System.Linq;.
Compara. Sin LINQ, para quedarte con los enemigos vivos:
List<Enemigo> vivos = new List<Enemigo>();
foreach (Enemigo e in enemigos)
if (e.Vida > 0)
vivos.Add(e);Con LINQ, una línea:
using System.Linq;
var vivos = enemigos.Where(e => e.Vida > 0).ToList();Las operaciones más útiles#
using System.Linq;
// Filtrar
var cercanos = enemigos.Where(e => e.Distancia < 10f);
// Transformar (proyectar)
var nombres = enemigos.Select(e => e.Nombre);
// Ordenar
var porVida = enemigos.OrderBy(e => e.Vida);
var porVidaDesc = enemigos.OrderByDescending(e => e.Vida);
// Encontrar uno
Enemigo jefe = enemigos.FirstOrDefault(e => e.EsJefe); // null si no hay
// Comprobaciones
bool hayJefe = enemigos.Any(e => e.EsJefe);
int cuantos = enemigos.Count(e => e.Vida > 50);
// Agregados
int vidaTotal = enemigos.Sum(e => e.Vida);
Enemigo masFuerte = enemigos.OrderByDescending(e => e.Daño).First();First lanza excepción si no encuentra nada; FirstOrDefault devuelve el valor por defecto (null para clases). Para buscar algo que puede no existir, usa FirstOrDefault y comprueba el resultado.
Encadenar operaciones#
Lo potente es combinarlas:
// Los 3 enemigos vivos más cercanos, por nombre
var objetivos = enemigos
.Where(e => e.Vida > 0)
.OrderBy(e => e.Distancia)
.Take(3)
.Select(e => e.Nombre)
.ToList();Se lee casi como una frase: "de los enemigos, los vivos, ordenados por distancia, coge 3, sus nombres, a lista".
La advertencia de rendimiento (importante en Unity)#
LINQ es precioso, pero genera basura para el recolector (GC): crea iteradores y objetos intermedios en cada consulta. Ejecutarlo cada fotograma (en Update) o para miles de elementos provoca tirones. Reglas:
- LINQ en código puntual (al inicio, en un evento, al abrir un menú): perfecto.
- En el bucle de juego (Update, sobre muchos objetos): usa un
foreach/fornormal, que no asigna basura. - ¿Dudas? Mide con el Profiler la columna GC Alloc.
Deferred execution: una sutileza#
LINQ es perezoso: enemigos.Where(...) no filtra hasta que recorres el resultado (o llamas a .ToList(), .Count()...). Esto tiene una consecuencia:
var consulta = enemigos.Where(e => e.Vida > 0);
// ...si aquí cambian las vidas...
foreach (var e in consulta) { } // se evalúa AHORA, con los datos actualesSi quieres "congelar" el resultado en el momento, añade .ToList().
Cuándo usar LINQ#
- ¿Consulta puntual y quieres código legible (menú, inicio, evento)? → LINQ, adelante
- ¿En Update o sobre miles de elementos por frame? → for/foreach normal (sin basura)
- ¿Es una operación crítica de rendimiento? → mídela; a menudo el bucle manual gana
Errores frecuentes#
- Usar LINQ en
Updatey provocar tirones por basura del GC. Firsten vez deFirstOrDefaulty petar cuando no hay resultados.- Olvidar
using System.Linq;. - Esperar que la consulta se evalúe al declararla (es perezosa; usa
.ToList()para materializarla).
Ponte a prueba#
Resumen#
LINQ (using System.Linq;) consulta colecciones de forma legible: Where (filtrar), Select (transformar), OrderBy (ordenar), FirstOrDefault/Any/Count/Sum, y se encadenan. Es ideal para código puntual, pero evítalo en Update y bucles calientes porque genera basura para el GC (usa for/foreach). Recuerda que es de evaluación perezosa: .ToList() para materializar.