CodeGym /Cursos /C# SELF /Funções agregadas: Sum

Funções agregadas: Sum, Count, Average, Max, Min

C# SELF
Nível 32 , Lição 1
Disponível

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
Count()
Conta quantos elementos tem
int
Sum()
Soma os valores numéricos Depende do tipo dos elementos (
int
,
double
, ...)
Average()
Calcula a média aritmética
double
ou outro tipo numérico
Max()
Acha o maior elemento Elemento da coleção
Min()
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.

Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION