Espressioni in Visual Basic

Un'espressione è una sequenza di operatori e operandi che specifica un calcolo di un valore o che definisce una variabile o una costante. Questo capitolo definisce la sintassi, l'ordine di valutazione degli operandi e degli operatori e il significato delle espressioni.

Expression
    : SimpleExpression
    | TypeExpression
    | MemberAccessExpression
    | DictionaryAccessExpression
    | InvocationExpression
    | IndexExpression
    | NewExpression
    | CastExpression
    | OperatorExpression
    | ConditionalExpression
    | LambdaExpression
    | QueryExpression
    | XMLLiteralExpression
    | XMLMemberAccessExpression
    ;

Classificazioni di espressioni

Ogni espressione viene classificata come una delle seguenti:

  • Un valore. Ogni valore ha un tipo associato.

  • Una variabile. Ogni variabile ha un tipo associato, ovvero il tipo dichiarato della variabile.

  • Uno spazio dei nomi (namespace). Un'espressione con questa classificazione può essere visualizzata solo come lato sinistro di un accesso ai membri. In qualsiasi altro contesto, un'espressione classificata come "namespace" provoca un errore in fase di compilazione.

  • Tipo Un'espressione con questa classificazione può essere visualizzata solo come lato sinistro di un accesso ai membri. In qualsiasi altro contesto, un'espressione classificata come tipo genera un errore in fase di compilazione.

  • Un gruppo di metodi, che è un set di metodi di overload con lo stesso nome. Un gruppo di metodi può avere un'espressione di destinazione associata e un elenco di argomenti di tipo associato.

  • Puntatore al metodo, che rappresenta la posizione di un metodo. Un puntatore al metodo può avere un'espressione di destinazione associata e un elenco di argomenti di tipo associato.

  • Metodo lambda, che è un metodo anonimo.

  • Gruppo di proprietà, che è un set di proprietà di cui è stato eseguito l'overload sullo stesso nome. Un gruppo di proprietà può avere un'espressione di destinazione associata.

  • Accesso a una proprietà. Ogni accesso alle proprietà ha un tipo associato, ovvero il tipo della proprietà. Un accesso alle proprietà può avere un'espressione di destinazione associata.

  • Accesso ad associazione tardiva, che rappresenta un metodo o un accesso alle proprietà posticipato fino alla fase di esecuzione. Un accesso ad associazione tardiva può avere un'espressione di destinazione associata e un elenco di argomenti di tipo associato. Il tipo di accesso ad associazione tardiva è sempre Object.

  • Accesso a un evento. Ogni accesso agli eventi ha un tipo associato, ovvero il tipo dell'evento. Un accesso a un evento può avere un'espressione di destinazione associata. Un accesso agli eventi può essere visualizzato come primo argomento delle RaiseEventistruzioni , AddHandlere RemoveHandler . In qualsiasi altro contesto, un'espressione classificata come accesso a un evento causa un errore in fase di compilazione.

  • Valore letterale matrice, che rappresenta i valori iniziali di una matrice il cui tipo non è ancora stato determinato.

  • Vuoto. Ciò si verifica quando l'espressione è una chiamata di una subroutine o un'espressione dell'operatore await senza risultati. Un'espressione classificata come void è valida solo nel contesto di un'istruzione di chiamata o di un'istruzione await.

  • Valore predefinito. Solo il valore letterale Nothing produce questa classificazione.

Il risultato finale di un'espressione è in genere un valore o una variabile, con le altre categorie di espressioni che funzionano come valori intermedi consentiti solo in determinati contesti.

Si noti che le espressioni il cui tipo è un parametro di tipo possono essere usate in istruzioni ed espressioni che richiedono il tipo di un'espressione per avere determinate caratteristiche, ad esempio un tipo riferimento, un tipo valore, la derivazione da un tipo e così via, se i vincoli imposti sul parametro di tipo soddisfano tali caratteristiche.

Riclassificazione delle espressioni

In genere, quando un'espressione viene usata in un contesto che richiede una classificazione diversa da quella dell'espressione, si verifica un errore in fase di compilazione, ad esempio il tentativo di assegnare un valore a un valore letterale. Tuttavia, in molti casi è possibile modificare la classificazione di un'espressione tramite il processo di riclassificazione.

Se la riclassificazione riesce, la riclassificazione viene giudicata come più ampia o ridotta. Se non diversamente specificato, tutte le riclassificazioni in questo elenco vengono ampliate.

È possibile riclassificare i tipi di espressioni seguenti:

  • Una variabile può essere riclassificata come valore. Il valore archiviato nella variabile viene recuperato.

  • Un gruppo di metodi può essere riclassificato come valore. L'espressione del gruppo di metodi viene interpretata come espressione di chiamata con l'espressione di destinazione associata e l'elenco di parametri di tipo e le parentesi vuote, f ovvero viene interpretata come f() e f(Of Integer) viene interpretata come f(Of Integer)(). Questa riclassificazione può comportare la riclassificazione dell'espressione come void.

  • Un puntatore al metodo può essere riclassificato come valore. Questa riclassificazione può verificarsi solo nel contesto di una conversione in cui è noto il tipo di destinazione. L'espressione puntatore al metodo viene interpretata come argomento di un'espressione di creazione di istanze del delegato del tipo appropriato con l'elenco di argomenti di tipo associato. Per esempio:

    Delegate Sub D(i As Integer)
    
    Module Test
        Sub F(i As Integer)
        End Sub
    
        Sub Main()
            Dim del As D
    
            ' The next two lines are equivalent.
            del = AddressOf F
            del = New D(AddressOf F)
        End Sub
    End Module
    
  • Un metodo lambda può essere riclassificato come valore. Se la riclassificazione si verifica nel contesto di una conversione in cui è noto il tipo di destinazione, può verificarsi una delle due riclassificazioni:

    1. Se il tipo di destinazione è un tipo delegato, il metodo lambda viene interpretato come argomento di un'espressione di costruzione delegato del tipo appropriato.

    2. Se il tipo di destinazione è System.Linq.Expressions.Expression(Of T)e T è un tipo delegato, il metodo lambda viene interpretato come se fosse usato nell'espressione di costruzione del delegato per T e quindi convertito in un albero delle espressioni.

    Un metodo lambda asincrono o iteratore può essere interpretato solo come argomento di un'espressione di costruzione delegato, se il delegato non ha parametri ByRef.

    Se la conversione da uno dei tipi di parametro del delegato ai tipi di parametro lambda corrispondenti è una conversione di tipo narrowing, la riclassificazione viene considerata ridotta; in caso contrario, si sta ampliando.

    Nota. La conversione esatta tra metodi lambda e alberi delle espressioni potrebbe non essere fissa tra le versioni del compilatore e non rientra nell'ambito di questa specifica. Per Microsoft Visual Basic 11.0, tutte le espressioni lambda possono essere convertite in alberi delle espressioni soggetti alle restrizioni seguenti: (1) 1. Solo le espressioni lambda a riga singola senza parametri ByRef possono essere convertite in alberi delle espressioni. Delle espressioni lambda a riga Sub singola, solo le istruzioni di chiamata possono essere convertite in alberi delle espressioni. (2) Le espressioni di tipo anonimo non possono essere convertite in alberi delle espressioni se viene usato un inizializzatore di campo precedente per inizializzare un inizializzatore di campo successivo, ad esempio New With {.a=1, .b=.a}. (3) Le espressioni di inizializzatore oggetto non possono essere convertite in alberi delle espressioni se un membro dell'oggetto corrente da inizializzare viene utilizzato in uno degli inizializzatori di campo, ad esempio New C1 With {.a=1, .b=.Method1()}. (4) Le espressioni di creazione di matrici multidimensionali possono essere convertite solo in alberi delle espressioni se dichiarano il tipo di elemento in modo esplicito. (5) Le espressioni di associazione tardiva non possono essere convertite in alberi delle espressioni. (6) Quando una variabile o un campo viene passato ByRef a un'espressione di chiamata ma non ha esattamente lo stesso tipo del parametro ByRef o quando viene passata una proprietà ByRef, la semantica VB normale è passata a ByRef e il relativo valore finale viene quindi copiato nuovamente nella variabile o campo o proprietà. Negli alberi delle espressioni il copyback non viene eseguito. (7) Tutte queste restrizioni si applicano anche alle espressioni lambda annidate.

    Se il tipo di destinazione non è noto, il metodo lambda viene interpretato come argomento di un'espressione di creazione di istanze delegato di un tipo delegato anonimo con la stessa firma del metodo lambda. Se viene usata la semantica strict e il tipo di uno dei parametri viene omesso, si verifica un errore in fase di compilazione; in caso contrario, Object viene sostituito con qualsiasi tipo di parametro mancante. Per esempio:

    Module Test
        Sub Main()
            ' Type of x will be equivalent to Func(Of Object, Object, Object)
            Dim x = Function(a, b) a + b
    
            ' Type of y will be equivalent to Action(Of Object, Object)
            Dim y = Sub(a, b) Console.WriteLine(a + b)
        End Sub
    End Module
    
  • Un gruppo di proprietà può essere riclassificato come accesso alle proprietà. L'espressione del gruppo di proprietà viene interpretata come un'espressione di indice con parentesi vuote, f ovvero viene interpretata come f().

  • Un accesso alle proprietà può essere riclassificato come valore. L'espressione di accesso alle proprietà viene interpretata come espressione di chiamata della Get funzione di accesso della proprietà . Se la proprietà non dispone di getter, si verifica un errore in fase di compilazione.

  • Un accesso ad associazione tardiva può essere riclassificato come metodo ad associazione tardiva o accesso a proprietà ad associazione tardiva. In una situazione in cui un accesso ad associazione tardiva può essere riclassificato sia come accesso al metodo che come accesso alle proprietà, è preferibile riclassificare l'accesso a una proprietà.

  • Un accesso ad associazione tardiva può essere riclassificato come valore.

  • Un valore letterale matrice può essere riclassificato come valore. Il tipo del valore viene determinato nel modo seguente:

    1. Se la riclassificazione si verifica nel contesto di una conversione in cui il tipo di destinazione è noto e il tipo di destinazione è un tipo di matrice, il valore letterale della matrice viene riclassificato come valore di tipo T(). Se il tipo di destinazione è System.Collections.Generic.IList(Of T), ICollection(Of T)IReadOnlyList(Of T), IReadOnlyCollection(Of T), o IEnumerable(Of T)e il valore letterale della matrice ha un livello di annidamento, il valore letterale della matrice viene riclassificato come valore di tipo T().

    2. In caso contrario, il valore letterale della matrice viene riclassificato in un valore il cui tipo è una matrice di rango uguale al livello di annidamento viene usato, con il tipo di elemento determinato dal tipo dominante degli elementi nell'inizializzatore; se non è possibile determinare alcun tipo dominante, Object viene utilizzato . Per esempio:

      ' x Is GetType(Double(,,))
      Dim x = { { { 1, 2.0 }, { 3, 4 } }, { { 5, 6 }, { 7, 8 } } }.GetType()
      
      ' y Is GetType(Integer())
      Dim y = { 1, 2, 3 }.GetType()
      
      ' z Is GetType(Object())
      Dim z = { 1, "2" }.GetType()
      
      ' Error: Inconsistent nesting
      Dim a = { { 10 }, { 20, 30 } }.GetType()
      

    Nota. È stata apportata una leggera modifica del comportamento tra la versione 9.0 e la versione 10.0 della lingua. Prima della versione 10.0, gli inizializzatori degli elementi di matrice non influiscono sull'inferenza del tipo di variabile locale e ora lo fanno. Quindi Dim a() = { 1, 2, 3 } sarebbe stato dedotto Object() come tipo di a nella versione 9.0 della lingua e Integer() nella versione 10.0.

    La riclassificazione reinterpreta quindi il valore letterale della matrice come espressione di creazione di matrici. Ecco gli esempi:

    Dim x As Double = { 1, 2, 3, 4 }
    Dim y = { "a", "b" }
    

    sono equivalenti a:

    Dim x As Double = New Double() { 1, 2, 3, 4 }
    Dim y = New String() { "a", "b" }
    

    La riclassificazione viene considerata ridotta se qualsiasi conversione da un'espressione di elemento al tipo di elemento matrice è ridotta; in caso contrario, viene giudicata come un'estensione.

  • Il valore Nothing predefinito può essere riclassificato come valore. In un contesto in cui è noto il tipo di destinazione, il risultato è il valore predefinito del tipo di destinazione. In un contesto in cui il tipo di destinazione non è noto, il risultato è un valore Null di tipo Object.

Non è possibile riclassificare un'espressione dello spazio dei nomi, un'espressione di tipo, un'espressione di accesso agli eventi o un'espressione void. È possibile eseguire più riclassificazioni contemporaneamente. Per esempio:

Module Test
    Sub F(i As Integer)
    End Sub

    ReadOnly Property P() As Integer
        Get
        End Get
    End Sub

    Sub Main()
        F(P)
    End Property
End Module

In questo caso, l'espressione P del gruppo di proprietà viene prima riclassificata da un gruppo di proprietà a un accesso alle proprietà e quindi riclassificata da un accesso a una proprietà a un valore. Il minor numero di riclassificazioni viene eseguito per raggiungere una classificazione valida nel contesto.

Espressioni costanti

Un'espressione costante è un'espressione il cui valore può essere valutato completamente in fase di compilazione.

ConstantExpression
    : Expression
    ;

Il tipo di un'espressione costante può essere Byte, SByte, UShort, Short, UInteger, Integer, LongCharStringULongDoubleObjectSingleDecimalDateBooleano qualsiasi tipo di enumerazione. Nelle espressioni costanti sono consentiti i costrutti seguenti:

  • Valori letterali (incluso Nothing).

  • Riferimenti a membri di tipo costante o variabili locali costanti.

  • Riferimenti ai membri dei tipi di enumerazione.

  • Sottoespressioni racchiuse tra parentesi.

  • Espressioni di coercizione, a condizione che il tipo di destinazione sia uno dei tipi elencati in precedenza. Le coercizioni da e String verso sono un'eccezione a questa regola e sono consentite solo sui valori Null perché String le conversioni vengono sempre eseguite nelle impostazioni cultura correnti dell'ambiente di esecuzione in fase di esecuzione. Si noti che le espressioni di coercizione costante possono usare solo conversioni intrinseche.

  • Gli +operatori , - e Not unari, a condizione che l'operando e il risultato siano di un tipo elencato in precedenza.

  • Gli +operatori binari , ^<-*ModAnd&Or>>Xor<<AndAlsoOrElse\/><>=<=, e => , purché ogni operando e risultato siano di un tipo elencato in precedenza.

  • L'operatore condizionale If, specificato ogni operando e risultato è di un tipo elencato in precedenza.

  • Le funzioni di runtime seguenti: Microsoft.VisualBasic.Strings.ChrW; Microsoft.VisualBasic.Strings.Chr se il valore costante è compreso tra 0 e 128; Microsoft.VisualBasic.Strings.AscW se la stringa costante non è vuota; Microsoft.VisualBasic.Strings.Asc se la stringa costante non è vuota.

I costrutti seguenti non sono consentiti nelle espressioni costanti:

  • Associazione implicita tramite un With contesto.

Le espressioni costanti di un tipo integrale (ULong, Long, UInteger, IntegerShortSByteUShorto Byte) possono essere convertite in modo implicito in un tipo integrale più stretto e le espressioni costanti di tipo Double possono essere convertite in modo implicito in Single, a condizione che il valore dell'espressione costante sia compreso nell'intervallo del tipo di destinazione. Queste conversioni di restringimento sono consentite indipendentemente dal fatto che vengano usate semantiche permissive o rigide.

espressioni Late-Bound

Quando la destinazione di un'espressione di accesso ai membri o di un'espressione di indice è di tipo Object, l'elaborazione dell'espressione può essere posticipata fino alla fase di esecuzione. Rinviare l'elaborazione in questo modo viene chiamata associazione tardiva. L'associazione tardiva consente l'uso Object delle variabili in modo senza tipi , in cui tutta la risoluzione dei membri è basata sul tipo di runtime effettivo del valore nella variabile. Se la semantica strict viene specificata dall'ambiente di compilazione o da Option Strict, l'associazione tardiva genera un errore in fase di compilazione. I membri non pubblici vengono ignorati quando si esegue l'associazione tardiva, incluso ai fini della risoluzione dell'overload. Si noti che, a differenza del caso con associazione anticipata, richiamando o accedendo a un Shared membro con associazione tardiva, la destinazione di chiamata verrà valutata in fase di esecuzione. Se l'espressione è un'espressione di chiamata per un membro definito in System.Object, l'associazione tardiva non verrà eseguita.

In generale, gli accessi con associazione tardiva vengono risolti in fase di esecuzione cercando l'identificatore nel tipo di runtime effettivo dell'espressione. Se la ricerca di membri con associazione tardiva non riesce in fase di esecuzione, viene generata un'eccezione System.MissingMemberException . Poiché la ricerca di membri ad associazione tardiva viene eseguita esclusivamente dal tipo di runtime dell'espressione di destinazione associata, il tipo di runtime di un oggetto non è mai un'interfaccia. Pertanto, è impossibile accedere ai membri dell'interfaccia in un'espressione di accesso ai membri con associazione tardiva.

Gli argomenti per l'accesso a un membro ad associazione tardiva vengono valutati nell'ordine in cui vengono visualizzati nell'espressione di accesso ai membri: non l'ordine in cui i parametri vengono dichiarati nel membro con associazione tardiva. L'esempio seguente illustra questa differenza:

Class C
    Public Sub f(ByVal x As Integer, ByVal y As Integer)
    End Sub
End Class

Module Module1
    Sub Main()
        Console.Write("Early-bound: ")
        Dim c As C = New C
        c.f(y:=t("y"), x:=t("x"))

        Console.Write(vbCrLf & "Late-bound: ")
        Dim o As Object = New C
        o.f(y:=t("y"), x:=t("x"))
    End Sub

    Function t(ByVal s As String) As Integer
        Console.Write(s)
        Return 0
    End Function
End Module

Questo codice visualizza:

Early-bound: xy
Late-bound: yx

Poiché la risoluzione dell'overload con associazione tardiva viene eseguita sul tipo di runtime degli argomenti, è possibile che un'espressione possa produrre risultati diversi in base al fatto che venga valutato in fase di compilazione o in fase di esecuzione. L'esempio seguente illustra questa differenza:

Class Base
End Class

Class Derived
    Inherits Base
End Class

Module Test
    Sub F(b As Base)
        Console.WriteLine("F(Base)")
    End Sub

    Sub F(d As Derived)
        Console.WriteLine("F(Derived)")
    End Sub

    Sub Main()
        Dim b As Base = New Derived()
        Dim o As Object = b

        F(b)
        F(o)
    End Sub
End Module

Questo codice visualizza:

F(Base)
F(Derived)

Espressioni semplici

Le espressioni semplici sono valori letterali, espressioni racchiuse tra parentesi, espressioni di istanza o espressioni di nome semplici.

SimpleExpression
    : LiteralExpression
    | ParenthesizedExpression
    | InstanceExpression
    | SimpleNameExpression
    | AddressOfExpression
    ;

Espressioni letterali

Le espressioni letterali restituiscono il valore rappresentato dal valore letterale. Un'espressione letterale viene classificata come valore, ad eccezione del valore letterale Nothing, che viene classificato come valore predefinito.

LiteralExpression
    : Literal
    ;

Espressioni racchiuse tra parentesi

Un'espressione tra parentesi è costituita da un'espressione racchiusa tra parentesi. Un'espressione tra parentesi viene classificata come valore e l'espressione racchiusa deve essere classificata come valore. Un'espressione tra parentesi restituisce il valore dell'espressione tra parentesi.

ParenthesizedExpression
    : OpenParenthesis Expression CloseParenthesis
    ;

Espressioni di istanza

Un'espressione di istanza è la parola chiave Me. Può essere usato solo all'interno del corpo di un metodo, di un costruttore o di una funzione di accesso alle proprietà non condivisa. Viene classificato come valore. La parola chiave Me rappresenta l'istanza del tipo contenente il metodo o la funzione di accesso alla proprietà in esecuzione. Se un costruttore richiama in modo esplicito un altro costruttore ( Costruttori di sezione), Me non può essere utilizzato fino a dopo la chiamata al costruttore, perché l'istanza non è ancora stata costruita.

InstanceExpression
    : 'Me'
    ;

Espressioni nome semplice

Un'espressione di nome semplice è costituita da un singolo identificatore seguito da un elenco di argomenti di tipo facoltativo.

SimpleNameExpression
    : Identifier ( OpenParenthesis 'Of' TypeArgumentList CloseParenthesis )?
    ;

Il nome viene risolto e classificato in base alle seguenti "regole di risoluzione dei nomi semplici":

  1. A partire dal blocco immediatamente racchiuso e continuando con ogni blocco esterno racchiuso (se presente), se l'identificatore corrisponde al nome di una variabile locale, variabile statica, costante locale, parametro del tipo di metodo o parametro, l'identificatore fa riferimento all'entità corrispondente.

    Se l'identificatore corrisponde a una variabile locale, una variabile statica o una costante locale e è stato specificato un elenco di argomenti di tipo, si verifica un errore in fase di compilazione. Se l'identificatore corrisponde a un parametro del tipo di metodo e è stato specificato un elenco di argomenti di tipo, non viene eseguita alcuna corrispondenza e la risoluzione continua. Se l'identificatore corrisponde a una variabile locale, la variabile locale corrispondente è la funzione implicita o Get la funzione di accesso restituisce una variabile locale e l'espressione fa parte di un'espressione di chiamata, di chiamata o di un'espressione AddressOf , non si verifica alcuna corrispondenza e la risoluzione continua.

    L'espressione viene classificata come variabile se è una variabile locale, una variabile statica o un parametro. L'espressione viene classificata come tipo se si tratta di un parametro di tipo metodo. L'espressione viene classificata come valore se è una costante locale.

  2. Per ogni tipo annidato contenente l'espressione, a partire dall'elemento più interno e passando al più esterno, se una ricerca dell'identificatore nel tipo produce una corrispondenza con un membro accessibile:

    1. Se il membro del tipo corrispondente è un parametro di tipo, il risultato viene classificato come tipo ed è il parametro di tipo corrispondente. Se è stato specificato un elenco di argomenti di tipo, non si verifica alcuna corrispondenza e la risoluzione continua.
    2. In caso contrario, se il tipo è il tipo che racchiude immediatamente e la ricerca identifica un membro di tipo non condiviso, il risultato corrisponde all'accesso membro del modulo Me.E(Of A), dove E è l'identificatore e A è l'elenco di argomenti di tipo, se presente.
    3. In caso contrario, il risultato è esattamente uguale all'accesso a un membro del modulo T.E(Of A), dove T è il tipo contenente il membro corrispondente, E è l'identificatore e A è l'elenco di argomenti di tipo, se presente. In questo caso, è un errore per l'identificatore fare riferimento a un membro non condiviso.
  3. Per ogni spazio dei nomi annidato, a partire dal più interno e passando allo spazio dei nomi più esterno, eseguire le operazioni seguenti:

    1. Se lo spazio dei nomi contiene un tipo accessibile con il nome specificato e ha lo stesso numero di parametri di tipo forniti nell'elenco di argomenti di tipo, se presente, l'identificatore fa riferimento a tale tipo e viene classificato come tipo.
    2. In caso contrario, se non è stato specificato alcun elenco di argomenti di tipo e lo spazio dei nomi contiene un membro dello spazio dei nomi con il nome specificato, l'identificatore fa riferimento a tale spazio dei nomi e viene classificato come spazio dei nomi.
    3. In caso contrario, se lo spazio dei nomi contiene uno o più moduli standard accessibili e una ricerca del nome membro dell'identificatore produce una corrispondenza accessibile in un modulo standard, il risultato è esattamente lo stesso di un accesso membro del modulo M.E(Of A), dove M è il modulo standard contenente il membro corrispondente, E è l'identificatore e A è l'elenco di argomenti di tipo, se presenti. Se l'identificatore corrisponde ai membri dei tipi accessibili in più moduli standard, si verifica un errore in fase di compilazione.
  4. Se il file di origine ha uno o più alias di importazione e l'identificatore corrisponde al nome di uno di essi, l'identificatore fa riferimento a tale spazio dei nomi o tipo. Se viene fornito un elenco di argomenti di tipo, si verifica un errore in fase di compilazione.

  5. Se il file di origine contenente il riferimento al nome ha una o più importazioni:

    1. Se l'identificatore corrisponde esattamente a un'importazione del nome di un tipo accessibile con lo stesso numero di parametri di tipo fornito nell'elenco di argomenti di tipo, se presente o membro di tipo, l'identificatore fa riferimento a tale tipo o membro di tipo. Se l'identificatore corrisponde a più di un'importazione del nome di un tipo accessibile con lo stesso numero di parametri di tipo fornito nell'elenco di argomenti di tipo, se presente o accessibile, si verifica un errore in fase di compilazione.
    2. In caso contrario, se non è stato specificato alcun elenco di argomenti di tipo e l'identificatore corrisponde esattamente a un'importazione del nome di uno spazio dei nomi con tipi accessibili, l'identificatore fa riferimento a tale spazio dei nomi. Se non è stato specificato alcun elenco di argomenti di tipo e l'identificatore corrisponde in più di un'importazione del nome di uno spazio dei nomi con tipi accessibili, si verifica un errore in fase di compilazione.
    3. In caso contrario, se le importazioni contengono uno o più moduli standard accessibili e la ricerca di un nome membro dell'identificatore produce una corrispondenza accessibile in un modulo standard, il risultato corrisponde esattamente a un accesso membro del modulo M.E(Of A), dove M è il modulo standard contenente il membro corrispondente, E è l'identificatore e A è l'elenco di argomenti di tipo, se presenti. Se l'identificatore corrisponde ai membri dei tipi accessibili in più moduli standard, si verifica un errore in fase di compilazione.
  6. Se l'ambiente di compilazione definisce uno o più alias di importazione e l'identificatore corrisponde al nome di uno di essi, l'identificatore fa riferimento a tale spazio dei nomi o tipo. Se viene fornito un elenco di argomenti di tipo, si verifica un errore in fase di compilazione.

  7. Se l'ambiente di compilazione definisce una o più importazioni:

    1. Se l'identificatore corrisponde esattamente a un'importazione del nome di un tipo accessibile con lo stesso numero di parametri di tipo fornito nell'elenco di argomenti di tipo, se presente o membro di tipo, l'identificatore fa riferimento a tale tipo o membro di tipo. Se l'identificatore corrisponde a più di un'importazione del nome di un tipo accessibile con lo stesso numero di parametri di tipo fornito nell'elenco di argomenti di tipo, se presente o membro di tipo, si verifica un errore in fase di compilazione.
    2. In caso contrario, se non è stato specificato alcun elenco di argomenti di tipo e l'identificatore corrisponde esattamente a un'importazione del nome di uno spazio dei nomi con tipi accessibili, l'identificatore fa riferimento a tale spazio dei nomi. Se non è stato specificato alcun elenco di argomenti di tipo e l'identificatore corrisponde in più di un'importazione del nome di uno spazio dei nomi con tipi accessibili, si verifica un errore in fase di compilazione.
    3. In caso contrario, se le importazioni contengono uno o più moduli standard accessibili e la ricerca di un nome membro dell'identificatore produce una corrispondenza accessibile in un modulo standard, il risultato corrisponde esattamente a un accesso membro del modulo M.E(Of A), dove M è il modulo standard contenente il membro corrispondente, E è l'identificatore e A è l'elenco di argomenti di tipo, se presenti. Se l'identificatore corrisponde ai membri dei tipi accessibili in più moduli standard, si verifica un errore in fase di compilazione.
  8. In caso contrario, il nome specificato dall'identificatore non è definito.

Un'espressione di nome semplice non definita è un errore in fase di compilazione.

In genere, un nome può verificarsi una sola volta in uno spazio dei nomi specifico. Tuttavia, poiché gli spazi dei nomi possono essere dichiarati in più assembly .NET, è possibile avere una situazione in cui due assembly definiscono un tipo con lo stesso nome completo. In tal caso, un tipo dichiarato nel set corrente di file di origine è preferibile rispetto a un tipo dichiarato in un assembly .NET esterno. In caso contrario, il nome è ambiguo e non esiste alcun modo per disambiguare il nome.

Espressioni AddressOf

Un'espressione AddressOf viene usata per produrre un puntatore al metodo. L'espressione è costituita dalla AddressOf parola chiave e da un'espressione che deve essere classificata come gruppo di metodi o accesso ad associazione tardiva. Il gruppo di metodi non può fare riferimento ai costruttori.

Il risultato viene classificato come puntatore al metodo, con la stessa espressione di destinazione associata e lo stesso elenco di argomenti di tipo (se presente) come gruppo di metodi.

