C# LINQ

LINQ ("Language Integrated Query", consulta integrada à linguagem) são os métodos que fazem perguntas a qualquer coleção: "quais custam menos de R$ 10?", "qual o total?", "agrupe por categoria". Esta página lista todos os operadores que você vai usar, por grupo, com exemplo e resultado. Para aprender com calma, comece por O que é LINQ; para ver tudo em uso num programa, o projeto Estoque da Lojinha.

Os resultados vêm do programa abaixo, rodado no dotnet 10.0.112 da máquina da Ana (pasta ~/cs/linq, cultura pt-BR) e no editor do guia, que deram a mesma saída. O LINQ está em System.Linq, que o C# já traz sem using.

Atalhos: O programa · Filtrar e projetar · Ordenar · Particionar · Um elemento · Perguntar · Agregar · Agrupar · Conjuntos e junções · Converter e gerar · Sintaxe de consulta · Execução adiada · Quando dá errado · Experimente


O programa de exemplo

Os exemplos usam dois conjuntos: o array n = [5, 3, 8, 1, 9, 2, 8] e uma lista de produtos da lojinha da Ana. A função J só junta o resultado numa linha, com espaços. Cada linha da saída começa com o número que aparece na coluna "Linha" das tabelas:

// linq.cs — um exemplo de cada operador
List<Produto> estoque =
[
    new("Caderno", "Papelaria", 12, 15.90m),
    new("Caneta", "Escrita", 40, 2.50m),
    new("Borracha", "Escrita", 3, 1.75m),
    new("Mochila", "Bolsas", 2, 89.90m),
    new("Lápis", "Escrita", 60, 1.20m),
    new("Papel A4", "Papelaria", 4, 27.50m),
];
int[] n = [5, 3, 8, 1, 9, 2, 8];
static string J<T>(IEnumerable<T> s) => string.Join(" ", s);

Console.WriteLine("-- filtrar e projetar");
Console.WriteLine($"1  {J(n.Where(x => x > 4))} | {J(n.Distinct())} | {J(n.Select(x => x * 10))}");
Console.WriteLine($"2  {J(estoque.Where(p => p.Qtd < 5).Select(p => p.Nome))}");
Console.WriteLine($"3  {J(n.Select((x, i) => $"{i}:{x}"))}");
Console.WriteLine($"4  {J(new[] { "a b", "c d e" }.SelectMany(s => s.Split(' ')))}");
Console.WriteLine($"5  {J(new object[] { 1, "dois", 3.0, 4 }.OfType<int>())} | {J(estoque.DistinctBy(p => p.Categoria).Select(p => p.Nome))}");

Console.WriteLine("-- ordenar");
Console.WriteLine($"6  {J(n.Order())} | {J(n.OrderDescending())} | {J(n.Reverse())}");
Console.WriteLine($"7  {J(estoque.OrderBy(p => p.Preco).Select(p => p.Nome))}");
Console.WriteLine($"8  {J(estoque.OrderBy(p => p.Categoria).ThenByDescending(p => p.Qtd).Select(p => $"{p.Categoria}/{p.Nome}"))}");

Console.WriteLine("-- particionar");
Console.WriteLine($"9  {J(n.Take(3))} | {J(n.Skip(5))} | {J(n.TakeLast(2))} | {J(n.SkipLast(5))} | {J(n.TakeWhile(x => x != 1))} | {J(n.SkipWhile(x => x != 1))}");
Console.WriteLine($"10 {J(n.Chunk(3).Select(c => $"[{J(c)}]"))} | {J(n.Take(2..4))}");

Console.WriteLine("-- elementos");
Console.WriteLine($"11 {n.First()} {n.First(x => x > 5)} {n.Last()} {n.ElementAt(2)} {n.FirstOrDefault(x => x > 100)} {n.FirstOrDefault(x => x > 100, -1)} {n.Single(x => x == 9)}");
Console.WriteLine($"12 [{estoque.FirstOrDefault(p => p.Nome == "Régua")?.Nome ?? "null"}] {J(new int[0].DefaultIfEmpty(7))}");

Console.WriteLine("-- perguntar");
Console.WriteLine($"13 {n.Any()} {n.Any(x => x > 8)} {n.All(x => x > 0)} {n.Contains(8)} {new int[0].Any()} {n.SequenceEqual([5, 3, 8, 1, 9, 2, 8])}");

