1. LINQ é map/filter/reduce, e mais
Se você já é confortável com .map(), .filter()
e .reduce() em JavaScript, LINQ (Language Integrated Query) vai parecer
natural — é o mesmo estilo de programação funcional aplicado a qualquer coleção
(IEnumerable<T>), com muito mais operadores prontos.
| JavaScript | LINQ (C#) |
|---|---|
.map(x => x.nome) | .Select(x => x.Nome) |
.filter(x => x.ativo) | .Where(x => x.Ativo) |
.reduce((a,b) => a+b, 0) | .Sum() / .Aggregate(...) |
.find(x => ...) | .FirstOrDefault(x => ...) |
.some(x => ...) | .Any(x => ...) |
.every(x => ...) | .All(x => ...) |
.sort((a,b) => ...) | .OrderBy(x => ...) |
2. Sintaxe de método (a mais usada)
var clientes = new List<Cliente>
{
new() { Nome = "Ana", Ativo = true, TotalCompras = 1500m },
new() { Nome = "Bruno", Ativo = false, TotalCompras = 200m },
new() { Nome = "Carla", Ativo = true, TotalCompras = 3200m },
};
var nomesAtivosOrdenados = clientes
.Where(c => c.Ativo)
.OrderByDescending(c => c.TotalCompras)
.Select(c => c.Nome)
.ToList();
// ["Carla", "Ana"]
decimal totalGeral = clientes.Sum(c => c.TotalCompras);
bool existeVip = clientes.Any(c => c.TotalCompras > 3000);
Cliente? maiorComprador = clientes.OrderByDescending(c => c.TotalCompras).FirstOrDefault();
Cada operador (.Where, .Select...)
devolve uma nova sequência — a lista original nunca é alterada. É o mesmo
princípio de imutabilidade dos métodos de array do JS (.map/.filter
também não alteram o array original).
3. Execução adiada (deferred execution)
Uma diferença real de comportamento: a maior parte dos operadores LINQ não executa
imediatamente — eles só descrevem a operação. A execução de fato só acontece quando você
itera o resultado (foreach) ou chama algo que força a materialização,
como .ToList() ou .Count().
var numeros = new List<int> { 1, 2, 3 };
var pares = numeros.Where(n => n % 2 == 0); // ainda não rodou nada
numeros.Add(4);
foreach (var n in pares) // AGORA a query roda, sobre a lista JÁ com o 4
Console.WriteLine(n); // imprime 2 e 4 — não só 2!
Se você precisa "congelar" o resultado no momento em que a query foi escrita, force a execução
imediata com .ToList(). Isso é especialmente importante quando a
fonte de dados pode mudar entre a definição da query e o momento em que ela é usada — e crítico
quando a "coleção" na verdade é uma tabela do banco via Entity Framework (módulo de integração),
onde .ToList() é o que decide quando a query realmente vai ao banco.
4. Agrupamento com GroupBy
O equivalente LINQ do GROUP BY que você já usou intensamente no
Módulo 2:
var pedidos = new List<Pedido> { /* ... */ };
var totalPorCliente = pedidos
.GroupBy(p => p.ClienteId)
.Select(grupo => new
{
ClienteId = grupo.Key,
TotalPedidos = grupo.Count(),
ValorTotal = grupo.Sum(p => p.Valor)
})
.OrderByDescending(x => x.ValorTotal)
.ToList();
Repare a semelhança estrutural com o SQL do capítulo 6 do Módulo 2 (GROUP BY
+ SUM/COUNT) — LINQ é, em essência, SQL
expresso como uma cadeia de métodos C#, operando sobre objetos em memória em vez de linhas de
tabela.
5. Tipos anônimos
new {'{'} ClienteId = grupo.Key, ... {'}'} no exemplo acima cria um
tipo anônimo — uma classe sem nome, gerada pelo compilador, útil quando você
precisa de uma forma de dado temporária que não justifica criar uma classe nomeada:
var resumo = new { Nome = "Ana", Total = 1500m };
Console.WriteLine(resumo.Nome); // "Ana" — propriedades acessíveis normalmente
// mas resumo não tem um nome de tipo que você possa escrever em outro lugar
6. Sintaxe de query (menos comum, mas você vai encontrá-la)
LINQ também tem uma sintaxe parecida com SQL de verdade — funcionalmente idêntica à sintaxe de método, só uma forma alternativa de escrever a mesma coisa:
var nomesAtivos =
from c in clientes
where c.Ativo
orderby c.TotalCompras descending
select c.Nome;
7. Onde isso te leva
LINQ vai reaparecer constantemente — inclusive como a base do Entity Framework Core, que traduz exatamente essas mesmas expressões para SQL de verdade. Antes disso, o próximo capítulo cobre async/await: como escrever código que espera uma operação (como uma chamada ao banco) sem travar a aplicação inteira.