AddressOfExpression
    : 'AddressOf' Expression
    ;

Espressioni di tipo

Un'espressione di tipo è un'espressioneGetType, un'espressioneTypeOf...Is, un'espressione Is o un'espressioneGetXmlNamespace.

TypeExpression
    : GetTypeExpression
    | TypeOfIsExpression
    | IsExpression
    | GetXmlNamespaceExpression
    ;

Espressioni GetType

Un'espressione GetType è costituita dalla parola chiave GetType e dal nome di un tipo.

GetTypeExpression
    : 'GetType' OpenParenthesis GetTypeTypeName CloseParenthesis
    ;

GetTypeTypeName
    : TypeName
    | QualifiedOpenTypeName
    ;

QualifiedOpenTypeName
    : Identifier TypeArityList? (Period IdentifierOrKeyword TypeArityList?)*
    | 'Global' Period IdentifierOrKeyword TypeArityList?
      (Period IdentifierOrKeyword TypeArityList?)*
    ;

TypeArityList
    : OpenParenthesis 'Of' CommaList? CloseParenthesis
    ;

CommaList
    : Comma Comma*
    ;

Un'espressione GetType viene classificata come valore e il relativo valore è la classe reflection (System.Type) che rappresenta il nome GetTypeTypeName. Se GetTypeTypeName è un parametro di tipo, l'espressione restituirà l'oggetto System.Type che corrisponde all'argomento di tipo fornito per il parametro di tipo in fase di esecuzione.

GetTypeTypeName è speciale in due modi:

  • È consentito essere System.Void, l'unica posizione nella lingua in cui è possibile fare riferimento a questo nome di tipo.

  • Può trattarsi di un tipo generico costruito con gli argomenti di tipo omessi. Ciò consente all'espressione GetType di restituire l'oggetto System.Type che corrisponde al tipo generico stesso.

Nell'esempio seguente viene illustrata l'espressione GetType :

Module Test
    Sub Main()
        Dim t As Type() = { GetType(Integer), GetType(System.Int32), _
            GetType(String), GetType(Double()) }
        Dim i As Integer

        For i = 0 To t.Length - 1
            Console.WriteLine(t(i).Name)
        Next i
    End Sub
End Module

L'output risultante è:

Int32
Int32
String
Double[]

TypeOf... Espressioni is

Un'espressione TypeOf...Is viene usata per verificare se il tipo di runtime di un valore è compatibile con un determinato tipo. Il primo operando deve essere classificato come valore, non può essere un metodo lambda riclassificato e deve essere di un tipo riferimento o di un tipo di parametro di tipo non vincolato. Il secondo operando deve essere un nome di tipo. Il risultato dell'espressione viene classificato come valore e è un Boolean valore. L'espressione restituisce True se il tipo di runtime dell'operando ha un'identità, un valore predefinito, un riferimento, una matrice, un tipo di valore o una conversione di parametri di tipo nel tipo, False in caso contrario. Se non esiste alcuna conversione tra il tipo dell'espressione e il tipo specifico, si verifica un errore in fase di compilazione.

TypeOfIsExpression
    : 'TypeOf' Expression 'Is' LineTerminator? TypeName
    ;

Espressioni is

Un'espressione Is o IsNot viene usata per eseguire un confronto di uguaglianza dei riferimenti.

IsExpression
    : Expression 'Is' LineTerminator? Expression
    | Expression 'IsNot' LineTerminator? Expression
    ;

Ogni espressione deve essere classificata come valore e il tipo di ogni espressione deve essere un tipo riferimento, un tipo di parametro di tipo non vincolato o un tipo valore nullable. Se il tipo di un'espressione è un tipo di parametro di tipo non vincolato o un tipo valore nullable, l'altra espressione deve essere il valore letterale Nothing.

Il risultato viene classificato come valore e viene tipizzato come Boolean. Un'operazione Is restituisce True se entrambi i valori fanno riferimento alla stessa istanza o entrambi i valori sono Nothingo False in caso contrario. Un'operazione IsNot restituisce False se entrambi i valori fanno riferimento alla stessa istanza o entrambi i valori sono Nothingo True in caso contrario.

Espressioni GetXmlNamespace

Un'espressione GetXmlNamespace è costituita dalla parola chiave GetXmlNamespace e dal nome di uno spazio dei nomi XML dichiarato dal file di origine o dall'ambiente di compilazione.

GetXmlNamespaceExpression
    : 'GetXmlNamespace' OpenParenthesis XMLNamespaceName? CloseParenthesis
    ;

Un'espressione GetXmlNamespace viene classificata come valore e il relativo valore è un'istanza di System.Xml.Linq.XNamespace che rappresenta XMLNamespaceName. Se tale tipo non è disponibile, si verificherà un errore in fase di compilazione.

Per esempio:

Imports <xmlns:db="http://example.org/database">

Module Test
    Sub Main()
        Dim db = GetXmlNamespace(db)

        ' The following are equivalent
        Dim customer1 = _
            New System.Xml.Linq.XElement(db + "customer", "Bob")
        Dim customer2 = <db:customer>Bob</>
    End Sub
End Module

Tutti gli elementi tra parentesi sono considerati parte del nome dello spazio dei nomi, quindi vengono applicate regole XML, ad esempio spazi vuoti. Per esempio:

Imports <xmlns:db-ns="http://example.org/database">

Module Test
    Sub Main()

        ' Error, XML name expected
        Dim db1 = GetXmlNamespace( db-ns )

        ' Error, ')' expected
        Dim db2 = GetXmlNamespace(db _
            )

        ' OK.
        Dim db3 = GetXmlNamespace(db-ns)
    End Sub
End Module

È anche possibile omettere l'espressione dello spazio dei nomi XML, nel qual caso l'espressione restituisce l'oggetto che rappresenta lo spazio dei nomi XML predefinito.

Espressioni di accesso ai membri

Un'espressione di accesso ai membri viene usata per accedere a un membro di un'entità.

MemberAccessExpression
    : MemberAccessBase? Period IdentifierOrKeyword
      ( OpenParenthesis 'Of' TypeArgumentList CloseParenthesis )?
    ;

MemberAccessBase
    : Expression
    | NonArrayTypeName
    | 'Global'
    | 'MyClass'
    | 'MyBase'
    ;

L'accesso ai membri del modulo , dove E è un'espressione, un nome di tipo non matrice, la parola chiave Globalo omesso e I è un identificatore con un elenco Adi argomenti di tipo facoltativo , viene valutato e classificato come E.I(Of A)segue:

  1. Se E viene omesso, l'espressione dall'istruzione che lo contiene With immediatamente viene sostituita e E viene eseguito l'accesso ai membri. Se non è presente alcuna istruzione contenitore With , si verifica un errore in fase di compilazione.

  2. Se E è classificato come spazio dei nomi o E è la parola chiave Global, la ricerca del membro viene eseguita nel contesto dello spazio dei nomi specificato. Se I è il nome di un membro accessibile dello spazio dei nomi con lo stesso numero di parametri di tipo forniti nell'elenco di argomenti di tipo, se presente, il risultato è tale membro. Il risultato viene classificato come spazio dei nomi o un tipo a seconda del membro. In caso contrario, si verifica un errore in fase di compilazione.

  3. Se E è un tipo o un'espressione classificata come tipo, la ricerca del membro viene eseguita nel contesto del tipo specificato. Se I è il nome di un membro accessibile di E, E.I viene valutato e classificato come segue:

    1. Se I è la parola chiave New e E non è un'enumerazione, si verifica un errore in fase di compilazione.
    2. Se I identifica un tipo con lo stesso numero di parametri di tipo forniti nell'elenco di argomenti di tipo, se presente, il risultato è tale tipo.
    3. Se I identifica uno o più metodi, il risultato è un gruppo di metodi con l'elenco di argomenti di tipo associato e nessuna espressione di destinazione associata.
    4. Se I identifica una o più proprietà e non è stato fornito alcun elenco di argomenti di tipo, il risultato è un gruppo di proprietà senza espressione di destinazione associata.
    5. Se I identifica una variabile condivisa e non è stato specificato alcun elenco di argomenti di tipo, il risultato è una variabile o un valore. Se la variabile è di sola lettura e il riferimento si verifica all'esterno del costruttore condiviso del tipo in cui viene dichiarata la variabile, il risultato è il valore della variabile I condivisa in E. In caso contrario, il risultato è la variabile I condivisa in E.
    6. Se I identifica un evento condiviso e non è stato fornito alcun elenco di argomenti di tipo, il risultato è un accesso agli eventi senza espressione di destinazione associata.
    7. Se I identifica una costante e non è stato specificato alcun elenco di argomenti di tipo, il risultato è il valore di tale costante.
    8. Se I identifica un membro di enumerazione e non è stato fornito alcun elenco di argomenti di tipo, il risultato è il valore di tale membro di enumerazione.
    9. In caso contrario, E.I è un riferimento membro non valido e si verifica un errore in fase di compilazione.
  4. Se E è classificato come variabile o valore, il tipo di cui è T, la ricerca del membro viene eseguita nel contesto di T. Se I è il nome di un membro accessibile di T, E.I viene valutato e classificato come segue:

    1. Se I è la parola chiave New, E è Me, MyBaseo MyClasse non sono stati forniti argomenti di tipo, il risultato è un gruppo di metodi che rappresenta i costruttori di istanza del tipo di con un'espressione di E destinazione associata di E e nessun elenco di argomenti di tipo. In caso contrario, si verifica un errore in fase di compilazione.
    2. Se I identifica uno o più metodi, inclusi i metodi di estensione se T non Objectè , il risultato è un gruppo di metodi con l'elenco di argomenti di tipo associato e un'espressione di destinazione associata di E.
    3. Se I identifica una o più proprietà e non sono stati forniti argomenti di tipo, il risultato è un gruppo di proprietà con un'espressione di destinazione associata di E.
    4. Se I identifica una variabile condivisa o una variabile di istanza e non sono stati forniti argomenti di tipo, il risultato è una variabile o un valore. Se la variabile è di sola lettura e il riferimento si verifica all'esterno di un costruttore della classe in cui la variabile è dichiarata appropriata per il tipo di variabile (condivisa o istanza), il risultato è il valore della variabile I nell'oggetto a cui fa Eriferimento . Se T è un tipo riferimento, il risultato è la variabile I nell'oggetto a cui fa Eriferimento . In caso contrario, se T è un tipo di valore e l'espressione E viene classificata come variabile, il risultato è una variabile; in caso contrario, il risultato è un valore.
    5. Se I identifica un evento e non sono stati forniti argomenti di tipo, il risultato è un accesso agli eventi con un'espressione di destinazione associata di E.
    6. Se I identifica una costante e non sono stati forniti argomenti di tipo, il risultato è il valore di tale costante.
    7. Se I identifica un membro di enumerazione e non sono stati forniti argomenti di tipo, il risultato è il valore di tale membro di enumerazione.
    8. Se T è Object, il risultato è una ricerca membro ad associazione tardiva classificata come accesso ad associazione tardiva con l'elenco di argomenti di tipo associato e un'espressione di destinazione associata di E.
  5. In caso contrario, E.I è un riferimento membro non valido e si verifica un errore in fase di compilazione.

L'accesso a un membro del modulo MyClass.I(Of A) equivale a Me.I(Of A), ma tutti i membri a cui si accede vengono considerati come se i membri non siano sottoponibili a override. Di conseguenza, il membro a cui si accede non sarà interessato dal tipo di runtime del valore a cui si accede al membro.

L'accesso ai membri del modulo MyBase.I(Of A) equivale a CType(Me, T).I(Of A) dove T è il tipo di base diretto del tipo contenente l'espressione di accesso ai membri. Tutte le chiamate al metodo su di esso vengono considerate come se il metodo richiamato non sia sottoposto a override. Questa forma di accesso ai membri è detta anche accesso di base.

Nell'esempio seguente viene illustrato come Mecorrelare e MyBaseMyClass :

Class Base
    Public Overridable Sub F()
        Console.WriteLine("Base.F")
    End Sub
End Class

Class Derived
    Inherits Base

    Public Overrides Sub F()
        Console.WriteLine("Derived.F")
    End Sub

    Public Sub G()
        MyClass.F()
    End Sub
End Class

Class MoreDerived
    Inherits Derived

    Public Overrides Sub F()
        Console.WriteLine("MoreDerived.F")
    End Sub

    Public Sub H()
        MyBase.F()
    End Sub
End Class

Module Test
    Sub Main()
        Dim x As MoreDerived = new MoreDerived()

        x.F()
        x.G()
        x.H()
    End Sub

End Module

Questo codice stampa:

MoreDerived.F
Derived.F
Derived.F

Quando un'espressione di accesso ai membri inizia con la parola chiave Global, la parola chiave rappresenta lo spazio dei nomi senza nome più esterno, utile nelle situazioni in cui una dichiarazione ombreggiata uno spazio dei nomi racchiuso. La Global parola chiave consente l'escape allo spazio dei nomi più esterno in tale situazione. Per esempio:

Class System
End Class

Module Test
    Sub Main()
        ' Error: Class System does not contain Console
        System.Console.WriteLine("Hello, world!") 


        ' Legal, binds to System in outermost namespace
        Global.System.Console.WriteLine("Hello, world!") 
    End Sub
End Module

Nell'esempio precedente la prima chiamata al metodo non è valida perché l'identificatore System viene associato alla classe System, non allo spazio dei nomi System. L'unico modo per accedere allo spazio dei nomi consiste nell'usare System per uscire Global dallo spazio dei nomi più esterno.

Se il membro a cui si accede viene condiviso, qualsiasi espressione sul lato sinistro del periodo è superflua e non viene valutata a meno che l'accesso al membro non venga eseguito con associazione tardiva. Si consideri ad esempio il codice seguente:

Class C
    Public Shared F As Integer = 10
End Class

Module Test
    Public Function ReturnC() As C
        Console.WriteLine("Returning a new instance of C.")
        Return New C()
    End Function

    Public Sub Main()
        Console.WriteLine("The value of F is: " & ReturnC().F)
    End Sub
End Module

Viene stampato The value of F is: 10 perché non è necessario chiamare la funzione ReturnC per fornire un'istanza di C per accedere al membro Fcondiviso.

Nomi identici di tipo e membro

Non è raro denominare i membri usando lo stesso nome del tipo. In questa situazione, tuttavia, il nome scomodo nascosto può verificarsi:

Enum Color
    Red
    Green
    Yellow
End Enum

Class Test
    ReadOnly Property Color() As Color
        Get
            Return Color.Red
        End Get
    End Property

    Shared Function DefaultColor() As Color
        Return Color.Green    ' Binds to the instance property!
    End Function
End Class

Nell'esempio precedente il nome Color semplice in DefaultColor viene associato alla proprietà dell'istanza anziché al tipo . Poiché non è possibile fare riferimento a un membro di istanza in un membro condiviso, in genere si tratta di un errore.

Tuttavia, una regola speciale consente l'accesso al tipo in questo caso. Se l'espressione di base di un'espressione di accesso ai membri è un nome semplice e viene associata a una costante, un campo, una proprietà, una variabile locale o un parametro il cui tipo ha lo stesso nome, l'espressione di base può fare riferimento al membro o al tipo . Questo non può mai causare ambiguità perché i membri a cui è possibile accedere da uno di essi sono uguali.

Istanze predefinite

In alcune situazioni, le classi derivate da una classe base comune in genere o hanno sempre una sola istanza. Ad esempio, la maggior parte delle finestre visualizzate in un'interfaccia utente ha sempre una sola istanza visualizzata sullo schermo in qualsiasi momento. Per semplificare l'utilizzo di questi tipi di classi, Visual Basic può generare automaticamente istanze predefinite delle classi che forniscono un'istanza a cui si fa facilmente riferimento per ogni classe.

Le istanze predefinite vengono sempre create per una famiglia di tipi anziché per un tipo specifico. Invece di creare un'istanza predefinita per una classe Form1 che deriva da Form, vengono create istanze predefinite per tutte le classi derivate da Form. Ciò significa che ogni singola classe che deriva dalla classe base non deve essere contrassegnata appositamente per avere un'istanza predefinita.

L'istanza predefinita di una classe è rappresentata da una proprietà generata dal compilatore che restituisce l'istanza predefinita di tale classe. La proprietà generata come membro di una classe denominata classe group che gestisce l'allocazione e l'eliminazione delle istanze predefinite per tutte le classi derivate dalla classe base specifica. Ad esempio, tutte le proprietà di istanza predefinite delle classi derivate da Form possono essere raccolte nella MyForms classe . Se un'istanza della classe group viene restituita dall'espressione My.Forms, il codice seguente accede alle istanze predefinite delle classi Form1 derivate e Form2:

Class Form1
    Inherits Form
    Public x As Integer
End Class

Class Form2
    Inherits Form
    Public y As Integer
End Class

Module Main
    Sub Main()
        My.Forms.Form1.x = 10
        Console.WriteLine(My.Forms.Form2.y)
    End Sub
End Module

Le istanze predefinite non verranno create fino al primo riferimento. Il recupero della proprietà che rappresenta l'istanza predefinita determina la creazione dell'istanza predefinita se non è già stata creata o è stata impostata su Nothing. Per consentire il test dell'esistenza di un'istanza predefinita, quando un'istanza predefinita è la destinazione di un Is operatore o IsNot , l'istanza predefinita non verrà creata. Pertanto, è possibile verificare se un'istanza predefinita è Nothing o un altro riferimento senza causare la creazione dell'istanza predefinita.

Le istanze predefinite consentono di fare riferimento facilmente all'istanza predefinita dall'esterno della classe con l'istanza predefinita. L'uso di un'istanza predefinita dall'interno di una classe che lo definisce può causare confusione in base all'istanza a cui viene fatto riferimento, ad esempio l'istanza predefinita o l'istanza corrente. Ad esempio, il codice seguente modifica solo il valore x nell'istanza predefinita, anche se viene chiamato da un'altra istanza. Il codice stampa quindi il valore 5 anziché 10:

Class Form1
    Inherits Form

    Public x As Integer = 5

    Public Sub ChangeX()
        Form1.x = 10
    End Sub
End Class

Module Main
    Sub Main()
        Dim f As Form1 = New Form1()
        f.ChangeX()
        Console.WriteLine(f.x)
    End Sub
End Module

Per evitare questo tipo di confusione, non è valido fare riferimento a un'istanza predefinita dall'interno di un metodo di istanza del tipo dell'istanza predefinita.

Istanze predefinite e nomi dei tipi

Un'istanza predefinita può anche essere accessibile direttamente tramite il nome del tipo. In questo caso, in qualsiasi contesto di espressione in cui il nome del tipo non è consentito all'espressione E, dove E rappresenta il nome completo della classe con un'istanza predefinita, viene modificato in E', dove E' rappresenta un'espressione che recupera la proprietà dell'istanza predefinita. Ad esempio, se le istanze predefinite per le classi derivate da Form consentono l'accesso all'istanza predefinita tramite il nome del tipo, il codice seguente equivale al codice nell'esempio precedente:

Module Main
    Sub Main()
        Form1.x = 10
        Console.WriteLine(Form2.y)
    End Sub
End Module

Ciò significa anche che un'istanza predefinita accessibile tramite il nome del tipo è anche assegnabile tramite il nome del tipo. Ad esempio, il codice seguente imposta l'istanza predefinita di Form1 su Nothing:

Module Main
    Sub Main()
        Form1 = Nothing
    End Sub
End Module

Si noti che il significato di E.I è rappresentato da E una classe e I rappresenta un membro condiviso non cambia. Tale espressione accede comunque al membro condiviso direttamente dall'istanza della classe e non fa riferimento all'istanza predefinita.

Classi di gruppo