Console.WriteLine("-- agregar");
Console.WriteLine($"14 {n.Count()} {n.Count(x => x == 8)} {n.Sum()} {n.Min()} {n.Max()} {n.Average():F2}");
Console.WriteLine($"15 {estoque.Sum(p => p.Qtd * p.Preco):C} {estoque.Average(p => p.Preco):C} {estoque.Max(p => p.Preco):C} {estoque.MaxBy(p => p.Preco)!.Nome} {estoque.MinBy(p => p.Qtd)!.Nome}");
Console.WriteLine($"16 {n.Aggregate((a, b) => a * b)} {n.Aggregate(100, (total, x) => total - x)}");

Console.WriteLine("-- agrupar");
foreach (var g in estoque.GroupBy(p => p.Categoria))
{
    Console.WriteLine($"17 {g.Key}: produtos {g.Count()}, unidades {g.Sum(p => p.Qtd)}");
}
Console.WriteLine($"18 {J(estoque.CountBy(p => p.Categoria).Select(kv => $"{kv.Key}={kv.Value}"))}");
Console.WriteLine($"19 {J(estoque.AggregateBy(p => p.Categoria, 0m, (t, p) => t + p.Qtd * p.Preco).Select(kv => $"{kv.Key}={kv.Value}"))}");

Console.WriteLine("-- conjuntos e junções");
int[] a = [1, 2, 3, 4], b = [3, 4, 5];
Console.WriteLine($"20 {J(a.Concat(b))} | {J(a.Union(b))} | {J(a.Intersect(b))} | {J(a.Except(b))}");
Console.WriteLine($"21 {J(a.Zip(b, (x, y) => x + y))} | {J(a.Append(9))} | {J(a.Prepend(0))}");
List<(string Categoria, string Empresa)> fornecedores = [("Papelaria", "Distribuidora Sol"), ("Escrita", "Canetas Brasil")];
var juntos = estoque.Join(fornecedores, p => p.Categoria, f => f.Categoria, (p, f) => $"{p.Nome}<-{f.Empresa}");
Console.WriteLine($"22 {string.Join(", ", juntos)}");
var todos = estoque.LeftJoin(fornecedores, p => p.Categoria, f => f.Categoria, (p, f) => $"{p.Nome}<-{f.Empresa ?? "?"}");
Console.WriteLine($"23 {string.Join(", ", todos)}");
Console.WriteLine($"24 {J(n.Index().Where(t => t.Item == 8).Select(t => t.Index))}");

Console.WriteLine("-- converter e gerar");
Dictionary<string, decimal> precos = estoque.ToDictionary(p => p.Nome, p => p.Preco);
Console.WriteLine($"25 {precos["Mochila"]:C} {n.ToHashSet().Count} {n.ToList().Count} {n.ToArray().Length}");
Console.WriteLine($"26 {J(Enumerable.Range(1, 5))} | {J(Enumerable.Repeat("ha", 3))} | {Enumerable.Empty<int>().Count()}");

record Produto(string Nome, string Categoria, int Qtd, decimal Preco);
$ dotnet run linq.cs
-- filtrar e projetar
1  5 8 9 8 | 5 3 8 1 9 2 | 50 30 80 10 90 20 80
2  Borracha Mochila Papel A4
3  0:5 1:3 2:8 3:1 4:9 5:2 6:8
4  a b c d e
5  1 4 | Caderno Caneta Mochila
-- ordenar
6  1 2 3 5 8 8 9 | 9 8 8 5 3 2 1 | 8 2 9 1 8 3 5
7  Lápis Borracha Caneta Caderno Papel A4 Mochila
8  Bolsas/Mochila Escrita/Lápis Escrita/Caneta Escrita/Borracha Papelaria/Caderno Papelaria/Papel A4
-- particionar
9  5 3 8 | 2 8 | 2 8 | 5 3 | 5 3 8 | 1 9 2 8
10 [5 3 8] [1 9 2] [8] | 8 1
-- elementos
11 5 8 8 8 0 -1 9
12 [null] 7
-- perguntar
13 True True True True False True
-- agregar
14 7 2 36 1 9 5,14
15 R$ 657,85 R$ 23,13 R$ 89,90 Mochila Mochila
16 17280 64
-- agrupar
17 Papelaria: produtos 2, unidades 16
17 Escrita: produtos 3, unidades 103
17 Bolsas: produtos 1, unidades 2
18 Papelaria=2 Escrita=3 Bolsas=1
19 Papelaria=300,80 Escrita=177,25 Bolsas=179,80
-- conjuntos e junções
20 1 2 3 4 3 4 5 | 1 2 3 4 5 | 3 4 | 1 2
21 4 6 8 | 1 2 3 4 9 | 0 1 2 3 4
22 Caderno<-Distribuidora Sol, Caneta<-Canetas Brasil, Borracha<-Canetas Brasil, Lápis<-Canetas Brasil, Papel A4<-Distribuidora Sol
23 Caderno<-Distribuidora Sol, Caneta<-Canetas Brasil, Borracha<-Canetas Brasil, Mochila<-?, Lápis<-Canetas Brasil, Papel A4<-Distribuidora Sol
24 2 6
-- converter e gerar
25 R$ 89,90 6 7 7
26 1 2 3 4 5 | ha ha ha | 0

