1. Introduction
Imagine que tu débarques à la bibliothèque et que tu dois trouver tous les livres sur la programmation publiés après 2020, triés par nom d’auteur. Tu ne vas sûrement pas courir d’étagère en étagère pour checker chaque bouquin à la main, non ? Tu demanderais plutôt au bibliothécaire, qui sait comment trouver vite ce qu’il te faut.
LINQ (Language Integrated Query, ou "Requêtes intégrées au langage") – c’est notre "bibliothécaire malin", un "moteur SQL" (donc, un outil pour écrire des requêtes sur les données) directement dans C# !
Pense à LINQ comme à un langage qui te permet de décrire ce que tu veux obtenir des données, et pas comment le faire. Au lieu de dire : "Prends le premier élément, vérifie la condition, si ça colle, ajoute-le à une nouvelle liste, puis passe au deuxième...", tu dis juste : "Donne-moi tous les produits dont le prix est supérieur à 1000". Et C# se débrouille pour faire ça de la façon la plus efficace.
L’idée clé de LINQ : fournir une syntaxe standard pour les requêtes sur n’importe quelle source de données qui implémente l’interface IEnumerable<T>. Et c’est génial, parce que List<T>, les tableaux, HashSet<T> – ils implémentent tous IEnumerable<T>. Et si tes données sont dans une base, des libs spéciales (genre Entity Framework) transforment tes requêtes LINQ en vrai SQL !
LINQ est arrivé dans C# il y a plus de 10 ans. C’était un moment clé qui a changé la façon de bosser avec les données dans .NET. Avant LINQ, les devs devaient écrire plein de code répétitif pour filtrer, trier et transformer les collections. Ou utiliser des chaînes SQL, qui n’étaient pas vérifiées par le compilateur et pouvaient planter à l’exécution. LINQ a amené le concept de "requêtes" directement dans le langage, les rendant typées et plus lisibles.
Beaucoup pensent que LINQ — c’est l’une des plus grosses innovations de C# depuis l’arrivée des Generics.
2. Les avantages de LINQ : Pourquoi tout le monde l’adore ?
Code court et lisible : Pour filtrer, par exemple, les produits avec un prix supérieur à 1000 dans une grosse base, il fallait écrire plusieurs lignes de code. Avec LINQ, tu fais ça en une seule. Moins de code — moins de bugs potentiels, plus de lisibilité et de clarté. Le code devient plus proche du langage naturel.
Blague de dev : "Moins j’écris de code, moins je peux y mettre de bugs." LINQ aide carrément là-dessus !
Puissance et flexibilité : LINQ offre un paquet d’opérations : filtrage (Where), projection (Select), tri (OrderBy), groupement (GroupBy), agrégation (Sum, Average), jointure (Join) et plein d’autres. Tu peux résoudre des tâches complexes de traitement de données en combinant ces opérations.
Sécurité de typage : Super important ! Quand tu écris une requête SQL sous forme de chaîne, le compilateur n’en sait rien. Si tu te plantes dans le nom d’une colonne, tu le sauras seulement à l’exécution, quand ton appli va planter. Avec LINQ, le compilateur C# vérifie ta requête à la compilation. Si tu demandes un champ qui n’existe pas dans Product, le compilateur te le dit direct. C’est comme avoir un correcteur perso qui repère tes fautes avant tout le monde.
Intégration au langage : Les requêtes LINQ — c’est pas des chaînes "magiques". Ce sont des vraies constructions C#, qui utilisent les lambdas que tu connais déjà et bossent avec les types de données habituels. Du coup, passer à LINQ, c’est super fluide.
Exécution différée (Deferred Execution) : C’est un des trucs les plus cools (et parfois les plus tordus) de LINQ, mais c’est super important. En gros, une requête LINQ ne s’exécute pas tout de suite quand tu l’écris. Elle se "prépare" et attend que tu demandes vraiment le résultat (genre quand tu la parcours avec foreach). Ça permet d’optimiser les requêtes, surtout avec de grosses quantités de données. On en parlera plus en détail dans la Conférence 166. Pour l’instant, retiens juste : LINQ est malin, il ne fait pas de boulot inutile.
Universalité et extensibilité : LINQ — ce n’est pas que pour les listes en mémoire. Il existe différents "providers" LINQ :
- LINQ to Objects : pour les collections en mémoire (List<T>, tableaux, etc.).
- LINQ to SQL / Entity Framework Core : pour les bases SQL (tes requêtes LINQ sont traduites en requêtes SQL).
- LINQ to XML : pour bosser avec des documents XML.
- Et plein d’autres.
Ça veut dire qu’en maîtrisant LINQ, tu peux bosser avec des données de toutes sortes de sources, en utilisant la même logique de requêtes.
3. Le problème de bosser avec les collections "à l’ancienne"
Avant LINQ, le code pour manipuler les collections en C# ressemblait à ça :
var products = new List<Product> { /* ... */ };
var expensive = new List<Product>();
foreach (var prod in products)
{
if (prod.Price > 100)
expensive.Add(prod);
}
expensive.Sort((a, b) => a.Price.CompareTo(b.Price));
foreach (var item in expensive)
Console.WriteLine(item.Name);
Ce genre d’approche est typique : d’abord tu filtres à la main, puis tu tries — obligé de créer une liste intermédiaire. Plus il y a de logique, plus il y a de code, d’erreurs et de variables. Ça ressemble à un atelier de montage de meubles : t’as les pièces, mais l’assemblage est manuel et chiant.
Alice au pays des boucles
Imagine que t’as une énorme liste d’utilisateurs, et tu veux juste les noms de ceux qui ont plus de 18 ans et vivent dans la ville Neonville, les trier par ordre alphabétique et afficher les trois premiers. Tu vas devoir pondre des boucles imbriquées, des conditions, des tris, des listes d’aide... ou juste utiliser LINQ.
4. À quoi ressemble une requête LINQ ? Exemples simples
LINQ propose deux syntaxes principales :
- Method Syntax (chaînage de méthodes)
- Query Syntax (langage façon SQL)
La plupart du temps, on utilise Method Syntax, surtout dans les projets modernes. Voilà un exemple basé sur notre appli :
var expensive = products
.Where(p => p.Price > 100)
.OrderBy(p => p.Price)
.Select(p => p.Name)
.Take(3);
foreach (var name in expensive)
Console.WriteLine(name);
Tout est clair : filtre, tri, sélection d’un champ, limitation du nombre. Minimum de code — maximum de sens.
Pareil avec Query Syntax
var expensive = from p in products
where p.Price > 100
orderby p.Price
select p.Name;
foreach (var name in expensive.Take(3))
Console.WriteLine(name);
Les deux donnent le même résultat — choisis celui que tu préfères (mais le method-syntax est quand même plus courant).
5. Architecture de LINQ : qu’est-ce qu’il y a sous le capot ?
LINQ marche avec toutes les collections qui implémentent l’interface IEnumerable<T> (ou IQueryable<T>, on en parlera plus tard).
Les composants principaux de LINQ
| Composant | Description courte |
|---|---|
| Classes d’extension LINQ | Méthodes statiques (, , etc.) dans la classe |
| Délégués | Le plus souvent, on utilise et |
| Exécution différée | La requête est évaluée seulement au premier parcours (genre avec ) |
| Query Provider | (pour LINQ to SQL, LINQ to Entities) — transforme la chaîne de méthodes en requêtes SQL, etc. |
Schéma visuel
Collection (List<Product>, T[], ...)
│
▼
Méthodes LINQ (Where, OrderBy, Select...)
│
▼
Requête (IEnumerable<T>)
│
▼
Exécution réelle (foreach, ToList, Count, ...)
6. Exemple : analyse étape par étape d’une chaîne LINQ dans notre appli
On revient à notre mini-appli avec la classe Product :
// Classe produit
class Product
{
public string Name { get; set; }
public double Price { get; set; }
}
Voilà la liste des produits :
var products = new List<Product>
{
new Product { Name = "Fromage", Price = 250.5 },
new Product { Name = "Pain", Price = 30 },
new Product { Name = "Lait", Price = 80 },
new Product { Name = "Café", Price = 330 },
new Product { Name = "Beurre", Price = 140 }
};
Disons que notre objectif est le suivant : afficher à l’écran les noms de tous les produits à plus de 100 euros, triés par prix.
Méthode à l’ancienne
var filtered = new List<Product>();
foreach (var p in products)
{
if (p.Price > 100)
filtered.Add(p);
}
filtered.Sort((a, b) => a.Price.CompareTo(b.Price));
foreach (var p in filtered)
Console.WriteLine(p.Name);
Méthode LINQ
var expensive = products
.Where(p => p.Price > 100)
.OrderBy(p => p.Price)
.Select(p => p.Name);
foreach (var name in expensive)
Console.WriteLine(name);
Dans la première ligne, on décrit l’essence même de la tâche : "Filtre ceux dont Price > 100, trie par prix, prends les noms".
7. Opérations LINQ courantes et leurs équivalents "à l’ancienne"
| Opération | Logique "à l’ancienne" | LINQ |
|---|---|---|
| Filtrage | foreach + if + Add | |
| Projection (sélection d’un champ) | foreach + Add(field) | |
| Tri | Sort(comparer) | , |
| Valeurs uniques | foreach + contains + Add | |
| Comptage | foreach + counter++ | , |
| Vérification de condition | foreach + if | , |
| Recherche du premier/dernier | foreach + if + break | , , |
| Limiter le nombre | foreach + compteur + break | , |
8. Pratique : on ajoute LINQ à l’appli
On prend le code de base de notre appli (la liste de Product) et on va essayer de faire quelques opérations utiles avec LINQ.
Filtrage et tri
// Sélectionner les produits à moins de 200 euros et trier par nom
var cheapProducts = products
.Where(p => p.Price < 200)
.OrderBy(p => p.Name);
foreach (var p in cheapProducts)
Console.WriteLine($"{p.Name}: {p.Price} euros");
Transformation (projection) de la collection
// Obtenir la liste des prix (double)
var prices = products.Select(p => p.Price);
foreach (var price in prices)
Console.WriteLine(price);
Recherche du premier élément qui correspond
// Premier produit dont le nom commence par "C"
var firstK = products.FirstOrDefault(p => p.Name.StartsWith("C"));
Console.WriteLine(firstK?.Name ?? "Non trouvé");
Compter le nombre de produits à plus de 100 euros
int count = products.Count(p => p.Price > 100);
Console.WriteLine($"Il y en a : {count} pcs.");
À partir de cette conf, on va utiliser LINQ à fond pour manipuler les collections, filtrer, sélectionner les données qu’il faut, faire des agrégations et plein d’autres trucs. On verra les nouveaux méthodes et possibilités de LINQ (y compris les nouveautés de .NET 9 !) dans les prochaines confs, mais pour l’instant — n’hésite pas à expérimenter avec des requêtes simples pour sentir toute la puissance de cet outil !
GO TO FULL VERSION