Relations des types dans des opérations de requête LINQ (C#)

Pour écrire efficacement des requêtes, vous devez comprendre comment les types des variables dans une opération de requête complète sont liés les uns aux autres. Si vous comprenez ces relations, vous comprendrez plus facilement les exemples de code et les échantillons LINQ dans la documentation. En outre, vous comprendrez ce qui se passe lorsque des variables sont implicitement typées en utilisant var.

Les opérations de requête LINQ sont fortement typées dans la source de données, dans la requête elle-même et dans l’exécution de la requête. Le type des variables dans la requête doit être compatible avec le type des éléments dans la source de données, ainsi qu’avec le type de la variable d’itération dans l’instruction foreach. Ce typage fort garantit l’interception des erreurs de type au moment de la compilation lorsqu’elles peuvent être corrigées avant que les utilisateurs ne les rencontrent.

Pour illustrer ces relations de types, la plupart des exemples qui suivent utilisent le typage explicite pour toutes les variables. Le dernier exemple montre comment les mêmes principes s’appliquent même si vous utilisez le typage implicite en tirant parti de var.

Requêtes qui ne transforment pas les données sources

L’illustration suivante montre une opération de requête LINQ to Objects qui n’effectue aucune transformation des données. La source contient une séquence de chaînes et le résultat de la requête est également une séquence de chaînes.

Diagramme illustrant la relation entre les types de données dans une requête LINQ.

  1. L’argument de type de la source de données détermine le type de la variable de portée.
  2. Le type de l’objet sélectionné détermine le type de la variable de requête. Ici, name est une chaîne. Par conséquent, la variable de requête est un IEnumerable<string>.
  3. La variable de requête fait l’objet d’une itération dans l’instruction foreach. Comme la variable de requête est une séquence de chaînes, la variable d’itération est également une chaîne.

Requêtes qui transforment les données sources

L’illustration suivante montre une opération de requête LINQ to SQL qui effectue une transformation simple des données. La requête accepte une séquence d’objets Customer en entrée et sélectionne uniquement la propriété Name dans le résultat. Comme Name est une chaîne, la requête produit une séquence de chaînes en sortie.

Diagramme montrant une requête qui transforme le type de données.

  1. L’argument de type de la source de données détermine le type de la variable de portée.
  2. L’instruction select retourne la propriété Name à la place de l’objet Customer complet. Comme Name est une chaîne, l’argument de type de custNameQuery est string, et non pas Customer.
  3. Comme custNameQuery est une séquence de chaînes, la variable d’itération de la boucle foreach doit également être de type string.

L’illustration suivante montre une transformation légèrement plus complexe. L’instruction select retourne un type anonyme qui capture seulement deux membres de l’objet Customer original.

Diagramme montrant une requête plus complexe qui transforme le type de données.

  1. L’argument de type de la source de données est toujours le type de la variable de portée dans la requête.
  2. Comme l’instruction select génère un type anonyme, la variable de requête doit être implicitement typée à l’aide de var.
  3. Comme le type de la variable de requête est implicite, la variable d’itération dans la boucle foreach doit également être implicite.

Laisser le compilateur déduire les informations de type

Vous devez comprendre les relations des types dans une opération de requête. Toutefois, vous avez la possibilité de laisser le compilateur faire tout le travail à votre place. Le mot clé var peut être utilisé pour toute variable locale dans une opération de requête. L’illustration suivante est similaire à l’exemple numéro 2 qui a été abordé précédemment. Toutefois, le compilateur fournit le type fort pour chaque variable dans l’opération de requête.

Diagramme illustrant le flux de type avec typage implicite.

LINQ et types génériques (C#)

Les requêtes LINQ sont basées sur des types génériques. Vous ne devez pas avoir une connaissance approfondie des génériques avant de commencer à écrire des requêtes. Il peut cependant être important de comprendre deux concepts de base :

  1. Quand vous créez une instance d’une classe de collection générique comme List<T>, vous remplacez le « T » par le type des objets contenus dans la liste. Par exemple, une liste de chaînes est exprimée sous la forme List<string>, et une liste d’objets Customer est exprimée sous la forme List<Customer>. Une liste générique est fortement typée et offre de nombreux avantages par rapport aux collections qui stockent leurs éléments en tant que Object. Si vous essayez d’ajouter un Customer à un List<string>, vous obtenez une erreur à la compilation. Il est facile d’utiliser des collections génériques, car vous ne devez pas effectuer un cast de type à l’exécution.
  2. IEnumerable<T> est l’interface qui permet l’énumération des classes de collection génériques avec l’instruction foreach. Les classes de collection génériques prennent en charge IEnumerable<T> de la même façon que les classes de collection non génériques telles que ArrayList prennent en charge IEnumerable.

Pour plus d’informations sur les génériques, consultez Génériques.

Variables IEnumerable<T> dans les requêtes LINQ

Les variables de requête LINQ sont typées en tant que IEnumerable<T> ou un type dérivé tel que IQueryable<T>. Quand vous voyez une variable de requête typée en IEnumerable<Customer>, cela signifie simplement que la requête génère à l’exécution une séquence de zéro ou plusieurs objets Customer.

IEnumerable<Customer> customerQuery =
    from cust in customers
    where cust.City == "London"
    select cust;

foreach (Customer customer in customerQuery)
{
    Console.WriteLine($"{customer.LastName}, {customer.FirstName}");
}

Laisser le compilateur gérer les déclarations de type générique

Si vous préférez, vous pouvez éviter une syntaxe générique en utilisant le mot clé var. Le mot clé var indique au compilateur d’inférer le type d’une variable de requête en examinant la source de données spécifiée dans la clause from. L’exemple suivant génère le même code compilé que l’exemple précédent :

var customerQuery2 =
    from cust in customers
    where cust.City == "London"
    select cust;

foreach(var customer in customerQuery2)
{
    Console.WriteLine($"{customer.LastName}, {customer.FirstName}");
}

Le mot clé var est utile quand le type de la variable est évident ou quand il est particulièrement important de spécifier explicitement des types génériques imbriqués, comme ceux qui sont produits par des requêtes de groupe. D’une façon générale, si vous utilisez var, sachez qu’il peut rendre votre code plus difficile à lire par d’autres développeurs. Pour plus d’informations, consultez Variables locales implicitement typées.