Quase todos os operadores recebem uma lambda: x => x > 4 lê-se "dado x, diga se x é maior que 4". E quase todos devolvem uma sequência (IEnumerable<T>), por isso dá para encadear: .Where(…).OrderBy(…).Select(…).

Filtrar e projetar

OperadorO que fazExemploResultadoLinhaAula
Where(condição)fica com os que cumprem a condiçãon.Where(x => x > 4)5 8 9 81Filtrar e Projetar
Select(transformação)troca cada item por outra coisan.Select(x => x * 10) · estoque.Where(…).Select(p => p.Nome)50 30 80 … · Borracha Mochila Papel A41, 2
Select((x, i) => …)o mesmo, recebendo também a posiçãon.Select((x, i) => $"{i}:{x}")0:5 1:3 2:8 …3
SelectMany(…)cada item vira uma sequência, e todas viram uma só["a b", "c d e"].SelectMany(s => s.Split(' '))a b c d e4
OfType<T>()fica só com os do tipo T[1, "dois", 3.0, 4].OfType<int>()1 45
Distinct()tira os repetidosn.Distinct()5 3 8 1 9 21Filtrar e Projetar
DistinctBy(chave) .NET 6um item por chave (o primeiro de cada)estoque.DistinctBy(p => p.Categoria)Caderno Caneta Mochila5

Ordenar

OperadorO que fazExemploResultadoLinhaAula
OrderBy(chave)do menor para o maior pela chaveestoque.OrderBy(p => p.Preco)Lápis Borracha Caneta …7Ordenar
OrderByDescending(chave)do maior para o menorestoque.OrderByDescending(p => p.Preco)(veja a missão)—
ThenBy, ThenByDescendingdesempate: o segundo critério.OrderBy(p => p.Categoria).ThenByDescending(p => p.Qtd)Bolsas/Mochila Escrita/Lápis Escrita/Caneta …8
Order(), OrderDescending() .NET 7ordena pelo próprio valorn.Order() · n.OrderDescending()1 2 3 5 8 8 9 · 9 8 8 5 3 2 16
Reverse()inverte a ordemn.Reverse()8 2 9 1 8 3 56

Um segundo OrderBy desfaz o primeiro; para desempatar, é sempre ThenBy. Textos são ordenados como no dicionário (veja string).

Particionar

OperadorO que fazExemploResultadoLinha
Take(k)os k primeirosn.Take(3)5 3 89
Skip(k)pula os k primeirosn.Skip(5)2 89
TakeLast(k), SkipLast(k)os k últimos; todos menos os k últimosn.TakeLast(2) · n.SkipLast(5)2 8 · 5 39
TakeWhile(c), SkipWhile(c)pega (ou pula) enquanto a condição vale; para no primeiro que falhan.TakeWhile(x => x != 1) · n.SkipWhile(x => x != 1)5 3 8 · 1 9 2 89
Take(a..b) .NET 6um intervalon.Take(2..4)8 110
Chunk(k) .NET 6corta em pedaços de k (o último pode ser menor)n.Chunk(3)[5 3 8] [1 9 2] [8]10

Skip e Take juntos fazem paginação: a página p (do 0), com 10 por página, é .Skip(p * 10).Take(10). Aula: Filtrar e Projetar.

Um elemento só

