1. Intro
Filtrer, c’est prendre dans une collection seulement les éléments qui remplissent une certaine condition. Imagine : t’as 10 000 produits dans ta base, et le manager veut « juste » 12 qui matchent un nouveau critère tordu. Écrire à chaque fois une boucle et plein de checks à la main, c’est pas fun, et ton code devient vite illisible. LINQ fait tout ça pour toi.
Dans les applis réelles, filtrer c’est l’opération la plus courante : on montre à l’utilisateur que les enregistrements qui l’intéressent, on « nettoie » la collection des éléments indésirables, on fait des sélections pour des rapports, des recherches ou des envois d’e-mails. En entretien, les questions sur le filtrage LINQ tombent tout le temps. Comprendre ce sujet, c’est la base pour un dev .NET débutant.
2. On découvre la méthode Where
Le sens et le principe
La méthode Where c’est une méthode d’extension LINQ qui prend une fonction (ou une lambda) qui définit la condition de filtrage. Elle renvoie une nouvelle séquence (sans toucher à l’originale !), qui contient seulement les éléments pour lesquels la condition renvoie true.
Signature de base :
public static IEnumerable<TSource> Where<TSource>(
this IEnumerable<TSource> source,
Func<TSource, bool> predicate)
T’inquiète pas pour les generics et les mots chelous. Ce qui compte ici : Where prend ta collection de base et la condition que tu veux. En vrai, c’est simple :
- source — la collection à filtrer (genre List<Product>)
- predicate — la fonction/condition (genre p => p.Price > 100)
Un petit exo pour commencer
Imaginons qu’on a une liste de produits, et on veut juste ceux qui coûtent plus de 100. À l’ancienne, on aurait fait une boucle foreach et ajouté à la main les bons éléments dans une nouvelle liste. Avec LINQ, c’est une seule ligne.
3. Filtrage
Notre classe Product et la collection de départ
Dans le module d’avant, on avait écrit une classe produit comme ça :
class Product
{
public string Name { get; set; }
public double Price { get; set; }
// Pour un affichage stylé :
public override string ToString()
{
return $"{Name} (prix : {Price})";
}
}
Imaginons qu’on a une liste de produits :
var products = new List<Product>
{
new Product { Name = "Pain", Price = 30 },
new Product { Name = "Lait", Price = 87 },
new Product { Name = "Fromage", Price = 250 },
new Product { Name = "Chocolat", Price = 130 }
};
Filtrer les produits à plus de 100 avec Where
var expensiveProducts = products.Where(p => p.Price > 100);
foreach (var product in expensiveProducts)
{
Console.WriteLine(product);
}
Ce qui se passe sous le capot :
- Where parcourt tous les éléments de products,
- Pour chaque, il appelle la fonction (ici : p => p.Price > 100),
- Si la fonction renvoie true, le produit va dans la nouvelle collection.
Résultat à l’écran :
Fromage (prix : 250)
Chocolat (prix : 130)
À noter : la liste d’origine products n’a pas bougé ! LINQ ne casse pas tes données.
4. Filtrage paresseux (Deferred Execution)
Un truc important : le résultat de Where est calculé seulement quand tu utilises vraiment les éléments. Genre, dès que tu lances la boucle foreach, LINQ « va » dans la collection de base et filtre à la volée. Si tu n’utilises pas le résultat, rien ne se passe. Ça s’appelle exécution différée (deferred execution).
Ça donne plein d’avantages :
- Tu peux faire des chaînes de méthodes super longues (genre filtrer puis trier), et tout ne s’exécute qu’au premier vrai accès au résultat.
- Économie de mémoire : pas de collections intermédiaires inutiles.
- Tu peux annuler ou « stopper » après avoir trouvé assez d’éléments.
Schéma visuel
Collection de base —► Where (condition) —► On parcourt que les bons éléments
[ x ] [ x ] [ + ] [ + ] —► p.Price > 100 —► [ + ] [ + ]
(x — l’élément ne passe pas la condition, + — il passe)
5. Variantes de syntaxe
Syntaxe méthode classique (Method Syntax)
C’est le style qu’on utilise déjà :
var result = products.Where(p => p.Price > 100);
Alternative : Query Syntax
LINQ supporte une syntaxe qui ressemble aux requêtes SQL :
var result = from p in products
where p.Price > 100
select p;
Le résultat est le même !
Le choix de la syntaxe, c’est une question de goût, mais la method syntax (avec les points) est plus courante, surtout dans les vrais projets et pour les chaînes longues.
6. Conditions de filtrage complexes
Plusieurs conditions
Tu peux utiliser les opérateurs logiques (&&, ||, !) :
// Trouver tous les produits chers sauf "Chocolat"
var filtered = products.Where(
p => p.Price > 100 && p.Name != "Chocolat"
);
Filtrer sur une chaîne (sous-chaîne, casse, Contains)
// Les produits dont le nom contient la lettre "l"
var lProducts = products.Where(p => p.Name.Contains("l"));
Attention : Contains est sensible à la casse ! Si tu veux ignorer la casse, tu peux faire :
var lProducts = products.Where(p => p.Name
.ToLower().Contains("l")); // maintenant "Lait" et "Chocolat" seront tous les deux pris
Filtrer sur plusieurs collections
Imaginons que t’as deux listes — produits et paiements. Tu peux filtrer les produits qui sont dans la liste des paiements — mais pour ça, c’est mieux d’utiliser Any et Join, on en parlera plus tard. Juste pour info : LINQ sait filtrer de façon bien plus balèze !
7. Petits tips utiles
Filtrage imbriqué et chaînes de méthodes
Tu peux enchaîner plusieurs étapes de filtrage en appelant Where plusieurs fois :
var filtered = products
.Where(p => p.Price > 100)
.Where(p => p.Name.StartsWith("Ch"));
Mais c’est mieux de regrouper les conditions dans un seul Where avec des opérateurs logiques — c’est plus efficace et plus lisible.
Comparateurs intégrés et fonctions custom
Parfois les opérateurs de base ne suffisent pas. Genre, tu veux filtrer des chaînes sans tenir compte de la casse et de la culture. Là, c’est pratique d’utiliser les méthodes de comparaison :
var frProducts = products.Where(
p => p.Name.StartsWith("ch", StringComparison.OrdinalIgnoreCase)
);
8. Filtrage et input utilisateur
On va faire un petit filtrage interactif ! Par exemple, on demande à l’utilisateur le prix minimum, puis on affiche tous les produits qui vont bien.
Console.Write("Prix minimum du produit ? ");
var minPriceStr = Console.ReadLine();
if (double.TryParse(minPriceStr, out double minPrice))
{
var filtered = products.Where(p => p.Price >= minPrice);
foreach (var product in filtered)
{
Console.WriteLine(product);
}
}
else
{
Console.WriteLine("Erreur : ce n’est pas un nombre.");
}
Maintenant notre « mini-appli » ressemble déjà à une boutique !
9. Pièges et erreurs classiques avec Where
En prog comme dans la vie, y’a des pièges. Voilà ce qu’il faut surveiller.
Oublié d’appeler ToList() ou ToArray()
Le résultat de Where — c’est pas une liste ni un tableau ! C’est un objet IEnumerable<Product>. La plupart du temps ça roule dans une boucle foreach, mais si tu veux vraiment une collection (genre pour accéder par index), pense à appeler .ToList() ou .ToArray() :
var filteredList = products.Where(p => p.Price > 100).ToList();
Si tu zappes ça, tu peux avoir une erreur du genre "Collection was modified during enumeration" — surtout si tu modifies la collection de base pendant la boucle.
Modifier la collection de base
Comme le filtrage LINQ est différé, si tu supprimes des éléments de la liste de base pendant la boucle, tu peux avoir une exception. Surtout en multi-thread ou si la collection change pendant le filtrage.
Valeurs null et prédicats
Si la collection contient null et que tu accèdes à un champ dans le prédicat sans check, tu vas te prendre un NullReferenceException. Par exemple :
var filtered = products.Where(p => p.Name.StartsWith("A"));
Si un élément de products est null, ça va planter.
Conseil : mets toujours un check sur null si tu risques d’avoir ce genre d’éléments :
var filtered = products.Where(p => p != null && p.Name.StartsWith("A"));
GO TO FULL VERSION