Comparaison de la syntaxe de requête LINQ et de la syntaxe de méthode (C#)
La plupart des requêtes dans la documentation LINQ d'introduction sont écrites comme des expressions de requête à l'aide de la syntaxe de requête déclarative présentée dans C# 3.0. Toutefois, le common language runtime .NET (CLR) ne renferme lui-même aucune notion de syntaxe de requête. Par conséquent, à la compilation, les expressions de requête sont traduites en appels de méthode que le CLR comprend. Ces méthodes sont appelées les opérateurs de requête standard et ont des noms tels que Where, Select, GroupBy, Join, Max, Average, etc. Vous pouvez les appeler directement en utilisant la syntaxe de méthode plutôt que la syntaxe de requête.
En général, nous recommandons l'utilisation de la syntaxe de requête car elle est habituellement plus simple et plus lisible ; toutefois, il n'y a aucune différence sémantique entre la syntaxe de méthode et la syntaxe de requête. De plus, certaines requêtes, telles que celles qui récupèrent le nombre d'éléments qui correspondent à une condition spécifiée ou qui récupèrent l'élément qui a la valeur maximale dans une séquence source, peuvent être exprimées uniquement comme appels de méthode. En général, la documentation de référence pour les opérateurs de requête standard dans l'espace de noms System.Linq utilise la syntaxe de méthode. Par conséquent, même lorsque vous commencez à écrire des requêtes LINQ, il est utile de se familiariser avec l'utilisation de la syntaxe de méthode dans les requêtes et dans les expressions de requête elles-mêmes.
Méthodes d'extension d'opérateur de requête standard
L'exemple suivant présente une expression de requête simple et la requête sémantiquement équivalente écrite comme une requête basée sur une méthode.
class QueryVMethodSyntax
{
static void Main()
{
int[] numbers = { 5, 10, 8, 3, 6, 12};
//Query syntax:
IEnumerable<int> numQuery1 =
from num in numbers
where num % 2 == 0
orderby num
select num;
//Method syntax:
IEnumerable<int> numQuery2 = numbers.Where(num => num % 2 == 0).OrderBy(n => n);
foreach (int i in numQuery1)
{
Console.Write(i + " ");
}
Console.WriteLine(System.Environment.NewLine);
foreach (int i in numQuery2)
{
Console.Write(i + " ");
}
// Keep the console open in debug mode.
Console.WriteLine(System.Environment.NewLine);
Console.WriteLine("Press any key to exit");
Console.ReadKey();
}
}
/*
Output:
6 8 10 12
6 8 10 12
*/
La sortie des deux exemples est identique. Vous pouvez voir que le type de la variable de requête est le même dans les deux formes : IEnumerable<T>.
Pour comprendre la requête basée sur une méthode, examinons-la plus attentivement. Du côté droit de l'expression, remarquez que la clause where est maintenant exprimée comme une méthode d'instance sur l'objet numbers, qui, comme vous pouvez vous en rappeler, a un type IEnumerable<int>. Si vous êtes un habitué de l'interface IEnumerable<T> générique, vous savez qu'elle n'a pas de méthode Where. Toutefois, si vous appelez la liste de saisie semi-automatique IntelliSense dans l'IDE de Visual Studio, vous ne verrez pas seulement une méthode Where, mais de nombreuses autres méthodes telles que Select, SelectMany, Join et Orderby. Il s'agit de tous les opérateurs de requête standard.
Même s'il semble que IEnumerable<T> a été redéfini pour inclure ces méthodes supplémentaires, ce n'est pas le cas, en réalité. Les opérateurs de requête standard sont implémentés comme nouveau type de méthode appelé méthodes d'extension. Les méthodes d'extensions « étendent » un type existant ; elles peuvent être appelées comme s'il s'agissait de méthodes d'instance sur le type. Les opérateurs de requête standard étendent IEnumerable<T> et c'est pourquoi vous pouvez écrire numbers.Where(...).
Pour commencer à utiliser LINQ, tout ce que vous devez savoir sur les méthodes d'extension est la façon de les mettre à portée de votre application à l'aide des directives using appropriées. Cette opération est expliquée plus en détails dans Comment : créer un projet LINQ. Du point de vue de votre application, une méthode d'extension et une méthode d'instance normale n'ont pas de différence.
Pour plus d'informations sur les méthodes d'extension, consultez Méthodes d'extension (Guide de programmation C#). Pour plus d'informations sur les opérateurs de requête standard, consultez Vue d'ensemble des opérateurs de requête standard. Certains fournisseurs LINQ, comme LINQ to SQL et LINQ to XML, implémentent leurs propres opérateurs de requête standard et leurs propres méthodes d'extension supplémentaires pour d'autres types que IEnumerable<T>.
Expressions lambda
Dans l'exemple précédent, remarquez que l'expression conditionnelle (num % 2 == 0) est passée comme un argument aligné avec la méthode Where: Where(num => num % 2 == 0). Cette expression inline est appelée une expression lambda. C'est un moyen pratique d'écrire du code qui devrait normalement être écrit de façon plus fastidieuse comme une méthode anonyme ou un délégué générique ou une arborescence d'expression. En C#, => est l'opérateur lambda, qui se lit « est redirigé vers ». Le num à gauche de l'opérateur est la variable d'entrée qui correspond à num dans l'expression de requête. Le compilateur peut déduire le type de num car il sait que numbers est un type IEnumerable<T> générique. Le corps de l'expression lambda est exactement le même que l'expression dans une syntaxe de requête ou dans toute autre expression ou instruction C# ; il peut inclure des appels de méthode et une autre logique complexe. La « valeur de retour » est simplement le résultat de l'expression.
Vous n'avez pas besoin d'utiliser fréquemment les expressions lambda pour commencer à utiliser LINQ. Toutefois, quelques requêtes peuvent être exprimées uniquement en syntaxe de méthode et certaines d'entre elles requièrent des expressions lambda. Une fois que vous vous êtes familiarisé avec les expressions lambda, vous constaterez qu'elles sont un outil puissant et flexible dans votre boîte à outils LINQ. Pour plus d'informations, consultez Expressions lambda (Guide de programmation C#).
Composabilité des requêtes
Dans l'exemple de code précédent, notez que la méthode OrderBy est appelée en utilisant l'opérateur point sur l'appel de Where. Where produit une séquence filtrée, puis Orderby fonctionne sur cette séquence en la triant. Étant donné que les requêtes retournent un IEnumerable, vous les composez dans la syntaxe de méthode en chaînant les appels de méthode ensemble. Le compilateur effectue cette opération en arrière-plan lorsque vous écrivez des requêtes à l'aide de la syntaxe de requête. Étant donné qu'une variable de requête ne stocke pas les résultats de la requête, vous pouvez la modifier ou l'utiliser à tout moment comme base d'une nouvelle requête, même après son exécution.