Blog ENI : Toute la veille numérique !
🐠 -25€ dès 75€ 
+ 7 jours d'accès à la Bibliothèque Numérique ENI. Cliquez ici
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
  1. Livres et vidéos
  2. C# 10 et Visual Studio Code
  3. LINQ
Extrait - C# 10 et Visual Studio Code Les fondamentaux du langage
Extraits du livre
C# 10 et Visual Studio Code Les fondamentaux du langage
2 avis
Revenir à la page d'achat du livre

LINQ

Fonctionnement de base

Arrivées avec la version 3 du langage C#, les lambdas ont bouleversé le quotidien des développeurs. Une lambda est une façon d’écrire une fonction directement au sein d’une ligne sans avoir à la définir explicitement au sein de la classe. L’avantage de cette approche est que cette fonction peut dès lors être utilisée comme variable et être exécutée par une autre fonction, voire même passée directement à une fonction. Cette nouvelle façon d’écrire une fonction (aussi appelée parfois fonction fléchée) est un raccourci fort apprécié et largement adopté. De plus, lors de la version 3 du framework .NET, Microsoft a inclus un nouveau système de gestion des collections : LINQ (pour Language INtegrated Query).

Les développeurs sont souvent confrontés à une base de données lors de la création d’un projet. Beaucoup connaissent le langage SQL, norme répandue pour les systèmes de gestion de bases de données relationnelles.

Dans une optique de mutualisation des compétences, Microsoft a introduit LINQ dans le framework .NET pour permettre aux développeurs .NET de considérer toute collection comme étant une base de données et d’avoir un langage de requête...

Variables anonymes

C# est un langage fortement typé, ce qui fait qu’un objet d’un type donné ne peut pas changer de type au cours de son existence. Néanmoins, avec l’arrivée de LINQ, il arrive que l’on souhaite récupérer un ensemble de valeurs ayant un regroupement logique sans pour autant en faire une classe ou une structure définie.

Avec la version 3 du C#, le concept de variable anonyme a été introduit. Il s’agit de créer un objet, fortement typé, mais qui n’appartient pas un type connu ni déclaré. Cette flexibilité permet de créer un objet sur mesure, qui respecte toujours toutes les contraintes. Par exemple, si l’on souhaite créer une voiture sans pour autant avoir à définir une classe pour héberger les données relatives, on écrit le code suivant :

var voiture = new { Marque = "Peugeot", Chevaux = 120, Fonctionne = true }; 

Comme on le constate, il n’y a pas de type après l’instruction new. L’ouverture des accolades permet de définir directement la liste des propriétés que va contenir cet objet. Cette flexibilité est très utile pour générer à la volée un objet en retour de requête LINQ, afin de produire un nouvel objet à partir de données d’une...

Principes des opérateurs LINQ

LINQ met nativement à disposition un grand nombre d’opérateurs. Si la liste proposée ne suffit pas à votre usage, un projet communautaire en ajoute beaucoup d’autres : https://github.com/morelinq/MoreLINQ. Néanmoins, nous nous contenterons d’étudier dans ce chapitre ceux qui sont fournis nativement dans le framework.

Tout d’abord, il faut comprendre que LINQ fonctionne dans un ordre inverse de celui de SQL. En effet, une requête SQL simple est souvent formulée de la sorte :

SELECT ... FROM Table WHERE ... ORDER BY ... 

En traduisant cette requête en français, on obtient la logique suivante : dans la table Table (FROM), où on applique telle condition (WHERE), en ordonnant les lignes selon tel ordre (ORDER BY), récupère-moi les colonnes suivantes (SELECT).

Les opérateurs LINQ prennent en paramètre une lambda définissant ce qui est attendu par la méthode. La majorité des lambdas pour LINQ sont formées de la façon suivante : la lambda prend en paramètre l’instance actuelle qui correspond à l’élément courant de la collection et l’utilise dans le corps de la fonction. Il est possible de passer par une fonction classique pour obtenir le même résultat. Ainsi, les deux façons d’écrire suivantes sont similaires :

public void TestLinq() 
{ 
    List<int> l = new() 
    { 
           1, 2, 3, 4, 5, 6, 7, 8, 9 
    }; 
    var entiers = l.Where(i => i < 3); 
    var entiers2 = l.Where(Filtre); 
} 
 
public bool Filtre(int i) 
{ 
    return i < 3; 
}  

Le deuxième cas est moins courant car on passe en paramètre à l’opérateur LINQ le nom de la fonction qui doit être appelée pour le filtre. Cela ne fonctionne que si la fonction a les bons nombre et types de paramètres et type de retour, c’est pourquoi il est recommandé d’utiliser...

Expression de requête LINQ

Jusqu’à présent, nous avons couvert LINQ par le prisme des méthodes d’extension sur l’interface IEnumerable<T>. Cependant, LINQ propose une autre syntaxe, appelée "expression de requête" (ou query expression en anglais). Cette dernière se rapproche d’un formalisme ressemblant davantage à ce que l’on peut trouver en SQL, même si l’ordre logique est inversé.

En effet, lorsque l’on utilise cette approche, il faut commencer par décrire le nom de la variable avec laquelle on souhaite travailler dans une collection donnée, en utilisant le formalisme from ... in ...

À la suite de cette extraction, on peut ajouter un ou plusieurs opérateurs :

  • where.

  • orderby/orderby ... descending. Par défaut, l’opérateur order by fonctionne de façon ascendante sans qu’il soit nécessaire de le préciser. Néanmoins, on peut ajouter le mot-clé ascending pour être explicite.

  • thenby/thenby ... descending. Par défaut, then by fonctionne de façon ascendante sans qu’il soit nécessaire de le préciser. Néanmoins, on peut ajouter le mot-clé ascending pour être explicite.

  • group ... by ....

  • join.

La requête se termine par un select.

Par exemple :

var entiers = new List<int> { 1, 2, 3, 4, 5 }; 
var tri = from i in entiers where i > 2 orderby i descending select 
i.ToString(); // on aura une collection de chaînes, qui contient 
les entiers supérieurs à 2, triés en ordre décroissant 

Comme on peut le constater, cette façon d’écrire une requête LINQ se rapproche du SQL, exception faite de l’inversion de la logique...

Exercice

Il est temps de mettre en pratique ce qui a été vu dans de ce chapitre sur LINQ.

1. Énoncé

Cet exercice consiste à générer (à l’aide des opérateurs LINQ) une liste d’entiers, de 1 à 100, et de ne retourner que la liste des nombres qui sont divisibles par 7. Afin que l’exercice ne soit pas trop simple, il est demandé d’écrire les requêtes LINQ sous les deux formes qui ont été étudiées : par le biais des méthodes d’extension et avec l’expression de requête.

Les chiffres en résultat sont 7, 14, 21, 28, 35, 42, 49, 56, 63, 70, 77, 84, 91 et 98.

Une fois ce résultat obtenu, on le scinde en deux groupes : les chiffres pairs et les chiffres impairs. Chacun des groupes obtenus est identifié par son nom (pair ou impair) et contient les nombres concernés.

Voici un exemple d’affichage de ce qui est attendu :

images/05EI03.PNG

Résultats d’exécution attendus pour l’exercice

La double présence des chiffres en haut s’explique par le fait que les deux requêtes LINQ ont été itérées (celle avec les méthodes d’extension et celle avec l’expression de requête LINQ). Pour obtenir un décalage régulier entre les chiffres, on peut utiliser la tabulation, en insérant le caractère...