Relazioni tra i tipi nelle operazioni di query (Visual Basic)

Le variabili usate nelle operazioni di query LINQ (Language Integrated Query) sono fortemente tipate e devono essere compatibili tra loro. La digitazione avanzata viene usata nell'origine dati, nella query stessa e nell'esecuzione della query. La figura seguente identifica i termini usati per descrivere una query LINQ. Per altre informazioni sulle parti di una query, vedere Operazioni di query di base (Visual Basic).

Screenshot showing a pseudocode query with elements highlighted.

Il tipo della variabile di intervallo nella query deve essere compatibile con il tipo degli elementi nell'origine dati. Il tipo della variabile di query deve essere compatibile con l'elemento sequenza definito nella clausola Select. Infine, il tipo degli elementi della sequenza deve essere compatibile anche con il tipo della variabile di controllo ciclo usata nell'istruzione For Each che esegue la query. Questa digitazione avanzata facilita l'identificazione degli errori di tipo in fase di compilazione.

Visual Basic semplifica la digitazione implementando l'inferenza del tipo locale, nota anche come tipizzazione implicita. Questa funzionalità viene usata nell'esempio precedente e verrà usata in tutti gli esempi e la documentazione di LINQ. In Visual Basic, l'inferenza del tipo locale viene eseguita semplicemente usando un'istruzione Dim senza una clausola As. Nell'esempio seguente, city viene fortemente tipizzato come stringa.

Dim city = "Seattle"

Nota

L'inferenza del tipo locale funziona solo quando Option Infer è impostata su On. Per altre informazioni, vedere Istruzione Option Infer.

Tuttavia, anche se si usa l'inferenza del tipo locale in una query, le stesse relazioni di tipo sono presenti tra le variabili nell'origine dati, la variabile di query e il ciclo di esecuzione della query. È utile avere una conoscenza di base di queste relazioni di tipo quando si scrivono query LINQ o si lavora con gli esempi e i codici della documentazione.

Potrebbe essere necessario specificare un tipo esplicito per una variabile di intervallo che non corrisponde al tipo restituito dall'origine dati. È possibile specificare il tipo della variabile di intervallo usando una clausola As. Tuttavia, viene generato un errore se la conversione è una conversione che supporta un minor numero di dati e Option Strict viene impostata su On. È pertanto consigliabile eseguire la conversione sui valori recuperati dall'origine dati. È possibile convertire i valori dall'origine dati al tipo di variabile di intervallo esplicito usando il metodo Cast. È anche possibile eseguire il cast dei valori selezionati nella clausola Select a un tipo esplicito diverso dal tipo della variabile di intervallo. Questi punti sono illustrati nel codice seguente.

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)

Query che restituiscono interi elementi dei dati di origine

Nell'esempio seguente viene illustrata un'operazione di query LINQ che restituisce una sequenza di elementi selezionati dai dati di origine. L'origine, names, contiene una matrice di stringhe e l'output della query è una sequenza contenente stringhe che iniziano con la lettera 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

Equivale al codice seguente, ma è molto più breve e più facile da scrivere. La dipendenza dall'inferenza del tipo locale nelle query è lo stile preferito in 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

Le relazioni seguenti esistono in entrambi gli esempi di codice precedenti, indipendentemente dal fatto che i tipi vengano determinati in modo implicito o esplicito.

  1. Il tipo degli elementi nell'origine dati, names, è il tipo della variabile di intervallo, name, nella query.

  2. Il tipo di oggetto selezionato, name, determina il tipo della variabile di query, mNames. Ecco una stringa name, quindi la variabile di query è IEnumerable(Of String) in Visual Basic.

  3. La query definita in mNames viene eseguita nel ciclo For Each. Il ciclo esegue l'iterazione del risultato dell'esecuzione della query. Poiché mNames, quando viene eseguito, restituirà una sequenza di stringhe, la variabile di iterazione del ciclo, nm, è anche una stringa.

Query che restituiscono un campo dagli elementi selezionati

Nell'esempio seguente viene illustrata un'operazione di query LINQ to SQL che restituisce una sequenza contenente una sola parte di ogni elemento selezionato dall'origine dati. La query accetta una raccolta di oggetti Customer come origine dati e proietta solo la proprietà Name nel risultato. Poiché il nome del cliente è una stringa, la query produce una sequenza di stringhe come output.

' 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

Le relazioni tra le variabili sono simili a quelle dell'esempio più semplice.

  1. Il tipo degli elementi nell'origine dati, customers, è il tipo della variabile di intervallo, cust, nella query. In questo esempio, il tipo è Customer.

  2. L'istruzione Select restituisce la proprietà Name di ogni oggetto Customer anziché l'intero oggetto. Poiché Name è una stringa, la variabile di query, custNames, sarà nuovamente IEnumerable(Of String), non di Customer.

  3. Poiché custNames rappresenta una sequenza di stringhe, la variabile di iterazione del ciclo For Each, custName, deve essere una stringa.

Senza inferenza del tipo locale, l'esempio precedente sarebbe più complesso da scrivere e comprendere, come illustrato nell'esempio seguente.

' 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

Query che richiedono tipi anonimi

L'esempio seguente mostra una situazione più complessa. Nell'esempio precedente era poco pratico specificare i tipi per tutte le variabili in modo esplicito. In questo esempio, è impossibile. Anziché selezionare interi elementi Customer dall'origine dati, o un singolo campo da ogni elemento, la clausola Select in questa query restituisce due proprietà dell'oggetto Customer originale: Name e City. In risposta alla clausola Select, il compilatore definisce un tipo anonimo che contiene queste due proprietà. Il risultato dell'esecuzione di nameCityQuery nel ciclo For Each è una raccolta di istanze del nuovo tipo anonimo. Poiché il tipo anonimo non ha un nome utilizzabile, non è possibile specificare il tipo di nameCityQuery o custInfo in modo esplicito. Ovvero, con un tipo anonimo, non si dispone di alcun nome di tipo da usare al posto di String in IEnumerable(Of String). Per altre informazioni, vedere Tipi anonimi.

' 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

Anche se non è possibile specificare tipi per tutte le variabili nell'esempio precedente, le relazioni rimangono invariate.

  1. Il tipo degli elementi nell'origine dati è di nuovo il tipo della variabile di intervallo nella query. In questo esempio, cust è un'istanza di Customer.

  2. Poiché l'istruzione Select produce un tipo anonimo, la variabile di query, nameCityQuery, deve essere tipizzata in modo implicito come tipo anonimo. Un tipo anonimo non ha un nome utilizzabile e pertanto non può essere specificato in modo esplicito.

  3. Il tipo della variabile di iterazione nel ciclo For Each è il tipo anonimo creato nel passaggio 2. Poiché il tipo non ha un nome utilizzabile, il tipo della variabile di iterazione del ciclo deve essere determinato in modo implicito.

Vedi anche