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
| Operador | O que faz | Exemplo | Resultado | Linha | Aula |
|---|---|---|---|---|---|
Where(condição) | fica com os que cumprem a condição | n.Where(x => x > 4) | 5 8 9 8 | 1 | Filtrar e Projetar |
Select(transformação) | troca cada item por outra coisa | n.Select(x => x * 10) · estoque.Where(…).Select(p => p.Nome) | 50 30 80 … · Borracha Mochila Papel A4 | 1, 2 | |
Select((x, i) => …) | o mesmo, recebendo também a posição | n.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 e | 4 | |
OfType<T>() | fica só com os do tipo T | [1, "dois", 3.0, 4].OfType<int>() | 1 4 | 5 | |
Distinct() | tira os repetidos | n.Distinct() | 5 3 8 1 9 2 | 1 | Filtrar e Projetar |
DistinctBy(chave) .NET 6 | um item por chave (o primeiro de cada) | estoque.DistinctBy(p => p.Categoria) | Caderno Caneta Mochila | 5 |
Ordenar
| Operador | O que faz | Exemplo | Resultado | Linha | Aula |
|---|---|---|---|---|---|
OrderBy(chave) | do menor para o maior pela chave | estoque.OrderBy(p => p.Preco) | Lápis Borracha Caneta … | 7 | Ordenar |
OrderByDescending(chave) | do maior para o menor | estoque.OrderByDescending(p => p.Preco) | (veja a missão) | — | |
ThenBy, ThenByDescending | desempate: o segundo critério | .OrderBy(p => p.Categoria).ThenByDescending(p => p.Qtd) | Bolsas/Mochila Escrita/Lápis Escrita/Caneta … | 8 | |
Order(), OrderDescending() .NET 7 | ordena pelo próprio valor | n.Order() · n.OrderDescending() | 1 2 3 5 8 8 9 · 9 8 8 5 3 2 1 | 6 | |
Reverse() | inverte a ordem | n.Reverse() | 8 2 9 1 8 3 5 | 6 |
Um segundo OrderBy desfaz o primeiro; para desempatar, é sempre ThenBy. Textos são ordenados como no dicionário (veja string).
Particionar
| Operador | O que faz | Exemplo | Resultado | Linha |
|---|---|---|---|---|
Take(k) | os k primeiros | n.Take(3) | 5 3 8 | 9 |
Skip(k) | pula os k primeiros | n.Skip(5) | 2 8 | 9 |
TakeLast(k), SkipLast(k) | os k últimos; todos menos os k últimos | n.TakeLast(2) · n.SkipLast(5) | 2 8 · 5 3 | 9 |
TakeWhile(c), SkipWhile(c) | pega (ou pula) enquanto a condição vale; para no primeiro que falha | n.TakeWhile(x => x != 1) · n.SkipWhile(x => x != 1) | 5 3 8 · 1 9 2 8 | 9 |
Take(a..b) .NET 6 | um intervalo | n.Take(2..4) | 8 1 | 10 |
Chunk(k) .NET 6 | corta 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ó
| Operador | Devolve | Se não há nenhum | Se há mais de um | Exemplo → resultado (linha 11, 12) |
|---|---|---|---|---|
First(), First(c) | o primeiro | exceção | o primeiro | n.First() → 5; n.First(x => x > 5) → 8 |
FirstOrDefault(c) | o primeiro | o padrão (0, null) ou o que você passar | o primeiro | n.FirstOrDefault(x => x > 100) → 0; com , -1 → -1; num produto → null |
Last(), LastOrDefault() | o último | exceção / padrão | o último | n.Last() → 8 |
Single(c) | o único | exceção | exceção | n.Single(x => x == 9) → 9 |
SingleOrDefault(c) | o único | o padrão | exceção | — |
ElementAt(i), ElementAtOrDefault(i) | o da posição i | exceçã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)
| Operador | Pergunta | Exemplo | Resultado (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)
| Operador | O que calcula | Exemplo | Resultado | Linha |
|---|---|---|---|---|
Count(), Count(c) | quantos; quantos cumprem | n.Count() · n.Count(x => x == 8) | 7 · 2 | 14 |
Sum(), Sum(seletor) | a soma | n.Sum() · estoque.Sum(p => p.Qtd * p.Preco) | 36 · R$ 657,85 | 14, 15 |
Min(), Max() | o menor, o maior valor | n.Min() · estoque.Max(p => p.Preco) | 1 · R$ 89,90 | 14, 15 |
Average() | a média (de inteiros, dá double) | n.Average() · estoque.Average(p => p.Preco) | 5,14 (com :F2) · R$ 23,13 | 14, 15 |
MinBy(chave), MaxBy(chave) .NET 6 | o item com o menor, o maior valor da chave | estoque.MaxBy(p => p.Preco)!.Nome · estoque.MinBy(p => p.Qtd)!.Nome | Mochila · Mochila | 15 |
Aggregate(f) | vai combinando os itens, dois a dois | n.Aggregate((a, b) => a * b) | 17280 (o produto de todos) | 16 |
Aggregate(início, f) | o mesmo, começando de um valor | n.Aggregate(100, (total, x) => total - x) | 64 | 16 |
LongCount() | como Count, em long | — | — | — |
Aula: Agregar (Count, Sum, Average).
Agrupar
| Operador | O que faz | Exemplo | Resultado | Linha | Aula |
|---|---|---|---|---|---|
GroupBy(chave) | uma "pilha" por chave; cada pilha g tem g.Key e é uma sequência | estoque.GroupBy(p => p.Categoria) | Papelaria: produtos 2, unidades 16 … | 17 | Agrupar |
CountBy(chave) .NET 9 | quantos por chave, direto | estoque.CountBy(p => p.Categoria) | Papelaria=2 Escrita=3 Bolsas=1 | 18 | |
AggregateBy(chave, início, f) .NET 9 | um Aggregate por chave | estoque.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
| Operador | O que faz | Exemplo (a = [1, 2, 3, 4], b = [3, 4, 5]) | Resultado | Linha |
|---|---|---|---|---|
Concat(b) | uma depois da outra, com repetidos | a.Concat(b) | 1 2 3 4 3 4 5 | 20 |
Union(b) | todos, sem repetidos | a.Union(b) | 1 2 3 4 5 | 20 |
Intersect(b) | os que estão nas duas | a.Intersect(b) | 3 4 | 20 |
Except(b) | os de a que não estão em b | a.Except(b) | 1 2 | 20 |
Zip(b, f) | junta item a item; para na menor | a.Zip(b, (x, y) => x + y) | 4 6 8 | 21 |
Append(x), Prepend(x) | um item no fim, no começo | a.Append(9) · a.Prepend(0) | 1 2 3 4 9 · 0 1 2 3 4 | 21 |
Join(outra, chaveA, chaveB, f) | junta os pares com a mesma chave; quem não tem par fica de fora | estoque.Join(fornecedores, p => p.Categoria, f => f.Categoria, …) | a Mochila não aparece | 22 |
LeftJoin(…) .NET 10 | como Join, mas todos da esquerda ficam; sem par, o da direita vem como padrão | estoque.LeftJoin(fornecedores, …, (p, f) => … f.Empresa ?? "?") | Mochila<-? aparece | 23 |
RightJoin(…) .NET 10 | o contrário: todos da direita ficam | — | — | — |
GroupJoin(…) | cada item da esquerda com a lista dos pares da direita | — | — | — |
Index() .NET 9 | cada item com a sua posição, como tupla (Index, Item) | n.Index().Where(t => t.Item == 8).Select(t => t.Index) | 2 6 | 24 |
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
| Operador | O que faz | Exemplo | Resultado | Linha |
|---|---|---|---|---|
ToList(), ToArray() | roda a consulta e guarda numa lista, num array | n.ToList().Count · n.ToArray().Length | 7 · 7 | 25 |
ToDictionary(chave, valor) | monta um dicionário (chave repetida = exceção) | estoque.ToDictionary(p => p.Nome, p => p.Preco)["Mochila"] | R$ 89,90 | 25 |
ToHashSet() | monta um conjunto, sem repetidos | n.ToHashSet().Count | 6 | 25 |
Cast<T>() | converte cada item para T (exceção se não der) | — | — | — |
Enumerable.Range(início, quantos) | uma sequência de números | Enumerable.Range(1, 5) | 1 2 3 4 5 | 26 |
Enumerable.Repeat(x, k) | x repetido k vezes | Enumerable.Repeat("ha", 3) | ha ha ha | 26 |
Enumerable.Empty<T>() | uma sequência vazia | Enumerable.Empty<int>().Count() | 0 | 26 |
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 consulta | Mé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, Contains | Where, 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ódigo | Mensagem 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 element | O Single exige exatamente um. |
vazia.Average(), vazia.Max() | System.InvalidOperationException: Sequence contains no elements | Nã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: Ana | A 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(…).Nome | error 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),CountByeAggregateBy(.NET 9). Junções:Joine, no .NET 10,LeftJoin/RightJoin. - As consultas são adiadas: rodam quando percorridas.
ToList()guarda o resultado do momento. First,Single,AverageeMaxnuma sequência vazia lançamInvalidOperationException.