OperadorDevolveSe não há nenhumSe há mais de umExemplo → resultado (linha 11, 12)
First(), First(c)o primeiroexceçãoo primeiron.First() → 5; n.First(x => x > 5) → 8
FirstOrDefault(c)o primeiroo padrão (0, null) ou o que você passaro primeiron.FirstOrDefault(x => x > 100) → 0; com , -1 → -1; num produto → null
Last(), LastOrDefault()o últimoexceção / padrãoo últimon.Last() → 8
Single(c)o únicoexceçãoexceçãon.Single(x => x == 9) → 9
SingleOrDefault(c)o únicoo padrãoexceção—
ElementAt(i), ElementAtOrDefault(i)o da posição iexceção / padrão—n.ElementAt(2) → 8
DefaultIfEmpty(v)a própria sequência, ou só v se ela estiver vazia[v]—new int[0].DefaultIfEmpty(7) → 7

Regra prática: quando "não achar" é normal (busca pelo que o usuário digitou), use FirstOrDefault e confira o null. Aula: Filtrar e Projetar.

Perguntar (devolvem bool)

OperadorPerguntaExemploResultado (linha 13)
Any()tem algum item?n.Any() · new int[0].Any()True · False
Any(c)algum cumpre?n.Any(x => x > 8)True
All(c)todos cumprem? (numa sequência vazia: True)n.All(x => x > 0)True
Contains(x)tem esse valor?n.Contains(8)True
SequenceEqual(outra)as duas têm os mesmos itens, na mesma ordem?n.SequenceEqual([5, 3, 8, 1, 9, 2, 8])True

Para saber se há itens, Any() é melhor que Count() > 0: ele para no primeiro. Aula: Agregar.

Agregar (um valor no fim)

OperadorO que calculaExemploResultadoLinha
Count(), Count(c)quantos; quantos cumpremn.Count() · n.Count(x => x == 8)7 · 214
Sum(), Sum(seletor)a soman.Sum() · estoque.Sum(p => p.Qtd * p.Preco)36 · R$ 657,8514, 15
Min(), Max()o menor, o maior valorn.Min() · estoque.Max(p => p.Preco)1 · R$ 89,9014, 15
Average()a média (de inteiros, dá double)n.Average() · estoque.Average(p => p.Preco)5,14 (com :F2) · R$ 23,1314, 15
MinBy(chave), MaxBy(chave) .NET 6o item com o menor, o maior valor da chaveestoque.MaxBy(p => p.Preco)!.Nome · estoque.MinBy(p => p.Qtd)!.NomeMochila · Mochila15
Aggregate(f)vai combinando os itens, dois a doisn.Aggregate((a, b) => a * b)17280 (o produto de todos)16
Aggregate(início, f)o mesmo, começando de um valorn.Aggregate(100, (total, x) => total - x)6416
LongCount()como Count, em long———

Aula: Agregar (Count, Sum, Average).

Agrupar

OperadorO que fazExemploResultadoLinhaAula
GroupBy(chave)uma "pilha" por chave; cada pilha g tem g.Key e é uma sequênciaestoque.GroupBy(p => p.Categoria)Papelaria: produtos 2, unidades 16 …17Agrupar
CountBy(chave) .NET 9quantos por chave, diretoestoque.CountBy(p => p.Categoria)Papelaria=2 Escrita=3 Bolsas=118
AggregateBy(chave, início, f) .NET 9um Aggregate por chaveestoque.AggregateBy(p => p.Categoria, 0m, (t, p) => t + p.Qtd * p.Preco)Papelaria=300,80 …19
ToLookup(chave)como GroupBy, mas roda na hora e permite lookup["Escrita"]var porCat = estoque.ToLookup(p => p.Categoria);——

As pilhas do GroupBy saem na ordem em que cada chave apareceu pela primeira vez.

Conjuntos e junções

OperadorO que fazExemplo (a = [1, 2, 3, 4], b = [3, 4, 5])ResultadoLinha
Concat(b)uma depois da outra, com repetidosa.Concat(b)1 2 3 4 3 4 520
Union(b)todos, sem repetidosa.Union(b)1 2 3 4 520
Intersect(b)os que estão nas duasa.Intersect(b)3 420
Except(b)os de a que não estão em ba.Except(b)1 220
Zip(b, f)junta item a item; para na menora.Zip(b, (x, y) => x + y)4 6 821
Append(x), Prepend(x)um item no fim, no começoa.Append(9) · a.Prepend(0)1 2 3 4 9 · 0 1 2 3 421
Join(outra, chaveA, chaveB, f)junta os pares com a mesma chave; quem não tem par fica de foraestoque.Join(fornecedores, p => p.Categoria, f => f.Categoria, …)a Mochila não aparece22
LeftJoin(…) .NET 10como Join, mas todos da esquerda ficam; sem par, o da direita vem como padrãoestoque.LeftJoin(fornecedores, …, (p, f) => … f.Empresa ?? "?")Mochila<-? aparece23
RightJoin(…) .NET 10o contrário: todos da direita ficam———
GroupJoin(…)cada item da esquerda com a lista dos pares da direita———
Index() .NET 9cada item com a sua posição, como tupla (Index, Item)n.Index().Where(t => t.Item == 8).Select(t => t.Index)2 624

