Relations des types dans des opérations de requête (Visual Basic)

Les variables utilisées dans les opérations de requête Language-Integrated Query (LINQ) sont fortement typées et doivent être compatibles les unes avec les autres. Les opérations de requête 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. L’illustration suivante identifie les termes utilisés pour décrire une requête LINQ. Pour plus d’informations concernant les parties d’une requête, consultez Opérations de requête de base (Visual Basic).

Screenshot showing a pseudocode query with elements highlighted.

Le type des variables de portée dans la requête doit être compatible avec le type des éléments dans la source de données. Le type de la variable de requête doit être compatible avec l’élément de séquence défini dans la clause Select. Enfin, le type des éléments de séquence doit également être compatible avec le type de la variable du contrôle de boucle utilisée dans l’instruction For Each exécutant la requête. Ce typage fort facilite l’identification des erreurs de type lors de la compilation.

Visual Basic facilite le typage fort en implémentant l’inférence de type local, également appelée typage implicite. Cette fonctionnalité est utilisée dans l’exemple précédent, et vous verrez qu’elle est utilisée dans les exemples et documentation LINQ. Dans Visual Basic, l’inférence de type local s’effectue tout simplement à l’aide d’une Dim instruction sans clause As. Dans l’exemple suivant, city est fortement typé sous forme de chaîne.

Dim city = "Seattle"

Notes

L’inférence de type local fonctionne uniquement lorsque Option Infer est défini sur On. Pour plus d’informations, consultez Instruction Option Infer.

Toutefois, bien que vous utilisez l’inférence de type local dans une requête, les mêmes relations de type sont présentes entre les variables de la source de données, la variable de requête et la boucle d’exécution de requête. Une compréhension de base de ces types de relations est utile lorsque vous écrivez des requêtes LINQ ou travaillez avec les échantillons et les exemples de code de la documentation.

Vous devez peut-être spécifier un type explicite pour une variable de plage qui ne correspond pas au type renvoyé par la source de données. Vous pouvez spécifier le type de la variable de plage à l’aide d’une clause As. Toutefois, ceci entraîne une erreur si la conversion est une conversion restrictive et si Option Strict est défini sur On. Par conséquent, nous vous recommandons d’effectuer la conversion sur les valeurs récupérées à partir de la source de données. Vous pouvez convertir les valeurs de la source de données en type de variable de plage explicite à l’aide de la méthode Cast. Vous pouvez également caster les valeurs sélectionnées dans la clause Select en un type explicite différent du type de la variable de plage. Certains points sont illustrés dans le code suivant.