L'attributo Microsoft.VisualBasic.MyGroupCollectionAttribute indica la classe group per una famiglia di istanze predefinite. L'attributo ha quattro parametri:

  • Il parametro TypeToCollect specifica la classe di base per il gruppo. Tutte le classi di cui è possibile creare un'istanza senza parametri di tipo aperti che derivano da un tipo con questo nome (indipendentemente dai parametri di tipo) avranno automaticamente un'istanza predefinita.

  • Il parametro CreateInstanceMethodName specifica il metodo da chiamare nella classe group per creare una nuova istanza in una proprietà di istanza predefinita.

  • Il parametro DisposeInstanceMethodName specifica il metodo da chiamare nella classe group per eliminare una proprietà di istanza predefinita se alla proprietà dell'istanza predefinita viene assegnato il valore Nothing.

  • Il parametro DefaultInstanceAlias specifica l'espressione E' da sostituire con il nome della classe se le istanze predefinite sono accessibili direttamente tramite il nome del tipo. Se questo parametro è Nothing o una stringa vuota, le istanze predefinite di questo tipo non sono accessibili direttamente tramite il nome del tipo. (Nota. In tutte le implementazioni correnti del linguaggio Visual Basic, il DefaultInstanceAlias parametro viene ignorato, tranne nel codice fornito dal compilatore.

È possibile raccogliere più tipi nello stesso gruppo separando i nomi dei tipi e dei metodi nei primi tre parametri usando le virgole. Deve essere presente lo stesso numero di elementi in ogni parametro e gli elementi dell'elenco vengono confrontati in ordine. Ad esempio, la dichiarazione di attributo seguente raccoglie i tipi che derivano da C1C2 o C3 in un singolo gruppo:

<Microsoft.VisualBasic.MyGroupCollection("C1, C2, C3", _
    "CreateC1, CreateC2, CreateC3", _
    "DisposeC1, DisposeC2, DisposeC3", "My.Cs")>
Public NotInheritable Class MyCs
    ...
End Class

La firma del metodo create deve essere del formato Shared Function <Name>(Of T As {New, <Type>})(Instance Of T) As T. Il metodo dispose deve essere del formato Shared Sub <Name>(Of T As <Type>)(ByRef Instance Of T). Di conseguenza, la classe group per l'esempio nella sezione precedente può essere dichiarata come segue:

<Microsoft.VisualBasic.MyGroupCollection("Form", "Create", _
    "Dispose", "My.Forms")> _
Public NotInheritable Class MyForms
    Private Shared Function Create(Of T As {New, Form}) _
        (Instance As T) As T
        If Instance Is Nothing Then
            Return New T()
        Else
            Return Instance
        End If
    End Function

    Private Shared Sub Dispose(Of T As Form)(ByRef Instance As T)
        Instance.Close()
        Instance = Nothing
    End Sub
End Class

Se un file di origine ha dichiarato una classe Form1derivata , la classe di gruppo generata sarà equivalente a:

<Microsoft.VisualBasic.MyGroupCollection("Form", "Create", _
    "Dispose", "My.Forms")> _
Public NotInheritable Class MyForms
    Private Shared Function Create(Of T As {New, Form}) _
        (Instance As T) As T
        If Instance Is Nothing Then
            Return New T()
        Else
            Return Instance
        End If
    End Function

    Private Shared Sub Dispose(Of T As Form)(ByRef Instance As T)
        Instance.Close()
        Instance = Nothing
    End Sub

    Private m_Form1 As Form1

    Public Property Form1() As Form1
        Get
            Return Create(m_Form1)
        End Get
        Set (Value As Form1)
            If Value IsNot Nothing AndAlso Value IsNot m_Form1 Then
                Throw New ArgumentException( _
                    "Property can only be set to Nothing.")
            End If
            Dispose(m_Form1)
        End Set
    End Property
End Class

Insieme di metodi di estensione

I metodi di estensione per l'espressione E.I di accesso ai membri vengono raccolti raccogliendo tutti i metodi di estensione con il nome I disponibili nel contesto corrente:

  1. In primo luogo, ogni tipo annidato contenente l'espressione viene controllato, a partire dall'interno e passando al più esterno.
  2. Viene quindi controllato ogni spazio dei nomi annidato, a partire dal più interno e passando allo spazio dei nomi più esterno.
  3. Vengono quindi controllate le importazioni nel file di origine.
  4. Vengono quindi controllate le importazioni definite dall'ambiente di compilazione.

Un metodo di estensione viene raccolto solo se è presente una conversione nativa più ampia dal tipo di espressione di destinazione al tipo del primo parametro del metodo di estensione. A differenza del normale binding di espressioni dei nomi semplici, la ricerca raccoglie tutti i metodi di estensione; la raccolta non si arresta quando viene trovato un metodo di estensione. Per esempio:

Imports System.Runtime.CompilerServices

Class C1
End Class


Namespace N1
    Module N1C1Extensions
        <Extension> _
        Sub M1(c As C1, x As Integer)
        End Sub
    End Module
End Namespace

Namespace N1.N2
    Module N2C1Extensions
        <Extension> _
        Sub M1(c As C1, y As Double)
        End Sub
    End Module
End Namespace

Namespace N1.N2.N3
    Module Test
        Sub Main()
            Dim x As New C1()

            ' Calls N1C1Extensions.M1
            x.M1(10)
        End Sub
    End Module
End Namespace

In questo esempio, anche se N2C1Extensions.M1 viene trovato prima N1C1Extensions.M1di , entrambi vengono considerati come metodi di estensione. Dopo aver raccolto tutti i metodi di estensione, vengono quindi curried. Currying accetta la destinazione della chiamata al metodo di estensione e la applica alla chiamata al metodo di estensione, ottenendo una nuova firma del metodo con il primo parametro rimosso (perché è stato specificato). Per esempio:

Imports System.Runtime.CompilerServices

Module Ext1
    <Extension> _
    Sub M(x As Integer, y As Integer)
    End Sub
End Module

Module Ext2
    <Extension> _
    Sub M(x As Integer, y As Double)
    End Sub
End Module

Module Main
    Sub Test()
        Dim v As Integer = 10

        ' The curried method signatures considered are:
        '        Ext1.M(y As Integer)
        '        Ext2.M(y As Double)
        v.M(10)
    End Sub
End Module

Nell'esempio precedente il risultato curried dell'applicazione v a Ext1.M è la firma Sub M(y As Integer)del metodo .

Oltre a rimuovere il primo parametro del metodo di estensione, currying rimuove anche tutti i parametri del tipo di metodo che fanno parte del tipo del primo parametro. Quando si applica un metodo di estensione con il parametro del tipo di metodo, l'inferenza del tipo viene applicata al primo parametro e il risultato viene corretto per tutti i parametri di tipo dedotti. Se l'inferenza del tipo ha esito negativo, il metodo viene ignorato. Per esempio:

Imports System.Runtime.CompilerServices

Module Ext1
    <Extension> _
    Sub M(Of T, U)(x As T, y As U)
    End Sub
End Module

Module Ext2
    <Extension> _
    Sub M(Of T)(x As T, y As T)
    End Sub
End Module

Module Main
    Sub Test()
        Dim v As Integer = 10

        ' The curried method signatures considered are:
        '        Ext1.M(Of U)(y As U)
        '        Ext2.M(y As Integer)
        v.M(10)
    End Sub
End Module

Nell'esempio precedente il risultato curried dell'applicazione v a Ext1.M è la firma Sub M(Of U)(y As U)del metodo , perché il parametro T di tipo viene dedotto come risultato del currying e ora è fisso. Poiché il parametro U di tipo non è stato dedotto come parte del currying, rimane un parametro aperto. Analogamente, poiché il parametro T di tipo viene dedotto in seguito all'applicazione v a Ext2.M, il tipo di parametro y diventa fisso come Integer. Non verrà dedotto per essere qualsiasi altro tipo. Quando si applica la firma, vengono applicati anche tutti i vincoli ad eccezione dei New vincoli. Se i vincoli non sono soddisfatti o dipendono da un tipo che non è stato dedotto come parte del currying, il metodo di estensione viene ignorato. Per esempio:

Imports System.Runtime.CompilerServices

Module Ext1
    <Extension> _
    Sub M1(Of T As Structure)(x As T, y As Integer)
    End Sub

    <Extension> _
    Sub M2(Of T As U, U)(x As T, y As U)
    End Sub
End Module

Module Main
    Sub Test()
        Dim s As String = "abc"

        ' Error: String does not satisfy the Structure constraint
        s.M1(10)

        ' Error: T depends on U, which cannot be inferred
        s.M2(10)
    End Sub
End Module

Nota. Uno dei motivi principali per eseguire il currying dei metodi di estensione è che consente alle espressioni di query di dedurre il tipo dell'iterazione prima di valutare gli argomenti in un metodo del modello di query. Poiché la maggior parte dei metodi del modello di query accetta espressioni lambda, che richiedono l'inferenza del tipo stessa, questo semplifica notevolmente il processo di valutazione di un'espressione di query.

A differenza dell'ereditarietà dell'interfaccia normale, sono disponibili metodi di estensione che estendono due interfacce che non si riferiscono l'uno all'altro, purché non abbiano la stessa firma curried:

Imports System.Runtime.CompilerServices

Interface I1
End Interface

Interface I2
End Interface

Class C1
    Implements I1, I2
End Class

Module I1Ext
    <Extension> _
    Sub M1(i As I1, x As Integer)
    End Sub

    <Extension> _
    Sub M2(i As I1, x As Integer)
    End Sub
End Module

Module I2Ext
    <Extension> _
    Sub M1(i As I2, x As Integer)
    End Sub

    <Extension> _
    Sub M2(I As I2, x As Double)
    End Sub
End Module

Module Main
    Sub Test()
        Dim c As New C1()

        ' Error: M is ambiguous between I1Ext.M1 and I2Ext.M1.
        c.M1(10)

        ' Calls I1Ext.M2
        c.M2(10)
    End Sub
End Module

Infine, è importante ricordare che i metodi di estensione non vengono considerati quando si esegue l'associazione tardiva:

Module Test
    Sub Main()
        Dim o As Object = ...

        ' Ignores extension methods
        o.M1()
    End Sub
End Module

Espressioni di accesso ai membri del dizionario

Un'espressione di accesso ai membri del dizionario viene utilizzata per cercare un membro di una raccolta. L'accesso ai membri del dizionario ha la forma di E!I, dove E è un'espressione classificata come valore ed I è un identificatore.

DictionaryAccessExpression
    : Expression? '!' IdentifierOrKeyword
    ;

Il tipo dell'espressione deve avere una proprietà predefinita indicizzata da un singolo String parametro. L'espressione E!I di accesso ai membri del dizionario viene trasformata nell'espressione E.D("I"), dove D è la proprietà predefinita di E. Per esempio:

Class Keys
    Public ReadOnly Default Property Item(s As String) As Integer
        Get
            Return 10
        End Get
    End Property 
End Class

Module Test
    Sub Main()
        Dim x As Keys = new Keys()
        Dim y As Integer
        ' The two statements are equivalent.
        y = x!abc
        y = x("abc")
    End Sub
End Module

Se viene specificato un punto esclamativo senza espressione, viene presupposta l'espressione dall'istruzione che lo contiene With immediatamente. Se non è presente alcuna istruzione contenitore With , si verifica un errore in fase di compilazione.

Espressioni di chiamata

Un'espressione di chiamata è costituita da una destinazione di chiamata e da un elenco di argomenti facoltativi.

InvocationExpression
    : Expression ( OpenParenthesis ArgumentList? CloseParenthesis )?
    ;

ArgumentList
    : PositionalArgumentList
    | PositionalArgumentList Comma NamedArgumentList
    | NamedArgumentList
    ;

PositionalArgumentList
    : Expression? ( Comma Expression? )*
    ;

NamedArgumentList
    : IdentifierOrKeyword ColonEquals Expression
      ( Comma IdentifierOrKeyword ColonEquals Expression )*
    ;

L'espressione di destinazione deve essere classificata come gruppo di metodi o un valore il cui tipo è un tipo delegato. Se l'espressione di destinazione è un valore il cui tipo è un tipo delegato, la destinazione dell'espressione di chiamata diventa il gruppo di metodi per il Invoke membro del tipo delegato e l'espressione di destinazione diventa l'espressione di destinazione associata del gruppo di metodi.

Un elenco di argomenti include due sezioni: argomenti posizionali e argomenti denominati. Gli argomenti posizionali sono espressioni e devono precedere qualsiasi argomento denominato. Gli argomenti denominati iniziano con un identificatore che può corrispondere alle parole chiave, seguito da := e da un'espressione.

Se il gruppo di metodi contiene solo un metodo accessibile, inclusi i metodi di istanza e di estensione e tale metodo non accetta argomenti e è una funzione, il gruppo di metodi viene interpretato come espressione di chiamata con un elenco di argomenti vuoto e il risultato viene utilizzato come destinazione di un'espressione di chiamata con gli elenchi di argomenti forniti. Per esempio:

Class C1
    Function M1() As Integer()
        Return New Integer() { 1, 2, 3 }
    End Sub
End Class

Module Test
    Sub Main()
        Dim c As New C1()

        ' Prints 3
        Console.WriteLine(c.M1(2))
    End Sub
End Module

In caso contrario, la risoluzione dell'overload viene applicata ai metodi per selezionare il metodo più applicabile per gli elenchi di argomenti specificati. Se il metodo più applicabile è una funzione, il risultato dell'espressione di chiamata viene classificato come valore tipizzato come tipo restituito della funzione. Se il metodo più applicabile è una subroutine, il risultato viene classificato come void. Se il metodo più applicabile è un metodo parziale senza corpo, l'espressione di chiamata viene ignorata e il risultato viene classificato come void.

Per un'espressione di chiamata con associazione anticipata, gli argomenti vengono valutati nell'ordine in cui i parametri corrispondenti vengono dichiarati nel metodo di destinazione. Per un'espressione di accesso ai membri ad associazione tardiva, vengono valutate nell'ordine in cui vengono visualizzate nell'espressione di accesso ai membri: vedere Sezione Late-Bound Espressioni.

Risoluzione del metodo di overload:

Per risoluzione dell'overload, specificità di membri/tipi in base a un elenco di argomenti, genericità, applicabilità all'elenco di argomenti, passaggio di argomenti e selezione di argomenti per parametri facoltativi, metodi condizionali e inferenza di argomenti di tipo: vedere Risoluzione dell'overload della sezione.

Espressioni di indice

Un'espressione di indice restituisce un elemento di matrice o riclassifica un gruppo di proprietà in un accesso alle proprietà. Un'espressione di indice è costituita da, in ordine, espressione, parentesi di apertura, elenco di argomenti di indice e parentesi di chiusura.

IndexExpression
    : Expression OpenParenthesis ArgumentList? CloseParenthesis
    ;

La destinazione dell'espressione di indice deve essere classificata come un gruppo di proprietà o un valore. Un'espressione di indice viene elaborata come segue:

  • Se l'espressione di destinazione viene classificata come valore e se il tipo non è un tipo di matrice, Objecto System.Array, il tipo deve avere una proprietà predefinita. L'indice viene eseguito su un gruppo di proprietà che rappresenta tutte le proprietà predefinite del tipo. Anche se non è valido dichiarare una proprietà predefinita senza parametri in Visual Basic, altri linguaggi possono consentire la dichiarazione di tale proprietà. Di conseguenza, l'indicizzazione di una proprietà senza argomenti è consentita.

  • Se l'espressione restituisce un valore di tipo matrice, il numero di argomenti nell'elenco di argomenti deve essere uguale al rango del tipo di matrice e potrebbe non includere argomenti denominati. Se uno degli indici non è valido in fase di esecuzione, viene generata un'eccezione System.IndexOutOfRangeException . Ogni espressione deve essere convertibile in modo implicito nel tipo Integer. Il risultato dell'espressione di indice è la variabile in corrispondenza dell'indice specificato e viene classificata come variabile.

  • Se l'espressione viene classificata come gruppo di proprietà, viene utilizzata la risoluzione dell'overload per determinare se una delle proprietà è applicabile all'elenco di argomenti di indice. Se il gruppo di proprietà contiene solo una proprietà con una Get funzione di accesso e se tale funzione di accesso non accetta argomenti, il gruppo di proprietà viene interpretato come un'espressione di indice con un elenco di argomenti vuoto. Il risultato viene usato come destinazione dell'espressione di indice corrente. Se non sono applicabili proprietà, si verifica un errore in fase di compilazione. In caso contrario, l'espressione restituisce un accesso alle proprietà con l'espressione di destinazione associata (se presente) del gruppo di proprietà.

  • Se l'espressione viene classificata come gruppo di proprietà ad associazione tardiva o come valore il cui tipo è Object o System.Array, l'elaborazione dell'espressione di indice viene posticipata fino all'ora di esecuzione e l'indicizzazione è tardiva. L'espressione restituisce un accesso a proprietà ad associazione tardiva digitato come Object. L'espressione di destinazione associata è l'espressione di destinazione, se è un valore o l'espressione di destinazione associata del gruppo di proprietà. In fase di esecuzione l'espressione viene elaborata come segue:

  • Se l'espressione è classificata come gruppo di proprietà ad associazione tardiva, l'espressione può comportare un gruppo di metodi, un gruppo di proprietà o un valore (se il membro è un'istanza o una variabile condivisa). Se il risultato è un gruppo di metodi o un gruppo di proprietà, la risoluzione dell'overload viene applicata al gruppo per determinare il metodo corretto per l'elenco di argomenti. Se la risoluzione dell'overload ha esito negativo, viene generata un'eccezione System.Reflection.AmbiguousMatchException . Il risultato viene quindi elaborato come accesso alle proprietà o come chiamata e viene restituito il risultato. Se la chiamata è di una subroutine, il risultato è Nothing.

  • Se il tipo di runtime dell'espressione di destinazione è un tipo di matrice o System.Array, il risultato dell'espressione di indice è il valore della variabile in corrispondenza dell'indice specificato.

  • In caso contrario, il tipo di runtime dell'espressione deve avere una proprietà predefinita e l'indice viene eseguito sul gruppo di proprietà che rappresenta tutte le proprietà predefinite del tipo. Se il tipo non dispone di una proprietà predefinita, viene generata un'eccezione System.MissingMemberException .

Nuove espressioni

L'operatore New viene usato per creare nuove istanze di tipi. Esistono quattro forme di New espressioni:

  • Le espressioni di creazione di oggetti vengono usate per creare nuove istanze di tipi di classe e tipi valore.

  • Le espressioni di creazione di matrici vengono usate per creare nuove istanze di tipi di matrice.

  • Le espressioni di creazione del delegato (che non hanno una sintassi distinta dalle espressioni di creazione di oggetti) vengono usate per creare nuove istanze di tipi delegati.

  • Le espressioni anonime per la creazione di oggetti vengono usate per creare nuove istanze di tipi di classe anonimi.

NewExpression
    : ObjectCreationExpression
    | ArrayExpression
    | AnonymousObjectCreationExpression
    ;

Un'espressione New viene classificata come valore e il risultato è la nuova istanza del tipo.

espressioni Object-Creation

Un'espressione di creazione di oggetti viene utilizzata per creare una nuova istanza di un tipo di classe o di un tipo di struttura.

ObjectCreationExpression
    : 'New' NonArrayTypeName ( OpenParenthesis ArgumentList? CloseParenthesis )?
      ObjectCreationExpressionInitializer?
    ;

ObjectCreationExpressionInitializer
    : ObjectMemberInitializer
    | ObjectCollectionInitializer
    ;

ObjectMemberInitializer
    : 'With' OpenCurlyBrace FieldInitializerList CloseCurlyBrace
    ;

FieldInitializerList
    : FieldInitializer ( Comma FieldInitializer )*
    ;

FieldInitializer
    : 'Key'? ('.' IdentifierOrKeyword Equals )? Expression
    ;

ObjectCollectionInitializer
    : 'From' CollectionInitializer
    ;

CollectionInitializer
    : OpenCurlyBrace CollectionElementList? CloseCurlyBrace
    ;

CollectionElementList
    : CollectionElement ( Comma CollectionElement )*
    ;

CollectionElement
    : Expression
    | CollectionInitializer
    ;

Il tipo di un'espressione di creazione di oggetti deve essere un tipo di classe, un tipo di struttura o un parametro di tipo con un New vincolo e non può essere una MustInherit classe. Dato un'espressione di creazione dell'oggetto del modulo New T(A), dove T è un tipo di classe o un tipo di struttura ed A è un elenco di argomenti facoltativo, la risoluzione dell'overload determina il costruttore corretto di T da chiamare. Un parametro di tipo con un New vincolo viene considerato un singolo costruttore senza parametri. Se non è possibile chiamare alcun costruttore, si verifica un errore in fase di compilazione; in caso contrario, l'espressione comporta la creazione di una nuova istanza di T utilizzando il costruttore scelto. Se non sono presenti argomenti, è possibile omettere le parentesi.

La posizione in cui un'istanza viene allocata dipende dal fatto che l'istanza sia un tipo di classe o un tipo di valore. New Le istanze dei tipi di classe vengono create nell'heap di sistema, mentre le nuove istanze dei tipi valore vengono create direttamente nello stack.

Un'espressione di creazione di oggetti può facoltativamente specificare un elenco di inizializzatori di membri dopo gli argomenti del costruttore. Questi inizializzatori di membri sono preceduti dalla parola chiave Withe l'elenco di inizializzatori viene interpretato come se fosse nel contesto di un'istruzione With . Ad esempio, data la classe :

Class Customer
    Dim Name As String
    Dim Address As String
End Class

Il codice:

Module Test
    Sub Main()
        Dim x As New Customer() With { .Name = "Bob Smith", _
            .Address = "123 Main St." }
    End Sub
End Module

Equivale approssimativamente a:

Module Test
    Sub Main()
        Dim x, _t1 As Customer

        _t1 = New Customer()
        With _t1
            .Name = "Bob Smith"
            .Address = "123 Main St."
        End With

        x = _t1
    End Sub
End Module

Ogni inizializzatore deve specificare un nome da assegnare e il nome deve essere una variabile o unaReadOnly proprietà non di istanza del tipo costruito. L'accesso ai membri non verrà associato in ritardo se il tipo costruito è Object. Gli inizializzatori potrebbero non usare la Key parola chiave . Ogni membro di un tipo può essere inizializzato una sola volta. Le espressioni di inizializzatore, tuttavia, possono fare riferimento l'una all'altra. Per esempio:

Module Test
    Sub Main()
        Dim x As New Customer() With { .Name = "Bob Smith", _
            .Address = .Name & " St." }
    End Sub
End Module

Gli inizializzatori vengono assegnati da sinistra a destra, quindi se un inizializzatore fa riferimento a un membro che non è ancora stato inizializzato, verrà visualizzato qualsiasi valore della variabile di istanza dopo l'esecuzione del costruttore:

Module Test
    Sub Main()
        ' The value of Address will be " St." since Name has not been
        ' assigned yet.
        Dim x As New Customer() With { .Address = .Name & " St." }
    End Sub
End Module

Gli inizializzatori possono essere annidati:

Class Customer
    Dim Name As String
    Dim Address As Address
    Dim Age As Integer
End Class

Class Address
    Dim Street As String
    Dim City As String
    Dim State As String
    Dim ZIP As String
End Class

Module Test
    Sub Main()
        Dim c As New Customer() With { _
            .Name = "John Smith", _
            .Address = New Address() With { _
                .Street = "23 Main St.", _
                .City = "Peoria", _
                .State = "IL", _
                .ZIP = "13934" }, _
            .Age = 34 }
    End Sub
End Module

Se il tipo creato è un tipo di raccolta e ha un metodo di istanza denominato Add (inclusi i metodi di estensione e i metodi condivisi), l'espressione di creazione dell'oggetto può specificare un inizializzatore di raccolta preceduto dalla parola chiave From. Un'espressione di creazione di oggetti non può specificare sia un inizializzatore membro che un inizializzatore di raccolta. Ogni elemento nell'inizializzatore di raccolta viene passato come argomento a una chiamata della Add funzione. Per esempio:

Dim list = New List(Of Integer)() From { 1, 2, 3, 4 }

Equivale a:

Dim list = New List(Of Integer)()
list.Add(1)
list.Add(2)
list.Add(3)

Se un elemento è un inizializzatore di raccolta stesso, ogni elemento dell'inizializzatore della raccolta secondaria verrà passato come singolo argomento alla Add funzione. Ad esempio, quanto segue:

Dim dict = Dictionary(Of Integer, String) From { { 1, "One" },{ 2, "Two" } }

Equivale a:

Dim dict = New Dictionary(Of Integer, String)
dict.Add(1, "One")
dict.Add(2, "Two")

Questa espansione viene sempre eseguita ed è mai fatto un livello profondo; successivamente, i sub-inizializzatori sono considerati valori letterali di matrice. Per esempio:

' Error: List(Of T) does not have an Add method that takes two parameters.
Dim list = New List(Of Integer())() From { { 1, 2 }, { 3, 4 } }

' OK, this initializes the dictionary with (Integer, Integer()) pairs.
Dim dict = New Dictionary(Of Integer, Integer())() From _
        { {  1, { 2, 3 } }, { 3, { 4, 5 } } }

Espressioni di matrice

Un'espressione di matrice viene usata per creare una nuova istanza di un tipo di matrice. Esistono due tipi di espressioni di matrice: espressioni di creazione di matrici e valori letterali di matrice.

Espressioni di creazione di matrici

Se viene fornito un modificatore di inizializzazione delle dimensioni della matrice, il tipo di matrice risultante viene derivato eliminando ognuno dei singoli argomenti dall'elenco di argomenti di inizializzazione delle dimensioni della matrice. Il valore di ogni argomento determina il limite superiore della dimensione corrispondente nell'istanza della matrice appena allocata. Se l'espressione dispone di un inizializzatore di raccolta non vuoto, ogni argomento nell'elenco di argomenti deve essere una costante e le lunghezze di rango e dimensione specificate dall'elenco di espressioni devono corrispondere a quelle dell'inizializzatore della raccolta.

Dim a() As Integer = New Integer(2) {}
Dim b() As Integer = New Integer(2) { 1, 2, 3 }
Dim c(,) As Integer = New Integer(1, 2) { { 1, 2, 3 } , { 4, 5, 6 } }

' Error, length/initializer mismatch.
Dim d() As Integer = New Integer(2) { 0, 1, 2, 3 }

Se non viene fornito un modificatore di inizializzazione delle dimensioni della matrice, il nome del tipo deve essere un tipo di matrice e l'inizializzatore della raccolta deve essere vuoto o avere lo stesso numero di livelli di annidamento del rango del tipo di matrice specificato. Tutti gli elementi nel livello di annidamento più interno devono essere convertibili in modo implicito nel tipo di elemento della matrice e devono essere classificati come valore. Il numero di elementi in ogni inizializzatore di raccolta annidato deve essere sempre coerente con le dimensioni delle altre raccolte allo stesso livello. Le lunghezze delle singole dimensioni vengono dedotte dal numero di elementi in ognuno dei livelli di annidamento corrispondenti dell'inizializzatore di raccolta. Se l'inizializzatore di raccolta è vuoto, la lunghezza di ogni dimensione è zero.

Dim e() As Integer = New Integer() { 1, 2, 3 }
Dim f(,) As Integer = New Integer(,) { { 1, 2, 3 } , { 4, 5, 6 } }

' Error: Inconsistent numbers of elements!
Dim g(,) As Integer = New Integer(,) { { 1, 2 }, { 4, 5, 6 } }

' Error: Inconsistent levels of nesting!
Dim h(,) As Integer = New Integer(,) { 1, 2, { 3, 4 } }

Il livello di annidamento più esterno di un inizializzatore di raccolta corrisponde alla dimensione più a sinistra di una matrice e il livello di annidamento più interno corrisponde alla dimensione più a destra. Esempio:

Dim array As Integer(,) = _
    { { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, { 8, 9 } }

Equivale a quanto segue:

Dim array(4, 1) As Integer

array(0, 0) = 0: array(0, 1) = 1
array(1, 0) = 2: array(1, 1) = 3
array(2, 0) = 4: array(2, 1) = 5
array(3, 0) = 6: array(3, 1) = 7
array(4, 0) = 8: array(4, 1) = 9

Se l'inizializzatore di raccolta è vuoto (ovvero, uno contenente parentesi graffe ma nessun elenco di inizializzatori) e i limiti delle dimensioni della matrice inizializzata sono noti, l'inizializzatore di raccolta vuoto rappresenta un'istanza della matrice delle dimensioni specificate in cui tutti gli elementi sono stati inizializzati sul valore predefinito del tipo di elemento. Se i limiti delle dimensioni della matrice inizializzata non sono noti, l'inizializzatore di raccolta vuoto rappresenta un'istanza della matrice in cui tutte le dimensioni sono pari a zero.

La classificazione e la lunghezza di ogni dimensione di un'istanza di matrice sono costanti per l'intera durata dell'istanza. In altre parole, non è possibile modificare il rango di un'istanza di matrice esistente, né è possibile ridimensionarne le dimensioni.

Valori letterali di matrice

Un valore letterale matrice indica una matrice il cui tipo di elemento, classificazione e limiti viene dedotto da una combinazione del contesto di espressione e di un inizializzatore di raccolta. Questa operazione è illustrata in Riclassificazione delle espressioni di sezione.

ArrayExpression
    : ArrayCreationExpression
    | ArrayLiteralExpression
    ;

ArrayCreationExpression
    : 'New' NonArrayTypeName ArrayNameModifier CollectionInitializer
    ;

ArrayLiteralExpression
    : CollectionInitializer
    ;

Per esempio:

' array of integers
Dim a = {1, 2, 3}

' array of shorts
Dim b = {1S, 2S, 3S}

' array of shorts whose type is taken from the context
Dim c As Short() = {1, 2, 3}

' array of type Integer(,)
Dim d = {{1, 0}, {0, 1}}

' jagged array of rank ()()
Dim e = {({1, 0}), ({0, 1})}

' error: inconsistent rank
Dim f = {{1}, {2, 3}}

' error: inconsistent rank
Dim g = {1, {2}}

Il formato e i requisiti per l'inizializzatore di raccolta in un valore letterale di matrice corrispondono esattamente a quello per l'inizializzatore di raccolta in un'espressione di creazione di matrice.

Nota. Un valore letterale matrice non crea la matrice in e di se stessa; È invece la riclassificazione dell'espressione in un valore che determina la creazione della matrice. Ad esempio, la conversione CType(new Integer() {1,2,3}, Short()) non è possibile perché non esiste alcuna conversione da Integer() a Short(), ma l'espressione CType({1,2,3},Short()) è possibile perché prima riclassifica il valore letterale della matrice nell'espressione New Short() {1,2,3}di creazione della matrice .

espressioni Delegate-Creation

Un'espressione di creazione del delegato viene usata per creare una nuova istanza di un tipo delegato. L'argomento di un'espressione di creazione del delegato deve essere un'espressione classificata come puntatore al metodo o come metodo lambda.

Se l'argomento è un puntatore al metodo, uno dei metodi a cui fa riferimento il puntatore al metodo deve essere applicabile alla firma del tipo delegato. Un metodo M è applicabile a un tipo D delegato se:

  • M non Partial è o ha un corpo.

  • Entrambe M le funzioni e D sono o D è una subroutine.

  • M e D hanno lo stesso numero di parametri.

  • I tipi di parametro di M ognuno hanno una conversione dal tipo del tipo di parametro corrispondente di De i relativi modificatori (ad esempio ByRef, ) ByValcorrispondono.

  • Il tipo restituito di M, se presente, ha una conversione nel tipo restituito di D.

Se il puntatore al metodo fa riferimento a un accesso con associazione tardiva, si presuppone che l'accesso con associazione tardiva sia a una funzione con lo stesso numero di parametri del tipo delegato.

Se la semantica strict non viene usata ed è presente un solo metodo a cui fa riferimento il puntatore al metodo, ma non è applicabile a causa del fatto che non ha parametri e il tipo delegato lo fa, il metodo viene considerato applicabile e i parametri o il valore restituito vengono semplicemente ignorati. Per esempio:

Delegate Sub F(x As Integer)

Module Test
    Sub M()
    End Sub

    Sub Main()
        ' Valid
        Dim x As F = AddressOf M
    End Sub
End Module

Nota. Questo relax è consentito solo quando la semantica rigorosa non viene usata a causa di metodi di estensione. Poiché i metodi di estensione vengono considerati solo se un metodo regolare non è applicabile, è possibile che un metodo di istanza senza parametri nasconda un metodo di estensione con parametri allo scopo della costruzione del delegato.

Se più di un metodo a cui fa riferimento il puntatore al metodo è applicabile al tipo delegato, viene usata la risoluzione dell'overload per scegliere tra i metodi candidati. I tipi dei parametri per il delegato vengono usati come tipi di argomenti ai fini della risoluzione dell'overload. Se nessuno dei candidati al metodo è più applicabile, si verifica un errore in fase di compilazione. Nell'esempio seguente la variabile locale viene inizializzata con un delegato che fa riferimento al secondo Square metodo perché tale metodo è più applicabile alla firma e al tipo restituito di DoubleFunc.

Delegate Function DoubleFunc(x As Double) As Double

Module Test
    Function Square(x As Single) As Single
        Return x * x
    End Function 

    Function Square(x As Double) As Double
        Return x * x
    End Function

    Sub Main()
        Dim a As New DoubleFunc(AddressOf Square)
    End Sub
End Module

Se il secondo Square metodo non fosse presente, sarebbe stato scelto il primo Square metodo. Se la semantica strict viene specificata dall'ambiente di compilazione o da Option Strict, si verifica un errore in fase di compilazione se il metodo più specifico a cui fa riferimento il puntatore al metodo è più stretto della firma del delegato. Un metodo M è considerato più stretto di un tipo D delegato se:

  • Un tipo di parametro di M ha una conversione di tipo widening nel tipo di parametro corrispondente di D.

  • In alternativa, il tipo restituito, se presente, ha M una conversione di tipo narrowing al tipo restituito di D.

Se gli argomenti di tipo sono associati al puntatore al metodo, vengono considerati solo i metodi con lo stesso numero di argomenti di tipo. Se nessun argomento di tipo è associato al puntatore al metodo, l'inferenza del tipo viene utilizzata quando le firme corrispondono a un metodo generico. A differenza di altri tipi normali, il tipo restituito del delegato viene usato durante l'inferenza degli argomenti di tipo, ma i tipi restituiti non vengono ancora considerati quando si determina l'overload meno generico. Nell'esempio seguente vengono illustrati entrambi i modi per fornire un argomento di tipo a un'espressione di creazione del delegato:

Delegate Function D(s As String, i As Integer) As Integer
Delegate Function E() As Integer

Module Test
    Public Function F(Of T)(s As String, t1 As T) As T
    End Function

    Public Function G(Of T)() As T
    End Function

    Sub Main()
        Dim d1 As D = AddressOf f(Of Integer)    ' OK, type arg explicit
        Dim d2 As D = AddressOf f                ' OK, type arg inferred

        Dim e1 As E = AddressOf g(Of Integer)    ' OK, type arg explicit
        Dim e2 As E = AddressOf g                ' OK, infer from return
  End Sub
End Module

Nell'esempio precedente è stata creata un'istanza di un tipo delegato non generico usando un metodo generico. È anche possibile creare un'istanza di un tipo delegato costruito usando un metodo generico. Per esempio:

Delegate Function Predicate(Of U)(u1 As U, u2 As U) As Boolean

Module Test
    Function Compare(Of T)(t1 As List(of T), t2 As List(of T)) As Boolean
        ...
    End Function

    Sub Main()
        Dim p As Predicate(Of List(Of Integer))
        p = AddressOf Compare(Of Integer)
    End Sub
End Module

Se l'argomento dell'espressione di creazione del delegato è un metodo lambda, il metodo lambda deve essere applicabile alla firma del tipo delegato. Un metodo L lambda è applicabile a un tipo D delegato se:

  • Se L ha parametri, D ha lo stesso numero di parametri. Se L non contiene parametri, i parametri di D vengono ignorati.

  • I tipi di parametro di L ognuno hanno una conversione nel tipo del tipo di parametro corrispondente di De i relativi modificatori (ad esempio ByRef, ) ByValcorrispondono.

  • Se D è una funzione, il tipo restituito di L ha una conversione nel tipo restituito di D. Se D è una subroutine, il valore restituito di L viene ignorato.

Se viene omesso il tipo di parametro di L , viene dedotto il tipo del parametro corrispondente in D ; se il parametro di ha modificatori di L nomi di matrice o nullable, viene restituito un errore in fase di compilazione. Quando tutti i tipi di parametro di L sono disponibili, viene dedotto il tipo dell'espressione nel metodo lambda. Per esempio:

Delegate Function F(x As Integer, y As Long) As Long

Module Test
    Sub Main()
        ' b inferred to Integer, c and return type inferred to Long
        Dim a As F = Function(b, c) b + c

        ' e and return type inferred to Integer, f inferred to Long
        Dim d As F = Function(e, f) e + CInt(f)
    End Sub
End Module

In alcune situazioni in cui la firma del delegato non corrisponde esattamente al metodo lambda o alla firma del metodo, .NET Framework potrebbe non supportare la creazione del delegato in modo nativo. In questo caso, viene usata un'espressione di metodo lambda per trovare la corrispondenza con i due metodi. Per esempio:

Delegate Function IntFunc(x As Integer) As Integer

Module Test
    Function SquareString(x As String) As String
        Return CInt(x) * CInt(x)
    End Function 

    Sub Main()
        ' The following two lines are equivalent
        Dim a As New IntFunc(AddressOf SquareString)
        Dim b As New IntFunc( _
            Function(x As Integer) CInt(SquareString(CStr(x))))
    End Sub
End Module

Il risultato di un'espressione di creazione del delegato è un'istanza del delegato che fa riferimento al metodo corrispondente con l'espressione di destinazione associata (se presente) dall'espressione puntatore al metodo. Se l'espressione di destinazione viene tipizzata come tipo valore, il tipo di valore viene copiato nell'heap di sistema perché un delegato può puntare solo a un metodo di un oggetto nell'heap. Il metodo e l'oggetto a cui fa riferimento un delegato rimangono costanti per l'intera durata del delegato. In altre parole, non è possibile modificare la destinazione o l'oggetto di un delegato dopo la creazione.

Espressioni Object-Creation anonime

Un'espressione di creazione di oggetti con inizializzatori membro può anche omettere completamente il nome del tipo.

AnonymousObjectCreationExpression
    : 'New' ObjectMemberInitializer
    ;

In tal caso, un tipo anonimo viene costruito in base ai tipi e ai nomi dei membri inizializzati come parte dell'espressione. Per esempio:

Module Test
    Sub Main()
        Dim Customer = New With { .Name = "John Smith", .Age = 34 }

        Console.WriteLine(Customer.Name)
    End Sub
End Module

Il tipo creato da un'espressione anonima di creazione di oggetti è una classe senza nome, eredita direttamente da Objecte ha un set di proprietà con lo stesso nome dei membri assegnati a nell'elenco di inizializzatori membro. Il tipo di ogni proprietà viene dedotto usando le stesse regole dell'inferenza del tipo di variabile locale. I tipi anonimi generati eseguono anche l'override ToStringdi , restituendo una rappresentazione di stringa di tutti i membri e i relativi valori. Il formato esatto di questa stringa esula dall'ambito di questa specifica.

Per impostazione predefinita, le proprietà generate dal tipo anonimo sono di lettura/scrittura. È possibile contrassegnare una proprietà di tipo anonimo come di sola lettura usando il Key modificatore . Il Key modificatore specifica che il campo può essere utilizzato per identificare in modo univoco il valore rappresentato dal tipo anonimo. Oltre a rendere di sola lettura la proprietà, fa sì che il tipo anonimo venga sottoposto a override Equals e GetHashCode che implementi l'interfaccia System.IEquatable(Of T) (inserendo il tipo anonimo per T). I membri sono definiti come segue:

Function Equals(obj As Object) As Boolean e Function Equals(val As T) As Boolean vengono implementati convalidando che le due istanze siano dello stesso tipo e quindi confrontando ogni Key membro usando Object.Equals. Se tutti i Key membri sono uguali, Equals restituisce True, in caso contrario Equals restituisce False.

Function GetHashCode() As Integer viene implementato in modo che se Equals è true per due istanze del tipo anonimo, restituirà GetHashCode lo stesso valore. L'hash inizia con un valore di inizializzazione e quindi, per ogni Key membro, moltiplica l'hash per 31 e aggiunge il Key valore hash del membro (fornito da GetHashCode) se il membro non è un tipo riferimento o un tipo valore nullable con il valore di Nothing.

Ad esempio, il tipo creato nell'istruzione :

Dim zipState = New With { Key .ZipCode = 98112, .State = "WA" }

crea una classe simile alla seguente (anche se l'implementazione esatta può variare):

Friend NotInheritable Class $Anonymous1
    Implements IEquatable(Of $Anonymous1)

    Private ReadOnly _zipCode As Integer
    Private _state As String

    Public Sub New(zipCode As Integer, state As String)
        _zipCode = zipcode
        _state = state
    End Sub

    Public ReadOnly Property ZipCode As Integer
        Get
            Return _zipCode
        End Get
    End Property

    Public Property State As String
        Get
            Return _state
        End Get
        Set (value As Integer)
            _state = value
        End Set
    End Property

    Public Overrides Function Equals(obj As Object) As Boolean
        Dim val As $Anonymous1 = TryCast(obj, $Anonymous1)
        Return Equals(val)
    End Function

    Public Overloads Function Equals(val As $Anonymous1) As Boolean _
        Implements IEquatable(Of $Anonymous1).Equals

        If val Is Nothing Then 
            Return False
        End If

        If Not Object.Equals(_zipCode, val._zipCode) Then 
            Return False
        End If

        Return True
    End Function

    Public Overrides Function GetHashCode() As Integer
        Dim hash As Integer = 0

        hash = hash Xor _zipCode.GetHashCode()

        Return hash
    End Function

    Public Overrides Function ToString() As String
        Return "{ Key .ZipCode = " & _zipCode & ", .State = " & _state & " }"
    End Function
End Class

Per semplificare la situazione in cui viene creato un tipo anonimo dai campi di un altro tipo, i nomi dei campi possono essere dedotti direttamente dalle espressioni nei casi seguenti:

  • Un'espressione x di nome semplice deduce il nome x.

  • Un'espressione x.y di accesso ai membri deduce il nome y.

  • Un'espressione x!y di ricerca del dizionario deduce il nome y.

  • Una chiamata o un'espressione di indice senza argomenti x() deduce il nome x.

  • Un'espressione x.<y>di accesso ai membri XML , x...<y>, x.@y deduce il nome y.

  • Un'espressione di accesso ai membri XML che è la destinazione di un'espressione x.<y>.z di accesso ai membri deduce il nome z.

  • Un'espressione di accesso ai membri XML che rappresenta la destinazione di una chiamata o di un'espressione di indice senza argomenti x.<y>.z() deduce il nome z.

  • Un'espressione di accesso ai membri XML che rappresenta la destinazione di una chiamata o di un'espressione x.<y>(0) di indice deduce il nome y.

L'inizializzatore viene interpretato come un'assegnazione dell'espressione al nome dedotto. Ad esempio, gli inizializzatori seguenti sono equivalenti:

Class Address
    Public Street As String
    Public City As String
    Public State As String
    Public ZIP As String
End Class

Class C1
    Sub Test(a As Address)
        Dim cityState1 = New With { .City = a.City, .State = a.State }
        Dim cityState2 = New With { a.City, a.State }
    End Sub
End Class

Se un nome di membro viene dedotto che è in conflitto con un membro esistente del tipo, ad esempio GetHashCode, si verifica un errore di fase di compilazione. A differenza degli inizializzatori di membri regolari, le espressioni anonime di creazione di oggetti non consentono agli inizializzatori di membri di avere riferimenti circolari o di fare riferimento a un membro prima che sia stato inizializzato. Per esempio:

Module Test
    Sub Main()
        ' Error: Circular references
        Dim x = New With { .a = .b, .b = .a }

        ' Error: Referring to .b before it has been assigned to
        Dim y = New With { .a = .b, .b = 10 }

        ' Error: Referring to .a before it has been assigned to
        Dim z = New With { .a = .a }
    End Sub
End Module

Se due espressioni di creazione di classi anonime si verificano all'interno dello stesso metodo e producono la stessa forma risultante, se l'ordine delle proprietà, i nomi delle proprietà e i tipi di proprietà corrispondono, entrambi fanno riferimento alla stessa classe anonima. L'ambito del metodo di un'istanza o di una variabile membro condiviso con un inizializzatore è il costruttore in cui viene inizializzata la variabile.

Nota. È possibile che un compilatore possa scegliere di unificare ulteriormente i tipi anonimi, ad esempio a livello di assembly, ma al momento non può essere considerato attendibile.

Espressioni cast

Un'espressione cast crea una coercizione di un'espressione a un determinato tipo. Le parole chiave cast specifiche comeguano le espressioni nei tipi primitivi. Tre parole chiave cast generali, CType, TryCast e DirectCast, comeguano un'espressione in un tipo.

CastExpression
    : 'DirectCast' OpenParenthesis Expression Comma TypeName CloseParenthesis
    | 'TryCast' OpenParenthesis Expression Comma TypeName CloseParenthesis
    | 'CType' OpenParenthesis Expression Comma TypeName CloseParenthesis
    | CastTarget OpenParenthesis Expression CloseParenthesis
    ;

CastTarget
    : 'CBool' | 'CByte' | 'CChar'  | 'CDate'  | 'CDec' | 'CDbl' | 'CInt'
    | 'CLng'  | 'CObj'  | 'CSByte' | 'CShort' | 'CSng' | 'CStr' | 'CUInt'
    | 'CULng' | 'CUShort'
    ;

DirectCast e TryCast hanno comportamenti speciali. Per questo motivo, supportano solo conversioni native. Inoltre, il tipo di destinazione in un'espressione TryCast non può essere un tipo valore. Gli operatori di conversione definiti dall'utente non vengono considerati quando DirectCast vengono usati o TryCast . (Nota. Il set DirectCast di conversione che e TryCast il supporto sono limitati perché implementano conversioni "CLR native". Lo scopo di DirectCast è fornire la funzionalità dell'istruzione "unbox", mentre lo scopo di TryCast è fornire la funzionalità dell'istruzione "isinst". Poiché eseguono il mapping alle istruzioni CLR, il supporto delle conversioni non direttamente supportate da CLR sconfisse lo scopo previsto.

DirectCast converte le espressioni tipate in Object modo diverso da CType. Quando si converte un'espressione di tipo Object il cui tipo di runtime è un tipo di valore primitivo, DirectCast genera un'eccezione System.InvalidCastException se il tipo specificato non è uguale al tipo di runtime dell'espressione o se System.NullReferenceException l'espressione restituisce Nothing. (Nota. Come indicato in precedenza, DirectCast esegue il mapping direttamente all'istruzione CLR "unbox" quando il tipo dell'espressione è Object. Al contrario, CType si trasforma in una chiamata a un helper di runtime per eseguire la conversione in modo che le conversioni tra tipi primitivi possano essere supportate. Nel caso in cui un'espressione Object venga convertita in un tipo di valore primitivo e il tipo dell'istanza effettiva corrispondano al tipo di destinazione, DirectCast sarà notevolmente più veloce di CType.)

TryCast converte le espressioni ma non genera un'eccezione se l'espressione non può essere convertita nel tipo di destinazione. Verrà invece TryCast generato Nothing se l'espressione non può essere convertita in fase di esecuzione. (Nota. Come indicato in precedenza, TryCast esegue il mapping direttamente all'istruzione CLR "isinst". Combinando il controllo del tipo e la conversione in un'unica operazione, TryCast può essere più conveniente rispetto a e TypeOf ... Is quindi a .CType

Per esempio:

Interface ITest
    Sub Test()
End Interface

Module Test
    Sub Convert(o As Object)
        Dim i As ITest = TryCast(o, ITest)

        If i IsNot Nothing Then
            i.Test()
        End If
    End Sub
End Module

Se non esiste alcuna conversione dal tipo dell'espressione al tipo specificato, si verifica un errore in fase di compilazione. In caso contrario, l'espressione viene classificata come valore e il risultato è il valore prodotto dalla conversione.

Espressioni di operatore

Esistono due tipi di operatori. Gli operatori unari accettano un operando e usano la notazione del prefisso (ad esempio, -x). Gli operatori binari accettano due operandi e usano la notazione infix (ad esempio, x + y). Ad eccezione degli operatori relazionali, che generano sempre , un operatore definito per un particolare tipo restituisce Booleantale tipo. Gli operandi di un operatore devono essere sempre classificati come valore; il risultato di un'espressione di operatore viene classificato come valore.

OperatorExpression
    : ArithmeticOperatorExpression
    | RelationalOperatorExpression
    | LikeOperatorExpression
    | ConcatenationOperatorExpression
    | ShortCircuitLogicalOperatorExpression
    | LogicalOperatorExpression
    | ShiftOperatorExpression
    | AwaitOperatorExpression
    ;

Precedenza e associatività degli operatori

Quando un'espressione contiene più operatori binari, la precedenza degli operatori controlla l'ordine in cui vengono valutati i singoli operatori binari. Ad esempio, l'espressione x + y * z viene valutata come x + (y * z) perché l'operatore * ha una precedenza maggiore rispetto all'operatore + . La tabella seguente elenca gli operatori binari in ordine decrescente di precedenza:

Categoria Operatori
Primaria Tutte le espressioni non di operatore
Aspettare Await
Esponenziazione ^
Negazione unaria +, -
Moltiplicativo *, /
Divisione di interi \
Modulo Mod
Additivo +, -
Concatenazione &
Spostamento <<, >>
Relazionale =, <>, <, >, <=>=, Like, , IsIsNot
operatore logico NOT Not
E logico And, AndAlso
oppure logico Or, OrElse
Operatore logico XOR Xor

Quando un'espressione contiene due operatori con la stessa precedenza, l'associatività degli operatori controlla l'ordine in cui vengono eseguite le operazioni. Tutti gli operatori binari sono associati a sinistra, ovvero le operazioni vengono eseguite da sinistra a destra. La precedenza e l'associatività possono essere controllate usando espressioni tra parentesi.

Operandi oggetto

Oltre ai tipi regolari supportati da ogni operatore, tutti gli operatori supportano operandi di tipo Object. Gli operatori applicati agli Object operandi vengono gestiti in modo analogo alle chiamate al metodo effettuate sui Object valori: è possibile scegliere una chiamata al metodo con associazione tardiva, nel qual caso il tipo di runtime degli operandi, anziché il tipo di fase di compilazione, determina la validità e il tipo dell'operazione. Se la semantica rigorosa viene specificata dall'ambiente di compilazione o da Option Strict, qualsiasi operatore con operandi di tipo Object genera un errore in fase di compilazione, ad eccezione degli TypeOf...Isoperatori , Is e IsNot .

Quando la risoluzione degli operatori determina che un'operazione deve essere eseguita con associazione tardiva, il risultato dell'operazione è il risultato dell'applicazione dell'operatore ai tipi di operando se i tipi di runtime degli operandi sono tipi supportati dall'operatore. Il valore Nothing viene considerato come valore predefinito del tipo dell'altro operando in un'espressione di operatore binario. In un'espressione di operatore unario o se entrambi gli operandi si trovano Nothing in un'espressione di operatore binario, il tipo dell'operazione è Integer o l'unico tipo di risultato dell'operatore, se l'operatore non restituisce Integer. Il risultato dell'operazione viene sempre restituito a Object. Se i tipi di operando non hanno un operatore valido, viene generata un'eccezione System.InvalidCastException . Le conversioni in fase di esecuzione vengono eseguite indipendentemente dal fatto che siano implicite o esplicite.

Se il risultato di un'operazione binaria numerica genera un'eccezione di overflow (indipendentemente dal fatto che il controllo dell'overflow di integer sia attivato o disattivato), il tipo di risultato viene alzato di livello al tipo numerico più ampio successivo, se possibile. Si consideri ad esempio il codice seguente:

Module Test
    Sub Main()
        Dim o As Object = CObj(CByte(2)) * CObj(CByte(255))

        Console.WriteLine(o.GetType().ToString() & " = " & o)
    End Sub
End Module

Stampa il risultato seguente:

System.Int16 = 512

Se non è disponibile alcun tipo numerico più ampio per contenere il numero, viene generata un'eccezione System.OverflowException .

Risoluzione degli operatori

Dato un tipo di operatore e un set di operandi, la risoluzione degli operatori determina quale operatore utilizzare per gli operandi. Quando si risolve gli operatori, gli operatori definiti dall'utente verranno considerati per primi, attenendosi alla procedura seguente:

  1. Innanzitutto, vengono raccolti tutti gli operatori candidati. Gli operatori candidati sono tutti gli operatori definiti dall'utente del tipo di operatore specifico nel tipo di origine e tutti gli operatori definiti dall'utente del tipo specifico nel tipo di destinazione. Se il tipo di origine e il tipo di destinazione sono correlati, gli operatori comuni vengono considerati una sola volta.

  2. La risoluzione dell'overload viene quindi applicata agli operatori e agli operandi per selezionare l'operatore più specifico. Nel caso di operatori binari, ciò può comportare una chiamata ad associazione tardiva.

Quando si raccolgono gli operatori candidati per un tipo T?, vengono invece usati gli operatori di tipo T . TAnche gli operatori definiti dall'utente che coinvolgono solo tipi valore non nullable vengono lifted. Un operatore lifted usa la versione nullable di qualsiasi tipo valore, con l'eccezione i tipi restituiti di IsTrue e IsFalse (che devono essere Boolean). Gli operatori lifted vengono valutati convertendo gli operandi nella versione non nullable, quindi valutando l'operatore definito dall'utente e quindi convertendo il tipo di risultato nella versione nullable. Se l'operando ether è Nothing, il risultato dell'espressione è un valore Nothing tipizzato come versione nullable del tipo di risultato. Per esempio:

Structure T
    ...
End Structure

Structure S
    Public Shared Operator +(ByVal op1 As S, ByVal op2 As T) As T
        ...
    End Operator
End Structure

Module Test
    Sub Main()
        Dim x As S?
        Dim y, z As T?

        ' Valid, as S + T = T is lifted to S? + T? = T?
        z = x + y 
    End Sub
End Module

Se l'operatore è un operatore binario e uno degli operandi è di tipo riferimento, l'operatore viene anche sollevato, ma qualsiasi associazione all'operatore genera un errore. Per esempio:

Structure S1
    Public F1 As Integer

    Public Shared Operator +(left As S1, right As String) As S1
       ...
    End Operator
End Structure

Module Test
    Sub Main()
        Dim a? As S1
        Dim s As String
        
        ' Error: '+' is not defined for S1? and String
        a = a + s
    End Sub
End Module

Nota. Questa regola esiste perché è stato considerato se si desidera aggiungere tipi riferimento di propagazione Null in una versione futura, nel qual caso il comportamento nel caso degli operatori binari tra i due tipi cambierebbe.

Come per le conversioni, gli operatori definiti dall'utente sono sempre preferiti rispetto agli operatori lifted.

Quando si risolve gli operatori di overload, potrebbero esserci differenze tra le classi definite in Visual Basic e quelle definite in altri linguaggi:

  • In altri linguaggi, Not, Ande Or possono essere sovraccaricati sia come operatori logici che come operatori bit per bit. Dopo l'importazione da un assembly esterno, entrambi i moduli vengono accettati come overload validi per questi operatori. Tuttavia, per un tipo che definisce operatori logici e bit per bit, verrà considerata solo l'implementazione bit per bit.

  • In altri linguaggi >> e << può essere sottoposto a overload sia come operatori firmati che come operatori senza segno. Al momento dell'importazione da un assembly esterno, una delle due forme viene accettata come overload valido. Tuttavia, per un tipo che definisce operatori firmati e senza segno, verrà considerata solo l'implementazione firmata.

  • Se nessun operatore definito dall'utente è più specifico per gli operandi, gli operatori intrinseci verranno considerati. Se non viene definito alcun operatore intrinseco per gli operandi e uno degli operandi ha tipo Object, l'operatore verrà risolto con associazione tardiva; in caso contrario, viene restituito un errore in fase di compilazione.

Nelle versioni precedenti di Visual Basic, se c'era esattamente un operando di tipo Object e nessun operatore definito dall'utente applicabile e nessun operatore intrinseco applicabile, si è verificato un errore. A partire da Visual Basic 11, viene ora risolto con associazione tardiva. Per esempio:

Module Module1
  Sub Main()
      Dim p As Object = Nothing
      Dim U As New Uri("http://www.microsoft.com")
      Dim j = U * p  ' is now resolved late-bound
   End Sub
End Module

Un tipo T con un operatore intrinseco definisce anche lo stesso operatore per T?. Il risultato dell'operatore su T? sarà uguale a per T, ad eccezione del fatto che se uno degli operandi è Nothing, il risultato dell'operatore sarà Nothing (ad esempio, il valore Null viene propagato). Ai fini della risoluzione del tipo di un'operazione, l'oggetto ? viene rimosso da tutti gli operandi che li contengono, viene determinato il tipo dell'operazione e viene ? aggiunto un al tipo dell'operazione se uno degli operandi era tipi di valore nullable. Per esempio:

Dim v1? As Integer = 10
Dim v2 As Long = 20

' Type of operation will be Long?
Console.WriteLine(v1 + v2)

Ogni operatore elenca i tipi intrinseci per e il tipo dell'operazione eseguita in base ai tipi di operando. Il risultato di un'operazione intrinseca segue queste regole generali:

  • Se tutti gli operandi sono dello stesso tipo e l'operatore viene definito per il tipo, non viene eseguita alcuna conversione e viene usato l'operatore per tale tipo.

  • Qualsiasi operando il cui tipo non è definito per l'operatore viene convertito usando i passaggi seguenti e l'operatore viene risolto in base ai nuovi tipi:

    • L'operando viene convertito nel tipo più ampio successivo definito sia per l'operatore che per l'operando e in cui è convertibile in modo implicito.

    • Se tale tipo non è presente, l'operando viene convertito nel tipo più stretto successivo definito sia per l'operatore che per l'operando e in cui è convertibile in modo implicito.

    • Se non esiste un tipo di questo tipo o la conversione non può verificarsi, si verifica un errore in fase di compilazione.

  • In caso contrario, gli operandi vengono convertiti nel più ampio dei tipi di operando e viene utilizzato l'operatore per tale tipo. Se il tipo di operando più stretto non può essere convertito in modo implicito nel tipo di operatore più ampio, si verifica un errore in fase di compilazione.

Nonostante queste regole generali, tuttavia, esistono diversi casi speciali evidenziati nelle tabelle dei risultati degli operatori.

Nota. Per motivi di formattazione, le tabelle dei tipi di operatore abbreviano i nomi predefiniti ai primi due caratteri. "By" è Byte, "UI" è UInteger, "St" è Stringe così via. "Err" significa che non è stata definita alcuna operazione per i tipi di operando specificati.

Operatori aritmetici

Gli *operatori , \/, ^Mod, +e - sono gli operatori aritmetici.

ArithmeticOperatorExpression
    : UnaryPlusExpression
    | UnaryMinusExpression
    | AdditionOperatorExpression
    | SubtractionOperatorExpression
    | MultiplicationOperatorExpression
    | DivisionOperatorExpression
    | ModuloOperatorExpression
    | ExponentOperatorExpression
    ;

Le operazioni aritmetiche a virgola mobile possono essere eseguite con precisione maggiore rispetto al tipo di risultato dell'operazione. Ad esempio, alcune architetture hardware supportano un tipo a virgola mobile "extended" o "long double" con un intervallo e una precisione maggiori rispetto al Double tipo ed eseguono in modo implicito tutte le operazioni a virgola mobile usando questo tipo di precisione superiore. Le architetture hardware possono essere eseguite per eseguire operazioni a virgola mobile con una precisione inferiore solo a un costo eccessivo delle prestazioni; invece di richiedere un'implementazione di perfedere sia le prestazioni che la precisione, Visual Basic consente di usare il tipo con precisione superiore per tutte le operazioni a virgola mobile. Oltre a fornire risultati più precisi, questo raramente ha effetti misurabili. Tuttavia, nelle espressioni del formato x * y / z, in cui la moltiplicazione produce un risultato esterno all'intervallo Double , ma la divisione successiva riporta il risultato temporaneo nell'intervallo Double , il fatto che l'espressione viene valutata in un formato di intervallo superiore può causare la produzione di un risultato finito anziché infinito.

Operatore Unary Plus

UnaryPlusExpression
    : '+' Expression
    ;

L'operatore più unario è definito per i Bytetipi , UShortSByte, , UIntegerShort, ULongLongSingleIntegerDoublee .Decimal

Tipo di operazione:

Bo SB By Zitto Stati Uniti In interfaccia utente Ecco UL De Si Cosa fare Da Ch San Ob
Zitto SB Presso Zitto Stati Uniti In INTERFACCIA UTENTE Ecco UL De Si Fai Errare Errare Fai Ob

Operatore meno unario

UnaryMinusExpression
    : '-' Expression
    ;

L'operatore meno unario è definito per i tipi seguenti:

SByte, Short, Integere Long. Il risultato viene calcolato sottraendo l'operando da zero. Se il controllo dell'overflow di integer è attivo e il valore dell'operando è il valore massimo negativo SByte, ShortInteger, o Long, viene generata un'eccezioneSystem.OverflowException. In caso contrario, se il valore dell'operando è il valore massimo negativo SByte, Short, Integero Long, il risultato è lo stesso valore e l'overflow non viene segnalato.

Single e Double. Il risultato è il valore dell'operando con il segno invertito, inclusi i valori 0 e Infinity. Se l'operando è NaN, il risultato è anche NaN.

Decimal. Il risultato viene calcolato sottraendo l'operando da zero.

Tipo di operazione:

Bo SB By Zitto Stati Uniti In interfaccia utente Ecco UL De Si Cosa fare Da Ch San Ob
Zitto SB Zitto Zitto In In Ecco Ecco De De Si Fai Errare Errare Fai Ob

Operatore addizione

L'operatore di addizione calcola la somma dei due operandi.

AdditionOperatorExpression
    : Expression '+' LineTerminator? Expression
    ;

L'operatore di addizione è definito per i tipi seguenti:

  • Byte, SByte, UShort, Short, UInteger, Integer, ULong e Long. Se il controllo dell'overflow integer è attivo e la somma non rientra nell'intervallo del tipo di risultato, viene generata un'eccezione System.OverflowException . In caso contrario, gli overflow non vengono segnalati e i bit significativi di ordine elevato del risultato vengono eliminati.

  • Single e Double. La somma viene calcolata in base alle regole dell'aritmetica IEEE 754.

  • Decimal. Se il valore risultante è troppo grande per rappresentare nel formato decimale, viene generata un'eccezione System.OverflowException . Se il valore del risultato è troppo piccolo per rappresentare nel formato decimale, il risultato è 0.

  • String. I due String operandi vengono concatenati insieme.

  • Date. Il System.DateTime tipo definisce operatori di addizione di overload. Poiché System.DateTime equivale al tipo intrinseco Date , questi operatori sono disponibili anche nel Date tipo .

Tipo di operazione:

Bo SB By Zitto Stati Uniti In interfaccia utente Ecco UL De Si Cosa fare Da Ch San Ob
Bo Zitto SB Zitto Zitto In In Ecco Ecco De De Si Fai Errare Errare Fai Ob
SB SB Zitto Zitto In In Ecco Ecco De De Si Fai Errare Errare Fai Ob
By Presso Zitto Stati Uniti In INTERFACCIA UTENTE Ecco UL De Si Fai Errare Errare Fai Ob
Zitto Zitto In In Ecco Ecco De De Si Fai Errare Errare Fai Ob
Stati Uniti Stati Uniti In INTERFACCIA UTENTE Ecco UL De Si Fai Errare Errare Fai Ob
In In Ecco Ecco De De Si Fai Errare Errare Fai Ob
interfaccia utente INTERFACCIA UTENTE Ecco UL De Si Fai Errare Errare Fai Ob
Ecco Ecco De De Si Fai Errare Errare Fai Ob
UL UL De Si Fai Errare Errare Fai Ob
De De Si Fai Errare Errare Fai Ob
Si Si Fai Errare Errare Fai Ob
Cosa fare Fai Errare Errare Fai Ob
Da San Errare San Ob
Ch San San Ob
San San Ob
Ob Ob

Operatore di sottrazione

L'operatore di sottrazione sottrae il secondo operando dal primo operando.

SubtractionOperatorExpression
    : Expression '-' LineTerminator? Expression
    ;

L'operatore di sottrazione è definito per i tipi seguenti:

  • Byte, SByte, UShort, Short, UInteger, Integer, ULong e Long. Se il controllo dell'overflow integer è attivo e la differenza non rientra nell'intervallo del tipo di risultato, viene generata un'eccezione System.OverflowException . In caso contrario, gli overflow non vengono segnalati e i bit significativi di ordine elevato del risultato vengono eliminati.

  • Single e Double. La differenza viene calcolata in base alle regole dell'aritmetica IEEE 754.

  • Decimal. Se il valore risultante è troppo grande per rappresentare nel formato decimale, viene generata un'eccezione System.OverflowException . Se il valore del risultato è troppo piccolo per rappresentare nel formato decimale, il risultato è 0.

  • Date. Il System.DateTime tipo definisce gli operatori di sottrazione di overload. Poiché System.DateTime equivale al tipo intrinseco Date , questi operatori sono disponibili anche nel Date tipo .

Tipo di operazione:

Bo SB By Zitto Stati Uniti In interfaccia utente Ecco UL De Si Cosa fare Da Ch San Ob
Bo Zitto SB Zitto Zitto In In Ecco Ecco De De Si Fai Errare Errare Fai Ob
SB SB Zitto Zitto In In Ecco Ecco De De Si Fai Errare Errare Fai Ob
By Presso Zitto Stati Uniti In INTERFACCIA UTENTE Ecco UL De Si Fai Errare Errare Fai Ob
Zitto Zitto In In Ecco Ecco De De Si Fai Errare Errare Fai Ob
Stati Uniti Stati Uniti In INTERFACCIA UTENTE Ecco UL De Si Fai Errare Errare Fai Ob
In In Ecco Ecco De De Si Fai Errare Errare Fai Ob
interfaccia utente INTERFACCIA UTENTE Ecco UL De Si Fai Errare Errare Fai Ob
Ecco Ecco De De Si Fai Errare Errare Fai Ob
UL UL De Si Fai Errare Errare Fai Ob
De De Si Fai Errare Errare Fai Ob
Si Si Fai Errare Errare Fai Ob
Cosa fare Fai Errare Errare Fai Ob
Da Errare Errare Errare Errare
Ch Errare Errare Errare
San Fai Ob
Ob Ob

Operatore di moltiplicazione

L'operatore di moltiplicazione calcola il prodotto di due operandi.

MultiplicationOperatorExpression
    : Expression '*' LineTerminator? Expression
    ;

L'operatore di moltiplicazione è definito per i tipi seguenti:

  • Byte, SByte, UShort, Short, UInteger, Integer, ULong e Long. Se il controllo dell'overflow integer è attivo e il prodotto non è compreso nell'intervallo del tipo di risultato, viene generata un'eccezione System.OverflowException . In caso contrario, gli overflow non vengono segnalati e i bit significativi di ordine elevato del risultato vengono eliminati.

  • Single e Double. Il prodotto viene calcolato in base alle regole dell'aritmetica IEEE 754.

  • Decimal. Se il valore risultante è troppo grande per rappresentare nel formato decimale, viene generata un'eccezione System.OverflowException . Se il valore del risultato è troppo piccolo per rappresentare nel formato decimale, il risultato è 0.

Tipo di operazione:

Bo SB By Zitto Stati Uniti In interfaccia utente Ecco UL De Si Cosa fare Da Ch San Ob
Bo Zitto SB Zitto Zitto In In Ecco Ecco De De Si Fai Errare Errare Fai Ob
SB SB Zitto Zitto In In Ecco Ecco De De Si Fai Errare Errare Fai Ob
By Presso Zitto Stati Uniti In INTERFACCIA UTENTE Ecco UL De Si Fai Errare Errare Fai Ob
Zitto Zitto In In Ecco Ecco De De Si Fai Errare Errare Fai Ob
Stati Uniti Stati Uniti In INTERFACCIA UTENTE Ecco UL De Si Fai Errare Errare Fai Ob
In In Ecco Ecco De De Si Fai Errare Errare Fai Ob
interfaccia utente INTERFACCIA UTENTE Ecco UL De Si Fai Errare Errare Fai Ob
Ecco Ecco De De Si Fai Errare Errare Fai Ob
UL UL De Si Fai Errare Errare Fai Ob
De De Si Fai Errare Errare Fai Ob
Si Si Fai Errare Errare Fai Ob
Cosa fare Fai Errare Errare Fai Ob
Da Errare Errare Errare Errare
Ch Errare Errare Errare
San Fai Ob
Ob Ob

Operatori di divisione

Gli operatori di divisione calcolano il quoziente di due operandi. Esistono due operatori di divisione: l'operatore di divisione regolare (a virgola mobile) e l'operatore di divisione integer.

DivisionOperatorExpression
    : FPDivisionOperatorExpression
    | IntegerDivisionOperatorExpression
    ;

FPDivisionOperatorExpression
    : Expression '/' LineTerminator? Expression
    ;

IntegerDivisionOperatorExpression
    : Expression '\\' LineTerminator? Expression
    ;

L'operatore di divisione regolare è definito per i tipi seguenti:

  • Single e Double. Il quoziente viene calcolato in base alle regole dell'aritmetica IEEE 754.

  • Decimal. Se il valore dell'operando destro è zero, viene generata un'eccezione System.DivideByZeroException . Se il valore risultante è troppo grande per rappresentare nel formato decimale, viene generata un'eccezione System.OverflowException . Se il valore del risultato è troppo piccolo per rappresentare nel formato decimale, il risultato è zero. La scala del risultato, prima di qualsiasi arrotondamento, è la scala più vicina alla scala preferita che manterrà un risultato uguale al risultato esatto. La scala preferita è la scala del primo operando meno la scala del secondo operando.

In base alle normali regole di risoluzione degli operatori, la divisione regolare puramente tra operandi di tipi come Byte, Short, Integere Long causerebbe la conversione di entrambi gli operandi nel tipo Decimal. Tuttavia, quando si esegue la risoluzione degli operatori sull'operatore di divisione quando nessuno dei due tipi è Decimal, Double viene considerato più stretto di Decimal. Questa convenzione è seguita perché Double la divisione è più efficiente della Decimal divisione.

Tipo di operazione:

Bo SB By Zitto Stati Uniti In interfaccia utente Ecco UL De Si Cosa fare Da Ch San Ob
Bo Fai Fai Fai Fai Fai Fai Fai Fai Fai De Si Fai Errare Errare Fai Ob
SB Fai Fai Fai Fai Fai Fai Fai Fai De Si Fai Errare Errare Fai Ob
By Fai Fai Fai Fai Fai Fai Fai De Si Fai Errare Errare Fai Ob
Zitto Fai Fai Fai Fai Fai Fai De Si Fai Errare Errare Fai Ob
Stati Uniti Fai Fai Fai Fai Fai De Si Fai Errare Errare Fai Ob
In Fai Fai Fai Fai De Si Fai Errare Errare Fai Ob
interfaccia utente Fai Fai Fai De Si Fai Errare Errare Fai Ob
Ecco Fai Fai De Si Fai Errare Errare Fai Ob
UL Fai De Si Fai Errare Errare Fai Ob
De De Si Fai Errare Errare Fai Ob
Si Si Fai Errare Errare Fai Ob
Cosa fare Fai Errare Errare Fai Ob
Da Errare Errare Errare Errare
Ch Errare Errare Errare
San Fai Ob
Ob Ob

L'operatore di divisione integer è definito per Byte, SByteUShort, Short, ULongUIntegerInteger, , , e .Long Se il valore dell'operando destro è zero, viene generata un'eccezione System.DivideByZeroException . La divisione arrotonda il risultato verso zero e il valore assoluto del risultato è il numero intero più grande possibile minore del valore assoluto del quoziente dei due operandi. Il risultato è zero o positivo quando i due operandi hanno lo stesso segno e zero o negativo quando i due operandi hanno segni opposti. Se l'operando sinistro è il valore massimo negativo, , , o Longe l'operando destro è -1, si verifica un overflow; se il controllo dell'overflow integer è attivo, viene generata un'eccezioneSystem.OverflowException. IntegerShortSByte In caso contrario, l'overflow non viene segnalato e il risultato è invece il valore dell'operando sinistro.

Nota. Poiché i due operandi per i tipi senza segno saranno sempre zero o positivi, il risultato è sempre zero o positivo. Poiché il risultato dell'espressione sarà sempre minore o uguale al più grande dei due operandi, non è possibile che si verifichi un overflow. Di conseguenza, il controllo dell'overflow integer non viene eseguito per la divisione integer con due interi senza segno. Il risultato è il tipo come quello dell'operando sinistro.

Tipo di operazione:

Bo SB By Zitto Stati Uniti In interfaccia utente Ecco UL De Si Cosa fare Da Ch San Ob
Bo Zitto SB Zitto Zitto In In Ecco Ecco Ecco Ecco Ecco Ecco Errare Errare Ecco Ob
SB SB Zitto Zitto In In Ecco Ecco Ecco Ecco Ecco Ecco Errare Errare Ecco Ob
By Presso Zitto Stati Uniti In INTERFACCIA UTENTE Ecco UL Ecco Ecco Ecco Errare Errare Ecco Ob
Zitto Zitto In In Ecco Ecco Ecco Ecco Ecco Ecco Errare Errare Ecco Ob
Stati Uniti Stati Uniti In INTERFACCIA UTENTE Ecco UL Ecco Ecco Ecco Errare Errare Ecco Ob
In In Ecco Ecco Ecco Ecco Ecco Ecco Errare Errare Ecco Ob
interfaccia utente INTERFACCIA UTENTE Ecco UL Ecco Ecco Ecco Errare Errare Ecco Ob
Ecco Ecco Ecco Ecco Ecco Ecco Errare Errare Ecco Ob
UL UL Ecco Ecco Ecco Errare Errare Ecco Ob
De Ecco Ecco Ecco Errare Errare Ecco Ob
Si Ecco Ecco Errare Errare Ecco Ob
Cosa fare Ecco Errare Errare Ecco Ob
Da Errare Errare Errare Errare
Ch Errare Errare Errare
San Ecco Ob
Ob Ob

Operatore Mod

L'operatore Mod (modulo) calcola il resto della divisione tra due operandi.

ModuloOperatorExpression
    : Expression 'Mod' LineTerminator? Expression
    ;

L'operatore Mod è definito per i tipi seguenti:

  • Byte, SByte, UShort, ShortUInteger, , Integere ULongLong. Il risultato di x Mod y è il valore generato da x - (x \ y) * y. Se y è zero, viene generata un'eccezione System.DivideByZeroException . L'operatore modulo non causa mai un overflow.

  • Single e Double. Il resto viene calcolato in base alle regole dell'aritmetica IEEE 754.

  • Decimal. Se il valore dell'operando destro è zero, viene generata un'eccezione System.DivideByZeroException . Se il valore risultante è troppo grande per rappresentare nel formato decimale, viene generata un'eccezione System.OverflowException . Se il valore del risultato è troppo piccolo per rappresentare nel formato decimale, il risultato è zero.

Tipo di operazione:

Bo SB By Zitto Stati Uniti In interfaccia utente Ecco UL De Si Cosa fare Da Ch San Ob
Bo Zitto SB Zitto Zitto In In Ecco Ecco De De Si Fai Errare Errare Fai Ob
SB SB Zitto Zitto In In Ecco Ecco De De Si Fai Errare Errare Fai Ob
By Presso Zitto Stati Uniti In INTERFACCIA UTENTE Ecco UL De Si Fai Errare Errare Fai Ob
Zitto Zitto In In Ecco Ecco De De Si Fai Errare Errare Fai Ob
Stati Uniti Stati Uniti In INTERFACCIA UTENTE Ecco UL De Si Fai Errare Errare Fai Ob
In In Ecco Ecco De De Si Fai Errare Errare Fai Ob
interfaccia utente INTERFACCIA UTENTE Ecco UL De Si Fai Errare Errare Fai Ob
Ecco Ecco De De Si Fai Errare Errare Fai Ob
UL UL De Si Fai Errare Errare Fai Ob
De De Si Fai Errare Errare Fai Ob
Si Si Fai Errare Errare Fai Ob
Cosa fare Fai Errare Errare Fai Ob
Da Errare Errare Errare Errare
Ch Errare Errare Errare
San Fai Ob
Ob Ob

Operatore Exponentiation

L'operatore di exponentiation calcola il primo operando elevato alla potenza del secondo operando.

ExponentOperatorExpression
    : Expression '^' LineTerminator? Expression
    ;

L'operatore di exponentiation è definito per il tipo Double. Il valore viene calcolato in base alle regole dell'aritmetica IEEE 754.

Tipo di operazione:

Bo SB By Zitto Stati Uniti In interfaccia utente Ecco UL De Si Cosa fare Da Ch San Ob
Bo Fai Fai Fai Fai Fai Fai Fai Fai Fai Fai Fai Fai Errare Errare Fai Ob
SB Fai Fai Fai Fai Fai Fai Fai Fai Fai Fai Fai Errare Errare Fai Ob
By Fai Fai Fai Fai Fai Fai Fai Fai Fai Fai Errare Errare Fai Ob
Zitto Fai Fai Fai Fai Fai Fai Fai Fai Fai Errare Errare Fai Ob
Stati Uniti Fai Fai Fai Fai Fai Fai Fai Fai Errare Errare Fai Ob
In Fai Fai Fai Fai Fai Fai Fai Errare Errare Fai Ob
interfaccia utente Fai Fai Fai Fai Fai Fai Errare Errare Fai Ob
Ecco Fai Fai Fai Fai Fai Errare Errare Fai Ob
UL Fai Fai Fai Fai Errare Errare Fai Ob
De Fai Fai Fai Errare Errare Fai Ob
Si Fai Fai Errare Errare Fai Ob
Cosa fare Fai Errare Errare Fai Ob
Da Errare Errare Errare Errare
Ch Errare Errare Errare
San Fai Ob
Ob Ob

Operatori relazionali

Gli operatori relazionali confrontano i valori tra loro. Gli operatori di confronto sono =, <>, <>, <=, e >=.

RelationalOperatorExpression
    : Expression '=' LineTerminator? Expression
    | Expression '<' '>' LineTerminator? Expression
    | Expression '<' LineTerminator? Expression
    | Expression '>' LineTerminator? Expression
    | Expression '<' '=' LineTerminator? Expression
    | Expression '>' '=' LineTerminator? Expression
    ;

Tutti gli operatori relazionali generano un Boolean valore.

Gli operatori relazionali hanno il significato generale seguente:

  • L'operatore = verifica se i due operandi sono uguali.

  • L'operatore <> verifica se i due operandi non sono uguali.

  • L'operatore < verifica se il primo operando è minore del secondo operando.

  • L'operatore > verifica se il primo operando è maggiore del secondo operando.

  • L'operatore <= verifica se il primo operando è minore o uguale al secondo operando.

  • L'operatore >= verifica se il primo operando è maggiore o uguale al secondo operando.

Gli operatori relazionali sono definiti per i tipi seguenti:

  • Boolean. Gli operatori confrontano i valori di verità dei due operandi. True viene considerato minore di False, che corrisponde ai relativi valori numerici.

  • Byte, SByte, UShort, Short, UInteger, Integer, ULong e Long. Gli operatori confrontano i valori numerici dei due operandi integrali.

  • Single e Double. Gli operatori confrontano gli operandi in base alle regole dello standard IEEE 754.

  • Decimal. Gli operatori confrontano i valori numerici dei due operandi decimali.

  • Date. Gli operatori restituiscono il risultato del confronto dei due valori di data/ora.

  • Char. Gli operatori restituiscono il risultato del confronto dei due valori Unicode.

  • String. Gli operatori restituiscono il risultato del confronto dei due valori usando un confronto binario o un confronto di testo. Il confronto usato è determinato dall'ambiente di compilazione e dall'istruzione Option Compare . Un confronto binario determina se il valore Unicode numerico di ogni carattere in ogni stringa è lo stesso. Un confronto di testo esegue un confronto di testo Unicode basato sulle impostazioni cultura correnti in uso in .NET Framework. Quando si esegue un confronto di stringhe, un valore Null equivale al valore letterale ""stringa .

Tipo di operazione:

Bo SB By Zitto Stati Uniti In interfaccia utente Ecco UL De Si Cosa fare Da Ch San Ob
Bo Bo SB Zitto Zitto In In Ecco Ecco De De Si Fai Errare Errare Bo Ob
SB SB Zitto Zitto In In Ecco Ecco De De Si Fai Errare Errare Fai Ob
By Presso Zitto Stati Uniti In INTERFACCIA UTENTE Ecco UL De Si Fai Errare Errare Fai Ob
Zitto Zitto In In Ecco Ecco De De Si Fai Errare Errare Fai Ob
Stati Uniti Stati Uniti In INTERFACCIA UTENTE Ecco UL De Si Fai Errare Errare Fai Ob
In In Ecco Ecco De De Si Fai Errare Errare Fai Ob
interfaccia utente INTERFACCIA UTENTE Ecco UL De Si Fai Errare Errare Fai Ob
Ecco Ecco De De Si Fai Errare Errare Fai Ob
UL UL De Si Fai Errare Errare Fai Ob
De De Si Fai Errare Errare Fai Ob
Si Si Fai Errare Errare Fai Ob
Cosa fare Fai Errare Errare Fai Ob
Da Da Errare Da Ob
Ch Ch San Ob
San San Ob
Ob Ob

Operatore Like

L'operatore Like determina se una stringa corrisponde a un determinato criterio.

LikeOperatorExpression
    : Expression 'Like' LineTerminator? Expression
    ;

L'operatore Like è definito per il String tipo . Il primo operando è la stringa corrispondente e il secondo operando è il modello da associare. Il modello è costituito da caratteri Unicode. Le sequenze di caratteri seguenti hanno significati speciali:

  • Il carattere ? corrisponde a qualsiasi singolo carattere.

  • Il carattere * corrisponde a zero o più caratteri.

  • Il carattere # corrisponde a qualsiasi singola cifra (0-9).

  • Un elenco di caratteri racchiusi tra parentesi quadre ([ab...]) corrisponde a qualsiasi singolo carattere nell'elenco.

  • Un elenco di caratteri racchiusi tra parentesi quadre e preceduti da un punto esclamativo ([!ab...]) corrisponde a qualsiasi singolo carattere non incluso nell'elenco di caratteri.

  • Due caratteri in un elenco di caratteri separati da un trattino (-) specificano un intervallo di caratteri Unicode che iniziano con il primo carattere e terminano con il secondo carattere. Se il secondo carattere non è successivo nell'ordinamento del primo carattere, si verifica un'eccezione di run-time. Un trattino visualizzato all'inizio o alla fine di un elenco di caratteri specifica se stesso.

Per trovare la corrispondenza tra parentesi quadre speciali ([), punto interrogativo (?), segno di numero (#) e asterisco (*), le parentesi quadre devono racchiuderle. La parentesi quadra destra (]) non può essere usata all'interno di un gruppo per trovare la corrispondenza, ma può essere usata all'esterno di un gruppo come singolo carattere. La sequenza [] di caratteri è considerata il valore letterale ""stringa .

Si noti che i confronti di caratteri e l'ordinamento per gli elenchi di caratteri dipendono dal tipo di confronti in uso. Se vengono usati confronti binari, i confronti di caratteri e l'ordinamento si basano sui valori Unicode numerici. Se vengono usati confronti di testo, i confronti dei caratteri e l'ordinamento si basano sulle impostazioni locali correnti usate in .NET Framework.

In alcune lingue, i caratteri speciali nell'alfabeto rappresentano due caratteri separati e viceversa. Ad esempio, diverse lingue usano il carattere æ per rappresentare i caratteri a e e quando vengono visualizzati insieme, mentre i caratteri ^ e O possono essere usati per rappresentare il carattere Ô. Quando si usano confronti di testo, l'operatore Like riconosce tali equivalenze culturali. In tal caso, un'occorrenza del singolo carattere speciale in un criterio o in una stringa corrisponde alla sequenza di due caratteri equivalente nell'altra stringa. Analogamente, un singolo carattere speciale nel criterio racchiuso tra parentesi quadre (da solo, in un elenco o in un intervallo) corrisponde alla sequenza di due caratteri equivalente nella stringa e viceversa.

In un'espressione Like in cui entrambi gli operandi sono Nothing o un operando ha una conversione intrinseca in String e l'altro operando è Nothing, Nothing viene considerato come se fosse il valore letterale ""stringa vuoto .

Tipo di operazione:

Bo SB By Zitto Stati Uniti In interfaccia utente Ecco UL De Si Cosa fare Da Ch San Ob
Bo San San San San San San San San San San San San San San San Ob
SB San San San San San San San San San San San San San San Ob
By San San San San San San San San San San San San San Ob
Zitto San San San San San San San San San San San San Ob
Stati Uniti San San San San San San San San San San San Ob
In San San San San San San San San San San Ob
interfaccia utente San San San San San San San San San Ob
Ecco San San San San San San San San Ob
UL San San San San San San San Ob
De San San San San San San Ob
Si San San San San San Ob
Cosa fare San San San San Ob
Da San San San Ob
Ch San San Ob
San San Ob
Ob Ob

Operatore concatenazione

ConcatenationOperatorExpression
    : Expression '&' LineTerminator? Expression
    ;

L'operatore di concatenazione viene definito per tutti i tipi intrinseci, incluse le versioni nullable dei tipi di valore intrinseco. Viene anche definita per la concatenazione tra i tipi indicati in precedenza e System.DBNull, che viene considerata come una Nothing stringa. L'operatore di concatenazione converte tutti i relativi operandi Stringin . Nell'espressione tutte le conversioni in String vengono considerate di tipo widening, indipendentemente dal fatto che vengano utilizzate semantiche rigide. Un System.DBNull valore viene convertito nel valore letterale Nothing tipizzato come String. Tipo di valore nullable il cui valore viene Nothing convertito anche nel valore letterale Nothing tipizzato come String, anziché generare un errore di run-time.

Un'operazione di concatenazione comporta una stringa che rappresenta la concatenazione dei due operandi in ordine da sinistra a destra. Il valore Nothing viene considerato come se fosse il valore letterale ""stringa vuoto.

Tipo di operazione:

Bo SB By Zitto Stati Uniti In interfaccia utente Ecco UL De Si Cosa fare Da Ch San Ob
Bo San San San San San San San San San San San San San San San Ob
SB San San San San San San San San San San San San San San Ob
By San San San San San San San San San San San San San Ob
Zitto San San San San San San San San San San San San Ob
Stati Uniti San San San San San San San San San San San Ob
In San San San San San San San San San San Ob
interfaccia utente San San San San San San San San San Ob
Ecco San San San San San San San San Ob
UL San San San San San San San Ob
De San San San San San San Ob
Si San San San San San Ob
Cosa fare San San San San Ob
Da San San San Ob
Ch San San Ob
San San Ob
Ob Ob

Operatori logici

Gli Andoperatori , OrNot, e Xor vengono chiamati operatori logici.

LogicalOperatorExpression
    : 'Not' Expression
    | Expression 'And' LineTerminator? Expression
    | Expression 'Or' LineTerminator? Expression
    | Expression 'Xor' LineTerminator? Expression
    ;

Gli operatori logici vengono valutati come segue:

  • Per il Boolean tipo:

    • Un'operazione logica And viene eseguita sui due operandi.

    • Viene eseguita un'operazione logica Not sul relativo operando.

    • Un'operazione logica Or viene eseguita sui due operandi.

    • Un'operazione logica esclusiva-Or viene eseguita sui due operandi.

  • Per Byte, SByteUShort, Short, ULongUIntegerInteger, Long, e tutti i tipi enumerati, l'operazione specificata viene eseguita su ogni bit della rappresentazione binaria dei due operandi:

    • And: il bit del risultato è 1 se entrambi i bit sono 1; in caso contrario, il bit del risultato è 0.

    • Not: il bit di risultato è 1 se il bit è 0; in caso contrario, il bit del risultato è 1.

    • Or: il bit del risultato è 1 se uno dei due bit è 1; in caso contrario, il bit del risultato è 0.

    • Xor: il bit di risultato è 1 se uno dei due bit è 1 ma non entrambi i bit; in caso contrario, il bit del risultato è 0 (ovvero 1 Xor 0 = 1, 1 1 Xor = 0).

  • Quando gli operatori And logici e Or vengono lifted per il tipo Boolean?, vengono estesi per includere la logica booleana a tre valori, ad esempio:

    • And restituisce true se entrambi gli operandi sono true; false se uno degli operandi è false; Nothing altrimenti.

    • Or restituisce true se uno degli operandi è true; false è che entrambi gli operandi sono false; Nothing altrimenti.

Per esempio:

Module Test
    Sub Main()
        Dim x?, y? As Boolean

        x = Nothing
        y = True 

        If x Or y Then
            ' Will execute
        End If
    End Sub
End Module

Nota. Idealmente, gli operatori And logici e Or verrebbero rimossi usando la logica a tre valori per qualsiasi tipo che può essere usato in un'espressione booleana (ad esempio un tipo che implementa IsTrue e IsFalse), allo stesso modo che AndAlso e OrElse corto circuito in qualsiasi tipo che può essere usato in un'espressione booleana. Sfortunatamente, il lifting a tre valori viene applicato solo a Boolean?, quindi i tipi definiti dall'utente che desiderano una logica a tre valori devono farlo manualmente definendo And e Or operatori per la versione nullable.

Non si possono verificare overflow da queste operazioni. Gli operatori di tipo enumerati eseguono l'operazione bit per bit sul tipo sottostante del tipo enumerato, ma il valore restituito è il tipo enumerato.

Tipo di operazione non:

Bo SB By Zitto Stati Uniti In interfaccia utente Ecco UL De Si Cosa fare Da Ch San Ob
Bo SB Presso Zitto Stati Uniti In INTERFACCIA UTENTE Ecco UL Ecco Ecco Ecco Errare Errare Ecco Ob

Tipo di operazione And, Or, Xor:

Bo SB By Zitto Stati Uniti In interfaccia utente Ecco UL De Si Cosa fare Da Ch San Ob
Bo Bo SB Zitto Zitto In In Ecco Ecco Ecco Ecco Ecco Ecco Errare Errare Bo Ob
SB SB Zitto Zitto In In Ecco Ecco Ecco Ecco Ecco Ecco Errare Errare Ecco Ob
By Presso Zitto Stati Uniti In INTERFACCIA UTENTE Ecco UL Ecco Ecco Ecco Errare Errare Ecco Ob
Zitto Zitto In In Ecco Ecco Ecco Ecco Ecco Ecco Errare Errare Ecco Ob
Stati Uniti Stati Uniti In INTERFACCIA UTENTE Ecco UL Ecco Ecco Ecco Errare Errare Ecco Ob
In In Ecco Ecco Ecco Ecco Ecco Ecco Errare Errare Ecco Ob
interfaccia utente INTERFACCIA UTENTE Ecco UL Ecco Ecco Ecco Errare Errare Ecco Ob
Ecco Ecco Ecco Ecco Ecco Ecco Errare Errare Ecco Ob
UL UL Ecco Ecco Ecco Errare Errare Ecco Ob
De Ecco Ecco Ecco Errare Errare Ecco Ob
Si Ecco Ecco Errare Errare Ecco Ob
Cosa fare Ecco Errare Errare Ecco Ob
Da Errare Errare Errare Errare
Ch Errare Errare Errare
San Ecco Ob
Ob Ob

Operatori logici a corto circuito

Gli AndAlso operatori e OrElse sono le versioni a corto circuito degli And operatori logici e Or .

ShortCircuitLogicalOperatorExpression
    : Expression 'AndAlso' LineTerminator? Expression
    | Expression 'OrElse' LineTerminator? Expression
    ;

A causa del comportamento di corto circuito, il secondo operando non viene valutato in fase di esecuzione se il risultato dell'operatore è noto dopo la valutazione del primo operando.

Gli operatori logici a corto circuito vengono valutati come segue:

  • Se il primo operando in un'operazione AndAlso restituisce o restituisce False True dal relativo IsFalse operatore, l'espressione restituisce il primo operando. In caso contrario, il secondo operando viene valutato e viene eseguita un'operazione logica And sui due risultati.

  • Se il primo operando in un'operazione OrElse restituisce o restituisce True True dal relativo IsTrue operatore, l'espressione restituisce il primo operando. In caso contrario, il secondo operando viene valutato e viene eseguita un'operazione logica Or sui due risultati.

Gli AndAlso operatori e OrElse sono definiti per il tipo Booleano per qualsiasi tipo T che esegue l'overload degli operatori seguenti:

Public Shared Operator IsTrue(op As T) As Boolean
Public Shared Operator IsFalse(op As T) As Boolean

oltre a eseguire l'overload dell'operatore o Or corrispondenteAnd:

Public Shared Operator And(op1 As T, op2 As T) As T
Public Shared Operator Or(op1 As T, op2 As T) As T

Quando si valutano gli AndAlso operatori o OrElse , il primo operando viene valutato una sola volta e il secondo operando non viene valutato o valutato esattamente una volta. Si consideri ad esempio il codice seguente:

Module Test
    Function TrueValue() As Boolean
        Console.Write(" True")
        Return True
    End Function

    Function FalseValue() As Boolean
        Console.Write(" False")
        Return False
    End Function

    Sub Main()
        Console.Write("And:")
        If FalseValue() And TrueValue() Then
        End If
        Console.WriteLine()

        Console.Write("Or:")
        If TrueValue() Or FalseValue() Then
        End If
        Console.WriteLine()

        Console.Write("AndAlso:")
        If FalseValue() AndAlso TrueValue() Then
        End If
        Console.WriteLine()

        Console.Write("OrElse:")
        If TrueValue() OrElse FalseValue() Then
        End If
        Console.WriteLine()
    End Sub
End Module

Stampa il risultato seguente:

And: False True
Or: True False
AndAlso: False
OrElse: True

Nella forma lifted degli AndAlso operatori e OrElse , se il primo operando è null Boolean?, il secondo operando viene valutato, ma il risultato è sempre null Boolean?.

Tipo di operazione:

Bo SB By Zitto Stati Uniti In interfaccia utente Ecco UL De Si Cosa fare Da Ch San Ob
Bo Bo Bo Bo Bo Bo Bo Bo Bo Bo Bo Bo Bo Errare Errare Bo Ob
SB Bo Bo Bo Bo Bo Bo Bo Bo Bo Bo Bo Errare Errare Bo Ob
By Bo Bo Bo Bo Bo Bo Bo Bo Bo Bo Errare Errare Bo Ob
Zitto Bo Bo Bo Bo Bo Bo Bo Bo Bo Errare Errare Bo Ob
Stati Uniti Bo Bo Bo Bo Bo Bo Bo Bo Errare Errare Bo Ob
In Bo Bo Bo Bo Bo Bo Bo Errare Errare Bo Ob
interfaccia utente Bo Bo Bo Bo Bo Bo Errare Errare Bo Ob
Ecco Bo Bo Bo Bo Bo Errare Errare Bo Ob
UL Bo Bo Bo Bo Errare Errare Bo Ob
De Bo Bo Bo Errare Errare Bo Ob
Si Bo Bo Errare Errare Bo Ob
Cosa fare Bo Errare Errare Bo Ob
Da Errare Errare Errare Errare
Ch Errare Errare Errare
San Bo Ob
Ob Ob

Operatori shift

Gli operatori << binari ed >> eseguono operazioni di spostamento dei bit.

ShiftOperatorExpression
    : Expression '<' '<' LineTerminator? Expression
    | Expression '>' '>' LineTerminator? Expression
    ;

Gli operatori sono definiti per i Bytetipi , UShortSByte, Short, UInteger, Integer, ULong e Long . A differenza degli altri operatori binari, il tipo di risultato di un'operazione di spostamento viene determinato come se l'operatore fosse un operatore unario con solo l'operando sinistro. Il tipo dell'operando destro deve essere convertibile Integer in modo implicito in e non viene utilizzato per determinare il tipo di risultato dell'operazione.

L'operatore << fa in modo che i bit nel primo operando vengano spostati a sinistra del numero di posizioni specificate dall'importo dello spostamento. I bit di ordine elevato all'esterno dell'intervallo del tipo di risultato vengono eliminati e le posizioni dei bit liberate in ordine basso sono piene di zero.

L'operatore >> fa in modo che i bit nel primo operando vengano spostati a destra del numero di posizioni specificato dall'importo dello spostamento. I bit di ordine basso vengono eliminati e le posizioni dei bit liberate in ordine elevato vengono impostate su zero se l'operando sinistro è positivo o su uno se negativo. Se l'operando sinistro è di tipo Byte, UShort, UIntegero ULong i bit di ordine elevato vacanti sono pieni di zero.

Gli operatori di spostamento spostano i bit della rappresentazione sottostante del primo operando in base alla quantità del secondo operando. Se il valore del secondo operando è maggiore del numero di bit nel primo operando o è negativo, la quantità di spostamento viene calcolata come dove RightOperand And SizeMaskSizeMask è:

Tipo LeftOperand Maschera di dimensioni
Byte, SByte 7 (&H7)
UShort, Short 15 (&HF)
UInteger, Integer 31 (&H1F)
ULong, Long 63 (&H3F)

Se l'importo dello spostamento è zero, il risultato dell'operazione è identico al valore del primo operando. Non si possono verificare overflow da queste operazioni.

Tipo di operazione:

Bo SB By Zitto Stati Uniti In interfaccia utente Ecco UL De Si Cosa fare Da Ch San Ob
Zitto SB Presso Zitto Stati Uniti In INTERFACCIA UTENTE Ecco UL Ecco Ecco Ecco Errare Errare Ecco Ob

Espressioni booleane

Un'espressione booleana è un'espressione che può essere testata per verificare se è true o se è false.

BooleanExpression
    : Expression
    ;

Un tipo T può essere usato in un'espressione booleana se, in ordine di preferenza:

  • T è Boolean o Boolean?

  • T ha una conversione più ampia in Boolean

  • T ha una conversione più ampia in Boolean?

  • T definisce due pseudo operatori e IsTrueIsFalse.

  • T ha una conversione di tipo narrowing in Boolean? che non comporta una conversione da Boolean a Boolean?.

  • T ha una conversione di tipo narrowing in Boolean.

Nota. È interessante notare che se Option Strict è disattivato, un'espressione con una conversione Boolean verso un tipo di dati più piccolo verrà accettata senza un errore in fase di compilazione, ma il linguaggio preferisce comunque un IsTrue operatore, se presente. Ciò è dovuto al fatto che Option Strict cambia solo ciò che è e non viene accettato dal linguaggio e non cambia mai il significato effettivo di un'espressione. Pertanto, IsTrue deve essere sempre preferito rispetto a una conversione verso un tipo di dati più ristretto, indipendentemente da Option Strict.

Ad esempio, la classe seguente non definisce una conversione più ampia in Boolean. Di conseguenza, il relativo uso nell'istruzione If provoca una chiamata all'operatore IsTrue .

Class MyBool
    Public Shared Widening Operator CType(b As Boolean) As MyBool
        ...
    End Operator

    Public Shared Narrowing Operator CType(b As MyBool) As Boolean
        ...
    End Operator

    Public Shared Operator IsTrue(b As MyBool) As Boolean
        ...
    End Operator

    Public Shared Operator IsFalse(b As MyBool) As Boolean
        ...
    End Operator
End Class

Module Test
    Sub Main()
        Dim b As New MyBool

        If b Then Console.WriteLine("True")
    End Sub
End Module

Se un'espressione booleana viene digitata come o convertita in Boolean o Boolean?, è true se il valore è True e false in caso contrario.

In caso contrario, un'espressione booleana chiama l'operatore IsTrue e restituisce True se l'operatore ha restituito True; in caso contrario, è false (ma non chiama mai l'operatore IsFalse ).

Nell'esempio seguente è Integer presente una conversione di tipo narrowing in Boolean, quindi un valore Null Integer? ha una conversione di tipo narrowing in ( Boolean? che restituisce un valore Null Boolean) e a Boolean (che genera un'eccezione). La conversione di tipo narrowing in Boolean? è preferibile e pertanto il valore di "i" come espressione booleana è pertanto False.

Dim i As Integer? = Nothing
If i Then Console.WriteLine()

Espressioni lambda

Un'espressione lambda definisce un metodo anonimo denominato metodo lambda. I metodi lambda semplificano il passaggio di metodi "in linea" ad altri metodi che accettano tipi delegati.

LambdaExpression
    : SingleLineLambda
    | MultiLineLambda
    ;

SingleLineLambda
    : LambdaModifier* 'Function' ( OpenParenthesis ParameterList? CloseParenthesis )? Expression
    | 'Sub' ( OpenParenthesis ParameterList? CloseParenthesis )? Statement
    ;

MultiLineLambda
    : MultiLineFunctionLambda
    | MultiLineSubLambda
    ;

MultiLineFunctionLambda
    : LambdaModifier? 'Function' ( OpenParenthesis ParameterList? CloseParenthesis )? ( 'As' TypeName )? LineTerminator
      Block
      'End' 'Function'
    ;

MultiLineSubLambda
    : LambdaModifier? 'Sub' ( OpenParenthesis ParameterList? CloseParenthesis )? LineTerminator
      Block
      'End' 'Sub'
    ;

LambdaModifier
    : 'Async' | 'Iterator'
    ;

Esempio:

Module Test
    Delegate Function IntFunc(x As Integer) As Integer

    Sub Apply(a() As Integer, func As IntFunc)
        For index As Integer = 0 To a.Length - 1
            a(index) = func(a(index))
        Next index
    End Sub

    Sub Main()
        Dim a() As Integer = { 1, 2, 3, 4 }

        Apply(a, Function(x As Integer) x * 2)

        For Each value In a
            Console.Write(value & " ")
        Next value
    End Sub
End Module

verrà stampato:

2 4 6 8

Un'espressione lambda inizia con i modificatori facoltativi Async o Iterator, seguiti dalla parola chiave Function o Sub da un elenco di parametri. I parametri in un'espressione lambda non possono essere dichiarati Optional o ParamArray non possono avere attributi. A differenza dei metodi regolari, l'omissione di un tipo di parametro per un metodo lambda non deduce Objectautomaticamente . Al contrario, quando un metodo lambda viene riclassificato, i tipi di parametro e ByRef i modificatori omessi vengono dedotti dal tipo di destinazione. Nell'esempio precedente, l'espressione lambda potrebbe essere stata scritta come Function(x) x * 2e avrebbe dedotto il tipo di x che deve essere Integer quando è stato usato il metodo lambda per creare un'istanza del IntFunc tipo delegato. A differenza dell'inferenza delle variabili locali, se un parametro del metodo lambda omette un tipo ma ha un modificatore di nomi di matrice o nullable, si verifica un errore in fase di compilazione.

Un'espressione lambda regolare è un'espressione lambda regolare senza Async modificatori né Iterator con modificatori.

Un'espressione lambda dell'iteratore è una con il Iterator modificatore e nessun Async modificatore. Deve essere una funzione. Quando viene riclassificata in un valore, può essere riclassificata solo in un valore di tipo delegato il cui tipo restituito è IEnumerator, o IEnumerableo o IEnumerable(Of T)IEnumerator(Of T) per alcuni Te che non dispone di parametri ByRef.

Un'espressione lambda asincrona è una con il Async modificatore e nessun Iterator modificatore. Un'espressione lambda secondaria asincrona può essere riclassificata solo in un valore di tipo delegato secondario senza parametri ByRef. Un'espressione lambda asincrona può essere riclassificata solo in un valore di tipo delegato di funzione il cui tipo restituito è Task o Task(Of T) per alcuni Te che non ha parametri ByRef.

Le espressioni lambda possono essere a riga singola o su più righe. Function Le espressioni lambda a riga singola contengono una singola espressione che rappresenta il valore restituito dal metodo lambda. Sub Le espressioni lambda a riga singola contengono una singola istruzione senza la chiusura StatementTerminatordi . Per esempio:

Module Test
    Sub Do(a() As Integer, action As Action(Of Integer))
        For index As Integer = 0 To a.Length - 1
            action(a(index))
        Next index
    End Sub

    Sub Main()
        Dim a() As Integer = { 1, 2, 3, 4 }

        Do(a, Sub(x As Integer) Console.WriteLine(x))
    End Sub
End Module

I costrutti lambda a riga singola si associano meno strettamente rispetto a tutte le altre espressioni e istruzioni. Ad esempio, "Function() x + 5" equivale a "Function() (x+5)" anziché a "(Function() x) + 5". Per evitare ambiguità, un'espressione lambda a riga Sub singola potrebbe non contenere un'istruzione Dim o un'istruzione di dichiarazione di etichetta. Inoltre, a meno che non sia racchiuso tra parentesi, un'espressione lambda a riga singola Sub potrebbe non essere immediatamente seguita da due punti ":", da un operatore di accesso ai membri ".", da un operatore di accesso ai membri del dizionario "!" o da una parentesi aperta "(". Non può contenere alcuna istruzione block (With, SyncLock, If...EndIf, WhileFor, Do, , Using) né OnErrorResume.

Nota. Nell'espressione Function(i) x=ilambda il corpo viene interpretato come un'espressione (che verifica se x e i sono uguali). Nell'espressione Sub(i) x=ilambda, tuttavia, il corpo viene interpretato come un'istruzione (che assegna i a x).

Un'espressione lambda su più righe contiene un blocco di istruzioni e deve terminare con un'istruzione appropriata End ,ad esempio End Function o End Sub. Come per i metodi regolari, le istruzioni e SubEnd l'istruzione Function lambda di un metodo lambda su più righe devono trovarsi nelle proprie righe. Per esempio:

' Error: Function statement must be on its own line!
Dim x = Sub(x As Integer) : Console.WriteLine(x) : End Sub

' OK
Dim y = Sub(x As Integer)
               Console.WriteLine(x)
          End Sub

Function Le espressioni lambda a più righe possono dichiarare un tipo restituito, ma non possono inserire attributi. Se un'espressione lambda su più Function righe non dichiara un tipo restituito, ma il tipo restituito può essere dedotto dal contesto in cui viene usata l'espressione lambda , viene usato tale tipo restituito. In caso contrario, il tipo restituito della funzione viene calcolato come segue:

  • In un'espressione lambda regolare, il tipo restituito è il tipo dominante delle espressioni in tutte le Return istruzioni nel blocco di istruzioni.

  • In un'espressione lambda asincrona, il tipo restituito è Task(Of T) dove T è il tipo dominante delle espressioni in tutte le Return istruzioni nel blocco di istruzioni.

  • In un'espressione lambda dell'iteratore, il tipo restituito è IEnumerable(Of T) dove T è il tipo dominante delle espressioni in tutte le Yield istruzioni nel blocco di istruzioni.

Per esempio:

Function f(min As Integer, max As Integer) As IEnumerable(Of Integer)
    If min > max Then Throw New ArgumentException()
    Dim x = Iterator Function()
                  For i = min To max
                    Yield i
                Next
               End Function

    ' infers x to be a delegate with return type IEnumerable(Of Integer)
    Return x()
End Function

In tutti i casi, se non Return sono presenti istruzioni (rispettivamente Yield) o se non è presente alcun tipo dominante tra di essi e viene usata una semantica rigorosa, si verifica un errore in fase di compilazione; in caso contrario, il tipo dominante è implicitamente Object.

Si noti che il tipo restituito viene calcolato da tutte le Return istruzioni, anche se non sono raggiungibili. Per esempio:

' Return type is Double
Dim x = Function()
              Return 10
               Return 10.50
          End Function

Non esiste alcuna variabile restituita implicita, perché non esiste alcun nome per la variabile.

I blocchi di istruzione all'interno di espressioni lambda su più righe presentano le restrizioni seguenti:

  • On Error Le istruzioni e Resume non sono consentite, anche se Try le istruzioni sono consentite.

  • Le variabili locali statiche non possono essere dichiarate in espressioni lambda su più righe.

  • Non è possibile creare un ramo all'interno o all'esterno del blocco di istruzioni di un'espressione lambda su più righe, anche se le normali regole di diramazione si applicano al suo interno. Per esempio:

    Label1:
    Dim x = Sub()
                   ' Error: Cannot branch out
                   GoTo Label1
    
                   ' OK: Wholly within the lamba.
                   GoTo Label2:
              Label2:
              End Sub
    
    ' Error: Cannot branch in
    GoTo Label2
    

Un'espressione lambda equivale approssimativamente a un metodo anonimo dichiarato nel tipo contenitore. L'esempio iniziale è approssimativamente equivalente a:

Module Test
    Delegate Function IntFunc(x As Integer) As Integer

    Sub Apply(a() As Integer, func As IntFunc)
        For index As Integer = 0 To a.Length - 1
            a(index) = func(a(index))
        Next index
    End Sub

    Function $Lambda1(x As Integer) As Integer
        Return x * 2
    End Function

    Sub Main()
        Dim a() As Integer = { 1, 2, 3, 4 }

        Apply(a, AddressOf $Lambda1)

        For Each value In a
            Console.Write(value & " ")
        Next value
    End Sub
End Module

Chiusure

Le espressioni lambda hanno accesso a tutte le variabili nell'ambito, incluse le variabili locali o i parametri definiti nel metodo contenitore e nelle espressioni lambda. Quando un'espressione lambda fa riferimento a una variabile o a un parametro locale, l'espressione lambda acquisisce la variabile a cui viene fatto riferimento in una chiusura. Una chiusura è un oggetto che si trova nell'heap anziché nello stack e quando viene acquisita una variabile, tutti i riferimenti alla variabile vengono reindirizzati alla chiusura. Ciò consente alle espressioni lambda di continuare a fare riferimento a variabili e parametri locali anche dopo il completamento del metodo contenitore. Per esempio:

Module Test
    Delegate Function D() As Integer

    Function M() As D
        Dim x As Integer = 10
        Return Function() x
    End Function

    Sub Main()
        Dim y As D = M()

        ' Prints 10
        Console.WriteLine(y())
    End Sub
End Module

è approssimativamente equivalente a:

Module Test
    Delegate Function D() As Integer

    Class $Closure1
        Public x As Integer

        Function $Lambda1() As Integer
            Return x
        End Function
    End Class

    Function M() As D
        Dim c As New $Closure1()
        c.x = 10
        Return AddressOf c.$Lambda1
    End Function

    Sub Main()
        Dim y As D = M()

        ' Prints 10
        Console.WriteLine(y())
    End Sub
End Module

Una chiusura acquisisce una nuova copia di una variabile locale ogni volta che immette il blocco in cui viene dichiarata la variabile locale, ma la nuova copia viene inizializzata con il valore della copia precedente, se presente. Per esempio:

Module Test
    Delegate Function D() As Integer

    Function M() As D()
        Dim a(9) As D

        For i As Integer = 0 To 9
            Dim x
            a(i) = Function() x
            x += 1
        Next i

        Return a
    End Function

    Sub Main()
        Dim y() As D = M()

        For i As Integer = 0 To 9
            Console.Write(y(i)() & " ")
        Next i
    End Sub
End Module

Stampe

1 2 3 4 5 6 7 8 9 10

Invece di

9 9 9 9 9 9 9 9 9 9

Poiché le chiusura devono essere inizializzate quando si immette un blocco, non è consentito in GoTo un blocco con una chiusura dall'esterno di tale blocco, anche se è consentito in Resume un blocco con chiusura. Per esempio:

Module Test
    Sub Main()
        Dim a = 10

        If a = 10 Then
L1:
            Dim x = Function() a

            ' Valid, source is within block
            GoTo L2
L2:
        End If

        ' ERROR: target is inside block with closure
        GoTo L1
    End Sub
End Module

Poiché non possono essere acquisiti in una chiusura, non è possibile visualizzare quanto segue all'interno di un'espressione lambda:

  • Parametri di riferimento.

  • Espressioni di istanza (Me, MyClass, MyBase), se il tipo di Me non è una classe .

Membri di un'espressione di creazione di tipi anonima, se l'espressione lambda fa parte dell'espressione. Per esempio:

' Error: Lambda cannot refer to anonymous type field
Dim x = New With { .a = 12, .b = Function() .a }

ReadOnly variabili di istanza nei costruttori di istanze o ReadOnly variabili condivise nei costruttori condivisi in cui le variabili vengono usate in un contesto non valore. Per esempio:

Class C1
    ReadOnly F1 As Integer

    Sub New()
        ' Valid, doesn't modify F1
        Dim x = Function() F1

        ' Error, tries to modify F1
        Dim f = Function() ModifyValue(F1)
    End Sub

    Sub ModifyValue(ByRef x As Integer)
    End Sub
End Class

Espressioni di query

Un'espressione di query è un'espressione che applica una serie di operatori di query agli elementi di una raccolta queryable. Ad esempio, l'espressione seguente accetta una raccolta di Customer oggetti e restituisce i nomi di tutti i clienti nello stato di Washington:

Dim names = _
    From cust In Customers _
    Where cust.State = "WA" _
    Select cust.Name

Un'espressione di query deve iniziare con un From operatore o Aggregate e può terminare con qualsiasi operatore di query. Il risultato di un'espressione di query viene classificato come valore; Il tipo di risultato dell'espressione dipende dal tipo di risultato dell'ultimo operatore di query nell'espressione.

QueryExpression
    : FromOrAggregateQueryOperator QueryOperator*
    ;

FromOrAggregateQueryOperator
    : FromQueryOperator
    | AggregateQueryOperator
    ;

QueryOperator
    : FromQueryOperator
    | AggregateQueryOperator
    | SelectQueryOperator
    | DistinctQueryOperator
    | WhereQueryOperator
    | OrderByQueryOperator
    | PartitionQueryOperator
    | LetQueryOperator
    | GroupByQueryOperator
    | JoinOrGroupJoinQueryOperator
    ;

JoinOrGroupJoinQueryOperator
    : JoinQueryOperator
    | GroupJoinQueryOperator
    ;

Variabili di intervallo

Alcuni operatori di query introducono un tipo speciale di variabile denominato variabile di intervallo. Le variabili di intervallo non sono variabili reali; Rappresentano invece i singoli valori durante la valutazione della query sulle raccolte di input.

CollectionRangeVariableDeclarationList
    : CollectionRangeVariableDeclaration ( Comma CollectionRangeVariableDeclaration )*
    ;

CollectionRangeVariableDeclaration
    : Identifier ( 'As' TypeName )? 'In' LineTerminator? Expression
    ;

ExpressionRangeVariableDeclarationList
    : ExpressionRangeVariableDeclaration ( Comma ExpressionRangeVariableDeclaration )*
    ;

ExpressionRangeVariableDeclaration
    : Identifier ( 'As' TypeName )? Equals Expression
    ;

Le variabili di intervallo hanno come ambito l'operatore di query di introduzione alla fine di un'espressione di query o a un operatore di query, ad esempio Select che li nasconde. Ad esempio, nella query seguente

Dim waCusts = _
    From cust As Customer In Customers _
    Where cust.State = "WA"

L'operatore From di query introduce una variabile cust di intervallo tipizzata come Customer che rappresenta ogni cliente nella Customers raccolta. L'operatore di query seguente Where fa quindi riferimento alla variabile cust di intervallo nell'espressione di filtro per determinare se filtrare un singolo cliente dalla raccolta risultante.

Esistono due tipi di variabili di intervallo: variabili dell'intervallo di raccolta e variabili dell'intervallo di espressioni. Le variabili dell'intervallo di raccolta accettano i relativi valori dagli elementi delle raccolte su cui viene eseguita una query. L'espressione di raccolta in una dichiarazione di variabile dell'intervallo di raccolte deve essere classificata come valore il cui tipo è queryable. Se il tipo di una variabile dell'intervallo di raccolte viene omesso, viene dedotto come tipo di elemento dell'espressione di raccolta o Object se l'espressione di raccolta non dispone di un tipo di elemento ,ad esempio definisce solo un Cast metodo. Se l'espressione di raccolta non è queryabile,ad esempio il tipo di elemento della raccolta non può essere dedotto, viene restituito un errore in fase di compilazione.

Una variabile di intervallo di espressioni è una variabile di intervallo il cui valore viene calcolato da un'espressione anziché da una raccolta. Nell'esempio seguente l'operatore Select query introduce una variabile dell'intervallo di espressioni denominata cityState calcolata da due campi:

Dim cityStates = _
    From cust As Customer In Customers _
    Select cityState = cust.City & "," & cust.State _
    Where cityState.Length() < 10

Una variabile dell'intervallo di espressioni non è necessaria per fare riferimento a un'altra variabile di intervallo, anche se tale variabile può essere di tipo dubbio. L'espressione assegnata a una variabile di intervallo di espressioni deve essere classificata come valore e deve essere convertibile in modo implicito nel tipo della variabile di intervallo, se specificato.

Solo in un operatore Let può essere specificata una variabile dell'intervallo di espressioni. In altri operatori o se il tipo non è specificato, viene usata l'inferenza del tipo di variabile locale per determinare il tipo della variabile di intervallo.

Una variabile di intervallo deve seguire le regole per dichiarare le variabili locali in relazione all'ombreggiatura. Pertanto, una variabile di intervallo non può nascondere il nome di una variabile locale o di un parametro nel metodo di inclusione o in un'altra variabile di intervallo( a meno che l'operatore di query non nasconda in modo specifico tutte le variabili di intervallo correnti nell'ambito).

Tipi su cui è possibile eseguire query

Le espressioni di query vengono implementate convertendo l'espressione in chiamate a metodi noti in un tipo di raccolta. Questi metodi ben definiti definiscono il tipo di elemento della raccolta queryable, nonché i tipi di risultati degli operatori di query eseguiti nella raccolta. Ogni operatore di query specifica il metodo o i metodi in cui l'operatore di query viene in genere convertito, anche se la traduzione specifica dipende dall'implementazione. I metodi vengono specificati nella specifica usando un formato generale simile al seguente:

Function Select(selector As Func(Of T, R)) As CR

I metodi seguenti si applicano ai metodi :

  • Il metodo deve essere un'istanza o un membro di estensione del tipo di raccolta e deve essere accessibile.

  • Il metodo può essere generico, purché sia possibile dedurre tutti gli argomenti di tipo.

  • Il metodo può essere sottoposto a overload, nel qual caso viene usata la risoluzione dell'overload per determinare esattamente il metodo da usare.

  • È possibile usare un altro tipo delegato al posto del tipo delegato Func , purché abbia la stessa firma, incluso il tipo restituito, come tipo corrispondente Func .

  • Il tipo System.Linq.Expressions.Expression(Of D) può essere utilizzato al posto del tipo delegato Func , purché D sia un tipo delegato con la stessa firma, incluso il tipo restituito, come tipo corrispondente Func .

  • Il tipo T rappresenta il tipo di elemento della raccolta di input. Tutti i metodi definiti da un tipo di raccolta devono avere lo stesso tipo di elemento di input per il tipo di raccolta su cui eseguire query.

  • Il tipo S rappresenta il tipo di elemento della seconda raccolta di input nel caso di operatori di query che eseguono join.

  • Il tipo K rappresenta un tipo di chiave nel caso di operatori di query con un set di variabili di intervallo che fungono da chiavi.

  • Il tipo N rappresenta un tipo utilizzato come tipo numerico (anche se potrebbe essere ancora un tipo definito dall'utente e non un tipo numerico intrinseco).

  • Il tipo rappresenta un tipo B che può essere utilizzato in un'espressione booleana.

  • Il tipo R rappresenta il tipo di elemento della raccolta dei risultati, se l'operatore di query produce una raccolta di risultati. R dipende dal numero di variabili di intervallo nell'ambito alla conclusione dell'operatore di query. Se una singola variabile di intervallo è nell'ambito, R è il tipo di tale variabile di intervallo. Nell'esempio

    Dim custNames = From c In Customers
                    Select c.Name
    

    il risultato della query sarà un tipo di raccolta con un tipo di elemento .String Se nell'ambito sono presenti più variabili di intervallo, R è un tipo anonimo che contiene tutte le variabili di intervallo nell'ambito come Key campi. Nell'esempio:

    Dim custNames = From c In Customers, o In c.Orders 
                    Select Name = c.Name, ProductName = o.ProductName
    

    il risultato della query sarà un tipo di raccolta con un tipo di elemento di un tipo anonimo con una proprietà di sola lettura denominata di tipo e una proprietà di sola lettura denominata NameProductName di tipo String.String

    All'interno di un'espressione di query, i tipi anonimi generati per contenere variabili di intervallo sono trasparenti, il che significa che le variabili di intervallo sono sempre disponibili senza qualifica. Nell'esempio precedente, ad esempio, le variabili c di intervallo e o possono essere accessibili senza qualifica nell'operatore Select di query, anche se il tipo di elemento della raccolta di input era un tipo anonimo.

  • Il tipo CX rappresenta un tipo di raccolta, non necessariamente il tipo di raccolta di input, il cui tipo di elemento è di tipo X.

Un tipo di raccolta queryable deve soddisfare una delle condizioni seguenti, in ordine di preferenza:

  • Deve definire un metodo conforme Select .

  • Deve avere uno dei metodi seguenti

    Function AsEnumerable() As CT
    Function AsQueryable() As CT
    

    che può essere chiamato per ottenere una raccolta queryable. Se vengono forniti entrambi i metodi, AsQueryable è preferibile rispetto AsEnumerablea .

  • Deve avere un metodo

    Function Cast(Of T)() As CT
    

    che può essere chiamato con il tipo della variabile di intervallo per produrre una raccolta queryable.

Poiché la determinazione del tipo di elemento di una raccolta si verifica indipendentemente dalla chiamata a un metodo effettivo, non è possibile determinare l'applicabilità di metodi specifici. Pertanto, quando si determina il tipo di elemento di una raccolta se sono presenti metodi di istanza che corrispondono a metodi noti, tutti i metodi di estensione che corrispondono a metodi noti vengono ignorati.

La conversione degli operatori di query si verifica nell'ordine in cui si verificano gli operatori di query nell'espressione. Non è necessario che un oggetto raccolta implementi tutti i metodi necessari per tutti gli operatori di query, anche se ogni oggetto raccolta deve almeno supportare l'operatore Select di query. Se non è presente un metodo necessario, si verifica un errore in fase di compilazione. Quando si associano nomi di metodi noti, i metodi non vengono ignorati allo scopo di più ereditarietà nelle interfacce e nell'associazione di metodi di estensione, anche se la semantica di shadowing è ancora applicabile. Per esempio:

Class Q1
    Public Function [Select](selector As Func(Of Integer, Integer)) As Q1
    End Function
End Class

Class Q2
    Inherits Q1

    Public [Select] As Integer
End Class

Module Test
    Sub Main()
        Dim qs As New Q2()

        ' Error: Q2.Select still hides Q1.Select
        Dim zs = From q In qs Select q
    End Sub
End Module

Indicizzatore di query predefinito

Ogni tipo di raccolta queryable il cui tipo di elemento è T e non dispone già di una proprietà predefinita è considerata una proprietà predefinita del formato generale seguente:

Public ReadOnly Default Property Item(index As Integer) As T
    Get
        Return Me.ElementAtOrDefault(index)
    End Get
End Property

È possibile fare riferimento alla proprietà predefinita solo usando la sintassi di accesso alle proprietà predefinita; Non è possibile fare riferimento alla proprietà predefinita in base al nome. Per esempio:

Dim customers As IEnumerable(Of Customer) = ...
Dim customerThree = customers(2)

' Error, no such property
Dim customerFour = customers.Item(4)

Se il tipo di raccolta non dispone di un ElementAtOrDefault membro, si verificherà un errore in fase di compilazione.

Operatore From Query

L'operatore From query introduce una variabile di intervallo di raccolte che rappresenta i singoli membri di una raccolta su cui eseguire query.

FromQueryOperator
    : LineTerminator? 'From' LineTerminator? CollectionRangeVariableDeclarationList
    ;

Ad esempio, l'espressione di query:

From c As Customer In Customers ...

può essere considerato come equivalente a

For Each c As Customer In Customers
        ...
Next c

Quando un From operatore di query dichiara più variabili di intervallo di raccolte o non è il primo From operatore di query nell'espressione di query, ogni nuova variabile dell'intervallo di raccolta viene unita in join incrociato al set esistente di variabili di intervallo. Il risultato è che la query viene valutata sul prodotto incrociato di tutti gli elementi nelle raccolte unite. Ad esempio, l'espressione:

From c In Customers _
From e In Employees _
...

può essere considerato equivalente a:

For Each c In Customers
    For Each e In Employees
            ...
    Next e
Next c

ed è esattamente equivalente a:

From c In Customers, e In Employees ...

Le variabili di intervallo introdotte negli operatori di query precedenti possono essere usate in un operatore di query successivo From . Nell'espressione di query seguente, ad esempio, il secondo From operatore di query fa riferimento al valore della prima variabile di intervallo:

From c As Customer In Customers _
From o As Order In c.Orders _
Select c.Name, o

Sono supportate più variabili di intervallo in un From operatore di query o in più From operatori di query solo se il tipo di raccolta contiene uno o entrambi i metodi seguenti:

Function SelectMany(selector As Func(Of T, CR)) As CR
Function SelectMany(selector As Func(Of T, CS), _
                          resultsSelector As Func(Of T, S, R)) As CR

Codice

Dim xs() As Integer = ...
Dim ys() As Integer = ...
Dim zs = From x In xs, y In ys ...

viene in genere tradotto in

Dim xs() As Integer = ...
Dim ys() As Integer = ...
Dim zs = _
    xs.SelectMany( _
        Function(x As Integer) ys, _
        Function(x As Integer, y As Integer) New With {x, y})...

Nota. From non è una parola riservata.

Operatore di query Join

L'operatore Join query unisce le variabili di intervallo esistenti con una nuova variabile dell'intervallo di raccolta, generando una singola raccolta i cui elementi sono stati uniti insieme in base a un'espressione di uguaglianza.

JoinQueryOperator
    : LineTerminator? 'Join' LineTerminator? CollectionRangeVariableDeclaration
      JoinOrGroupJoinQueryOperator? LineTerminator? 'On' LineTerminator? JoinConditionList
    ;

JoinConditionList
    : JoinCondition ( 'And' LineTerminator? JoinCondition )*
    ;

JoinCondition
    : Expression 'Equals' LineTerminator? Expression
    ;

Per esempio:

Dim customersAndOrders = _
    From cust In Customers _
    Join ord In Orders On cust.ID Equals ord.CustomerID

L'espressione di uguaglianza è più limitata rispetto a un'espressione di uguaglianza regolare:

  • Entrambe le espressioni devono essere classificate come valore.

  • Entrambe le espressioni devono fare riferimento ad almeno una variabile di intervallo.

  • La variabile di intervallo dichiarata nell'operatore di query join deve fare riferimento a una delle espressioni e tale espressione non deve fare riferimento ad altre variabili di intervallo.

Se i tipi delle due espressioni non sono esattamente dello stesso tipo, allora

  • Se l'operatore di uguaglianza è definito per i due tipi, entrambe le espressioni sono convertibili in modo implicito e non Objectè , quindi convertire entrambe le espressioni in tale tipo.

  • In caso contrario, se è presente un tipo dominante in cui entrambe le espressioni possono essere convertite in modo implicito, convertire entrambe le espressioni in tale tipo.

  • In caso contrario, si verifica un errore in fase di compilazione.

Le espressioni vengono confrontate usando valori hash (ad esempio chiamando GetHashCode()) anziché usando operatori di uguaglianza per garantire l'efficienza. Un Join operatore di query può eseguire più join o condizioni di uguaglianza nello stesso operatore. Un Join operatore di query è supportato solo se il tipo di raccolta contiene un metodo:

Function Join(inner As CS, _
                  outerSelector As Func(Of T, K), _
                  innerSelector As Func(Of S, K), _
                  resultSelector As Func(Of T, S, R)) As CR

Codice

Dim xs() As Integer = ...
Dim ys() As Integer = ...
Dim zs = From x In xs _
            Join y In ys On x Equals y _
            ...

viene in genere tradotto in

Dim xs() As Integer = ...
Dim ys() As Integer = ...
Dim zs = _
    xs.Join( _
        ys, _
        Function(x As Integer) x, _
        Function(y As Integer) y, _
        Function(x As Integer, y As Integer) New With {x, y})...

Nota.JoinOn e Equals non sono parole riservate.

Operatore Let Query

L'operatore Let query introduce una variabile di intervallo di espressioni. Ciò consente di calcolare un valore intermedio una volta che verrà usato più volte negli operatori di query successivi.

LetQueryOperator
    : LineTerminator? 'Let' LineTerminator? ExpressionRangeVariableDeclarationList
    ;

Per esempio:

Dim taxedPrices = _
    From o In Orders _
    Let tax = o.Price * 0.088 _
    Where tax > 3.50 _
    Select o.Price, tax, total = o.Price + tax

può essere considerato equivalente a:

For Each o In Orders
    Dim tax = o.Price * 0.088
    ...
Next o

Un Let operatore di query è supportato solo se il tipo di raccolta contiene un metodo:

Function Select(selector As Func(Of T, R)) As CR

Codice

Dim xs() As Integer = ...
Dim zs = From x In xs _
            Let y = x * 10 _
            ...

viene in genere tradotto in

Dim xs() As Integer = ...
Dim zs = _
    xs.Select(Function(x As Integer) New With {x, .y = x * 10})...

Selezionare l'operatore query

L'operatore Select query è simile all'operatore query in quanto introduce le variabili dell'intervallo Let di espressioni. Tuttavia, un Select operatore di query nasconde le variabili di intervallo attualmente disponibili anziché aggiungerle. Inoltre, il tipo di una variabile di intervallo di espressioni introdotto da un Select operatore di query viene sempre dedotto usando regole di inferenza del tipo di variabile locale. Non è possibile specificare un tipo esplicito e, se non è possibile dedurre alcun tipo, si verifica un errore in fase di compilazione.

SelectQueryOperator
    : LineTerminator? 'Select' LineTerminator? ExpressionRangeVariableDeclarationList
    ;

Ad esempio, nella query:

Dim smiths = _
    From cust In Customers _
    Select name = cust.name _
    Where name.EndsWith("Smith")

L'operatore Where query ha accesso solo alla name variabile di intervallo introdotta dall'operatore ; se l'operatore SelectWhere ha tentato di fare riferimento custa , si sarebbe verificato un errore in fase di compilazione.

Anziché specificare in modo esplicito i nomi delle variabili di intervallo, un Select operatore di query può dedurre i nomi delle variabili di intervallo, usando le stesse regole delle espressioni di creazione di oggetti di tipo anonimo. Per esempio:

Dim custAndOrderNames = _
      From cust In Customers, ord In cust.Orders _
      Select cust.name, ord.ProductName _
        Where name.EndsWith("Smith")

Se il nome della variabile di intervallo non viene specificato e non è possibile dedurre un nome, si verifica un errore in fase di compilazione. Se l'operatore Select query contiene solo una singola espressione, non si verifica alcun errore se non è possibile dedurre un nome per tale variabile di intervallo, ma la variabile di intervallo è senza nome. Per esempio:

Dim custAndOrderNames = _
      From cust In Customers, ord In cust.Orders _
      Select cust.Name & " bought " & ord.ProductName _
        Take 10

Se si verifica un'ambiguità in un Select operatore di query tra l'assegnazione di un nome a una variabile di intervallo e un'espressione di uguaglianza, l'assegnazione del nome è preferibile. Per esempio:

Dim badCustNames = _
      From c In Customers _
        Let name = "John Smith" _
      Select name = c.Name ' Creates a range variable named "name"


Dim goodCustNames = _
      From c In Customers _
        Let name = "John Smith" _
      Select match = (name = c.Name)

Ogni espressione nell'operatore Select di query deve essere classificata come valore. Un Select operatore di query è supportato solo se il tipo di raccolta contiene un metodo:

Function Select(selector As Func(Of T, R)) As CR

Codice

Dim xs() As Integer = ...
Dim zs = From x In xs _
            Select x, y = x * 10 _
            ...

viene in genere tradotto in

Dim xs() As Integer = ...
Dim zs = _
    xs.Select(Function(x As Integer) New With {x, .y = x * 10})...

Operatore Distinct Query

L'operatore Distinct query limita i valori di una raccolta solo a quelli con valori distinti, come determinato confrontando il tipo di elemento per l'uguaglianza.

DistinctQueryOperator
    : LineTerminator? 'Distinct' LineTerminator?
    ;

Ad esempio, la query:

Dim distinctCustomerPrice = _
    From cust In Customers, ord In cust.Orders _
    Select cust.Name, ord.Price _
    Distinct

restituirà solo una riga per ogni coppia distinta di nome cliente e prezzo dell'ordine, anche se il cliente ha più ordini con lo stesso prezzo. Un Distinct operatore di query è supportato solo se il tipo di raccolta contiene un metodo:

Function Distinct() As CT

Codice

Dim xs() As Integer = ...
Dim zs = From x In xs _
            Distinct _
            ...

viene in genere tradotto in

Dim xs() As Integer = ...
Dim zs = xs.Distinct()...

Nota. Distinct non è una parola riservata.

Operatore Where Query

L'operatore Where di query limita i valori di una raccolta a quelli che soddisfano una determinata condizione.

WhereQueryOperator
    : LineTerminator? 'Where' LineTerminator? BooleanExpression
    ;

Un Where operatore di query accetta un'espressione booleana valutata per ogni set di valori di variabile di intervallo. Se il valore dell'espressione è true, i valori vengono visualizzati nell'insieme di output; in caso contrario, i valori vengono ignorati. Ad esempio, l'espressione di query:

From cust In Customers, ord In Orders _
Where cust.ID = ord.CustomerID _
...

può essere considerato come equivalente al ciclo annidato

For Each cust In Customers
    For Each ord In Orders
            If cust.ID = ord.CustomerID Then
                ...
            End If
    Next ord
Next cust

Un Where operatore di query è supportato solo se il tipo di raccolta contiene un metodo:

Function Where(predicate As Func(Of T, B)) As CT

Codice

Dim xs() As Integer = ...
Dim zs = From x In xs _
            Where x < 10 _
            ...

viene in genere tradotto in

Dim xs() As Integer = ...
Dim zs = _
    xs.Where(Function(x As Integer) x < 10)...

Nota. Where non è una parola riservata.

Operatori di query di partizione

PartitionQueryOperator
    : LineTerminator? 'Take' LineTerminator? Expression
    | LineTerminator? 'Take' 'While' LineTerminator? BooleanExpression
    | LineTerminator? 'Skip' LineTerminator? Expression
    | LineTerminator? 'Skip' 'While' LineTerminator? BooleanExpression
    ;

L'operatore Take query restituisce i primi n elementi di una raccolta. Se usato con il modificatore, l'operatore WhileTake restituisce i primi n elementi di una raccolta che soddisfano un'espressione booleana. L'operatore Skip ignora i primi n elementi di una raccolta e quindi restituisce il resto della raccolta. Se utilizzato insieme al While modificatore, l'operatore Skip ignora i primi n elementi di una raccolta che soddisfano un'espressione booleana e quindi restituisce il resto della raccolta. Le espressioni in un Take operatore di query o Skip devono essere classificate come valore.

Un Take operatore di query è supportato solo se il tipo di raccolta contiene un metodo:

Function Take(count As N) As CT

Un Skip operatore di query è supportato solo se il tipo di raccolta contiene un metodo:

Function Skip(count As N) As CT

Un Take While operatore di query è supportato solo se il tipo di raccolta contiene un metodo:

Function TakeWhile(predicate As Func(Of T, B)) As CT

Un Skip While operatore di query è supportato solo se il tipo di raccolta contiene un metodo:

Function SkipWhile(predicate As Func(Of T, B)) As CT

Codice

Dim xs() As Integer = ...
Dim zs = From x In xs _
            Skip 10 _
            Take 5 _
            Skip While x < 10 _
            Take While x > 5 _
            ...

viene in genere tradotto in

Dim xs() As Integer = ...
Dim zs = _
    xs.Skip(10). _
        Take(5). _
        SkipWhile(Function(x) x < 10). _
        TakeWhile(Function(x) x > 5)...

Nota. Take e Skip non sono parole riservate.

Operatore Order by Query

L'operatore Order By query ordina i valori visualizzati nelle variabili di intervallo.

OrderByQueryOperator
    : LineTerminator? 'Order' 'By' LineTerminator? OrderExpressionList
    ;

OrderExpressionList
    : OrderExpression ( Comma OrderExpression )*
    ;

OrderExpression
    : Expression Ordering?
    ;

Ordering
    : 'Ascending' | 'Descending'
    ;

Un Order By operatore di query accetta espressioni che specificano i valori di chiave da usare per ordinare le variabili di iterazione. Ad esempio, la query seguente restituisce i prodotti ordinati in base al prezzo:

Dim productsByPrice = _
    From p In Products _
    Order By p.Price _
    Select p.Name

Un ordinamento può essere contrassegnato come Ascending, nel qual caso i valori più piccoli vengono prima di valori più grandi o Descending, nel qual caso i valori più grandi vengono prima dei valori più piccoli. Il valore predefinito per un ordinamento se non è specificato alcun valore è Ascending. Ad esempio, la query seguente restituisce i prodotti ordinati in base al prezzo con il prodotto più costoso per primo:

Dim productsByPriceDesc = _
    From p In Products _
    Order By p.Price Descending _
    Select p.Name

L'operatore Order By di query può specificare più espressioni per l'ordinamento, nel qual caso la raccolta viene ordinata in modo annidato. Ad esempio, la query seguente ordina i clienti in base allo stato, quindi per città all'interno di ogni stato e quindi per codice postale all'interno di ogni città:

Dim customersByLocation = _
    From c In Customers _
    Order By c.State, c.City, c.ZIP _
    Select c.Name, c.State, c.City, c.ZIP

Le espressioni in un Order By operatore di query devono essere classificate come valore. Un Order By operatore di query è supportato solo se il tipo di raccolta contiene uno o entrambi i metodi seguenti:

Function OrderBy(keySelector As Func(Of T, K)) As CT
Function OrderByDescending(keySelector As Func(Of T, K)) As CT

Il tipo CT restituito deve essere una raccolta ordinata. Una raccolta ordinata è un tipo di raccolta che contiene uno o entrambi i metodi:

Function ThenBy(keySelector As Func(Of T, K)) As CT
Function ThenByDescending(keySelector As Func(Of T, K)) As CT

Codice

Dim xs() As Integer = ...
Dim zs = From x In xs _
            Order By x Ascending, x Mod 2 Descending _
            ...

viene in genere tradotto in

Dim xs() As Integer = ...
Dim zs = _
    xs.OrderBy(Function(x) x).ThenByDescending(Function(x) x Mod 2)...

Nota. Poiché gli operatori di query eseguono semplicemente il mapping della sintassi ai metodi che implementano una determinata operazione di query, la conservazione degli ordini non è determinata dal linguaggio e viene determinata dall'implementazione dell'operatore stesso. Questo è molto simile agli operatori definiti dall'utente in quanto l'implementazione per eseguire l'overload dell'operatore di addizione per un tipo numerico definito dall'utente potrebbe non eseguire alcuna operazione simile a un'aggiunta. Naturalmente, per mantenere la prevedibilità, l'implementazione di un elemento che non corrisponde alle aspettative degli utenti non è consigliato.

Nota. Order e By non sono parole riservate.

Operatore Group by Query

L'operatore Group By di query raggruppa le variabili di intervallo nell'ambito in base a una o più espressioni e quindi produce nuove variabili di intervallo in base a tali raggruppamenti.

GroupByQueryOperator
    : LineTerminator? 'Group' ( LineTerminator? ExpressionRangeVariableDeclarationList )?
      LineTerminator? 'By' LineTerminator? ExpressionRangeVariableDeclarationList
      LineTerminator? 'Into' LineTerminator? ExpressionRangeVariableDeclarationList
    ;

Ad esempio, la query seguente raggruppa tutti i clienti per Statee quindi calcola il conteggio e l'età media di ogni gruppo:

Dim averageAges = _
    From cust In Customers _
    Group By cust.State _
    Into Count(), Average(cust.Age)

L'operatore Group By di query include tre clausole: la clausola facoltativa Group , la By clausola e la Into clausola . La Group clausola ha la stessa sintassi e effetto di un Select operatore di query, ad eccezione del fatto che influisce solo sulle variabili di intervallo disponibili nella Into clausola e non sulla By clausola . Per esempio:

Dim averageAges = _
    From cust In Customers _
    Group cust.Age By cust.State _
    Into Count(), Average(Age)

La By clausola dichiara le variabili dell'intervallo di espressioni usate come valori chiave nell'operazione di raggruppamento. La Into clausola consente la dichiarazione di variabili dell'intervallo di espressioni che calcolano le aggregazioni su ognuno dei gruppi formati dalla By clausola . All'interno della Into clausola la variabile dell'intervallo di espressioni può essere assegnata solo a un'espressione che è una chiamata al metodo di una funzione di aggregazione. Una funzione di aggregazione è una funzione sul tipo di raccolta del gruppo (che potrebbe non essere necessariamente lo stesso tipo di raccolta dell'insieme originale) simile a uno dei metodi seguenti:

Function _name_() As _type_
Function _name_(selector As Func(Of T, R)) As R

Se una funzione di aggregazione accetta un argomento delegato, l'espressione di chiamata può avere un'espressione di argomento che deve essere classificata come valore. L'espressione dell'argomento può usare le variabili di intervallo nell'ambito; all'interno della chiamata a una funzione di aggregazione, tali variabili di intervallo rappresentano i valori del gruppo in fase di creazione, non tutti i valori nell'insieme. Ad esempio, nell'esempio originale di questa sezione la Average funzione calcola la media delle età dei clienti per stato anziché per tutti i clienti insieme.

Tutti i tipi di raccolta vengono considerati in base alla funzione Group di aggregazione definita, che non accetta parametri e restituisce semplicemente il gruppo. Altre funzioni di aggregazione standard che un tipo di raccolta può fornire sono:

Count e LongCount, che restituiscono il conteggio degli elementi nel gruppo o il conteggio degli elementi nel gruppo che soddisfano un'espressione booleana. Count e LongCount sono supportati solo se il tipo di raccolta contiene uno dei metodi seguenti:

Function Count() As N
Function Count(selector As Func(Of T, B)) As N
Function LongCount() As N
Function LongCount(selector As Func(Of T, B)) As N

Sum, che restituisce la somma di un'espressione in tutti gli elementi del gruppo. Sum è supportato solo se il tipo di raccolta contiene uno dei metodi seguenti:

Function Sum() As N
Function Sum(selector As Func(Of T, N)) As N

Min che restituisce il valore minimo di un'espressione in tutti gli elementi del gruppo. Min è supportato solo se il tipo di raccolta contiene uno dei metodi seguenti:

Function Min() As N
Function Min(selector As Func(Of T, N)) As N

Max, che restituisce il valore massimo di un'espressione in tutti gli elementi del gruppo. Max è supportato solo se il tipo di raccolta contiene uno dei metodi seguenti:

Function Max() As N
Function Max(selector As Func(Of T, N)) As N

Average, che restituisce la media di un'espressione in tutti gli elementi del gruppo. Average è supportato solo se il tipo di raccolta contiene uno dei metodi seguenti:

Function Average() As N
Function Average(selector As Func(Of T, N)) As N

Any, che determina se un gruppo contiene membri o se un'espressione booleana è true per qualsiasi elemento del gruppo. Any restituisce un valore che può essere utilizzato in un'espressione booleana ed è supportato solo se il tipo di raccolta contiene uno dei metodi:

Function Any() As B
Function Any(predicate As Func(Of T, B)) As B

All, che determina se un'espressione booleana è true per tutti gli elementi del gruppo. All restituisce un valore che può essere utilizzato in un'espressione booleana ed è supportato solo se il tipo di raccolta contiene un metodo:

Function All(predicate As Func(Of T, B)) As B

Dopo un Group By operatore di query, le variabili di intervallo in precedenza nell'ambito sono nascoste e le variabili di intervallo introdotte dalle By clausole e Into sono disponibili. Un Group By operatore di query è supportato solo se il tipo di raccolta contiene il metodo :

Function GroupBy(keySelector As Func(Of T, K), _
                      resultSelector As Func(Of K, CT, R)) As CR

Le dichiarazioni di variabili di intervallo nella Group clausola sono supportate solo se il tipo di raccolta contiene il metodo :

Function GroupBy(keySelector As Func(Of T, K), _
                      elementSelector As Func(Of T, S), _
                      resultSelector As Func(Of K, CS, R)) As CR

Codice

Dim xs() As Integer = ...
Dim zs = From x In xs _
            Group y = x * 10, z = x / 10 By evenOdd = x Mod 2 _
            Into Sum(y), Average(z) _
            ...

viene in genere tradotto in

Dim xs() As Integer = ...
Dim zs = _
    xs.GroupBy( _
        Function(x As Integer) x Mod 2, _
        Function(x As Integer) New With {.y = x * 10, .z = x / 10}, _
        Function(evenOdd, group) New With { _
            evenOdd, _
            .Sum = group.Sum(Function(e) e.y), _
            .Average = group.Average(Function(e) e.z)})...

Nota.Group, Bye Into non sono parole riservate.

Operatore query aggregate

L'operatore Aggregate query esegue una funzione simile all'operatore , ad eccezione del fatto che consente l'aggregazione Group By su gruppi già formati. Poiché il gruppo è già stato formato, la Into clausola di un Aggregate operatore di query non nasconde le variabili di intervallo nell'ambito (in questo modo, Aggregate è più simile a un Lete Group By è più simile a ).Select

AggregateQueryOperator
    : LineTerminator? 'Aggregate' LineTerminator? CollectionRangeVariableDeclaration QueryOperator*
      LineTerminator? 'Into' LineTerminator? ExpressionRangeVariableDeclarationList
    ;

Ad esempio, la query seguente aggrega il totale di tutti gli ordini effettuati dai clienti a Washington:

Dim orderTotals = _
    From cust In Customers _
    Where cust.State = "WA" _
    Aggregate order In cust.Orders _
    Into Sum(order.Total)

Il risultato di questa query è una raccolta il cui tipo di elemento è un tipo anonimo con una proprietà denominata tipizzata come Customer e una proprietà denominata custSum tipizzata come Integer.

A differenza di Group By, è possibile inserire operatori di query aggiuntivi tra le Aggregate clausole e Into . Tra una Aggregate clausola e la fine della Into clausola, è possibile usare tutte le variabili di intervallo nell'ambito, incluse quelle dichiarate dalla Aggregate clausola . Ad esempio, la query seguente aggrega il totale totale di tutti gli ordini effettuati dai clienti a Washington prima del 2006:

Dim orderTotals = _
    From cust In Customers _
    Where cust.State = "WA" _
    Aggregate order In cust.Orders _
    Where order.Date <= #01/01/2006# _
    Into Sum = Sum(order.Total)

L'operatore Aggregate può essere usato anche per avviare un'espressione di query. In questo caso, il risultato dell'espressione di query sarà il singolo valore calcolato dalla Into clausola . Ad esempio, la query seguente calcola la somma di tutti i totali dell'ordine prima del 1° gennaio 2006:

Dim ordersTotal = _
    Aggregate order In Orders _
    Where order.Date <= #01/01/2006# _
    Into Sum(order.Total)

Il risultato della query è un singolo Integer valore. Un Aggregate operatore di query è sempre disponibile (anche se la funzione di aggregazione deve essere disponibile anche per l'espressione per essere valida). Codice

Dim xs() As Integer = ...
Dim zs = _
    Aggregate x In xs _
    Where x < 5 _
    Into Sum()

viene in genere tradotto in

Dim xs() As Integer = ...
Dim zs = _
    xs.Where(Function(x) x < 5).Sum()

Nota.  Aggregate e Into non sono parole riservate.

Operatore di query Group Join

L'operatore Group Join query combina le funzioni degli Join operatori di query e Group By in un singolo operatore. Group Join unisce due raccolte in base alle chiavi corrispondenti estratte dagli elementi, raggruppando tutti gli elementi a destra del join che corrispondono a un particolare elemento sul lato sinistro del join. Pertanto, l'operatore produce un set di risultati gerarchici.

GroupJoinQueryOperator
    : LineTerminator? 'Group' 'Join' LineTerminator? CollectionRangeVariableDeclaration
      JoinOrGroupJoinQueryOperator? LineTerminator? 'On' LineTerminator? JoinConditionList
      LineTerminator? 'Into' LineTerminator? ExpressionRangeVariableDeclarationList
    ;

Ad esempio, la query seguente produce elementi che contengono il nome di un singolo cliente, un gruppo di tutti i relativi ordini e l'importo totale di tutti gli ordini:

Dim custsWithOrders = _
    From cust In Customers _
    Group Join order In Orders On cust.ID Equals order.CustomerID _ 
    Into Orders = Group, OrdersTotal = Sum(order.Total) _
    Select cust.Name, Orders, OrdersTotal

Il risultato della query è una raccolta il cui tipo di elemento è un tipo anonimo con tre proprietà: Name, tipizzato come String, Orders tipizzato come raccolta il cui tipo di elemento è Order, e OrdersTotal, tipizzato come Integer. Un Group Join operatore di query è supportato solo se il tipo di raccolta contiene il metodo :

Function GroupJoin(inner As CS, _
                         outerSelector As Func(Of T, K), _
                         innerSelector As Func(Of S, K), _
                         resultSelector As Func(Of T, CS, R)) As CR

Codice

Dim xs() As Integer = ...
Dim ys() As Integer = ...
Dim zs = From x In xs _
            Group Join y in ys On x Equals y _
            Into g = Group _
            ...

viene in genere tradotto in

Dim xs() As Integer = ...
Dim ys() As Integer = ...
Dim zs = _
    xs.GroupJoin( _
        ys, _
        Function(x As Integer) x, _
        Function(y As Integer) y, _
        Function(x, group) New With {x, .g = group})...

Nota.Group, Joine Into non sono parole riservate.

Espressioni condizionali

Un'espressione condizionale If testa un'espressione e restituisce un valore.

ConditionalExpression
    : 'If' OpenParenthesis BooleanExpression Comma Expression Comma Expression CloseParenthesis
    | 'If' OpenParenthesis Expression Comma Expression CloseParenthesis
    ;

A differenza della IIF funzione di runtime, tuttavia, un'espressione condizionale valuta solo gli operandi, se necessario. Pertanto, ad esempio, l'espressione If(c Is Nothing, c.Name, "Unknown") non genererà un'eccezione se il valore di c è Nothing. L'espressione condizionale ha due forme: una che accetta due operandi e una che accetta tre operandi.

Se vengono forniti tre operandi, tutte e tre le espressioni devono essere classificate come valori e il primo operando deve essere un'espressione booleana. Se il risultato è dell'espressione è true, la seconda espressione sarà il risultato dell'operatore; in caso contrario, la terza espressione sarà il risultato dell'operatore. Il tipo di risultato dell'espressione è il tipo dominante tra i tipi della seconda e della terza espressione. Se non è presente alcun tipo dominante, si verifica un errore in fase di compilazione.

Se vengono forniti due operandi, entrambi gli operandi devono essere classificati come valori e il primo operando deve essere un tipo riferimento o un tipo valore nullable. L'espressione If(x, y) viene quindi valutata come se fosse l'espressione If(x IsNot Nothing, x, y), con due eccezioni. In primo luogo, la prima espressione viene valutata una sola volta e la seconda, se il secondo tipo di operando è un tipo valore non nullable e il primo tipo di operando è , viene ? rimosso dal tipo del primo operando quando si determina il tipo dominante per il tipo di risultato dell'espressione. Per esempio:

Module Test
    Sub Main()
        Dim x?, y As Integer
        Dim a?, b As Long

        a = If(x, a)        ' Result type: Long?
        y = If(x, 0)        ' Result type: Integer
    End Sub
End Module

In entrambe le forme dell'espressione, se un operando è Nothing, il relativo tipo non viene usato per determinare il tipo dominante. Nel caso dell'espressione If(<expression>, Nothing, Nothing), il tipo dominante viene considerato .Object

Espressioni letterali XML

Un'espressione letterale XML rappresenta un valore XML (eXtensible Markup Language) 1.0.

XMLLiteralExpression
    : XMLDocument
    | XMLElement
    | XMLProcessingInstruction
    | XMLComment
    | XMLCDATASection
    ;

Il risultato di un'espressione letterale XML è un valore tipizzato come uno dei tipi dello spazio dei System.Xml.Linq nomi. Se i tipi in tale spazio dei nomi non sono disponibili, un'espressione letterale XML genererà un errore in fase di compilazione. I valori vengono generati tramite chiamate del costruttore tradotte dall'espressione letterale XML. Ad esempio, il codice:

Dim book As System.Xml.Linq.XElement = _
    <book title="My book"></book>

è approssimativamente equivalente al codice:

Dim book As System.Xml.Linq.XElement = _
    New System.Xml.Linq.XElement( _
        "book", _
        New System.Xml.Linq.XAttribute("title", "My book"))

Un'espressione letterale XML può assumere la forma di un documento XML, un elemento XML, un'istruzione di elaborazione XML, un commento XML o una sezione CDATA.

Nota. Questa specifica contiene solo una descrizione di XML sufficiente per descrivere il comportamento del linguaggio Visual Basic. Per altre informazioni sul codice XML, vedere http://www.w3.org/TR/REC-xml/.

Regole lessicali

XMLCharacter
    : '<Unicode tab character (0x0009)>'
    | '<Unicode linefeed character (0x000A)>'
    | '<Unicode carriage return character (0x000D)>'
    | '<Unicode characters 0x0020 - 0xD7FF>'
    | '<Unicode characters 0xE000 - 0xFFFD>'
    | '<Unicode characters 0x10000 - 0x10FFFF>'
    ;

XMLString
    : XMLCharacter+
    ;

XMLWhitespace
    : XMLWhitespaceCharacter+
    ;

XMLWhitespaceCharacter
    : '<Unicode carriage return character (0x000D)>'
    | '<Unicode linefeed character (0x000A)>'
    | '<Unicode space character (0x0020)>'
    | '<Unicode tab character (0x0009)>'
    ;

XMLNameCharacter
    : XMLLetter
    | XMLDigit
    | '.'
    | '-'
    | '_'
    | ':'
    | XMLCombiningCharacter
    | XMLExtender
    ;

XMLNameStartCharacter
    : XMLLetter
    | '_'
    | ':'
    ;

XMLName
    : XMLNameStartCharacter XMLNameCharacter*
    ;

XMLLetter
    : '<Unicode character as defined in the Letter production of the XML 1.0 specification>'
    ;

XMLDigit
    : '<Unicode character as defined in the Digit production of the XML 1.0 specification>'
    ;

XMLCombiningCharacter
    : '<Unicode character as defined in the CombiningChar production of the XML 1.0 specification>'
    ;

XMLExtender
    : '<Unicode character as defined in the Extender production of the XML 1.0 specification>'
    ;

Le espressioni letterali XML vengono interpretate usando le regole lessicali di XML anziché le regole lessicali del normale codice Visual Basic. I due set di regole in genere differiscono nei modi seguenti:

  • Lo spazio vuoto è significativo in XML. Di conseguenza, la grammatica per le espressioni letterali XML indica in modo esplicito dove è consentito lo spazio vuoto. Lo spazio vuoto non viene mantenuto, tranne quando si verifica nel contesto dei dati di tipo carattere all'interno di un elemento . Per esempio:

    ' The following element preserves no whitespace
    Dim e1 = _
        <customer>
            <name>Bob</>
        </>
    
    ' The following element preserves all of the whitespace
    Dim e2 = _
        <customer>
            Bob
        </>
    
  • Gli spazi vuoti di fine riga XML vengono normalizzati in base alla specifica XML.

  • XML fa distinzione tra maiuscole e minuscole. Le parole chiave devono corrispondere esattamente a maiuscole e minuscole. In caso contrario, si verificherà un errore in fase di compilazione.

  • I caratteri di terminazione riga sono considerati spazi vuoti in XML. Di conseguenza, non sono necessari caratteri di continuazione riga nelle espressioni letterali XML.

  • XML non accetta caratteri a larghezza intera. Se vengono utilizzati caratteri a larghezza intera, si verificherà un errore in fase di compilazione.

Espressioni incorporate

Le espressioni letterali XML possono contenere espressioni incorporate. Un'espressione incorporata è un'espressione di Visual Basic valutata e utilizzata per compilare uno o più valori nella posizione dell'espressione incorporata.

XMLEmbeddedExpression
    : '<' '%' '=' LineTerminator? Expression LineTerminator? '%' '>'
    ;

Ad esempio, il codice seguente inserisce la stringa John Smith come valore dell'elemento XML:

Dim name as String = "John Smith"
Dim element As System.Xml.Linq.XElement = <customer><%= name %></customer>

Le espressioni possono essere incorporate in diversi contesti. Ad esempio, il codice seguente produce un elemento denominato customer:

Dim name As String = "customer"
Dim element As System.Xml.Linq.XElement = <<%= name %>>John Smith</>

Ogni contesto in cui è possibile usare un'espressione incorporata specifica i tipi che verranno accettati. Quando all'interno del contesto della parte dell'espressione di un'espressione incorporata, è necessario usare le normali regole lessicali per il codice Visual Basic, ad esempio le continuazioni di riga:

' Visual Basic expression uses line continuation, XML does not
Dim element As System.Xml.Linq.XElement = _
    <<%= name & _
          name %>>John 
                     Smith</>

Documenti XML

XMLDocument
    : XMLDocumentPrologue XMLMisc* XMLDocumentBody XMLMisc*
    ;

XMLDocumentPrologue
    : '<' '?' 'xml' XMLVersion XMLEncoding? XMLStandalone? XMLWhitespace? '?' '>'
    ;

XMLVersion
    : XMLWhitespace 'version' XMLWhitespace? '=' XMLWhitespace? XMLVersionNumberValue
    ;

XMLVersionNumberValue
    : SingleQuoteCharacter '1' '.' '0' SingleQuoteCharacter
    | DoubleQuoteCharacter '1' '.' '0' DoubleQuoteCharacter
    ;

XMLEncoding
    : XMLWhitespace 'encoding' XMLWhitespace? '=' XMLWhitespace? XMLEncodingNameValue
    ;

XMLEncodingNameValue
    : SingleQuoteCharacter XMLEncodingName SingleQuoteCharacter
    | DoubleQuoteCharacter XMLEncodingName DoubleQuoteCharacter
    ;

XMLEncodingName
    : XMLLatinAlphaCharacter XMLEncodingNameCharacter*
    ;

XMLEncodingNameCharacter
    : XMLUnderscoreCharacter
    | XMLLatinAlphaCharacter
    | XMLNumericCharacter
    | XMLPeriodCharacter
    | XMLDashCharacter
    ;

XMLLatinAlphaCharacter
    : '<Unicode Latin alphabetic character (0x0041-0x005a, 0x0061-0x007a)>'
    ;

XMLNumericCharacter
    : '<Unicode digit character (0x0030-0x0039)>'
    ;

XMLHexNumericCharacter
    : XMLNumericCharacter
    | '<Unicode Latin hex alphabetic character (0x0041-0x0046, 0x0061-0x0066)>'
    ;

XMLPeriodCharacter
    : '<Unicode period character (0x002e)>'
    ;

XMLUnderscoreCharacter
    : '<Unicode underscore character (0x005f)>'
    ;

XMLDashCharacter
    : '<Unicode dash character (0x002d)>'
    ;

XMLStandalone
    : XMLWhitespace 'standalone' XMLWhitespace? '=' XMLWhitespace? XMLYesNoValue
    ;

XMLYesNoValue
    : SingleQuoteCharacter XMLYesNo SingleQuoteCharacter
    | DoubleQuoteCharacter XMLYesNo DoubleQuoteCharacter
    ;

XMLYesNo
    : 'yes'
    | 'no'
    ;

XMLMisc
    : XMLComment
    | XMLProcessingInstruction
    | XMLWhitespace
    ;

XMLDocumentBody
    : XMLElement
    | XMLEmbeddedExpression
    ;

Un documento XML restituisce un valore tipizzato come System.Xml.Linq.XDocument. A differenza della specifica XML 1.0, i documenti XML nelle espressioni letterali XML sono necessari per specificare il prologo del documento XML; Le espressioni letterali XML senza prologo del documento XML vengono interpretate come singole entità. Per esempio:

Dim doc As System.Xml.Linq.XDocument = _
    <?xml version="1.0"?>
    <?instruction?>
    <customer>Bob</>

Dim pi As System.Xml.Linq.XProcessingInstruction = _
    <?instruction?>

Un documento XML può contenere un'espressione incorporata il cui tipo può essere qualsiasi tipo; in fase di esecuzione, tuttavia, l'oggetto deve soddisfare i requisiti del XDocument costruttore o si verificherà un errore di run-time.

A differenza di XML normale, le espressioni di documento XML non supportano DTD (dichiarazioni di tipi di documento). Inoltre, l'attributo di codifica, se specificato, verrà ignorato poiché la codifica dell'espressione letterale Xml è sempre uguale alla codifica del file di origine stesso.

Nota. Anche se l'attributo di codifica viene ignorato, è ancora un attributo valido per mantenere la possibilità di includere qualsiasi documento Xml 1.0 valido nel codice sorgente.

Elementi XML

XMLElement
    : XMLEmptyElement
    | XMLElementStart XMLContent XMLElementEnd
    ;

XMLEmptyElement
    : '<' XMLQualifiedNameOrExpression XMLAttribute* XMLWhitespace? '/' '>'
    ;

XMLElementStart
    : '<' XMLQualifiedNameOrExpression XMLAttribute* XMLWhitespace? '>'
    ;

XMLElementEnd
    : '<' '/' '>'
    | '<' '/' XMLQualifiedName XMLWhitespace? '>'
    ;

XMLContent
    : XMLCharacterData? ( XMLNestedContent XMLCharacterData? )+
    ;

XMLCharacterData
    : '<Any XMLCharacterDataString that does not contain the string "]]>">'
    ;

XMLCharacterDataString
    : '<Any Unicode character except < or &>'+
    ;

XMLNestedContent
    : XMLElement
    | XMLReference
    | XMLCDATASection
    | XMLProcessingInstruction
    | XMLComment
    | XMLEmbeddedExpression
    ;

XMLAttribute
    : XMLWhitespace XMLAttributeName XMLWhitespace? '=' XMLWhitespace? XMLAttributeValue
    | XMLWhitespace XMLEmbeddedExpression
    ;

XMLAttributeName
    : XMLQualifiedNameOrExpression
    | XMLNamespaceAttributeName
    ;

XMLAttributeValue
    : DoubleQuoteCharacter XMLAttributeDoubleQuoteValueCharacter* DoubleQuoteCharacter
    | SingleQuoteCharacter XMLAttributeSingleQuoteValueCharacter* SingleQuoteCharacter
    | XMLEmbeddedExpression
    ;

XMLAttributeDoubleQuoteValueCharacter
    : '<Any XMLCharacter except <, &, or DoubleQuoteCharacter>'
    | XMLReference
    ;

XMLAttributeSingleQuoteValueCharacter
    : '<Any XMLCharacter except <, &, or SingleQuoteCharacter>'
    | XMLReference
    ;

XMLReference
    : XMLEntityReference
    | XMLCharacterReference
    ;

XMLEntityReference
    : '&' XMLEntityName ';'
    ;

XMLEntityName
    : 'lt' | 'gt' | 'amp' | 'apos' | 'quot'
    ;

XMLCharacterReference
    : '&' '#' XMLNumericCharacter+ ';'
    | '&' '#' 'x' XMLHexNumericCharacter+ ';'
    ;

Un elemento XML restituisce un valore tipizzato come System.Xml.Linq.XElement. A differenza del codice XML normale, gli elementi XML possono omettere il nome nel tag di chiusura e l'elemento più annidato corrente verrà chiuso. Per esempio:

Dim name = <name>Bob</>

Le dichiarazioni di attributo in un elemento XML generano valori tipizzati come System.Xml.Linq.XAttribute. I valori degli attributi vengono normalizzati in base alla specifica XML. Quando il valore di un attributo è Nothing l'attributo non verrà creato, pertanto l'espressione del valore dell'attributo non dovrà essere controllata per Nothing. Per esempio:

Dim expr = Nothing

' Throws null argument exception
Dim direct = New System.Xml.Linq.XElement( _
    "Name", _
    New System.Xml.Linq.XAttribute("Length", expr))

' Doesn't throw exception, the result is <Name/>
Dim literal = <Name Length=<%= expr %>/>

Gli elementi e gli attributi XML possono contenere espressioni annidate nelle posizioni seguenti:

Nome dell'elemento, nel qual caso l'espressione incorporata deve essere un valore di un tipo convertibile in modo implicito in System.Xml.Linq.XName. Per esempio:

Dim name = <<%= "name" %>>Bob</>

Nome di un attributo dell'elemento, nel qual caso l'espressione incorporata deve essere un valore di un tipo convertibile in modo implicito in System.Xml.Linq.XName. Per esempio:

Dim name = <name <%= "length" %>="3">Bob</>

Valore di un attributo dell'elemento, nel qual caso l'espressione incorporata può essere un valore di qualsiasi tipo. Per esempio:

Dim name = <name length=<%= 3 %>>Bob</>

Attributo dell'elemento, nel qual caso l'espressione incorporata può essere un valore di qualsiasi tipo. Per esempio:

Dim name = <name <%= new XAttribute("length", 3) %>>Bob</>

Il contenuto dell'elemento, nel qual caso l'espressione incorporata può essere un valore di qualsiasi tipo. Per esempio:

Dim name = <name><%= "Bob" %></>

Se il tipo dell'espressione incorporata è Object(), la matrice verrà passata come paramarray al XElement costruttore.

Spazi dei nomi XML

Gli elementi XML possono contenere dichiarazioni dello spazio dei nomi XML, come definito dalla specifica 1.0 degli spazi dei nomi XML.

XMLNamespaceAttributeName
    : XMLPrefixedNamespaceAttributeName
    | XMLDefaultNamespaceAttributeName
    ;

XMLPrefixedNamespaceAttributeName
    : 'xmlns' ':' XMLNamespaceName
    ;

XMLDefaultNamespaceAttributeName
    : 'xmlns'
    ;

XMLNamespaceName
    : XMLNamespaceNameStartCharacter XMLNamespaceNameCharacter*
    ;

XMLNamespaceNameStartCharacter
    : '<Any XMLNameCharacter except :>'
    ;

XMLNamespaceNameCharacter
    : XMLLetter
    | '_'
    ;

XMLQualifiedNameOrExpression
    : XMLQualifiedName
    | XMLEmbeddedExpression
    ;

XMLQualifiedName
    : XMLPrefixedName
    | XMLUnprefixedName
    ;

XMLPrefixedName
    : XMLNamespaceName ':' XMLNamespaceName
    ;

XMLUnprefixedName
    : XMLNamespaceName
    ;

Le restrizioni relative alla definizione degli spazi dei nomi xml e xmlns vengono applicate e genereranno errori in fase di compilazione. Le dichiarazioni dello spazio dei nomi XML non possono avere un'espressione incorporata per il relativo valore; Il valore specificato deve essere un valore letterale stringa non vuoto. Per esempio:

' Declares a valid namespace
Dim customer = <db:customer xmlns:db="http://example.org/database">Bob</>

' Error: xmlns cannot be re-defined
Dim bad1 = <elem xmlns:xmlns="http://example.org/namespace"/>

' Error: cannot have an embedded expression
Dim bad2 = <elem xmlns:db=<%= "http://example.org/database" %>>Bob</>

Nota. Questa specifica contiene solo una descrizione dello spazio dei nomi XML per descrivere il comportamento del linguaggio Visual Basic. Per altre informazioni sugli spazi dei nomi XML, vedere http://www.w3.org/TR/REC-xml-names/.

I nomi degli attributi e degli elementi XML possono essere qualificati usando i nomi degli spazi dei nomi. Gli spazi dei nomi sono associati come nel codice XML normale, con l'eccezione che qualsiasi importazione di spazi dei nomi dichiarata a livello di file viene considerata dichiarata in un contesto che racchiude la dichiarazione, che è racchiusa da qualsiasi importazione dello spazio dei nomi dichiarata dall'ambiente di compilazione. Se non è possibile trovare un nome dello spazio dei nomi, si verifica un errore in fase di compilazione. Per esempio:

Imports System.Xml.Linq
Imports <xmlns:db="http://example.org/database">

Module Test
    Sub Main()
        ' Binds to the imported namespace above.
        Dim c1 = <db:customer>Bob</>

        ' Binds to the namespace declaration in the element
        Dim c2 = _
            <db:customer xmlns:db="http://example.org/database-other">Mary</>

        ' Binds to the inner namespace declaration
        Dim c3 = _
            <database xmlns:db="http://example.org/database-one">
                <db:customer xmlns:db="http://example.org/database-two">Joe</>
            </>

        ' Error: namespace db2 cannot be found
        Dim c4 = _
            <db2:customer>Jim</>
    End Sub
End Module

Gli spazi dei nomi XML dichiarati in un elemento non si applicano ai valori letterali XML all'interno di espressioni incorporate. Per esempio:

' Error: Namespace prefix 'db' is not declared
Dim customer = _
    <db:customer xmlns:db="http://example.org/database">
        <%= <db:customer>Bob</> %>
    </>

Nota. Ciò è dovuto al fatto che l'espressione incorporata può essere qualsiasi elemento, inclusa una chiamata di funzione. Se la chiamata di funzione conteneva un'espressione letterale XML, non è chiaro se i programmatori si aspettano che lo spazio dei nomi XML venga applicato o ignorato.

Istruzioni per l'elaborazione XML

Un'istruzione di elaborazione XML restituisce un valore tipizzato come System.Xml.Linq.XProcessingInstruction. Le istruzioni di elaborazione XML non possono contenere espressioni incorporate, poiché sono una sintassi valida all'interno dell'istruzione di elaborazione.

XMLProcessingInstruction
    : '<' '?' XMLProcessingTarget ( XMLWhitespace XMLProcessingValue? )? '?' '>'
    ;

XMLProcessingTarget
    : '<Any XMLName except a casing permutation of the string "xml">'
    ;

XMLProcessingValue
    : '<Any XMLString that does not contain a question-mark followed by ">">'
    ;

Commenti XML

Un commento XML restituisce un valore tipizzato come System.Xml.Linq.XComment. I commenti XML non possono contenere espressioni incorporate, perché sono sintassi valida all'interno del commento.

XMLComment
    : '<' '!' '-' '-' XMLCommentCharacter* '-' '-' '>'
    ;

XMLCommentCharacter
    : '<Any XMLCharacter except dash (0x002D)>'
    | '-' '<Any XMLCharacter except dash (0x002D)>'
    ;

Sezioni CDATA

Una sezione CDATA restituisce un valore tipizzato come System.Xml.Linq.XCData. Le sezioni CDATA non possono contenere espressioni incorporate, perché sono sintassi valida all'interno della sezione CDATA.

XMLCDATASection
    : '<' '!' ( 'CDATA' '[' XMLCDATASectionString? ']' )? '>'
    ;

XMLCDATASectionString
    : '<Any XMLString that does not contain the string "]]>">'
    ;

Espressioni di accesso ai membri XML

Un'espressione di accesso ai membri XML accede ai membri di un valore XML.

XMLMemberAccessExpression
    : Expression '.' LineTerminator? '<' XMLQualifiedName '>'
    | Expression '.' LineTerminator? '@' LineTerminator? '<' XMLQualifiedName '>'
    | Expression '.' LineTerminator? '@' LineTerminator? IdentifierOrKeyword
    | Expression '.' '.' '.' LineTerminator? '<' XMLQualifiedName '>'
    ;

Esistono tre tipi di espressioni di accesso ai membri XML:

  • Accesso all'elemento, in cui un nome XML segue un singolo punto. Per esempio:

    Dim customer = _
        <customer>
            <name>Bob</>
        </>
    Dim customerName = customer.<name>.Value
    

    L'accesso agli elementi esegue il mapping alla funzione:

    Function Elements(name As System.Xml.Linq.XName) As _
        System.Collections.Generic.IEnumerable(Of _
            System.Xml.Linq.XNode)
    

    L'esempio precedente è quindi equivalente a:

    Dim customerName = customer.Elements("name").Value
    
  • Accesso agli attributi, in cui un identificatore di Visual Basic segue un punto e un segno oppure un nome XML segue un punto e un segno. Per esempio:

    Dim customer = <customer age="30"/>
    Dim customerAge = customer.@age
    

    L'accesso agli attributi esegue il mapping alla funzione:

    Function AttributeValue(name As System.Xml.Linq.XName) as String
    

    L'esempio precedente è quindi equivalente a:

    Dim customerAge = customer.AttributeValue("age")
    

    Nota. Il AttributeValue metodo di estensione (nonché la proprietà Valuedi estensione correlata ) non è attualmente definito in alcun assembly. Se i membri dell'estensione sono necessari, vengono definiti automaticamente nell'assembly in fase di produzione.

  • Accesso ai discendenti, in cui i nomi XML seguono tre puntini. Per esempio:

    Dim company = _
        <company>
            <customers>
                <customer>Bob</>
                <customer>Mary</>
                <customer>Joe</>
            </>
        </>
    Dim customers = company...<customer>
    

    L'accesso ai discendenti esegue il mapping alla funzione:

    Function Descendents(name As System.Xml.Linq.XName) As _
        System.Collections.Generic.IEnumerable(Of _
            System.Xml.Linq.XElement)
    

    L'esempio precedente è quindi equivalente a:

    Dim customers = company.Descendants("customer")
    

L'espressione di base di un'espressione di accesso ai membri XML deve essere un valore e deve essere di tipo :

  • Se un elemento o un discendente accede o System.Xml.Linq.XContainer un tipo derivato o System.Collections.Generic.IEnumerable(Of T) un tipo derivato, dove T è System.Xml.Linq.XContainer o un tipo derivato.

  • Se un accesso a un attributo o System.Xml.Linq.XElement un tipo derivato o System.Collections.Generic.IEnumerable(Of T) un tipo derivato, dove T è System.Xml.Linq.XElement o un tipo derivato.

I nomi nelle espressioni di accesso ai membri XML non possono essere vuoti. Possono essere qualificati per lo spazio dei nomi, usando qualsiasi spazio dei nomi definito dalle importazioni. Per esempio:

Imports <xmlns:db="http://example.org/database">

Module Test
    Sub Main()
        Dim customer = _
            <db:customer>
                <db:name>Bob</>
            </>
        Dim name = customer.<db:name>
    End Sub
End Module

Lo spazio vuoto non è consentito dopo i punti in un'espressione di accesso ai membri XML o tra parentesi angolari e il nome. Per esempio:

Dim customer = _
    <customer age="30">
        <name>Bob</>
    </>
' All the following are error cases
Dim age = customer.@ age
Dim name = customer.< name >
Dim names = customer...< name >

Se i tipi nello System.Xml.Linq spazio dei nomi non sono disponibili, un'espressione di accesso ai membri XML genererà un errore in fase di compilazione.

Operatore Await

L'operatore await è correlato ai metodi asincroni, descritti in Metodi asincroni della sezione.

AwaitOperatorExpression
    : 'Await' Expression
    ;

Await è una parola riservata se il metodo di inclusione immediata o l'espressione lambda in cui viene visualizzato ha un Async modificatore e, se Await viene visualizzato dopo tale Async modificatore, non è riservato altrove. È anche non disponibile nelle direttive del preprocessore. L'operatore await è consentito solo nel corpo di un metodo o di espressioni lambda in cui si tratta di una parola riservata. All'interno del metodo di inclusione immediata o lambda, un'espressione await potrebbe non verificarsi all'interno del corpo di un Catch blocco o Finally né all'interno del corpo di un'istruzione, né all'interno di un'espressione SyncLock di query.

L'operatore await accetta una singola espressione che deve essere classificata come valore e il cui tipo deve essere un tipo awaitable o Object. Se il tipo è Object , tutte le elaborazioni vengono posticipate fino al runtime. Si dice che un tipo C sia awaitable se sono soddisfatte tutte le condizioni seguenti:

  • C contiene un'istanza accessibile o un metodo di estensione denominato GetAwaiter senza argomenti e che restituisce un tipo E;

  • E contiene un'istanza leggibile o una proprietà di estensione denominata IsCompleted che non accetta argomenti e ha tipo Boolean;

  • E contiene un'istanza accessibile o un metodo di estensione denominato GetResult che non accetta argomenti;

  • E implementa System.Runtime.CompilerServices.INotifyCompletion o ICriticalNotifyCompletion.

Se GetResult è un oggetto Sub, l'espressione await viene classificata come void. In caso contrario, l'espressione await viene classificata come valore e il relativo tipo è il tipo restituito del GetResult metodo.

Di seguito è riportato un esempio di classe che può essere attesa:

Class MyTask(Of T)
    Function GetAwaiter() As MyTaskAwaiter(Of T)
        Return New MyTaskAwaiter With {.m_Task = Me}
    End Function

    ...
End Class

Structure MyTaskAwaiter(Of T)
    Implements INotifyCompletion

    Friend m_Task As MyTask(Of T)

    ReadOnly Property IsCompleted As Boolean
        Get
            Return m_Task.IsCompleted
        End Get
    End Property

    Sub OnCompleted(r As Action) Implements INotifyCompletion.OnCompleted
        ' r is the "resumptionDelegate"
        Dim sc = SynchronizationContext.Current
        If sc Is Nothing Then
            m_Task.ContinueWith(Sub() r())
        Else
            m_Task.ContinueWith(Sub() sc.Post(Sub() r(), Nothing))
        End If
    End Sub

    Function GetResult() As T
        If m_Task.IsCanceled Then Throw New TaskCanceledException(m_Task)
        If m_Task.IsFaulted Then Throw m_Task.Exception.InnerException
        Return m_Task.Result
    End Function
End Structure

Nota. È consigliabile che gli autori di librerie seguano il modello in cui richiamano il delegato di continuazione nello stesso SynchronizationContextOnCompleted modo in cui è stato richiamato. Inoltre, il delegato di ripresa non deve essere eseguito in modo sincrono all'interno del OnCompleted metodo perché può causare l'overflow dello stack: invece, il delegato deve essere accodato per l'esecuzione successiva.

Quando il flusso di controllo raggiunge un Await operatore, il comportamento è il seguente.

  1. Viene GetAwaiter richiamato il metodo dell'operando await. Il risultato di questa chiamata è definito awaiter.

  2. La proprietà del IsCompleted awaiter viene recuperata. Se il risultato è true, procedere come segue:

    1. Viene GetResult richiamato il metodo del awaiter. Se GetResult era una funzione, il valore dell'espressione await è il valore restituito di questa funzione.
  3. Se la proprietà IsCompleted non è true, procedere come segue:

    1. Viene ICriticalNotifyCompletion.UnsafeOnCompleted richiamato sul awaiter (se il tipo E di awaiter implementa ICriticalNotifyCompletion) o (in INotifyCompletion.OnCompleted caso contrario). In entrambi i casi passa un delegato di ripresa associato all'istanza corrente del metodo asincrono.

    2. Il punto di controllo dell'istanza del metodo asincrono corrente viene sospeso e il flusso di controllo riprende nel chiamante corrente (definito in Metodi asincroni della sezione).

    3. Se in seguito viene richiamato il delegato di ripresa,

      1. il delegato di ripresa ripristina System.Threading.Thread.CurrentThread.ExecutionContext prima di tutto ciò che era al momento OnCompleted della chiamata,
      2. riprende quindi il flusso di controllo nel punto di controllo dell'istanza del metodo asincrono (vedere La sezione Metodi asincroni),
      3. dove chiama il GetResult metodo del awaiter, come nella versione 2.1 precedente.

Se l'operando await ha tipo Object, questo comportamento viene posticipato fino al runtime:

  • Il passaggio 1 viene eseguito chiamando GetAwaiter() senza argomenti; può quindi essere associato in fase di esecuzione ai metodi di istanza che accettano parametri facoltativi.
  • Il passaggio 2 viene eseguito recuperando la proprietà IsCompleted() senza argomenti e tentando una conversione intrinseca in booleano.
  • Il passaggio 3.a viene eseguito tentando TryCast(awaiter, ICriticalNotifyCompletion)di eseguire e, se l'operazione ha esito negativo, DirectCast(awaiter, INotifyCompletion).

Il delegato di ripresa passato in 3.a può essere richiamato una sola volta. Se viene richiamato più volte, il comportamento non è definito.