Antes do .NET 10, o LeftJoin era escrito com GroupJoin + SelectMany + DefaultIfEmpty, uma receita que você ainda vai ver em código mais antigo. Aula: Juntar (Join).

Converter e gerar

OperadorO que fazExemploResultadoLinha
ToList(), ToArray()roda a consulta e guarda numa lista, num arrayn.ToList().Count · n.ToArray().Length7 · 725
ToDictionary(chave, valor)monta um dicionário (chave repetida = exceção)estoque.ToDictionary(p => p.Nome, p => p.Preco)["Mochila"]R$ 89,9025
ToHashSet()monta um conjunto, sem repetidosn.ToHashSet().Count625
Cast<T>()converte cada item para T (exceção se não der)———
Enumerable.Range(início, quantos)uma sequência de númerosEnumerable.Range(1, 5)1 2 3 4 526
Enumerable.Repeat(x, k)x repetido k vezesEnumerable.Repeat("ha", 3)ha ha ha26
Enumerable.Empty<T>()uma sequência vaziaEnumerable.Empty<int>().Count()026

Sintaxe de consulta

O LINQ tem uma segunda forma de escrever, parecida com SQL. O compilador transforma uma na outra: o programa é o mesmo.

// consulta.cs — a mesma pergunta nas duas sintaxes
int[] notas = [7, 4, 9, 5, 10, 6];

var metodos = notas.Where(n => n >= 6).OrderByDescending(n => n).Select(n => $"nota {n}");

var consulta = from n in notas
               where n >= 6
               orderby n descending
               select $"nota {n}";

Console.WriteLine(string.Join(", ", metodos));
Console.WriteLine(string.Join(", ", consulta));
$ dotnet run consulta.cs
nota 10, nota 9, nota 7, nota 6
nota 10, nota 9, nota 7, nota 6
Sintaxe de consultaMétodo equivalente
from x in fonte(o começo; x é o nome do item)
where condição.Where(x => condição)
orderby chave / orderby chave descending.OrderBy(…) / .OrderByDescending(…)
orderby a, b.OrderBy(…a).ThenBy(…b)
select expressão.Select(x => expressão)
group x by chave.GroupBy(x => chave)
join y in outra on x.A equals y.B.Join(outra, x => x.A, y => y.B, …)
let nome = expressão(um Select que guarda um valor no meio)
from a in x from b in a.Itens.SelectMany(…)

O guia usa a forma com métodos: ela é a mais comum e tem todos os operadores (Take, Count, MaxBy… não existem na sintaxe de consulta). Aula: O que é LINQ.

Execução adiada

Uma consulta como notas.Where(…) não calcula nada na hora: ela guarda a "receita", e a receita roda a cada vez que alguém percorre o resultado (com foreach, Count(), ToList()…).

// adiada.cs — a consulta roda quando é percorrida
List<int> notas = [7, 4, 9];
var aprovados = notas.Where(n => n >= 6);
var foto = notas.Where(n => n >= 6).ToList();
notas.Add(10);
Console.WriteLine($"consulta: {aprovados.Count()}  |  ToList: {foto.Count}");
$ dotnet run adiada.cs
consulta: 3  |  ToList: 2
Rodam na hora (devolvem um valor ou uma coleção pronta)Adiados (devolvem uma consulta)
ToList, ToArray, ToDictionary, ToHashSet, ToLookup, Count, Sum, Min, Max, Average, Aggregate, First, Single, Any, All, ContainsWhere, Select, SelectMany, OrderBy, ThenBy, GroupBy, Take, Skip, Distinct, Concat, Union, Join, Zip, Chunk, Index…

Se você vai percorrer o resultado várias vezes, ou a coleção pode mudar, termine com .ToList(). Exemplo completo no projeto Estoque.


Quando dá errado

