LINQ (Language Integrated Query) est une bibliothèque de méthodes pour filtrer, transformer, trier, grouper et résumer des séquences. Ajoutez using System.Linq; et chaque tableau, List<T>, Dictionary et autre IEnumerable<T> gagne des méthodes comme Where, Select et OrderBy, qui prennent chacune une lambda indiquant quoi faire d'un élément.
Une première requête : syntaxe de méthode et syntaxe de requête
LINQ a deux notations. La syntaxe de méthode enchaîne des appels ; la syntaxe de requête se lit comme du SQL. Le compilateur transforme la syntaxe de requête en ces mêmes appels de méthode, donc elles produisent des résultats identiques.
Sortie :
95, 91, 88
95, 91, 88
La syntaxe de méthode est la plus courante dans le code moderne, et certains opérateurs (Take, Distinct, Count, First, ToList) n'existent que sous forme de méthodes. La syntaxe de requête brille pour les jointures et pour les requêtes qui introduisent des variables intermédiaires avec let. La suite de cette page utilise la syntaxe de méthode et montre la syntaxe de requête là où elle apporte quelque chose.
Where et Select : filtrer et projeter
Where garde les éléments qui satisfont une condition. Select transforme chaque élément en autre chose. Ensemble, ils couvrent la plupart des requêtes du quotidien.
Sortie :
Mug, Kettle
MUG $8.50 | LAMP $32.00 | KETTLE $27.00 | RUG $120.00
Lamp: 38.40
Rug: 144.00
1. Mug 2. Lamp 3. Kettle 4. Rug
Where ne modifie jamais les éléments, et Select ne change jamais leur nombre. new { p.Name, WithTax = ... } crée un type anonyme, une classe que le compilateur écrit pour vous, dont vous lisez les propriétés avec la syntaxe à point habituelle. Il n'a pas de nom que vous puissiez écrire, donc une variable qui en contient un doit être déclarée avec var, et il convient aux résultats qui restent dans une seule méthode.
OrderBy, ThenBy et le tri sur plusieurs clés
Sortie :
Ana 60
Ana 15
Ben 90
Ben 40
Chloe 25
Ben, Ana, Ben, Chloe, Ana
OrderBy est stable : les éléments de clés égales restent dans leur ordre d'origine. Utilisez ThenBy ou ThenByDescending pour une seconde clé. Un second OrderBy retrierait tout selon sa propre clé, en ne laissant à la première qu'un rôle de départage : la priorité inverse. En syntaxe de requête, les clés sont séparées par des virgules : orderby o.Customer, o.Total descending.
Contrairement à List<T>.Sort, OrderBy laisse la source intacte et renvoie une nouvelle séquence.
First, Single, Last et leurs versions OrDefault
Ces opérateurs renvoient un seul élément au lieu d'une séquence. Les versions simples lèvent une exception quand il n'y a rien à renvoyer ; les versions OrDefault renvoient la valeur par défaut du type.
Sortie :
Ana
Ben
Bea
True
First: InvalidOperationException
Single: InvalidOperationException
0
L'exception de First sur une séquence vide est une InvalidOperationException avec le message « Sequence contains no elements » (ou « Sequence contains no matching element » quand aucun élément ne satisfait le prédicat), l'une des erreurs C# les plus recherchées. Utilisez FirstOrDefault quand « aucun » est un résultat normal, et testez null.
La dernière ligne montre le piège des types valeur : pour une séquence d'int, FirstOrDefault renvoie 0 quand rien ne correspond, ce qu'on ne peut pas distinguer d'un vrai 0. Filtrez avec Where et vérifiez d'abord Any(), ou, depuis .NET 6, passez votre propre valeur par défaut : scores.FirstOrDefault(s => s > 100, -1).
Single sert pour « exactement un » : il lève une exception s'il n'y en a aucun ou plus d'un, ce qui en fait autant une assertion qu'une requête.
Any, All, Count et Contains
Sortie :
True
True
True
3
True
True
Any() s'arrête à la première correspondance, donc items.Any() est la bonne façon de demander « y a-t-il quelque chose ? ». items.Count() > 0 lit la propriété Count quand la source est une collection comme une liste ou un tableau, mais sur une requête ou une autre séquence paresseuse, il parcourt d'abord chaque élément. All sur une séquence vide renvoie true, car aucun élément n'échoue à la condition.
Sum, Average, Min et Max
Sortie :
Total: 28.25
Items: 7
Priciest: 7.25
Average price: 4.92
0
Average: InvalidOperationException
Sum d'une séquence vide vaut 0, mais Average, Min et Max d'une séquence vide de nombres lèvent InvalidOperationException : il n'y a pas de réponse sensée. Protégez-vous avec Any(), ou utilisez d'abord DefaultIfEmpty() : empty.DefaultIfEmpty(0).Average().
Distinct, Take et Skip
Sortie :
home, shop, cart
Page 1: 1 2 3 4 5 6 7 8 9 10
Page 2: 11 12 13 14 15 16 17 18 19 20
Page 3: 21 22 23
12, 15
Distinct garde la première occurrence de chaque valeur, dans l'ordre. Skip(n).Take(m) est le schéma standard de pagination, et TakeWhile/SkipWhile s'arrêtent ou commencent au premier élément qui ne satisfait pas une condition. Enumerable.Range(start, count) génère une séquence d'entiers sans tableau.
GroupBy
GroupBy répartit les éléments dans des compartiments selon une clé. Chaque groupe a une Key et est lui-même une séquence, donc vous pouvez l'agréger.
Sortie :
North: 3 sales, total 600
South: 2 sales, total 400
East: 1 sales, total 350
Top rep: Ana 400
North -> Ana/Chloe/Ana; South -> Ben/Eli; East -> Dev
Les groupes sortent dans l'ordre où leurs clés apparaissent pour la première fois dans la source (North, South, East), pas triés ; ajoutez OrderBy(g => g.Key) si vous en avez besoin. En syntaxe de requête, group X by K choisit ce qui va dans chaque groupe (ici, seulement le nom du vendeur) et into g nomme le groupe pour que la requête puisse continuer.
ToList, ToArray et ToDictionary
Les méthodes To... exécutent la requête et stockent les résultats.
Sortie :
ben@x.com
2
ToDictionary: ArgumentException
ToDictionary lève ArgumentException quand deux éléments produisent la même clé, exactement comme Dictionary.Add. Si des doublons sont attendus, groupez d'abord (comme le fait wordCounts) ou utilisez ToLookup, qui associe chaque clé à une séquence de valeurs.
L'exécution différée
Where, Select, OrderBy, GroupBy et la plupart des autres opérateurs renvoient un objet requête et ne font aucun travail tant que quelque chose ne l'énumère pas. Cela a deux conséquences qui méritent d'être vues :
Sortie :
25, 40, 99
converting 10
converting 25
converting 40
converting 99
Count: 4
converting 10
converting 25
converting 40
converting 99
Max: 9900
converting 10
converting 25
converting 40
converting 99
Cached: 4, 9900
La première requête a vu 99 alors qu'il a été ajouté après l'écriture de la requête : le filtre s'est exécuté quand string.Join l'a énumérée. La seconde requête a affiché ses conversions deux fois, car Count() et Max() l'ont chacun énumérée depuis le début. Avec une lambda peu coûteuse, ce n'est que du travail gaspillé ; avec une requête de base de données, une lecture de fichier ou un nombre aléatoire dans la lambda, les deux passages peuvent renvoyer des données différentes. Quand vous utiliserez un résultat plus d'une fois, matérialisez-le avec ToList() ou ToArray().
Les opérateurs qui renvoient une seule valeur (Count, Sum, First, Any, Max) et les méthodes To... s'exécutent immédiatement. Voir IEnumerable et yield pour la façon dont cette paresse est construite.
Référence rapide
| Opérateur | Fait | Renvoie |
|---|---|---|
Where(x => cond) | Filtrer | Séquence (différée) |
Select(x => expr) | Transformer chaque élément | Séquence (différée) |
SelectMany(x => seq) | Aplatir des séquences imbriquées | Séquence (différée) |
OrderBy, ThenBy (+Descending) | Trier, de façon stable | Séquence (différée) |
GroupBy(x => key) | Répartir par clé | Séquence de groupes (différée) |
Distinct, Take, Skip | Dédoublonner, paginer | Séquence (différée) |
First, Single, Last | Un élément, exception s'il n'y en a pas | Élément |
FirstOrDefault etc. | Un élément ou la valeur par défaut | Élément |
Any, All, Contains | Tester | bool |
Count, Sum, Min, Max, Average | Agréger | Nombre |
ToList, ToArray, ToDictionary | Exécuter et stocker | Collection |
Erreurs courantes
First()sur une séquence qui peut être vide. Cela lève « Sequence contains no elements » ; utilisezFirstOrDefaultet testez.- Deux appels à
OrderBy. La seconde clé devient la principale ; utilisezThenBy. - Énumérer une requête plusieurs fois. Le travail se répète ; appelez
ToList()une fois. - S'attendre à ce qu'une requête fige sa source. Elle lit la source au moment de l'énumération, pas de l'écriture.
Average,Min,Maxsur une séquence vide. Ils lèvent une exception ; protégez-vous avecAny()ouDefaultIfEmpty.Count() > 0pour tester la présence d'éléments.Any()s'arrête au premier.
Questions fréquentes
Qu'est-ce que LINQ en C# ?
LINQ (Language Integrated Query) est un ensemble de méthodes d'extension de System.Linq, comme Where, Select, OrderBy et GroupBy, qui fonctionnent sur n'importe quel IEnumerable<T> : tableaux, listes, dictionnaires et résultats d'autres requêtes. C# a aussi une syntaxe de requête (from x in items where ... select ...) que le compilateur traduit en ces mêmes appels de méthode.
Quelle est la différence entre Select et Where dans LINQ ?
Where filtre : il garde les éléments pour lesquels la lambda renvoie true et les laisse inchangés. Select projette : il transforme chaque élément en autre chose, comme une propriété ou un nouvel objet, et garde le même nombre d'éléments. On les enchaîne souvent : people.Where(p => p.Age >= 18).Select(p => p.Name).
Quelle est la différence entre First et FirstOrDefault ?
First() renvoie le premier élément (ou la première correspondance d'un prédicat) et lève InvalidOperationException (« Sequence contains no elements » ou « no matching element ») quand il n'y en a pas. FirstOrDefault() renvoie à la place la valeur par défaut du type : null pour les classes et 0 pour int, donc vérifiez le résultat avant de l'utiliser.
Comment trier sur deux champs avec LINQ ?
Enchaînez ThenBy après OrderBy : orders.OrderBy(o => o.Customer).ThenByDescending(o => o.Total). Appeler deux fois OrderBy ne fonctionne pas : le second appel retrie toute la séquence, sa clé devient donc la clé principale et la première ne sert plus qu'à départager les égalités, l'inverse de ce qui était voulu.
Comment fonctionne GroupBy dans LINQ ?
GroupBy(x => key) renvoie un groupe par clé distincte. Chaque groupe est un IGrouping<TKey, TElement> : il a une propriété Key et est lui-même une séquence des éléments ayant cette clé, donc vous pouvez appeler Count(), Sum() ou Select dessus. Les groupes sortent dans l'ordre où leurs clés apparaissent pour la première fois.
Qu'est-ce que l'exécution différée dans LINQ ?
La plupart des opérateurs LINQ ne s'exécutent pas quand vous les appelez ; ils renvoient une requête qui s'exécute à chaque énumération (par foreach, ToList(), Count(), etc.). Une requête voit donc les modifications apportées à la source après sa définition, et l'énumérer deux fois refait le travail deux fois. Appelez ToList() ou ToArray() pour l'exécuter une fois et garder les résultats.