1. Introdução
Os métodos clássicos do LINQ são tipo comida de bandejão: escolhe "soma", "mínimo" ou "média" e segue o baile. Mas às vezes bate aquela vontade de algo diferente. É aí que entra o Aggregate — tipo um chef que faz do seu jeito.
Se comparar com programação funcional, Aggregate é o Reduce (ou fold): você passa pela coleção e vai dobrando ela num valor só, passo a passo. Como fazer isso? Você que manda. Controle total, criatividade liberada.
Problemas que ficam fáceis com Aggregate:
- Fazer somas diferentonas: tipo produto de todos os números, ou soma só dos pares/ímpares, ou soma com alguma regra maluca.
- Juntar strings com lógica própria (tipo, separador diferente pra índices pares e ímpares).
- Montar relatórios em texto ("listas Markdown", HTML, qualquer formato customizado).
- Construir coleções com estado mutável (tipo criar um dicionário de uma lista de objetos com uma regra especial pra chave).
- Qualquer contagem doida que não rola com os métodos agregadores padrão.
2. Assinatura e como funciona o método Aggregate
Bora dar uma olhada na documentação oficial da Microsoft sobre o Enumerable.Aggregate:
public static TAccumulate Aggregate<TSource, TAccumulate>(
this IEnumerable<TSource> source,
TAccumulate seed,
Func<TAccumulate, TSource, TAccumulate> func
)
O que é cada coisa aqui:
- source — sua coleção de origem.
- seed — "valor inicial" (pensa como o acumulador que começa o rolê).
- func — função que recebe dois argumentos: o valor acumulado (acc), o elemento atual da coleção e retorna o novo valor acumulado.
Tem também uma versão mais simples:
public static TSource Aggregate<TSource>(
this IEnumerable<TSource> source,
Func<TSource, TSource, TSource> func
)
Aqui, o "inicial" é o primeiro elemento da coleção, e depois a func vai do segundo até o último.
3. Exemplos básicos de uso do Aggregate
Vamos começar com uma pegadinha pros alunos: será que todo mundo sabe que o Aggregate pode substituir o Sum ou Product?
int[] numbers = { 2, 3, 4 };
// Calcular a soma
int sum = numbers.Aggregate((acc, val) => acc + val); // acc = acumulado, val = próximo elemento
// Calcular o produto
int product = numbers.Aggregate((acc, val) => acc * val);
Console.WriteLine(sum); // 9
Console.WriteLine(product); // 24
Parece o Sum e o Multiply, mas é na raça! Dá até pra brincar: com o Sum você só soma, mas com o Aggregate dá pra fazer soma, "anti-soma" e até "soma das raízes quadradas".
4. Usando Aggregate pra juntar strings
Vamos colar todas as strings numa só, separando por vírgula (sem vírgula sobrando no final):
string[] words = { "C#", "LINQ", "rocks" };
string result = words.Aggregate((acc, word) => acc + ", " + word);
// resultado: "C#, LINQ, rocks"
Se a coleção pode estar vazia, o valor inicial (seed) salva:
// Começa com string vazia
string report = words.Aggregate(
"Tecnologias: ",
(acc, word) => acc + word + "; ",
acc => acc.TrimEnd(' ', ';') // tira o "; " do final
);
Console.WriteLine(report); // "Tecnologias: C#; LINQ; rocks"
Repara no terceiro argumento — a função que transforma o resultado (result selector), ela só tem na versão com seed. Tipo "sobremesa" pra finalizar o prato.
5. Aggregate no app que a gente tá desenvolvendo
Lembra do nosso app de estudos, aquele que a gente mexe faz dias? Suponha que temos a classe Student:
public class Student
{
public string Name { get; set; }
public int Grade { get; set; }
}
Lista de estudantes:
var students = new List<Student>
{
new Student { Name = "Alisa", Grade = 5 },
new Student { Name = "Bob", Grade = 4 },
new Student { Name = "Vasya", Grade = 3 },
new Student { Name = "Maria", Grade = 5 }
};
Missão: gerar uma string tipo
"Melhores: Alisa, Maria"
— ou seja, todos com Grade == 5.
Como a galera iniciante faz:
var best = "";
foreach (var s in students)
{
if (s.Grade == 5)
best += s.Name + ", ";
}
best = best.TrimEnd(',', ' ');
Console.WriteLine("Melhores: " + best);
Agora — estilo LINQ com Aggregate:
var bestStr = students
.Where(s => s.Grade == 5)
.Select(s => s.Name)
.Aggregate("Melhores: ", (acc, name) => acc + name + ", ")
.TrimEnd(',', ' ');
Console.WriteLine(bestStr);
A graça: menos código, mais legível. Mesmo se seu chefe não manja de LINQ, ele vai sacar a ideia (ou pelo menos vai ver que você se esforçou).
6. Cenários mais avançados
Na real, o acumulador do Aggregate pode ser qualquer coisa: número, string, dicionário, até sua própria classe ou struct.
Por exemplo: contar quantos estudantes tem pra cada nota:
var gradeCounts = students.Aggregate(
new Dictionary<int, int>(),
(dict, student) => {
if (dict.ContainsKey(student.Grade))
dict[student.Grade]++;
else
dict[student.Grade] = 1;
return dict;
}
);
// Pra mostrar:
foreach (var pair in gradeCounts)
{
Console.WriteLine($"Nota {pair.Key}: {pair.Value} estudantes");
}
Esse esquema basicamente faz um GroupBy na mão. Por que não usar só o GroupBy? Às vezes você precisa de uma agregação diferente, tipo somar só se o estudante não for o Vasya, ou montar um relatório pra relatório.
7. Visualizando: como funciona o Aggregate (fluxograma)
Imagina um array { 2, 4, 3 } e queremos somar tudo:
acc: 2 (primeiro elemento)
|
v
val: 4
acc = acc + val = 2 + 4 = 6
|
v
val: 3
acc = acc + val = 6 + 3 = 9
|
v
[Todos os elementos processados]
|
v
Resultado: 9
Mesma ideia pra versão com seed:
seed: 0
|
v
val: 2
acc = 0 + 2 = 2
|
v
val: 4
acc = 2 + 4 = 6
|
v
val: 3
acc = 6 + 3 = 9
|
v
Resultado: 9
Comparando Aggregate com outros métodos agregadores
| Método | Comportamento padrão | Flexibilidade | Pra coleções vazias | Exemplo |
|---|---|---|---|---|
|
Soma números | Baixa | Retorna 0 | |
|
Conta elementos | Baixa | Retorna 0 | |
|
Qualquer contagem | Alta | Precisa de seed | |
|
Junta strings | Média | Se vazio = "" | |
8. Dicas práticas, erros comuns e pontos importantes
Por ser tão flexível, o Aggregate pode dar uns bugs se usar errado. Os vacilos mais comuns:
Às vezes a galera esquece do seed (valor inicial) e não percebe que se a coleção estiver vazia, a versão sem seed vai lançar uma exceção (InvalidOperationException). Então, pra coleções vazias, usa a versão com seed:
var sum = new int[0].Aggregate(0, (acc, n) => acc + n); // Suave! Retorna 0
Se você tá acumulando string com Aggregate, é fácil acabar com separador sobrando (tipo "," no final). Melhor tirar com .TrimEnd(',', ' ') — ou usa string.Join se só quer juntar strings.
Acumulador mutável (tipo List ou Dictionary) é comum no Aggregate, mas cuidado: se você muda ele direto (in-place), todas as referências apontam pro mesmo objeto. Isso pode dar uns bugs estranhos em operações paralelas ou se você espera cópia. No estilo funcional, o certo é retornar um novo objeto a cada passo, não mudar o antigo.
No código que outros vão usar, não complica o Aggregate só pra pagar de descolado: pra quem tá começando ele parece mais difícil que um foreach ou agregadores padrão. Se a tarefa é simples — usa Sum, Count, Join. Mas se for "montar texto com regra especial" — Aggregate é seu brother!
9. Ligação com o mercado, entrevistas e frameworks
No mercado, o método Aggregate aparece direto quando precisa de contagens ou reduções diferentonas, tipo montar relatórios complexos, estatísticas, grafos, calcular hash, gerar id único ou até montar componentes de UI de coleções com lógica própria.
Em entrevistas, sempre rola pergunta de LINQ tipo: "Como somar os elementos de um array?", "Como transformar uma lista de strings em uma só string?" ou "Como contar elementos únicos?" — e geralmente esperam soluções com LINQ, inclusive Aggregate. Às vezes vem pergunta criativa: "Consegue usar LINQ pra somar os quadrados dos números pares e depois devolver uma string explicando o processo?"
Em várias libs e frameworks .NET famosos, tipo Entity Framework, Dapper, RavenDB, o método Aggregate quase nunca é usado direto no banco, mas no código ele é super útil pra agregação em memória.
GO TO FULL VERSION