CódigoMensagem real (lab)O que significa e como consertar
vazia.First()System.InvalidOperationException: Sequence contains no elements"A sequência não tem elementos". Use FirstOrDefault, ou confira com Any().
estoque.First(p => p.Nome == "Régua")System.InvalidOperationException: Sequence contains no matching element"Nenhum elemento combina". Use FirstOrDefault e confira o null.
[5, 8, 8].Single(x => x == 8)System.InvalidOperationException: Sequence contains more than one matching element"Mais de um combina". Se pode haver vários, use First ou Where.
[5, 8].Single()System.InvalidOperationException: Sequence contains more than one elementO Single exige exatamente um.
vazia.Average(), vazia.Max()System.InvalidOperationException: Sequence contains no elementsNão há média nem máximo de nada. Confira Any() antes. (O Sum() de uma vazia dá 0, sem erro.)
["Ana", "Bruno", "Ana"].ToDictionary(n => n, …)System.ArgumentException: An item with the same key has already been added. Key: AnaA chave se repete. Agrupe antes (GroupBy) ou use DistinctBy.
List<int> aprovados = notas.Where(n => n >= 6);error CS0266: Não é possível converter implicitamente tipo "System.Collections.Generic.IEnumerable<int>" em "System.Collections.Generic.List<int>". Existe uma conversão explícita (há uma conversão ausente?)O Where devolve uma consulta, não uma List. Termine com .ToList(), ou declare com var ou IEnumerable<int>.
estoque.Where(…).Nomeerror CS1061: ‘IEnumerable<Produto>’ não contém uma definição para "Nome" …O resultado é uma sequência, não um produto. Use Select(p => p.Nome), foreach ou First(). Cenário completo no projeto Estoque.

Experimente

Missão: o programa deveria mostrar os 3 produtos mais caros, do mais caro para o mais barato, mas mostra só os 3 primeiros da lista. Acrescente a ordenação que falta antes do Take. O certo é Mochila, Papel A4, Caderno.

List<Produto> estoque =
[
    new("Caderno", 15.90m),
    new("Caneta", 2.50m),
    new("Borracha", 1.75m),
    new("Mochila", 89.90m),
    new("Lápis", 1.20m),
    new("Papel A4", 27.50m),
];

// Os 3 mais caros, do mais caro para o mais barato
var maisCaros = estoque.Take(3).Select(p => p.Nome);
Console.WriteLine(string.Join(", ", maisCaros));

record Produto(string Nome, decimal Preco);
List<Produto> estoque =
[
    new("Caderno", 15.90m),
    new("Caneta", 2.50m),
    new("Borracha", 1.75m),
    new("Mochila", 89.90m),
    new("Lápis", 1.20m),
    new("Papel A4", 27.50m),
];

// Os 3 mais caros, do mais caro para o mais barato
var maisCaros = estoque.OrderByDescending(p => p.Preco).Take(3).Select(p => p.Nome);
Console.WriteLine(string.Join(", ", maisCaros));

record Produto(string Nome, decimal Preco);

Experimente: troque os operadores e as lambdas. Tente Where(x => x % 2 == 0), OrderByDescending, Skip(2).Take(3), Chunk(2)…

int[] notas = [7, 4, 9, 5, 10, 6, 8];
Console.WriteLine(string.Join(" ", notas.Where(x => x >= 6)));
Console.WriteLine(string.Join(" ", notas.Order()));
Console.WriteLine($"Média: {notas.Average():F1}");
Console.WriteLine($"Aprovados: {notas.Count(x => x >= 6)} de {notas.Length}");
Console.WriteLine($"Maior: {notas.Max()}, menor: {notas.Min()}");
Console.WriteLine(notas.Any(x => x == 10) ? "Alguém tirou 10!" : "Ninguém tirou 10.");

Resumo

  • Filtrar: Where. Transformar: Select, SelectMany. Ordenar: OrderBy + ThenBy. Pedaços: Take, Skip, Chunk.
  • Um item: First/FirstOrDefault, Single, Last. Sim ou não: Any, All, Contains.
  • Contas: Count, Sum, Average, Min, Max, MinBy/MaxBy, Aggregate.
  • Grupos: GroupBy (g.Key), CountBy e AggregateBy (.NET 9). Junções: Join e, no .NET 10, LeftJoin/RightJoin.
  • As consultas são adiadas: rodam quando percorridas. ToList() guarda o resultado do momento.
  • First, Single, Average e Max numa sequência vazia lançam InvalidOperationException.