1. Intro
Quand tu bosses avec des collections, souvent t’as pas juste envie de boucler sur chaque élément, mais plutôt d’avoir un petit “résumé” sur tout le jeu de données. Genre :
- Compter combien d’étudiants ont eu la note maximale.
- Savoir combien il y a d’élèves dans l’école.
- Calculer la somme des points obtenus par tous les étudiants à l’exam.
- Trouver la note la plus haute et la plus basse.
- Calculer la moyenne de la classe.
Bien sûr, tout ça tu pourrais le faire avec une boucle classique et un compteur. Mais franchement, qui a envie d’écrire vingt lignes de code pour un truc aussi basique ?
LINQ te file tout un set de fonctions d’agrégation — des méthodes prêtes à l’emploi qui prennent ta collection, la parcourent et te sortent la réponse : somme, moyenne, max, min, et parfois des trucs plus malins.
Petit “tableau des agrégats” :
| Méthode | Ce qu’elle fait | Retourne |
|---|---|---|
|
Compte le nombre d’éléments | |
|
Fait la somme des valeurs numériques | Dépend du type des éléments (, , ...) |
|
Calcule la moyenne arithmétique | ou un autre type numérique |
|
Choppe l’élément le plus grand | Un élément de la collection |
|
Choppe l’élément le plus petit | Un élément de la collection |
Toutes ces méthodes sont des extension-methods pour les collections qui implémentent IEnumerable<T>. Plus de détails — doc officielle LINQ sur les méthodes d’agrégation.
2. Préparer les données pour la pratique
On va continuer avec une appli simple pour gérer des étudiants. Imaginons qu’on a cette classe :
// Modèle étudiant
public class Student
{
public string Name { get; set; }
public int Grade { get; set; } // Note sur 5
public string Email { get; set; }
}
Et une liste :
// Liste de base des étudiants
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. Compter les éléments : Count()
Stat de base
Par exemple, tu veux savoir combien t’as d’étudiants :
int totalStudents = students.Count(); // 5
Console.WriteLine($"Nombre total d’étudiants : {totalStudents}");
LINQ-magic : Easy ! La méthode Count() te retourne le nombre d’éléments dans la collection.
Compter avec une condition
Et combien de cracks dans la classe ?
int excellentStudents = students.Count(s => s.Grade == 5);
Console.WriteLine($"Nombre d’étudiants avec la note maximale : {excellentStudents}");
Ici, on passe à Count une lambda — elle va juste prendre ceux qui matchent la condition (s.Grade == 5). En interne, c’est comme Where(...).Count(), mais c’est plus court et un poil plus rapide.
Et si la collection est vide ?
Si la collection est vide, Count te retourne 0 — pas d’erreur.
4. Faire la somme : Sum()
Choper la somme de toutes les notes
Imaginons que tu veux la somme totale des points du groupe :
int sumOfGrades = students.Sum(s => s.Grade); // 5+4+3+5+2 = 19
Console.WriteLine($"Somme de toutes les notes : {sumOfGrades}");
Sum prend un sélecteur (lambda) qui retourne la valeur pour chaque élément.
Somme sur une collection de nombres
Si t’as juste une liste de nombres, pas besoin de sélecteur :
int[] numbers = { 1, 2, 3, 4, 5 };
int sum = numbers.Sum(); // 15
Erreurs classiques et subtilités
Si la collection est vide, Sum() pour les types numériques retourne 0. Mais si c’est une collection de types nullable (int?, double?), Sum gère aussi : il ignore les null.
5. Moyenne arithmétique : Average()
Calculer la moyenne de la classe
Classique : quelle est la moyenne des étudiants ?
double averageGrade = students.Average(s => s.Grade); // (5+4+3+5+2)/5 = 3.8
Console.WriteLine($"Moyenne de la classe : {averageGrade:F2}");
Average — super utile pour les stats. À noter : le retour est toujours un double, même si les valeurs de base étaient des int. Comme ça, pas de perte de décimales.
Si y’a aucun élément
Si la collection est vide, Average() balance une exception InvalidOperationException. C’est un piège classique : si t’es pas sûr qu’il y a des éléments, check avant !
if (students.Any())
Console.WriteLine(students.Average(s => s.Grade));
else
Console.WriteLine("Pas de données pour calculer la moyenne !");
Moyenne sur un tableau de nombres
double avg = numbers.Average();
6. Max et min : Max() et Min()
Qui est la star de la classe, et qui est au fond ?
Tu veux savoir qui cartonne et qui galère ? Facile :
int maxGrade = students.Max(s => s.Grade); // 5
int minGrade = students.Min(s => s.Grade); // 2
Console.WriteLine($"Note maximale : {maxGrade}");
Console.WriteLine($"Note minimale : {minGrade}");
Mais c’est qui ce héros ?
Parfois, tu veux pas juste la note, mais le nom du boss. On chope l’étudiant avec la meilleure note :
// On prend le premier après tri décroissant
var bestStudent = students.OrderByDescending(s => s.Grade).First();
Console.WriteLine($"Meilleur étudiant : {bestStudent.Name} ({bestStudent.Grade})");
Dans les versions récentes de .NET, y’a aussi MaxBy pour faire ça plus proprement. Depuis .NET 6 c’est dispo, et .NET 9 a encore amélioré le truc (voir MaxBy sur Microsoft Docs). Mais même sans MaxBy, le tri + .First() marche nickel.
Pièges et différences
Si la collection est vide, Max() et Min() balancent aussi une InvalidOperationException. Donc, sois prêt (surtout si t’as pas checké la collection avant) :
if (students.Any())
Console.WriteLine($"Note maximale : {students.Max(s => s.Grade)}");
else
Console.WriteLine("Pas d’étudiants pour chercher le max.");
7. Exemples de “chaînes” de méthodes d’agrégation
Souvent, tu combines les méthodes d’agrégation avec des filtres et des projections :
// Moyenne parmi les cracks
double avgExcellent = students
.Where(s => s.Grade == 5)
.Average(s => s.Grade); // toujours 5, mais c’est pour l’exemple
// Somme des notes pour ceux qui ont au moins 4
int sumGood = students
.Where(s => s.Grade >= 4)
.Sum(s => s.Grade);
// Nombre d’emails uniques (au cas où)
int uniqueEmails = students
.Select(s => s.Email)
.Distinct()
.Count();
C’est là que LINQ devient vraiment cool : tu combines les opérations et t’as un code expressif et lisible, même ton chat pourrait piger (bon, si ton chat est Junior C# Dev).
8. Comparaison avec la méthode “à la main” : pourquoi utiliser les agrégats ?
Pour bien piger, comparons LINQ avec une boucle classique pour calculer la moyenne :
Méthode classique :
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);
Même pour gérer une collection vide, c’est plus simple !
9. Détails utiles
Petit mot sur la perf et l’implémentation
Les agrégats LINQ, contrairement à la plupart des opérations LINQ, s’exécutent directement ! Donc, quand tu appelles Sum(), Count(), Average(), Max(), Min(), tout le parcours de la collection se fait à ce moment-là. Ces méthodes te retournent pas une collection, mais un résultat unique.
C’est super important : si t’as fait un truc lourd avant l’agrégat, genre un gros filtre ou une transformation — ça s’exécute qu’une seule fois, au moment de l’agrégat.
Les méthodes d’agrégation supportent-elles le query-syntax ?
Souvent, les débutants demandent : “On peut écrire tout ça en query-syntax ?” Réponse courte : le query-syntax n’a pas de mots-clés pour les agrégats, mais tu peux toujours mixer avec la method syntax :
var avg = (from s in students where s.Grade > 3 select s.Grade).Average();
Dans les parenthèses — du query-syntax classique, puis tu appelles la méthode d’agrégation. On fait ça plus souvent qu’on croit !
10. Erreurs classiques avec LINQ
Erreur n°1 : essayer d’utiliser Average(), Max() ou Min() sur une collection vide.
Si la collection est vide, Sum() et Count() te retournent tranquillou 0, mais Average(), Max() et Min() balancent une exception. Avant d’appeler ces méthodes, assure-toi qu’il y a au moins un élément.
Erreur n°2 : passer une lambda avec la mauvaise signature.
Par exemple, si tu passes une string au lieu d’un nombre à une fonction d’agrégation (Sum, Max, etc.), tu vas avoir une erreur de compilation. C’est facile de se planter avec les méthodes anonymes.
Erreur n°3 : vérifier le nombre d’éléments de façon pas optimale.
La méthode Where(...).Count() crée d’abord une nouvelle collection, puis compte les éléments. Utilise plutôt Count(predicate) — il compte direct les éléments qui matchent et c’est plus rapide.
Erreur n°4 : oublier les particularités des types nullable en agrégation.
Si tu fais la somme sur des int?, Sum() ignore les null. C’est normal, mais ça peut surprendre si t’y penses pas.
GO TO FULL VERSION