Dim numbers1() As Integer = {1, 2, 4, 16, 32, 64}
Dim numbers2() As Double = {5.0#, 10.0#, 15.0#}

' This code does not result in an error.
Dim numberQuery1 = From n As Integer In numbers1 Where n > 5

' This code results in an error with Option Strict set to On. The type Double
' cannot be implicitly cast as type Integer.
Dim numberQuery2 = From n As Integer In numbers2 Where n > 5

' This code casts the values in the data source to type Integer. The type of
' the range variable is Integer.
Dim numberQuery3 = From n In numbers2.Cast(Of Integer)() Where n > 5

' This code returns the value of the range variable converted to Integer. The type of
' the range variable is Double.
Dim numberQuery4 = From n In numbers2 Where n > 5 Select CInt(n)

Requêtes renvoyant des éléments entiers des données sources

L’exemple suivant montre une opération de requête LINQ renvoyant une séquence d’éléments sélectionnés à partir des données sources. La source, names, contient un tableau de chaînes, et la sortie de requête correspond à une séquence contenant des chaînes qui commencent par la lettre M.

Dim names = {"John", "Rick", "Maggie", "Mary"}
Dim mNames = From name In names
             Where name.IndexOf("M") = 0
             Select name

For Each nm In mNames
    Console.WriteLine(nm)
Next

Ceci équivaut au code suivant, mais beaucoup plus court et plus facile à écrire. Le recours à l’inférence locale de type dans les requêtes constitue le style préféré dans Visual Basic.

Dim names2 = {"John", "Rick", "Maggie", "Mary"}
Dim mNames2 As IEnumerable(Of String) =
    From name As String In names
    Where name.IndexOf("M") = 0
    Select name

For Each nm As String In mNames
    Console.WriteLine(nm)
Next

Les relations suivantes existent dans les deux exemples de code précédents, que les types soient déterminés implicitement ou explicitement.

  1. Le type des éléments dans la source de données, names, constitue le type de la variable de portée, name, dans la requête.

  2. Le type de l’objet sélectionné, name, détermine le type de la variable de requête, mNames. Ici, name correspond à une chaîne, la variable de requête est donc IEnumerable(Of String) dans Visual Basic.

  3. La requête définie dans mNames est exécutée dans la boucle For Each. La boucle itère sur le résultat de l’exécution de la requête. Étant donné que mNames, lorsqu’il est exécuté, renvoie une séquence de chaînes, la variable d’itération de boucle, nm, est également une chaîne.

Requêtes renvoyant un champ à partir d’éléments sélectionnés

L’exemple suivant montre une opération de requête LINQ to SQL renvoyant une séquence contenant une seule partie de chaque élément sélectionné à partir de la source de données. La requête prend une collection d’objets Customer comme source de données et projette uniquement la propriété Name dans le résultat. Puisque le nom du client est une chaîne, la requête produit une séquence de chaînes en sortie.

' Method GetTable returns a table of Customer objects.
Dim customers = db.GetTable(Of Customer)()
Dim custNames = From cust In customers
                Where cust.City = "London"
                Select cust.Name

For Each custName In custNames
    Console.WriteLine(custName)
Next

Les relations entre les variables sont similaires à celles de l’exemple le plus simple.

  1. Le type des éléments dans la source de données, customers, constitue le type de la variable de portée, cust, dans la requête. Dans cet exemple, ce type est Customer.

  2. L’instruction Select renvoie la propriété Name de chaque objet Customer au lieu de l’objet entier. Étant donné que Name est une chaîne, la variable de requête, custNames, sera à nouveau IEnumerable(Of String), et non de Customer.

  3. Comme custNames représente une séquence de chaînes, la variable d’itération de la boucle For Each doit également être de type custName.

Sans inférence de type local, l’exemple précédent serait plus fastidieux à écrire et à comprendre, comme le montre l’exemple suivant.

' Method GetTable returns a table of Customer objects.
 Dim customers As Table(Of Customer) = db.GetTable(Of Customer)()
 Dim custNames As IEnumerable(Of String) =
     From cust As Customer In customers
     Where cust.City = "London"
     Select cust.Name

 For Each custName As String In custNames
     Console.WriteLine(custName)
 Next

Requêtes nécessitant des types anonymes

L'exemple suivant illustre une clause une situation plus complexe. Dans l’exemple précédent, il n’était pas pratique de spécifier explicitement des types pour toutes les variables. Dans cet exemple, c’est impossible. Au lieu de sélectionner des éléments entiers Customer à partir de la source de données, ou un seul champ de chaque élément, la clause Select de cette requête renvoie deux propriétés de l’objet d’origine Customer : Name et City. En réponse à la clause Select, le compilateur définit un type anonyme contenant ces deux propriétés. Le résultat de l’exécution nameCityQuery dans la boucle For Each est une collection d’instances du nouveau type anonyme. Puisque le type anonyme ne comporte aucun nom utilisable, vous ne pouvez pas spécifier le type de nameCityQuery ou custInfo explicitement. Autrement dit, avec un type anonyme, vous n’avez aucun nom de type à utiliser à la place de String dans IEnumerable(Of String). Pour plus d’informations, consultez Types anonymes.

' Method GetTable returns a table of Customer objects.
Dim customers = db.GetTable(Of Customer)()
Dim nameCityQuery = From cust In customers
                    Where cust.City = "London"
                    Select cust.Name, cust.City

For Each custInfo In nameCityQuery
    Console.WriteLine(custInfo.Name)
Next

Bien qu’il ne soit pas possible de spécifier des types pour toutes les variables de l’exemple précédent, les relations restent les mêmes.

  1. Le type des éléments dans la source de données constitue le type de la variable de portée dans la requête. Dans cet exemple, cust est une instance de Customer.

  2. Comme l’instruction Select génère un type anonyme, la variable de requête,nameCityQuery, doit être implicitement typée comme un type anonyme. Un type anonyme n’a aucun nom utilisable et ne peut donc pas être spécifié explicitement.

  3. Le type de la variable d’itération dans la boucle For Each est le type anonyme créé à l’étape 2. Étant donné que le type n’a aucun nom utilisable, le type de la variable d’itération de boucle doit être déterminé implicitement.

Voir aussi