1. Introdução
Quando a gente trabalha com coleções, muitas vezes não basta só passar por cada elemento e fazer alguma coisa, mas sim pegar um "resumão" do conjunto de dados. Tipo:
- Contar quantos estudantes tiraram nota máxima.
- Saber quantos alunos tem na escola.
- Somar quantos pontos todos os estudantes fizeram na prova.
- Achar a maior e a menor nota.
- Calcular a média da turma.
Claro, dá pra resolver tudo isso com um for e uma variável de contador. Mas fala sério: ninguém curte escrever vinte linhas de código pra uma tarefa tão básica.
LINQ traz um monte de funções agregadas — métodos prontos que pegam a coleção, passam por tudo e devolvem a resposta: soma, média, máximo, mínimo e até umas paradas mais avançadas.
Aqui vai uma "tabela de agregados" resumida:
| Método | O que faz | Retorna |
|---|---|---|
|
Conta quantos elementos tem | |
|
Soma os valores numéricos | Depende do tipo dos elementos (, , ...) |
|
Calcula a média aritmética | ou outro tipo numérico |
|
Acha o maior elemento | Elemento da coleção |
|
Acha o menor elemento | Elemento da coleção |
Todos esses métodos são extension methods pra coleções que implementam IEnumerable<T>. Mais detalhes — documentação oficial dos métodos agregados do LINQ.
2. Preparando os dados pra praticar
Bora continuar com um app simples de cadastro de estudantes. Imagina que a gente tem essa classe aqui:
// Modelo de estudante
public class Student
{
public string Name { get; set; }
public int Grade { get; set; } // Nota de 0 a 5
public string Email { get; set; }
}
E a lista:
// Lista básica de estudantes
List<Student> students = new List<Student>
{
new Student { Name = "Ivan", Grade = 5, Email = "ivan@example.com" },
new Student { Name = "Olga", Grade = 4, Email = "olga@example.com" },
new Student { Name = "Artem", Grade = 3, Email = "artem@example.com" },
new Student { Name = "Darya", Grade = 5, Email = "darya@example.com" },
new Student { Name = "Petr", Grade = 2, Email = "petr@example.com" }
};
3. Contando elementos: Count()
Estatística básica
Digamos que tu quer saber quantos estudantes tem:
int totalStudents = students.Count(); // 5
Console.WriteLine($"Total de estudantes: {totalStudents}");
LINQ-magia: Simples assim! O método Count() devolve quantos elementos tem na coleção.
Contando com condição
Quantos tiraram nota máxima?
int excellentStudents = students.Count(s => s.Grade == 5);
Console.WriteLine($"Notas máximas: {excellentStudents}");
Aqui a gente passa uma lambda pro Count — só vai contar quem bate a condição (s.Grade == 5). Por dentro do LINQ, isso é igual a Where(...).Count(), mas é mais curto e roda um pouco mais rápido.
E se a coleção estiver vazia?
Se a coleção estiver vazia, Count devolve 0 — não rola erro nenhum.
4. Somando valores: Sum()
Pegando a soma de todas as notas
Imagina que tu quer saber o total de pontos que a turma fez:
int sumOfGrades = students.Sum(s => s.Grade); // 5+4+3+5+2 = 19
Console.WriteLine($"Soma das notas: {sumOfGrades}");
Sum recebe um seletor (lambda) que devolve o valor pra cada elemento.
Somando uma coleção de números
Se tu só tem uma lista de números, nem precisa de seletor:
int[] numbers = { 1, 2, 3, 4, 5 };
int sum = numbers.Sum(); // 15
Erros comuns e detalhes
Se a coleção estiver vazia, Sum() pra tipos numéricos devolve 0. Mas se a coleção for de tipos nullable (int?, double?), o Sum também funciona de boa: ele ignora os null.
5. Média aritmética: Average()
Calculando a média da turma
Clássico: quanto cada estudante tirou em média?
double averageGrade = students.Average(s => s.Grade); // (5+4+3+5+2)/5 = 3.8
Console.WriteLine($"Média da turma: {averageGrade:F2}");
Average é super útil pra estatística. Saca só: o valor retornado é sempre double, mesmo se os valores originais eram int. Assim tu não perde a parte decimal.
Se não tiver nenhum elemento
Se a coleção estiver vazia, chamar Average() vai dar uma exceção InvalidOperationException. Isso pega muita gente: se tu não tem certeza que tem algo na coleção, confere antes!
if (students.Any())
Console.WriteLine(students.Average(s => s.Grade));
else
Console.WriteLine("Sem dados pra calcular a média!");
Média de um array numérico
double avg = numbers.Average();
6. Máximo e mínimo: Max() e Min()
Quem é o destaque e quem tá na pior
Quer saber quem tá carregando a turma nas costas e quem... bom, tá dando trabalho pro professor? Fácil:
int maxGrade = students.Max(s => s.Grade); // 5
int minGrade = students.Min(s => s.Grade); // 2
Console.WriteLine($"Nota máxima: {maxGrade}");
Console.WriteLine($"Nota mínima: {minGrade}");
Mas quem é esse herói?
Às vezes tu quer saber não só o número, mas o nome de quem mandou bem. Bora pegar o estudante com a nota mais alta:
// Pega o primeiro com nota máxima depois de ordenar decrescente
var bestStudent = students.OrderByDescending(s => s.Grade).First();
Console.WriteLine($"Melhor estudante: {bestStudent.Name} ({bestStudent.Grade})");
Nas versões mais novas do .NET tem o MaxBy, que faz isso mais bonito. No .NET 6 já tem, e no .NET 9 ficou ainda melhor (confere MaxBy na Microsoft Docs). Mas mesmo sem MaxBy dá pra resolver com sort e .First().
Pegadinhas e diferenças
Se a coleção estiver vazia, chamar Max() e Min() também vai dar InvalidOperationException. Então fica esperto (principalmente se tu não conferiu antes):
if (students.Any())
Console.WriteLine($"Nota máxima: {students.Max(s => s.Grade)}");
else
Console.WriteLine("Sem estudantes pra buscar o máximo.");
7. Exemplos de "correntes" de métodos agregados
Muitas vezes os métodos agregados vêm junto com filtro e projeção:
// Média só dos que tiraram nota máxima
double avgExcellent = students
.Where(s => s.Grade == 5)
.Average(s => s.Grade); // sempre 5, mas serve de exemplo
// Soma das notas dos que tiraram pelo menos 4
int sumGood = students
.Where(s => s.Grade >= 4)
.Sum(s => s.Grade);
// Quantidade de emails únicos (por via das dúvidas)
int uniqueEmails = students
.Select(s => s.Email)
.Distinct()
.Count();
Aqui o LINQ mostra todo seu poder: combinar operações de forma simples deixa o código expressivo e fácil de ler, até pro teu gato (se ele for Junior C# Developer).
8. Comparando com o jeito "manual": por que usar agregados?
Pra entender, bora comparar LINQ com o for tradicional pra calcular a média:
Jeito tradicional:
int sum = 0;
int count = 0;
foreach (var s in students)
{
sum += s.Grade;
count++;
}
double average = (count != 0) ? (double)sum / count : 0;
LINQ:
double average = students.Average(s => s.Grade);
Até pra tratar coleção vazia fica mais fácil!
9. Dicas importantes
Resumo sobre performance e como funciona
Os agregados do LINQ, diferente da maioria das operações LINQ, são executados na hora! Ou seja, quando tu chama Sum(), Count(), Average(), Max(), Min(), ele já faz o loop na coleção naquele momento. Esses métodos não devolvem coleções, mas sim um resultado final.
Isso é importante: se tu fez algo pesado antes do agregado, tipo um filtro ou transformação, isso só roda uma vez, na hora do agregado.
Os métodos agregados funcionam no query-syntax?
Muita gente pergunta: "Dá pra escrever tudo isso no query-syntax?" Resposta curta: o query-syntax não tem palavras-chave pra agregados, mas tu pode misturar com method syntax:
var avg = (from s in students where s.Grade > 3 select s.Grade).Average();
Dentro dos parênteses é query-syntax normal, depois tu chama o método agregado. E olha que isso é bem comum!
10. Erros comuns usando LINQ
Erro nº1: tentar usar Average(), Max() ou Min() numa coleção vazia.
Se a coleção estiver vazia, Sum() e Count() devolvem 0 de boa, mas Average(), Max() e Min() vão lançar exceção. Antes de chamar esses métodos, garante que tem pelo menos um elemento.
Erro nº2: passar uma lambda com assinatura errada.
Tipo, se tu passar uma string em vez de número pra função agregada (Sum, Max etc.), vai dar erro de compilação. Isso é fácil de errar usando métodos anônimos.
Erro nº3: checar quantidade de elementos de forma não otimizada.
O método Where(...).Count() cria uma nova coleção e depois conta. Melhor usar Count(predicate) — ele já conta direto e é mais rápido.
Erro nº4: ignorar como os tipos nullable funcionam na agregação.
Se tu soma um int?, o Sum() ignora os null. Isso é o esperado, mas pode dar resultado estranho se tu não pensar nisso antes.
GO TO FULL VERSION