Partilhar via


Expressões no Visual Basic

Uma expressão é uma sequência de operadores e operandos que especifica um cálculo de um valor, ou que designa uma variável ou constante. Este capítulo define a sintaxe, a ordem de avaliação dos operandos e operadores e o significado das expressões.

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

Classificações de expressão

Cada expressão é classificada como uma das seguintes:

  • Um valor. Cada valor tem um tipo associado.

  • Uma variável. Cada variável tem um tipo associado, ou seja, o tipo declarado da variável.

  • Um namespace. Uma expressão com essa classificação só pode aparecer como o lado esquerdo de um acesso de membro. Em qualquer outro contexto, uma expressão classificada como um namespace causa um erro em tempo de compilação.

  • Um tipo. Uma expressão com essa classificação só pode aparecer como o lado esquerdo de um acesso de membro. Em qualquer outro contexto, uma expressão classificada como um tipo causa um erro em tempo de compilação.

  • Um grupo de métodos, que é um conjunto de métodos sobrecarregados com o mesmo nome. Um grupo de métodos pode ter uma expressão de destino associada e uma lista de argumentos de tipo associada.

  • Um ponteiro de método, que representa o local de um método. Um ponteiro de método pode ter uma expressão de destino associada e uma lista de argumentos de tipo associada.

  • Um método lambda, que é um método anônimo.

  • Um grupo de propriedades, que é um conjunto de propriedades sobrecarregadas com o mesmo nome. Um grupo de propriedades pode ter uma expressão de destino associada.

  • Um acesso à propriedade. Cada acesso à propriedade tem um tipo associado, ou seja, o tipo da propriedade. Um acesso à propriedade pode ter uma expressão de destino associada.

  • Um acesso de associação tardia, que representa um método ou acesso de propriedade adiado até o tempo de execução. Um acesso de associação tardia pode ter uma expressão de destino associada e uma lista de argumentos de tipo associada. O tipo de acesso tardio é sempre Object.

  • Entrada para um evento Cada acesso ao evento tem um tipo associado, ou seja, o tipo do evento. Um acesso a eventos pode ter uma expressão de destino associada. Um acesso a eventos pode aparecer como o primeiro argumento das RaiseEventinstruções , AddHandlere RemoveHandler . Em qualquer outro contexto, uma expressão classificada como um acesso a eventos causa um erro em tempo de compilação.

  • Um literal de matriz, que representa os valores iniciais de uma matriz cujo tipo ainda não foi determinado.

  • Nulo. Isso ocorre quando a expressão é uma invocação de uma sub-rotina ou uma expressão de operador de espera sem resultado. Uma expressão classificada como nula só é válida no contexto de uma declaração de invocação ou de uma declaração de espera.

  • Um valor padrão. Só o literal Nothing produz essa classificação.

O resultado final de uma expressão é geralmente um valor ou uma variável, com as outras categorias de expressões funcionando como valores intermediários que só são permitidos em determinados contextos.

Observe que expressões cujo tipo é um parâmetro de tipo podem ser usadas em instruções e expressões que exigem que o tipo de uma expressão tenha certas características (como ser um tipo de referência, tipo de valor, derivar de algum tipo, etc.) se as restrições impostas ao parâmetro de tipo satisfizerem essas características.

Reclassificação da expressão

Normalmente, quando uma expressão é usada em um contexto que requer uma classificação diferente da da expressão, ocorre um erro em tempo de compilação -- por exemplo, tentando atribuir um valor a um literal. No entanto, em muitos casos é possível alterar a classificação de uma expressão através do processo de reclassificação.

Se a reclassificação for bem-sucedida, então a reclassificação é julgada como alargamento ou estreitamento. Salvo indicação em contrário, todas as reclassificações nesta lista estão a aumentar.

Os seguintes tipos de expressões podem ser reclassificados:

  • Uma variável pode ser reclassificada como um valor. O valor armazenado na variável é buscado.

  • Um grupo de métodos pode ser reclassificado como um valor. A expressão de grupo de método é interpretada como uma expressão de invocação com a expressão de destino associada e a lista de parâmetros de tipo, e parênteses vazios (ou seja, f é interpretada como f() e f(Of Integer) é interpretada como f(Of Integer)()). Esta reclassificação pode levar a que a expressão seja novamente reclassificada como nula.

  • Um ponteiro de método pode ser reclassificado como um valor. Esta reclassificação só pode ocorrer no contexto de uma conversão em que o tipo de destino é conhecido. A expressão de ponteiro do método é interpretada como o argumento para uma expressão de instanciação delegada do tipo apropriado com a lista de argumentos de tipo associada. Por exemplo:

    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
    
  • Um método lambda pode ser reclassificado como um valor. Se a reclassificação ocorrer no contexto de uma conversão em que o tipo de destino é conhecido, então uma das duas reclassificações pode ocorrer:

    1. Se o tipo de destino for um tipo delegado, o método lambda será interpretado como o argumento para uma expressão de construção delegada do tipo apropriado.

    2. Se o tipo de destino for System.Linq.Expressions.Expression(Of T), e T for um tipo delegado, o método lambda será interpretado como se estivesse sendo usado na expressão de construção delegada para T e, em seguida, convertido em uma árvore de expressão.

    Um método lambda assíncrono ou iterador só pode ser interpretado como o argumento para uma expressão de construção delegada, se o delegado não tiver parâmetros ByRef.

    Se a conversão de qualquer um dos tipos de parâmetros do delegado para os tipos de parâmetros lambda correspondentes for uma conversão de estreitamento, então a reclassificação será julgada como estreitamento; caso contrário, está a alargar-se.

    Nota. A tradução exata entre métodos lambda e árvores de expressão pode não ser fixada entre versões do compilador e está além do escopo desta especificação. Para o Microsoft Visual Basic 11.0, todas as expressões lambda podem ser convertidas em árvores de expressão sujeitas às seguintes restrições: (1) 1. Somente expressões lambda de linha única sem parâmetros ByRef podem ser convertidas em árvores de expressão. Das lambdas de linha Sub única, apenas instruções de invocação podem ser convertidas em árvores de expressão. (2) Expressões de tipo anônimo não podem ser convertidas em árvores de expressão se um inicializador de campo anterior for usado para inicializar um inicializador de campo subsequente, por exemplo. New With {.a=1, .b=.a} (3) As expressões do inicializador de objeto não podem ser convertidas em árvores de expressão se um membro do objeto atual que está sendo inicializado for usado em um dos inicializadores de campo, por exemplo. New C1 With {.a=1, .b=.Method1()} (4) As expressões de criação de matrizes multidimensionais só podem ser convertidas em árvores de expressão se declararem explicitamente o seu tipo de elemento. (5) As expressões de ligação tardia não podem ser convertidas em árvores de expressão. (6) Quando uma variável ou campo é passado ByRef para uma expressão de invocação, mas não tem exatamente o mesmo tipo que o parâmetro ByRef, ou quando uma propriedade é passada ByRef, semântica VB normal é que uma cópia do argumento é passada ByRef e seu valor final é então copiado de volta para a variável ou campo ou propriedade. Nas árvores de expressão, o copy-back não acontece. (7) Todas estas restrições também se aplicam a expressões lambda aninhadas.

    Se o tipo de destino não for conhecido, o método lambda será interpretado como o argumento para uma expressão de instanciação delegada de um tipo de delegado anônimo com a mesma assinatura do método lambda. Se a semântica estrita estiver sendo usada e o tipo de qualquer um dos parâmetros for omitido, ocorrerá um erro em tempo de compilação; caso contrário, Object é substituído por qualquer tipo de parâmetro ausente. Por exemplo:

    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
    
  • Um grupo de propriedades pode ser reclassificado como um acesso à propriedade. A expressão de grupo de propriedades é interpretada como uma expressão de índice com parênteses vazios (ou seja, f é interpretada como f()).

  • Um acesso à propriedade pode ser reclassificado como um valor. A expressão de acesso à propriedade é interpretada como uma expressão de invocação do Get acessador da propriedade. Se a propriedade não tiver getter, ocorrerá um erro em tempo de compilação.

  • Um acesso de associação tardia pode ser reclassificado como um método de associação tardia ou acesso de propriedade de associação tardia. Em uma situação em que um acesso vinculado tardio pode ser reclassificado como um acesso de método e como um acesso de propriedade, a reclassificação para um acesso de propriedade é preferível.

  • Um acesso tardio pode ser reclassificado como um valor.

  • Um literal de matriz pode ser reclassificado como um valor. O tipo de valor é determinado da seguinte forma:

    1. Se a reclassificação ocorrer no contexto de uma conversão em que o tipo de destino é conhecido e o tipo de destino é um tipo de matriz, então o literal de matriz é reclassificado como um valor do tipo T(). Se o tipo de destino for , , , , ou IEnumerable(Of T), e o literal de matriz tiver um nível de aninhamento, então o literal de matriz será reclassificado como um valor do tipo T(). IReadOnlyCollection(Of T)ICollection(Of T)IReadOnlyList(Of T)System.Collections.Generic.IList(Of T)

    2. Caso contrário, o literal da matriz é reclassificado para um valor cujo tipo é uma matriz de classificação igual ao nível de aninhamento é usado, com o tipo de elemento determinado pelo tipo dominante dos elementos no inicializador; se nenhum tipo dominante puder ser determinado, Object é usado. Por exemplo:

      ' 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. Há uma ligeira mudança no comportamento entre a versão 9.0 e a versão 10.0 do idioma. Antes da 10.0, os inicializadores de elementos de matriz não afetavam a inferência de tipo de variável local e agora afetam. Assim Dim a() = { 1, 2, 3 } teria inferido Object() como o tipo de a na versão 9.0 da linguagem e Integer() na versão 10.0.

    Em seguida, a reclassificação reinterpreta o literal da matriz como uma expressão de criação de matriz. Assim, os exemplos:

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

    são equivalentes a:

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

    A reclassificação é julgada como estreitamento se qualquer conversão de uma expressão de elemento para o tipo de elemento de matriz estiver estreitando; caso contrário, julga-se que está a alargar-se.

  • O valor Nothing padrão pode ser reclassificado como um valor. Em um contexto onde o tipo de destino é conhecido, o resultado é o valor padrão do tipo de destino. Em um contexto onde o tipo de destino não é conhecido, o resultado é um valor nulo do tipo Object.

Uma expressão de namespace, expressão de tipo, expressão de acesso a evento ou expressão nula não pode ser reclassificada. Várias reclassificações podem ser feitas ao mesmo tempo. Por exemplo:

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

Nesse caso, a expressão P do grupo de propriedades é primeiro reclassificada de um grupo de propriedades para um acesso à propriedade e, em seguida, reclassificada de um acesso à propriedade para um valor. O menor número de reclassificações é realizado para alcançar uma classificação válida no contexto.

Expressões constantes

Uma expressão constante é uma expressão cujo valor pode ser totalmente avaliado em tempo de compilação.

ConstantExpression
    : Expression
    ;

O tipo de uma expressão constante pode ser Byte, SByte, , UShort, Short, IntegerUInteger, ULongCharStringLongDoubleObjectSingleDecimalDateBooleanou qualquer tipo de enumeração. As seguintes construções são permitidas em expressões constantes:

  • Literais (incluindo Nothing).

  • Referências a membros de tipo constante ou locais constantes.

  • Referências a membros de tipos de enumeração.

  • Subexpressões entre parênteses.

  • Expressões de coerção, desde que o tipo de destino seja um dos tipos listados acima. As coerções de e String para são uma exceção a essa regra e só são permitidas em valores nulos porque String as conversões são sempre feitas na cultura atual do ambiente de execução em tempo de execução. Observe que expressões de coerção constantes só podem usar conversões intrínsecas.

  • Os +operadores , - e Not unários, desde que o operando e o resultado sejam de um tipo listado acima.

  • Os +operadores binários , -, *, \<^And&Or>>Xor<<AndAlsoOrElse/Mod><>=<=e => binários, desde que cada operando e resultado seja de um tipo listado acima.

  • O operador condicional If, desde que cada operando e resultado seja de um tipo listado acima.

  • As seguintes funções de tempo de execução: Microsoft.VisualBasic.Strings.ChrW; Microsoft.VisualBasic.Strings.Chr se o valor da constante estiver entre 0 e 128; Microsoft.VisualBasic.Strings.AscW se a cadeia de caracteres constante não estiver vazia; Microsoft.VisualBasic.Strings.Asc se a cadeia de caracteres constante não estiver vazia.

As seguintes construções não são permitidas em expressões constantes:

  • Vinculação implícita através de um With contexto.

Expressões constantes de um tipo integral (ULong, Long, UInteger, Integer, UShort, ShortSByte, , ou Byte) podem ser implicitamente convertidas em um tipo integral mais estreito, e expressões constantes de tipo Double podem ser implicitamente convertidas em Single, desde que o valor da expressão constante esteja dentro do intervalo do tipo de destino. Essas conversões estreitas são permitidas independentemente de a semântica permissiva ou estrita estar sendo usada.

Late-Bound Expressões

Quando o destino de uma expressão de acesso de membro ou expressão de índice é do tipo Object, o processamento da expressão pode ser adiado até o tempo de execução. O adiamento do processamento dessa forma é chamado de vinculação tardia. A vinculação tardia permite que Object as variáveis sejam usadas de forma sem tipo , onde toda a resolução dos membros é baseada no tipo de tempo de execução real do valor na variável. Se a semântica estrita for especificada pelo ambiente de compilação ou pelo Option Strict, a vinculação tardia causará um erro em tempo de compilação. Os membros não públicos são ignorados quando fazem vinculação tardia, inclusive para fins de resolução de sobrecarga. Observe que, ao contrário do caso de associação antecipada, invocar ou acessar um Shared membro vinculado tardiamente fará com que o destino de invocação seja avaliado em tempo de execução. Se a expressão for uma expressão de invocação para um membro definido em System.Object, a vinculação tardia não ocorrerá.

Em geral, os acessos tardios são resolvidos em tempo de execução pesquisando o identificador no tipo de tempo de execução real da expressão. Se a pesquisa de membros com ligação tardia falhar em tempo de execução, uma System.MissingMemberException exceção será lançada. Como a pesquisa de membros de associação tardia é feita somente fora do tipo de tempo de execução da expressão de destino associada, o tipo de tempo de execução de um objeto nunca é uma interface. Portanto, é impossível acessar membros da interface em uma expressão de acesso de membro vinculado tardiamente.

Os argumentos para um acesso de membro vinculado tardiamente são avaliados na ordem em que aparecem na expressão de acesso de membro: não na ordem em que os parâmetros são declarados no membro vinculado tardiamente. O exemplo a seguir ilustra essa diferença:

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

Este código exibe:

Early-bound: xy
Late-bound: yx

Como a resolução de sobrecarga tardia é feita no tipo de tempo de execução dos argumentos, é possível que uma expressão produza resultados diferentes com base no fato de ser avaliada em tempo de compilação ou em tempo de execução. O exemplo a seguir ilustra essa diferença:

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

Este código exibe:

F(Base)
F(Derived)

Expressões simples

Expressões simples são literais, expressões entre parênteses, expressões de ocorrência ou expressões de nome simples.

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

Expressões literais

As expressões literais avaliam o valor representado pelo literal. Uma expressão literal é classificada como um valor, exceto para o literal Nothing, que é classificado como um valor padrão.

LiteralExpression
    : Literal
    ;

Expressões entre parênteses

Uma expressão entre parênteses consiste numa expressão entre parênteses. Uma expressão entre parênteses é classificada como um valor, e a expressão entre parênteses deve ser classificada como um valor. Uma expressão entre parênteses avalia o valor da expressão entre parênteses.

ParenthesizedExpression
    : OpenParenthesis Expression CloseParenthesis
    ;

Expressões de instância

Uma expressão de instância é a palavra-chave Me. Ele só pode ser usado dentro do corpo de um método não compartilhado, construtor ou acessador de propriedade. É classificado como um valor. A palavra-chave Me representa a instância do tipo que contém o método ou acessador de propriedade que está sendo executado. Se um construtor invoca explicitamente outro construtor (Section Constructors), Me não pode ser usado até depois dessa chamada do construtor, porque a instância ainda não foi construída.

InstanceExpression
    : 'Me'
    ;

Expressões de nome simples

Uma expressão de nome simples consiste em um único identificador seguido por uma lista de argumentos de tipo opcional.

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

O nome é resolvido e classificado pelas seguintes "regras simples de resolução de nomes":

  1. Começando com o bloco imediatamente fechado e continuando com cada bloco externo fechado (se houver), se o identificador corresponder ao nome de uma variável local, variável estática, constante local, parâmetro de tipo de método ou parâmetro, então o identificador refere-se à entidade correspondente.

    Se o identificador corresponder a uma variável local, variável estática ou constante local e uma lista de argumentos de tipo tiver sido fornecida, ocorrerá um erro em tempo de compilação. Se o identificador corresponder a um parâmetro de tipo de método e uma lista de argumentos de tipo tiver sido fornecida, nenhuma correspondência ocorrerá e a resolução continuará. Se o identificador corresponder a uma variável local, a variável local correspondente for a função implícita ou Get a variável local de retorno do acessador, e a expressão fizer parte de uma expressão de invocação, instrução de invocação ou expressão AddressOf , então nenhuma correspondência ocorrerá e a resolução continuará.

    A expressão é classificada como uma variável se for uma variável local, variável estática ou parâmetro. A expressão é classificada como um tipo se for um parâmetro de tipo de método. A expressão é classificada como um valor se for uma constante local.

  2. Para cada tipo aninhado que contém a expressão, começando do mais interno e indo para o mais externo, se uma pesquisa do identificador no tipo produzir uma correspondência com um membro acessível:

    1. Se o membro do tipo correspondente for um parâmetro type, o resultado será classificado como um tipo e será o parâmetro do tipo correspondente. Se uma lista de argumentos de tipo foi fornecida, nenhuma correspondência ocorrerá e a resolução continuará.
    2. Caso contrário, se o tipo for o tipo imediatamente anexado e a pesquisa identificar um membro do tipo não compartilhado, o resultado será o mesmo que um acesso de membro do formulário Me.E(Of A), onde E é o identificador e A é a lista de argumentos de tipo, se houver.
    3. Caso contrário, o resultado é exatamente o mesmo que um acesso de membro do formulário T.E(Of A), onde T é o tipo que contém o membro correspondente, E é o identificador e A é a lista de argumentos de tipo, se houver. Nesse caso, é um erro para o identificador se referir a um membro não compartilhado.
  3. Para cada namespace aninhado, começando do mais interno e indo para o namespace externo, faça o seguinte:

    1. Se o namespace contiver um tipo acessível com o nome fornecido e tiver o mesmo número de parâmetros de tipo que foi fornecido na lista de argumentos de tipo, se houver, o identificador se refere a esse tipo e é classificado como um tipo.
    2. Caso contrário, se nenhuma lista de argumentos de tipo foi fornecida e o namespace contém um membro de namespace com o nome fornecido, o identificador se refere a esse namespace e é classificado como um namespace.
    3. Caso contrário, se o namespace contiver um ou mais módulos padrão acessíveis e uma pesquisa de nome de membro do identificador produzir uma correspondência acessível em exatamente um módulo padrão, o resultado será exatamente o mesmo que um acesso de membro do formulário M.E(Of A), onde M é o módulo padrão que contém o membro correspondente, E é o identificador e A é a lista de argumentos de tipo, se for caso disso. Se o identificador corresponder a membros de tipo acessíveis em mais de um módulo padrão, ocorrerá um erro em tempo de compilação.
  4. Se o arquivo de origem tiver um ou mais aliases de importação e o identificador corresponder ao nome de um deles, o identificador se referirá a esse namespace ou tipo. Se uma lista de argumentos de tipo for fornecida, ocorrerá um erro em tempo de compilação.

  5. Se o ficheiro de origem que contém a referência de nome tiver uma ou mais importações:

    1. Se o identificador corresponder exatamente em uma importação o nome de um tipo acessível com o mesmo número de parâmetros de tipo que foi fornecido na lista de argumentos de tipo, se houver, ou um membro de tipo, então o identificador se refere a esse tipo ou membro do tipo. Se o identificador corresponder em mais de uma importação, o nome de um tipo acessível com o mesmo número de parâmetros de tipo que foi fornecido na lista de argumentos de tipo, se houver, ou um membro de tipo acessível, ocorrerá um erro em tempo de compilação.
    2. Caso contrário, se nenhuma lista de argumentos de tipo tiver sido fornecida e o identificador corresponder exatamente a uma importação do nome de um namespace com tipos acessíveis, o identificador se referirá a esse namespace. Se nenhuma lista de argumentos de tipo foi fornecida e o identificador corresponde em mais de uma importação o nome de um namespace com tipos acessíveis, ocorre um erro em tempo de compilação.
    3. Caso contrário, se as importações contiverem um ou mais módulos padrão acessíveis e uma pesquisa de nome de membro do identificador produzir uma correspondência acessível em exatamente um módulo padrão, o resultado será exatamente o mesmo que um acesso de membro do formulário M.E(Of A), onde M é o módulo padrão que contém o membro correspondente, E é o identificador e A é a lista de argumentos de tipo, se for caso disso. Se o identificador corresponder a membros de tipo acessíveis em mais de um módulo padrão, ocorrerá um erro em tempo de compilação.
  6. Se o ambiente de compilação definir um ou mais aliases de importação e o identificador corresponder ao nome de um deles, o identificador se refere a esse namespace ou tipo. Se uma lista de argumentos de tipo for fornecida, ocorrerá um erro em tempo de compilação.

  7. Se o ambiente de compilação definir uma ou mais importações:

    1. Se o identificador corresponder exatamente em uma importação o nome de um tipo acessível com o mesmo número de parâmetros de tipo que foi fornecido na lista de argumentos de tipo, se houver, ou um membro de tipo, então o identificador se refere a esse tipo ou membro do tipo. Se o identificador corresponder em mais de uma importação, o nome de um tipo acessível com o mesmo número de parâmetros de tipo que foi fornecido na lista de argumentos de tipo, se houver, ou um membro do tipo, ocorrerá um erro em tempo de compilação.
    2. Caso contrário, se nenhuma lista de argumentos de tipo tiver sido fornecida e o identificador corresponder exatamente a uma importação do nome de um namespace com tipos acessíveis, o identificador se referirá a esse namespace. Se nenhuma lista de argumentos de tipo foi fornecida e o identificador corresponde em mais de uma importação o nome de um namespace com tipos acessíveis, ocorre um erro em tempo de compilação.
    3. Caso contrário, se as importações contiverem um ou mais módulos padrão acessíveis e uma pesquisa de nome de membro do identificador produzir uma correspondência acessível em exatamente um módulo padrão, o resultado será exatamente o mesmo que um acesso de membro do formulário M.E(Of A), onde M é o módulo padrão que contém o membro correspondente, E é o identificador e A é a lista de argumentos de tipo, se for caso disso. Se o identificador corresponder a membros de tipo acessíveis em mais de um módulo padrão, ocorrerá um erro em tempo de compilação.
  8. Caso contrário, o nome dado pelo identificador será indefinido.

Uma expressão de nome simples que é indefinida é um erro em tempo de compilação.

Normalmente, um nome só pode ocorrer uma vez em um namespace específico. No entanto, como os namespaces podem ser declarados em vários assemblies .NET, é possível ter uma situação em que dois assemblies definem um tipo com o mesmo nome totalmente qualificado. Nesse caso, um tipo declarado no conjunto atual de arquivos de origem é preferível a um tipo declarado em um assembly .NET externo. Caso contrário, o nome é ambíguo e não há como desambiguar o nome.

EndereçoDas Expressões

Uma AddressOf expressão é usada para produzir um ponteiro de método. A expressão consiste na AddressOf palavra-chave e em uma expressão que deve ser classificada como um grupo de métodos ou um acesso tardio. O grupo de método não pode se referir a construtores.

O resultado é classificado como um ponteiro de método, com a mesma expressão de destino associada e lista de argumentos de tipo (se houver) que o grupo de métodos.

AddressOfExpression
    : 'AddressOf' Expression
    ;

Expressões de tipo

Uma expressão de tipo é uma GetType expressão, uma TypeOf...Is expressão, uma Is expressão ou uma GetXmlNamespace expressão.

TypeExpression
    : GetTypeExpression
    | TypeOfIsExpression
    | IsExpression
    | GetXmlNamespaceExpression
    ;

Expressões GetType

Uma GetType expressão consiste na palavra-chave GetType e no nome de um 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*
    ;

Uma GetType expressão é classificada como um valor, e seu valor é a classe de reflexão (System.Type) que representa seu GetTypeTypeName. Se GetTypeTypeName for um parâmetro type, a expressão retornará o System.Type objeto que corresponde ao argumento type fornecido para o parâmetro type em tempo de execução.

O GetTypeTypeName é especial de duas maneiras:

  • É permitido ser System.Void, o único lugar na língua onde este nome de tipo pode ser referenciado.

  • Pode ser um tipo genérico construído com os argumentos de tipo omitidos. Isso permite que a GetType expressão retorne o System.Type objeto que corresponde ao próprio tipo genérico.

O exemplo a seguir demonstra a GetType expressão:

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

A saída resultante é:

Int32
Int32
String
Double[]

Tipode... É Expressões

Uma TypeOf...Is expressão é usada para verificar se o tipo de tempo de execução de um valor é compatível com um determinado tipo. O primeiro operando deve ser classificado como um valor, não pode ser um método lambda reclassificado e deve ser de um tipo de referência ou um tipo de parâmetro de tipo não restrito. O segundo operando deve ser um nome de tipo. O resultado da expressão é classificado como um valor e é um Boolean valor. A expressão avalia True se o tipo de tempo de execução do operando tem uma conversão de identidade, padrão, referência, matriz, tipo de valor ou parâmetro de tipo para o tipo, False caso contrário. Um erro em tempo de compilação ocorre se não existir nenhuma conversão entre o tipo da expressão e o tipo específico.

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

É Expressões

Uma Is expressão ou IsNot é usada para fazer uma comparação de igualdade de referência.

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

Cada expressão deve ser classificada como um valor e o tipo de cada expressão deve ser um tipo de referência, um tipo de parâmetro de tipo não restrito ou um tipo de valor anulável. No entanto, se o tipo de uma expressão for um tipo de parâmetro de tipo sem restrições ou um tipo de valor anulável, a outra expressão deverá ser a literal Nothing.

O resultado é classificado como um valor e digitado como Boolean. Uma Is operação avalia se True ambos os valores se referem à mesma instância ou se ambos os valores são Nothing, ou False de outra forma. Uma IsNot operação avalia se False ambos os valores se referem à mesma instância ou se ambos os valores são Nothing, ou True de outra forma.

Expressões GetXmlNamespace

Uma GetXmlNamespace expressão consiste na palavra-chave GetXmlNamespace e no nome de um namespace XML declarado pelo arquivo de origem ou ambiente de compilação.

GetXmlNamespaceExpression
    : 'GetXmlNamespace' OpenParenthesis XMLNamespaceName? CloseParenthesis
    ;

Uma GetXmlNamespace expressão é classificada como um valor, e seu valor é uma instância que System.Xml.Linq.XNamespace representa o XMLNamespaceName. Se esse tipo não estiver disponível, ocorrerá um erro em tempo de compilação.

Por exemplo:

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

Tudo entre parênteses é considerado parte do nome do namespace, portanto, regras XML em torno de coisas como espaço em branco se aplicam. Por exemplo:

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

A expressão de namespace XML também pode ser omitida, caso em que a expressão retorna o objeto que representa o namespace XML padrão.

Expressões de acesso de membro

Uma expressão de acesso de membro é usada para acessar um membro de uma entidade.

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

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

Um acesso de membro do formulário E.I(Of A), onde E é uma expressão, um nome de tipo não-matriz, a palavra-chave Globalou omitido e I é um identificador com uma lista Ade argumentos de tipo opcional , é avaliado e classificado da seguinte forma:

  1. Se E for omitido, a expressão da instrução imediatamente contendo With será substituída E e o acesso de membro será executado. Se não houver nenhuma instrução contendo With , ocorrerá um erro em tempo de compilação.

  2. Se E for classificado como um namespace ou E for a palavra-chave Global, a pesquisa de membro será feita no contexto do namespace especificado. Se I for o nome de um membro acessível desse namespace com o mesmo número de parâmetros de tipo que foi fornecido na lista de argumentos de tipo, se houver, o resultado será esse membro. O resultado é classificado como um namespace ou um tipo, dependendo do membro. Caso contrário, ocorrerá um erro em tempo de compilação.

  3. Se E é um tipo ou uma expressão classificada como um tipo, então a pesquisa de membro é feita no contexto do tipo especificado. Se I é o nome de um membro acessível do E, então E.I é avaliado e classificado da seguinte forma:

    1. Se I é a palavra-chave New e E não é uma enumeração, então ocorre um erro em tempo de compilação.
    2. Se I identificar um tipo com o mesmo número de parâmetros de tipo que foi fornecido na lista de argumentos de tipo, se houver, o resultado será esse tipo.
    3. Se I identificar um ou mais métodos, o resultado será um grupo de métodos com a lista de argumentos de tipo associada e nenhuma expressão de destino associada.
    4. Se I identificar uma ou mais propriedades e nenhuma lista de argumentos de tipo tiver sido fornecida, o resultado será um grupo de propriedades sem expressão de destino associada.
    5. Se I identificar uma variável compartilhada e nenhuma lista de argumentos de tipo tiver sido fornecida, o resultado será uma variável ou um valor. Se a variável for somente leitura e a referência ocorrer fora do construtor compartilhado do tipo no qual a variável é declarada, o resultado será o valor da variável I compartilhada em E. Caso contrário, o resultado é a variável I compartilhada em E.
    6. Se I identificar um evento compartilhado e nenhuma lista de argumentos de tipo tiver sido fornecida, o resultado será um acesso a eventos sem expressão de destino associada.
    7. Se I identificar uma constante e nenhuma lista de argumentos de tipo tiver sido fornecida, o resultado será o valor dessa constante.
    8. Se I identificar um membro de enumeração e nenhuma lista de argumentos de tipo tiver sido fornecida, o resultado será o valor desse membro de enumeração.
    9. Caso contrário, E.I é uma referência de membro inválida e ocorre um erro em tempo de compilação.
  4. Se E é classificado como uma variável ou valor, cujo tipo é T, então a pesquisa de membro é feita no contexto de T. Se I é o nome de um membro acessível do T, então E.I é avaliado e classificado da seguinte forma:

    1. Se I for a palavra-chave New, E is Me, MyBaseou , e MyClassnenhum argumento de tipo tiver sido fornecido, o resultado será um grupo de métodos que representa os construtores de instância do tipo de com uma expressão de destino associada de e nenhuma lista de E argumentos de E tipo. Caso contrário, ocorrerá um erro em tempo de compilação.
    2. Se I identificar um ou mais métodos, incluindo métodos de extensão, se T não Objectfor , o resultado será um grupo de métodos com a lista de argumentos de tipo associada e uma expressão de destino associada de E.
    3. Se I identificar uma ou mais propriedades e nenhum argumento de tipo tiver sido fornecido, o resultado será um grupo de propriedades com uma expressão de destino associada de E.
    4. Se I identificar uma variável compartilhada ou uma variável de instância e nenhum argumento de tipo tiver sido fornecido, o resultado será uma variável ou um valor. Se a variável for somente leitura e a referência ocorrer fora de um construtor da classe na qual a variável é declarada apropriada para o tipo de variável (compartilhada ou instância), o resultado será o valor da variável I no objeto referenciado por E. Se T for um tipo de referência, o resultado será a variável I no objeto referenciado por E. Caso contrário, se T é um tipo de valor e a expressão E é classificada como uma variável, o resultado é uma variável, caso contrário, o resultado é um valor.
    5. Se I identificar um evento e nenhum argumento de tipo tiver sido fornecido, o resultado será um acesso a evento com uma expressão de destino associada de E.
    6. Se I identificar uma constante e nenhum argumento de tipo tiver sido fornecido, o resultado será o valor dessa constante.
    7. Se I identificar um membro de enumeração e nenhum argumento de tipo tiver sido fornecido, o resultado será o valor desse membro de enumeração.
    8. Se T for Object, o resultado é uma pesquisa de membro de associação tardia classificada como um acesso de associação tardia com a lista de argumentos de tipo associada e uma expressão de destino associada de E.
  5. Caso contrário, E.I é uma referência de membro inválida e ocorre um erro em tempo de compilação.

O acesso de um membro ao formulário MyClass.I(Of A) é equivalente ao Me.I(Of A), mas todos os membros acessados nele são tratados como se os membros não fossem substituíveis. Assim, o membro acessado não será afetado pelo tipo de tempo de execução do valor no qual o membro está sendo acessado.

Um acesso de membro do formulário MyBase.I(Of A) é equivalente a CType(Me, T).I(Of A) onde T é o tipo base direto do tipo que contém a expressão de acesso de membro. Todas as invocações de método nele são tratadas como se o método que está sendo invocado não fosse substituível. Esta forma de acesso de membro também é chamada de acesso base.

O exemplo a seguir demonstra como Me, MyBase e MyClass relacionar:

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

Este código imprime:

MoreDerived.F
Derived.F
Derived.F

Quando uma expressão de acesso de membro começa com a palavra-chave Global, a palavra-chave representa o namespace sem nome mais externo, o que é útil em situações em que uma declaração sombreia um namespace anexo. A Global palavra-chave permite "escapar" para o namespace externo nessa situação. Por exemplo:

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

No exemplo acima, a primeira chamada de método é inválida porque o identificador System se liga à classe System, não ao namespace System. A única maneira de acessar o System namespace é usar Global para escapar para o namespace mais externo.

Se o membro que está sendo acessado for compartilhado, qualquer expressão no lado esquerdo do período é supérflua e não é avaliada, a menos que o acesso do membro seja feito tardiamente. Por exemplo, considere o seguinte código:

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

Ele é impresso The value of F is: 10 porque a função ReturnC não precisa ser chamada para fornecer uma instância de C para acessar o membro Fcompartilhado.

Tipo idêntico e nomes de membros

Não é incomum nomear membros usando o mesmo nome que seu tipo. Nessa situação, no entanto, a ocultação inconveniente de nomes pode ocorrer:

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

No exemplo anterior, o nome Color simples em DefaultColor se liga à propriedade instance em vez do type. Como um membro da instância não pode ser referenciado em um membro compartilhado, isso normalmente seria um erro.

No entanto, uma regra especial permite o acesso ao tipo neste caso. Se a expressão base de uma expressão de acesso de membro for um nome simples e se ligar a uma constante, campo, propriedade, variável local ou parâmetro cujo tipo tenha o mesmo nome, então a expressão base pode referir-se ao membro ou ao tipo. Isso nunca pode resultar em ambiguidade, porque os membros que podem ser acessados fora de qualquer um deles são os mesmos.

Instâncias padrão

Em algumas situações, as classes derivadas de uma classe base comum geralmente ou sempre têm apenas uma única instância. Por exemplo, a maioria das janelas mostradas em uma interface do usuário só tem uma instância exibida na tela a qualquer momento. Para simplificar o trabalho com esses tipos de classes, o Visual Basic pode gerar automaticamente instâncias padrão das classes que fornecem uma única instância facilmente referenciada para cada classe.

As instâncias padrão são sempre criadas para uma família de tipos em vez de para um tipo específico. Portanto, em vez de criar uma instância padrão para uma classe Form1 que deriva de Form, instâncias padrão são criadas para todas as classes derivadas de Form. Isso significa que cada classe individual que deriva da classe base não precisa ser especialmente marcada para ter uma instância padrão.

A instância padrão de uma classe é representada por uma propriedade gerada pelo compilador que retorna a instância padrão dessa classe. A propriedade gerada como um membro de uma classe chamada classe de grupo que gerencia a alocação e destruição de instâncias padrão para todas as classes derivadas da classe base específica. Por exemplo, todas as propriedades de instância padrão de classes derivadas de Form podem ser coletadas na MyForms classe. Se uma instância da classe de grupo for retornada pela expressão My.Forms, o código a seguir acessará as instâncias padrão de classes Form1 derivadas 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

As instâncias padrão não serão criadas até a primeira referência a elas; Buscar a propriedade que representa a instância padrão faz com que a instância padrão seja criada se ainda não tiver sido criada ou tiver sido definida como Nothing. Para permitir o teste da existência de uma instância padrão, quando uma instância padrão é o destino de um Is operador OR IsNot , a instância padrão não será criada. Assim, é possível testar se uma instância padrão é Nothing ou alguma outra referência sem fazer com que a instância padrão seja criada.

As instâncias padrão destinam-se a facilitar a referência à instância padrão de fora da classe que tem a instância padrão. Usar uma instância padrão de dentro de uma classe que a define pode causar confusão sobre qual instância está sendo referida, ou seja, a instância padrão ou a instância atual. Por exemplo, o código a seguir modifica apenas o valor x na instância padrão, mesmo que ele esteja sendo chamado de outra instância. Assim, o código imprimiria o valor 5 em vez de 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

Para evitar esse tipo de confusão, não é válido fazer referência a uma instância padrão de dentro de um método de instância do tipo da instância padrão.

Instâncias padrão e nomes de tipo

Uma instância padrão também pode ser acessível diretamente através do nome de seu tipo. Nesse caso, em qualquer contexto de expressão em que o nome do tipo não seja permitido, a expressão E, onde E representa o nome totalmente qualificado da classe com uma instância padrão, é alterada para E', onde E' representa uma expressão que busca a propriedade de instância padrão. Por exemplo, se instâncias padrão para classes derivadas de permitir acessar a instância padrão através do nome do Form tipo, o código a seguir é equivalente ao código no exemplo anterior:

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

Isso também significa que uma instância padrão acessível por meio do nome do tipo também é atribuível por meio do nome do tipo. Por exemplo, o código a seguir define a instância padrão de Form1 como Nothing:

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

Observe que o significado de E.I were E representa uma classe e I representa um membro compartilhado não muda. Essa expressão ainda acessa o membro compartilhado diretamente da instância de classe e não faz referência à instância padrão.

Aulas de Grupo

O Microsoft.VisualBasic.MyGroupCollectionAttribute atributo indica a classe de grupo para uma família de instâncias padrão. O atributo tem quatro parâmetros:

  • O parâmetro TypeToCollect especifica a classe base para o grupo. Todas as classes instanciáveis sem parâmetros de tipo abertos que derivam de um tipo com esse nome (independentemente dos parâmetros de tipo) terão automaticamente uma instância padrão.

  • O parâmetro CreateInstanceMethodName especifica o método a ser chamado na classe de grupo para criar uma nova instância em uma propriedade de instância padrão.

  • O parâmetro DisposeInstanceMethodName especifica o método a ser chamado na classe de grupo para descartar uma propriedade de instância padrão se a propriedade de instância padrão for atribuída o valor Nothing.

  • O parâmetro DefaultInstanceAlias especifica a expressão E' para substituir o nome da classe se as instâncias padrão estiverem acessíveis diretamente por meio de seu nome de tipo. Se esse parâmetro for Nothing ou uma cadeia de caracteres vazia, as instâncias padrão nesse tipo de grupo não serão acessíveis diretamente por meio do nome do tipo. (Observação. Em todas as implementações atuais da linguagem Visual Basic, o DefaultInstanceAlias parâmetro é ignorado, exceto no código fornecido pelo compilador.)

Vários tipos podem ser coletados no mesmo grupo, separando os nomes dos tipos e métodos nos três primeiros parâmetros usando vírgulas. Deve haver o mesmo número de itens em cada parâmetro e os elementos da lista são correspondidos em ordem. Por exemplo, a declaração de atributo a seguir coleta tipos que derivam de C1ou C3C2 em um único grupo:

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

A assinatura do método create deve ser do formulário Shared Function <Name>(Of T As {New, <Type>})(Instance Of T) As T. O método de eliminação deve assumir a forma Shared Sub <Name>(Of T As <Type>)(ByRef Instance Of T). Assim, a classe de grupo para o exemplo na seção anterior pode ser declarada da seguinte forma:

<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 um arquivo de origem declarasse uma classe Form1derivada, a classe de grupo gerada seria 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

Coleção de métodos de extensão

Os métodos de extensão para a expressão E.I de acesso de membro são coletados reunindo todos os métodos de extensão com o nome I disponíveis no contexto atual:

  1. Primeiro, cada tipo aninhado que contém a expressão é verificado, começando do mais interno e indo para o mais externo.
  2. Em seguida, cada namespace aninhado é verificado, começando pelo namespace mais interno e indo para o namespace mais externo.
  3. Em seguida, as importações no arquivo de origem são verificadas.
  4. Em seguida, as importações definidas pelo ambiente de compilação são verificadas.

Um método de extensão é coletado somente se houver uma conversão nativa ampliada do tipo de expressão de destino para o tipo do primeiro parâmetro do método de extensão. E, ao contrário da vinculação de expressão de nome simples regular, a pesquisa coleta todos os métodos de extensão; A coleção não para quando um método de extensão é encontrado. Por exemplo:

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

Neste exemplo, embora N2C1Extensions.M1 seja encontrado antes N1C1Extensions.M1, ambos são considerados como métodos de extensão. Uma vez que todos os métodos de extensão tenham sido coletados, eles são então curados. Currying pega o destino da chamada do método de extensão e o aplica à chamada do método de extensão, resultando em uma nova assinatura de método com o primeiro parâmetro removido (porque ele foi especificado). Por exemplo:

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

No exemplo acima, o resultado da aplicação v é Ext1.M a assinatura Sub M(y As Integer)do método.

Além de remover o primeiro parâmetro do método de extensão, o currying também remove quaisquer parâmetros de tipo de método que fazem parte do tipo do primeiro parâmetro. Ao enrolar um método de extensão com o parâmetro de tipo de método, a inferência de tipo é aplicada ao primeiro parâmetro e o resultado é fixado para quaisquer parâmetros de tipo que são inferidos. Se a inferência de tipo falhar, o método será ignorado. Por exemplo:

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

No exemplo acima, o resultado curried de aplicar v a Ext1.M é a assinatura Sub M(Of U)(y As U)do método , porque o parâmetro T type é inferido como resultado do currying e agora é fixo. Como o parâmetro type não foi inferido como parte do currying U , ele permanece um parâmetro aberto. Da mesma forma, como o parâmetro T type é inferido como resultado da aplicação v ao Ext2.M, o tipo de parâmetro y torna-se fixo como Integer. Não se inferirá que seja de outro tipo. Ao enrolar a assinatura, todas as restrições, exceto New as restrições, também são aplicadas. Se as restrições não forem satisfeitas ou dependerem de um tipo que não foi inferido como parte do currying, o método de extensão será ignorado. Por exemplo:

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. Uma das principais razões para fazer currying de métodos de extensão é que ele permite que expressões de consulta inferam o tipo da iteração antes de avaliar os argumentos para um método de padrão de consulta. Como a maioria dos métodos de padrão de consulta usa expressões lambda, que exigem inferência de tipo, isso simplifica muito o processo de avaliação de uma expressão de consulta.

Ao contrário da herança de interface normal, os métodos de extensão que estendem duas interfaces que não se relacionam entre si estão disponíveis, desde que não tenham a mesma assinatura 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

Finalmente, é importante lembrar que os métodos de extensão não são considerados ao fazer a vinculação tardia:

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

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

Expressões de acesso de membro do dicionário

Uma expressão de acesso de membro de dicionário é usada para procurar um membro de uma coleção. Um acesso de membro do dicionário assume a forma de , onde E é uma expressão que é classificada E!Icomo um valor e I é um identificador.

DictionaryAccessExpression
    : Expression? '!' IdentifierOrKeyword
    ;

O tipo da expressão deve ter uma propriedade padrão indexada por um único String parâmetro. A expressão E!I de acesso de membro do dicionário é transformada na expressão E.D("I"), onde D é a propriedade padrão de E. Por exemplo:

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 um ponto de exclamação é especificado sem expressão, a expressão da instrução imediatamente contendo With é assumida. Se não houver nenhuma instrução contendo With , ocorrerá um erro em tempo de compilação.

Expressões de invocação

Uma expressão de invocação consiste em um destino de invocação e uma lista de argumentos opcionais.

InvocationExpression
    : Expression ( OpenParenthesis ArgumentList? CloseParenthesis )?
    ;

ArgumentList
    : PositionalArgumentList
    | PositionalArgumentList Comma NamedArgumentList
    | NamedArgumentList
    ;

PositionalArgumentList
    : Expression? ( Comma Expression? )*
    ;

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

A expressão de destino deve ser classificada como um grupo de métodos ou um valor cujo tipo é um tipo delegado. Se a expressão de destino for um valor cujo tipo é um tipo delegado, o destino da expressão de invocação se tornará o grupo de métodos para o Invoke membro do tipo delegado e a expressão de destino se tornará a expressão de destino associada do grupo de métodos.

Uma lista de argumentos tem duas seções: argumentos posicionais e argumentos nomeados. Argumentos posicionais são expressões e devem preceder quaisquer argumentos nomeados. Os argumentos nomeados começam com um identificador que pode corresponder a palavras-chave, seguido por := e uma expressão.

Se o grupo de métodos contiver apenas um método acessível, incluindo métodos de instância e extensão, e esse método não usar argumentos e for uma função, o grupo de métodos será interpretado como uma expressão de invocação com uma lista de argumentos vazia e o resultado será usado como o destino de uma expressão de invocação com a(s) lista(s) de argumentos fornecida(s). Por exemplo:

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

Caso contrário, a resolução de sobrecarga é aplicada aos métodos para escolher o método mais aplicável para a(s) lista(s) de argumentos. Se o método mais aplicável for uma função, o resultado da expressão de invocação será classificado como um valor digitado como o tipo de retorno da função. Se o método mais aplicável for uma sub-rotina, o resultado é classificado como nulo. Se o método mais aplicável for um método parcial que não tenha corpo, a expressão de invocação será ignorada e o resultado será classificado como nulo.

Para uma expressão de invocação de ligação antecipada, os argumentos são avaliados na ordem em que os parâmetros correspondentes são declarados no método de destino. Para uma expressão de acesso de membro de associação tardia, eles são avaliados na ordem em que aparecem na expressão de acesso de membro: consulte SeçãoLate-Bound Expressões.

Resolução de método sobrecarregado:

Para Resolução de sobrecarga, Especificidade de membros/tipos dados uma lista de argumentos, Genericidade, Aplicabilidade à lista de argumentos, Passando argumentos e Escolhendo argumentos para parâmetros opcionais, Métodos condicionais e Inferência de argumento de tipo: consulte Resolução de sobrecarga de seção.

Expressões de índice

Uma expressão de índice resulta em um elemento de matriz ou reclassifica um grupo de propriedades em um acesso à propriedade. Uma expressão de índice consiste, em ordem, em uma expressão, um parêntese de abertura, uma lista de argumentos de índice e um parêntese de fechamento.

IndexExpression
    : Expression OpenParenthesis ArgumentList? CloseParenthesis
    ;

O destino da expressão de índice deve ser classificado como um grupo de propriedades ou um valor. Uma expressão de índice é processada da seguinte forma:

  • Se a expressão de destino for classificada como um valor e se seu tipo não for um tipo de matriz, Objectou System.Array, o tipo deverá ter uma propriedade padrão. O índice é executado em um grupo de propriedades que representa todas as propriedades padrão do tipo. Embora não seja válido declarar uma propriedade padrão sem parâmetros no Visual Basic, outros idiomas podem permitir a declaração de tal propriedade. Consequentemente, a indexação de uma propriedade sem argumentos é permitida.

  • Se a expressão resultar em um valor de um tipo de matriz, o número de argumentos na lista de argumentos deve ser o mesmo que a classificação do tipo de matriz e não pode incluir argumentos nomeados. Se qualquer um dos índices for inválido em tempo de execução, uma System.IndexOutOfRangeException exceção será lançada. Cada expressão deve ser implicitamente convertível em tipo Integer. O resultado da expressão do índice é a variável no índice especificado e é classificado como uma variável.

  • Se a expressão for classificada como um grupo de propriedades, a resolução de sobrecarga será usada para determinar se uma das propriedades é aplicável à lista de argumentos de índice. Se o grupo de propriedades contiver apenas uma propriedade que tenha um Get acessador e se esse acessador não usar argumentos, o grupo de propriedades será interpretado como uma expressão de índice com uma lista de argumentos vazia. O resultado é usado como o destino da expressão de índice atual. Se nenhuma propriedade for aplicável, ocorrerá um erro em tempo de compilação. Caso contrário, a expressão resultará em um acesso à propriedade com a expressão de destino associada (se houver) do grupo de propriedades.

  • Se a expressão for classificada como um grupo de propriedades de associação tardia ou como um valor cujo tipo é Object ou System.Array, o processamento da expressão de índice será adiado até o tempo de execução e a indexação for vinculada tardiamente. A expressão resulta em um acesso à propriedade de associação tardia digitado como Object. A expressão de destino associada é a expressão de destino, se for um valor, ou a expressão de destino associada do grupo de propriedades. Em tempo de execução, a expressão é processada da seguinte forma:

  • Se a expressão for classificada como um grupo de propriedades de associação tardia, a expressão poderá resultar em um grupo de métodos, um grupo de propriedades ou um valor (se o membro for uma instância ou variável compartilhada). Se o resultado for um grupo de métodos ou um grupo de propriedades, a resolução de sobrecarga será aplicada ao grupo para determinar o método correto para a lista de argumentos. Se a resolução de sobrecarga falhar, uma System.Reflection.AmbiguousMatchException exceção será lançada. Em seguida, o resultado é processado como um acesso à propriedade ou como uma invocação e o resultado é retornado. Se a invocação for de uma sub-rotina, o resultado será Nothing.

  • Se o tipo de tempo de execução da expressão de destino for um tipo de matriz ou System.Array, o resultado da expressão de índice será o valor da variável no índice especificado.

  • Caso contrário, o tipo de tempo de execução da expressão deve ter uma propriedade padrão e o índice é executado no grupo de propriedades que representa todas as propriedades padrão no tipo. Se o tipo não tiver nenhuma propriedade padrão, uma System.MissingMemberException exceção será lançada.

Novas expressões

O operador New é usado para criar novas instâncias de tipos. Existem quatro formas de New expressão:

  • As expressões de criação de objeto são usadas para criar novas instâncias de tipos de classe e tipos de valor.

  • As expressões de criação de matriz são usadas para criar novas instâncias de tipos de matriz.

  • As expressões de criação delegada (que não têm uma sintaxe distinta das expressões de criação de objeto) são usadas para criar novas instâncias de tipos delegados.

  • Expressões anônimas de criação de objeto são usadas para criar novas instâncias de tipos de classe anônimos.

NewExpression
    : ObjectCreationExpression
    | ArrayExpression
    | AnonymousObjectCreationExpression
    ;

Uma New expressão é classificada como um valor e o resultado é a nova instância do tipo.

Object-Creation Expressões

Uma expressão de criação de objeto é usada para criar uma nova instância de um tipo de classe ou um tipo de estrutura.

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
    ;

O tipo de uma expressão de criação de objeto deve ser um tipo de classe, um tipo de estrutura ou um parâmetro de tipo com uma New restrição e não pode ser uma MustInherit classe. Dada uma expressão de criação de objeto do formulário New T(A), onde T é um tipo de classe ou tipo de estrutura e A é uma lista de argumentos opcional, a resolução de sobrecarga determina o construtor correto de T chamar. Um parâmetro type com uma New restrição é considerado como tendo um único construtor sem parâmetros. Se nenhum construtor for chamável, ocorrerá um erro em tempo de compilação; caso contrário, a expressão resulta na criação de uma nova instância de T uso do construtor escolhido. Se não houver argumentos, os parênteses podem ser omitidos.

Onde uma instância é alocada depende se a instância é um tipo de classe ou um tipo de valor. New Instâncias de tipos de classe são criadas no heap do sistema, enquanto novas instâncias de tipos de valor são criadas diretamente na pilha.

Uma expressão de criação de objeto pode, opcionalmente, especificar uma lista de inicializadores de membro após os argumentos do construtor. Esses inicializadores de membros são prefixados com a palavra-chave With, e a lista de inicializadores é interpretada como se estivesse no contexto de uma With instrução. Por exemplo, dada a classe:

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

O código:

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

É aproximadamente equivalente 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

Cada inicializador deve especificar um nome a ser atribuído, e o nome deve ser uma variável ou propriedade não-instânciaReadOnly do tipo que está sendo construído, o acesso do membro não será vinculado tardiamente se o tipo que está sendo construído for Object. Os inicializadores não podem usar a Key palavra-chave. Cada membro de um tipo só pode ser inicializado uma vez. As expressões do inicializador, no entanto, podem se referir umas às outras. Por exemplo:

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

Os inicializadores são atribuídos da esquerda para a direita, portanto, se um inicializador se referir a um membro que ainda não foi inicializado, ele verá qualquer valor que a variável de instância execute após a execução do construtor:

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

Os inicializadores podem ser aninhados:

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 o tipo que está sendo criado for um tipo de coleção e tiver um método de instância chamado Add (incluindo métodos de extensão e métodos compartilhados), a expressão de criação de objeto poderá especificar um inicializador de coleção prefixado pela palavra-chave From. Uma expressão de criação de objeto não pode especificar um inicializador de membro e um inicializador de coleção. Cada elemento no inicializador da coleção é passado como um argumento para uma invocação da Add função. Por exemplo:

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

é equivalente a:

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

Se um elemento for um inicializador de coleção em si, cada elemento do inicializador de subcoleção será passado como um argumento individual para a Add função. Por exemplo, o seguinte:

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

é equivalente a:

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

Esta expansão é sempre feita e só é feita a um nível de profundidade; Depois disso, os subinicializadores são considerados literais de matriz. Por exemplo:

' 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 } } }

Expressões de matriz

Uma expressão de matriz é usada para criar uma nova instância de um tipo de matriz. Existem dois tipos de expressões de matriz: expressões de criação de matriz e literais de matriz.

Expressões de criação de matriz

Se um modificador de inicialização de tamanho de matriz for fornecido, o tipo de matriz resultante será derivado excluindo cada um dos argumentos individuais da lista de argumentos de inicialização de tamanho de matriz. O valor de cada argumento determina o limite superior da dimensão correspondente na instância de matriz recém-alocada. Se a expressão tiver um inicializador de coleção não vazio, cada argumento na lista de argumentos deverá ser uma constante, e os comprimentos de classificação e dimensão especificados pela lista de expressões deverão corresponder aos do inicializador de coleção.

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 um modificador de inicialização de tamanho de matriz não for fornecido, o nome do tipo deverá ser um tipo de matriz e o inicializador de coleção deverá estar vazio ou ter o mesmo número de níveis de aninhamento que a classificação do tipo de matriz especificado. Todos os elementos no nível de aninhamento mais interno devem ser implicitamente conversíveis para o tipo de elemento da matriz e devem ser classificados como um valor. O número de elementos em cada inicializador de coleção aninhado deve ser sempre consistente com o tamanho das outras coleções no mesmo nível. Os comprimentos de dimensão individuais são inferidos a partir do número de elementos em cada um dos níveis de aninhamento correspondentes do inicializador de coleção. Se o inicializador da coleção estiver vazio, o comprimento de cada dimensão será 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 } }

O nível de aninhamento mais externo de um inicializador de coleção corresponde à dimensão mais à esquerda de uma matriz, e o nível de aninhamento mais interno corresponde à dimensão mais à direita. O exemplo:

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

É equivalente ao seguinte:

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 o inicializador de coleção estiver vazio (ou seja, um que contenha chaves mas nenhuma lista de inicializadores) e os limites das dimensões da matriz que está sendo inicializada forem conhecidos, o inicializador de coleção vazio representará uma instância de matriz do tamanho especificado onde todos os elementos foram inicializados com o valor padrão do tipo de elemento. Se os limites das dimensões da matriz que está sendo inicializada não forem conhecidos, o inicializador de coleção vazio representará uma ocorrência de matriz na qual todas as dimensões serão de tamanho zero.

A classificação e o comprimento de uma instância de matriz de cada dimensão são constantes durante todo o tempo de vida da instância. Em outras palavras, não é possível alterar a classificação de uma instância de matriz existente, nem é possível redimensionar suas dimensões.

Literais de matriz

Um literal de matriz denota uma matriz cujo tipo de elemento, classificação e limites são inferidos a partir de uma combinação do contexto da expressão e um inicializador de coleção. Isso é explicado na Seção Reclassificação de Expressões.

ArrayExpression
    : ArrayCreationExpression
    | ArrayLiteralExpression
    ;

ArrayCreationExpression
    : 'New' NonArrayTypeName ArrayNameModifier CollectionInitializer
    ;

ArrayLiteralExpression
    : CollectionInitializer
    ;

Por exemplo:

' 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}}

O formato e os requisitos para o inicializador de coleção em um literal de matriz é exatamente o mesmo que para o inicializador de coleção em uma expressão de criação de matriz.

Nota. Um literal de matriz não cria a matriz em si mesma; em vez disso, é a reclassificação da expressão em um valor que faz com que a matriz seja criada. Por exemplo, a conversão CType(new Integer() {1,2,3}, Short()) não é possível porque não há conversão de para Short(), mas a expressão CType({1,2,3},Short()) é possível porque primeiro reclassifica a matriz literal na expressão New Short() {1,2,3}de criação de Integer() matriz.

Delegate-Creation Expressões

Uma expressão de criação de delegado é usada para criar uma nova instância de um tipo de delegado. O argumento de uma expressão de criação delegada deve ser uma expressão classificada como um ponteiro de método ou um método lambda.

Se o argumento for um ponteiro de método, um dos métodos referenciados pelo ponteiro de método deve ser aplicável à assinatura do tipo delegado. Um método M é aplicável a um tipo D de delegado se:

  • M não Partial é ou tem um corpo.

  • Ambos M e D são funções, ou D é uma sub-rotina.

  • M e D têm o mesmo número de parâmetros.

  • Os tipos de parâmetros de M cada um têm uma conversão do tipo do tipo de parâmetro correspondente de D, e seus modificadores (ou seja ByRef, ByVal) correspondem.

  • O tipo de retorno de M, se houver, tem uma conversão para o tipo de retorno de D.

Se o ponteiro do método fizer referência a um acesso de ligação tardia, então o acesso de associação tardia é assumido como sendo para uma função que tem o mesmo número de parâmetros que o tipo de delegado.

Se semântica estrita não está sendo usada e há apenas um método referenciado pelo ponteiro do método, mas não é aplicável devido ao fato de que ele não tem parâmetros e o tipo delegado tem, então o método é considerado aplicável e os parâmetros ou valor de retorno são simplesmente ignorados. Por exemplo:

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. Este relaxamento só é permitido quando a semântica estrita não está sendo usada devido a métodos de extensão. Como os métodos de extensão só são considerados se um método regular não era aplicável, é possível para um método de instância sem parâmetros ocultar um método de extensão com parâmetros para fins de construção delegada.

Se mais de um método referenciado pelo ponteiro do método for aplicável ao tipo delegado, a resolução de sobrecarga será usada para escolher entre os métodos candidatos. Os tipos dos parâmetros para o delegado são usados como os tipos dos argumentos para fins de resolução de sobrecarga. Se nenhum candidato de método for mais aplicável, ocorrerá um erro em tempo de compilação. No exemplo a seguir, a variável local é inicializada com um delegado que se refere ao segundo Square método porque esse método é mais aplicável ao tipo de assinatura e retorno de 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 o segundo Square método não estivesse presente, o primeiro Square método teria sido escolhido. Se a semântica estrita for especificada pelo ambiente de compilação ou pelo Option Strict, ocorrerá um erro em tempo de compilação se o método mais específico referenciado pelo ponteiro do método for mais estreito do que a assinatura delegada. Um método M é considerado mais restrito do que um tipo D de delegado se:

  • Um tipo de parâmetro de tem uma conversão de alargamento para o tipo de M parâmetro correspondente de D.

  • Ou, o tipo de retorno, se houver, de tem uma conversão de M estreitamento para o tipo de retorno de D.

Se os argumentos de tipo estiverem associados ao ponteiro do método, somente os métodos com o mesmo número de argumentos de tipo serão considerados. Se nenhum argumento de tipo estiver associado ao ponteiro do método, a inferência de tipo será usada ao fazer a correspondência de assinaturas com um método genérico. Ao contrário de outras inferências de tipo normais, o tipo de retorno do delegado é usado ao inferir argumentos de tipo, mas os tipos de retorno ainda não são considerados ao determinar a menor sobrecarga genérica. O exemplo a seguir mostra as duas maneiras de fornecer um argumento type para uma expressão de criação delegada:

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

No exemplo acima, um tipo de delegado não genérico foi instanciado usando um método genérico. Também é possível criar uma instância de um tipo de delegado construído usando um método genérico. Por exemplo:

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 o argumento para a expressão de criação delegada for um método lambda, o método lambda deverá ser aplicável à assinatura do tipo delegado. Um método L lambda é aplicável a um tipo de delegado D se:

  • Se L tem parâmetros, D tem o mesmo número de parâmetros. (Se L não tiver parâmetros, os parâmetros de D são ignorados.)

  • Os tipos de parâmetros de L cada um têm uma conversão para o tipo do tipo de parâmetro correspondente de D, e seus modificadores (ou seja ByRef, ByVal) correspondem.

  • Se D for uma função, o tipo de retorno de L tem uma conversão para o tipo de retorno de D. (Se D for uma sub-rotina, o valor de retorno de L será ignorado.)

Se o tipo de parâmetro de um parâmetro de L é omitido, então o tipo do parâmetro correspondente em D é inferido, se o parâmetro de tem modificadores de matriz ou nome anulável, um erro em tempo de L compilação resulta. Uma vez que todos os tipos de parâmetros estão L disponíveis, então o tipo da expressão no método lambda é inferido. Por exemplo:

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

Em algumas situações em que a assinatura de delegado não corresponde exatamente ao método lambda ou assinatura de método, o .NET Framework pode não suportar a criação de delegado nativamente. Nessa situação, uma expressão de método lambda é usada para corresponder os dois métodos. Por exemplo:

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

O resultado de uma expressão de criação delegada é uma instância delegada que se refere ao método de correspondência com a expressão de destino associada (se houver) da expressão de ponteiro do método. Se a expressão de destino for digitada como um tipo de valor, o tipo de valor será copiado para o heap do sistema porque um delegado só pode apontar para um método de um objeto no heap. O método e o objeto ao qual um delegado se refere permanecem constantes durante todo o tempo de vida do delegado. Em outras palavras, não é possível alterar o destino ou objeto de um delegado depois que ele foi criado.

Expressões Object-Creation anónimas

Uma expressão de criação de objeto com inicializadores de membro também pode omitir totalmente o nome do tipo.

AnonymousObjectCreationExpression
    : 'New' ObjectMemberInitializer
    ;

Nesse caso, um tipo anônimo é construído com base nos tipos e nomes dos membros inicializados como parte da expressão. Por exemplo:

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

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

O tipo criado por uma expressão anônima de criação de objeto é uma classe que não tem nome, herda diretamente de e tem um conjunto de propriedades com o mesmo nome que os membros atribuídos na lista de inicializadores de Objectmembros. O tipo de cada propriedade é inferido usando as mesmas regras que a inferência de tipo de variável local. Os tipos anônimos gerados também substituem ToString, retornando uma representação de cadeia de caracteres de todos os membros e seus valores. (O formato exato desta cadeia de caracteres está além do escopo desta especificação).

Por padrão, as propriedades geradas pelo tipo anônimo são leitura-gravação. É possível marcar uma propriedade de tipo anônimo como somente leitura usando o Key modificador. O Key modificador especifica que o campo pode ser usado para identificar exclusivamente o valor que o tipo anônimo representa. Além de tornar a propriedade somente leitura, ele também faz com que o tipo anônimo substitua Equals e GetHashCode implemente a interface System.IEquatable(Of T) (preenchendo o tipo anônimo para T). Os membros são definidos do seguinte modo:

Function Equals(obj As Object) As Boolean e Function Equals(val As T) As Boolean são implementados validando que as duas instâncias são do mesmo tipo e, em seguida, comparando cada Key membro usando Object.Equals. Se todos os Key membros são iguais, então Equals retorna True, caso contrário Equals , retorna False.

Function GetHashCode() As Integer é implementado de tal forma que, se Equals for verdadeiro para duas instâncias do tipo anônimo, retornará GetHashCode o mesmo valor. O hash começa com um valor seed e, em seguida, para cada Key membro, em ordem, multiplica o hash por 31 e adiciona o valor de hash do Key membro (fornecido por GetHashCode) se o membro não for um tipo de referência ou tipo de valor anulável com o valor de Nothing.

Por exemplo, o tipo criado na instrução:

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

Cria uma classe que se parece aproximadamente com isso (embora a implementação exata possa variar):

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

Para simplificar a situação em que um tipo anônimo é criado a partir dos campos de outro tipo, os nomes de campo podem ser inferidos diretamente a partir de expressões nos seguintes casos:

  • Uma expressão x de nome simples infere o nome x.

  • Uma expressão x.y de acesso de membro infere o nome y.

  • Uma expressão x!y de pesquisa de dicionário infere o nome y.

  • Uma invocação ou expressão de índice sem argumentos x() infere o nome x.

  • Uma expressão x.<y>de acesso de membro XML , x...<y>, x.@y infere o nome y.

  • Uma expressão de acesso de membro XML que é o destino de uma expressão x.<y>.z de acesso de membro infere o nome z.

  • Uma expressão de acesso de membro XML que é o destino de uma invocação ou expressão de índice sem argumentos x.<y>.z() infere o nome z.

  • Uma expressão de acesso de membro XML que é o destino de uma invocação ou expressão x.<y>(0) de índice infere o nome y.

O inicializador é interpretado como uma atribuição da expressão ao nome inferido. Por exemplo, os seguintes inicializadores são equivalentes:

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 for inferido um nome de membro que entra em conflito com um membro existente do tipo, como GetHashCode, ocorrerá um erro de tempo de compilação. Ao contrário dos inicializadores de membros regulares, as expressões anônimas de criação de objetos não permitem que os inicializadores de membros tenham referências circulares ou se refiram a um membro antes que ele tenha sido inicializado. Por exemplo:

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 duas expressões de criação de classe anônimas ocorrerem dentro do mesmo método e produzirem a mesma forma resultante -- se a ordem da propriedade, os nomes de propriedade e os tipos de propriedade corresponderem -- ambos se referirão à mesma classe anônima. O escopo do método de uma instância ou variável de membro compartilhado com um inicializador é o construtor no qual a variável é inicializada.

Nota. É possível que um compilador opte por unificar ainda mais tipos anônimos, como no nível de assembly, mas isso não pode ser invocado no momento.

Transmitir expressões

Uma expressão de elenco coage uma expressão a um determinado tipo. Palavras-chave fundidas específicas coagem expressões nos tipos primitivos. Três palavras-chave gerais lançam, CTypee TryCastDirectCast, coagem uma expressão em um 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 ter comportamentos especiais. Por isso, eles só suportam conversões nativas. Além disso, o tipo de destino em uma TryCast expressão não pode ser um tipo de valor. Os operadores de conversão definidos pelo usuário não são considerados quando DirectCast ou TryCast são usados. (Observação. O conjunto de conversões e DirectCastTryCast o suporte são restritos porque implementam conversões "CLR nativas". O objetivo é DirectCast fornecer a funcionalidade da instrução "unbox", enquanto o objetivo é TryCast fornecer a funcionalidade da instrução "isinst". Como eles são mapeados para instruções CLR, o suporte a conversões não suportadas diretamente pelo CLR derrotaria a finalidade pretendida.)

DirectCast Converte expressões que são digitadas de Object forma diferente de CType. Ao converter uma expressão de tipo Object cujo tipo de tempo de execução é um tipo de valor primitivo, DirectCast lança uma System.InvalidCastException exceção se o tipo especificado não for o mesmo que o tipo de tempo de execução da expressão ou um System.NullReferenceException se a expressão for avaliada como Nothing. (Observação. Como observado acima, DirectCast mapeia diretamente para a instrução CLR "unbox" quando o tipo da expressão é Object. Em contraste, CType transforma-se em uma chamada para um auxiliar de tempo de execução para fazer a conversão para que as conversões entre tipos primitivos possam ser suportadas. No caso em que uma Object expressão está sendo convertida em um tipo de valor primitivo e o tipo da instância real corresponde ao tipo de destino, DirectCast será significativamente mais rápido do que CType.)

TryCast converte expressões, mas não lança uma exceção se a expressão não puder ser convertida para o tipo de destino. Em vez disso, TryCast resultará em Nothing se a expressão não puder ser convertida em tempo de execução. (Observação. Como mencionado acima, TryCast mapeia diretamente para a instrução CLR "isinst". Ao combinar a verificação de tipo e a conversão em uma única operação, TryCast pode ser mais barato do que fazer um TypeOf ... Is e depois um CType.)

Por exemplo:

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 não houver conversão do tipo da expressão para o tipo especificado, ocorrerá um erro em tempo de compilação. Caso contrário, a expressão é classificada como um valor e o resultado é o valor produzido pela conversão.

Expressões do operador

Existem dois tipos de operadores. Os operadores unários usam um operando e usam notação de prefixo (por exemplo, -x). Os operadores binários usam dois operandos e usam notação infix (por exemplo, x + y). Com exceção dos operadores relacionais, que sempre resultam em Boolean, um operador definido para um determinado tipo resulta nesse tipo. Os operandos para um operador devem ser sempre classificados como um valor; O resultado de uma expressão de operador é classificado como um valor.

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

Precedência e Associatividade de Operadores

Quando uma expressão contém vários operadores binários, a precedência dos operadores controla a ordem na qual os operadores binários individuais são avaliados. Por exemplo, a expressão x + y * z é avaliada como x + (y * z) porque o * operador tem maior precedência do que o + operador. A tabela a seguir lista os operadores binários em ordem decrescente de precedência:

Categoria Operadores
Primário Todas as expressões não-operador
Aguarde Await
Exponenciação ^
Negação unária +, -
Multiplicativo *, /
Divisão inteira \
Modulus Mod
Aditivo +, -
Concatenação &
Turno <<, >>
Relacional =, <>, <, , >, >=<=, Like, Is,IsNot
Lógico NÃO Not
Lógica E And, AndAlso
OU Lógico Or, OrElse
XOR lógico Xor

Quando uma expressão contém dois operadores com a mesma precedência, a associatividade dos operadores controla a ordem em que as operações são executadas. Todos os operadores binários são associados à esquerda, o que significa que as operações são executadas da esquerda para a direita. A precedência e a associatividade podem ser controladas usando expressões entre parênteses.

Operandos de objeto

Além dos tipos regulares suportados por cada operador, todos os operadores suportam operandos do tipo Object. Os operadores aplicados a Object operandos são tratados de forma semelhante às chamadas de método feitas em Object valores: uma chamada de método de ligação tardia pode ser escolhida, caso em que o tipo de tempo de execução dos operandos, em vez do tipo de tempo de compilação, determina a validade e o tipo da operação. Se semânticas estritas forem especificadas pelo ambiente de compilação ou por Option Strict, quaisquer operadores com operandos do tipo Object causarão um erro em tempo de compilação, exceto para os TypeOf...Isoperadores , Is e IsNot .

Quando a resolução do operador determina que uma operação deve ser executada tardiamente, o resultado da operação é o resultado da aplicação do operador aos tipos de operando se os tipos de tempo de execução dos operandos forem tipos suportados pelo operador. O valor Nothing é tratado como o valor padrão do tipo do outro operando em uma expressão de operador binário. Em uma expressão de operador unário, ou se ambos os operandos estiverem Nothing em uma expressão de operador binário, o tipo da operação é Integer ou o único tipo de resultado do operador, se o operador não resultar em Integer. O resultado da operação é sempre lançado de volta para Object. Se os tipos de operando não tiverem nenhum operador válido, uma System.InvalidCastException exceção será lançada. As conversões em tempo de execução são feitas independentemente de serem implícitas ou explícitas.

Se o resultado de uma operação binária numérica produzir uma exceção de estouro (independentemente de a verificação de estouro de número inteiro estar ativada ou desativada), o tipo de resultado será promovido para o próximo tipo numérico mais amplo, se possível. Por exemplo, considere o seguinte código:

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

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

Imprime o seguinte resultado:

System.Int16 = 512

Se nenhum tipo numérico mais amplo estiver disponível para armazenar o número, uma System.OverflowException exceção será lançada.

Resolução do Operador

Dado um tipo de operador e um conjunto de operandos, a resolução do operador determina qual operador usar para os operandos. Ao resolver operadores, os operadores definidos pelo usuário serão considerados primeiro, usando as seguintes etapas:

  1. Em primeiro lugar, todos os operadores candidatos são recolhidos. Os operadores candidatos são todos os operadores definidos pelo usuário do tipo de operador específico no tipo de origem e todos os operadores definidos pelo usuário do tipo específico no tipo de destino. Se o tipo de origem e o tipo de destino estiverem relacionados, os operadores comuns serão considerados apenas uma vez.

  2. Em seguida, a resolução de sobrecarga é aplicada aos operadores e operandos para selecionar o operador mais específico. No caso de operadores binários, isso pode resultar em uma chamada tardia.

Ao coletar os operadores candidatos para um tipo T?, os operadores do tipo T são usados em vez disso. Qualquer um dos operadores definidos pelo usuário que envolvem apenas tipos de Tvalor não anuláveis também são levantados. Um operador lifted usa a versão anulável de qualquer tipo de valor, com exceção dos tipos de retorno de IsTrue e IsFalse (que deve ser Boolean). Os operadores levantados são avaliados convertendo os operandos em sua versão não anulável, avaliando o operador definido pelo usuário e, em seguida, convertendo o tipo de resultado em sua versão anulável. Se o operando ether for Nothing, o resultado da expressão será um valor digitado Nothing como a versão anulável do tipo de resultado. Por exemplo:

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 o operador for um operador binário e um dos operandos for do tipo de referência, o operador também será levantado, mas qualquer ligação ao operador produzirá um erro. Por exemplo:

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. Esta regra existe porque houve consideração se desejamos adicionar tipos de referência de propagação nula em uma versão futura, caso em que o comportamento no caso de operadores binários entre os dois tipos mudaria.

Tal como acontece com as conversões, os operadores definidos pelo utilizador são sempre preferidos em relação aos operadores levantados.

Ao resolver operadores sobrecarregados, pode haver diferenças entre as classes definidas no Visual Basic e aquelas definidas em outras linguagens:

  • Em outros idiomas, Not, And, e Or pode ser sobrecarregado como operadores lógicos e operadores bitwise. Após a importação de um conjunto externo, qualquer formulário é aceito como uma sobrecarga válida para esses operadores. No entanto, para um tipo que define operadores lógicos e bitwise, apenas a implementação bitwise será considerada.

  • Em outros idiomas, >> e << pode ser sobrecarregado como operadores assinados e operadores não assinados. Após a importação de um assembly externo, qualquer formulário é aceito como uma sobrecarga válida. No entanto, para um tipo que define operadores assinados e não assinados, apenas a implementação assinada será considerada.

  • Se nenhum operador definido pelo usuário for mais específico para os operandos, os operadores intrínsecos serão considerados. Se nenhum operador intrínseco for definido para os operandos e qualquer operando tiver o tipo Object, o operador será resolvido tardiamente; caso contrário, um erro em tempo de compilação resulta.

Em versões anteriores do Visual Basic, se houvesse exatamente um operando do tipo Object, e nenhum operador definido pelo usuário aplicável, e nenhum operador intrínseco aplicável, então era um erro. A partir do Visual Basic 11, ele agora é resolvido tardiamente. Por exemplo:

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

Um tipo T que tem um operador intrínseco também define esse mesmo operador para T?. O resultado do operador em T? será o mesmo que para T, exceto que, se qualquer operando for Nothing, o resultado do operador será Nothing (ou seja, o valor nulo é propagado). Para fins de resolução do tipo de uma operação, o ? é removido de quaisquer operandos que os possuem, o tipo da operação é determinado e a ? é adicionado ao tipo da operação se qualquer um dos operandos fosse tipos de valor anuláveis. Por exemplo:

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

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

Cada operador lista os tipos intrínsecos para os quais é definido e o tipo de operação executada de acordo com os tipos de operando. O resultado do tipo de uma operação intrínseca segue estas regras gerais:

  • Se todos os operandos forem do mesmo tipo, e o operador for definido para o tipo, nenhuma conversão ocorrerá e o operador para esse tipo será usado.

  • Qualquer operando cujo tipo não esteja definido para o operador é convertido usando as seguintes etapas e o operador é resolvido em relação aos novos tipos:

    • O operando é convertido para o próximo tipo mais amplo que é definido para o operador e o operando e para o qual ele é implicitamente conversível.

    • Se não houver esse tipo, então o operando é convertido para o próximo tipo mais estreito que é definido para o operador e o operando e para o qual ele é implicitamente conversível.

    • Se não houver esse tipo ou a conversão não puder ocorrer, ocorrerá um erro em tempo de compilação.

  • Caso contrário, os operandos são convertidos para o mais amplo dos tipos de operando e o operador para esse tipo é usado. Se o tipo de operando mais estreito não puder ser convertido implicitamente para o tipo de operador mais amplo, ocorrerá um erro em tempo de compilação.

No entanto, apesar destas regras gerais, há uma série de casos especiais assinalados nos quadros de resultados do operador.

Nota. Por motivos de formatação, as tabelas de tipo de operador abreviam os nomes predefinidos para seus dois primeiros caracteres. Assim, "Por" é Byte, "UI" é UInteger, "St" é String, etc. "Err" significa que não há nenhuma operação definida para os tipos de operando fornecidos.

Operadores Aritméticos

Os *operadores , /, \, ^, Mod, +, e - são os operadores aritméticos.

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

As operações aritméticas de vírgula flutuante podem ser efetuadas com maior precisão do que o tipo de resultado da operação. Por exemplo, algumas arquiteturas de hardware suportam um tipo de ponto flutuante "estendido" ou "duplo longo" com maior alcance e precisão do que o Double tipo, e executam implicitamente todas as operações de ponto flutuante usando esse tipo de maior precisão. As arquiteturas de hardware podem ser feitas para executar operações de ponto flutuante com menos precisão apenas com um custo excessivo de desempenho; em vez de exigir uma implementação para perder o desempenho e a precisão, o Visual Basic permite que o tipo de precisão mais alta seja usado para todas as operações de ponto flutuante. Além de fornecer resultados mais precisos, isso raramente tem efeitos mensuráveis. No entanto, em expressões da forma x * y / z, onde a multiplicação produz um resultado que está fora do Double intervalo, mas a divisão subsequente traz o resultado temporário de volta para o Double intervalo, o fato de a expressão ser avaliada em um formato de intervalo mais alto pode fazer com que um resultado finito seja produzido em vez de infinito.

Operador Unary Plus

UnaryPlusExpression
    : '+' Expression
    ;

O operador unary plus é definido para os Bytetipos , SByte, UShort, Short, UInteger, ULongInteger, LongSingleDoublee , ,Decimal

Tipo de operação:

Bo SB Por Sh EUA em Interface do usuário Lo UL De Si Fazer Da Ch São Ob
Sh SB Por Sh EUA In IU Lo UL De Si Faz Err Err Faz Ob

Unary Menos Operador

UnaryMinusExpression
    : '-' Expression
    ;

O operador unário menos é definido para os seguintes tipos:

SByte, Short, Integer e Long. O resultado é calculado subtraindo o operando de zero. Se a verificação de estouro de número inteiro estiver ativada e o valor do operando for o negativo SBytemáximo , , ShortInteger, ou Long, uma System.OverflowException exceção será lançada. Caso contrário, se o valor do operando for o negativo SBytemáximo , , ShortInteger, ou Long, o resultado será esse mesmo valor, e o estouro não será relatado.

Single e Double. O resultado é o valor do operando com seu sinal invertido, incluindo os valores 0 e Infinito. Se o operando for NaN, o resultado também será NaN.

Decimal. O resultado é calculado subtraindo o operando de zero.

Tipo de operação:

Bo SB Por Sh EUA em Interface do usuário Lo UL De Si Fazer Da Ch São Ob
Sh SB Sh Sh In In Lo Lo De De Si Faz Err Err Faz Ob

Operador de Adição

O operador de adição calcula a soma dos dois operandos.

AdditionOperatorExpression
    : Expression '+' LineTerminator? Expression
    ;

O operador de adição é definido para os seguintes tipos:

  • Byte, SByte, , UShort, Short, UInteger, ULongInteger, e Long. Se a verificação de estouro de número inteiro estiver ativada e a soma estiver fora do intervalo do tipo de resultado, uma System.OverflowException exceção será lançada. Caso contrário, os estouros não são relatados e quaisquer bits significativos de alta ordem do resultado são descartados.

  • Single e Double. A soma é calculada de acordo com as regras da aritmética IEEE 754.

  • Decimal. Se o valor resultante for muito grande para representar no formato decimal, uma System.OverflowException exceção será lançada. Se o valor do resultado for muito pequeno para ser representado no formato decimal, o resultado será 0.

  • String. Os dois String operandos são concatenados juntos.

  • Date. O System.DateTime tipo define operadores de adição sobrecarregados. Por System.DateTime ser equivalente ao tipo intrínseco Date , esses operadores também estão disponíveis no Date tipo.

Tipo de operação:

Bo SB Por Sh EUA em Interface do usuário Lo UL De Si Fazer Da Ch São Ob
Bo Sh SB Sh Sh In In Lo Lo De De Si Faz Err Err Faz Ob
SB SB Sh Sh In In Lo Lo De De Si Faz Err Err Faz Ob
Por Por Sh EUA In IU Lo UL De Si Faz Err Err Faz Ob
Sh Sh In In Lo Lo De De Si Faz Err Err Faz Ob
EUA EUA In IU Lo UL De Si Faz Err Err Faz Ob
em In Lo Lo De De Si Faz Err Err Faz Ob
Interface do usuário IU Lo UL De Si Faz Err Err Faz Ob
Lo Lo De De Si Faz Err Err Faz Ob
UL UL De Si Faz Err Err Faz Ob
De De Si Faz Err Err Faz Ob
Si Si Faz Err Err Faz Ob
Fazer Faz Err Err Faz Ob
Da São Err São Ob
Ch São São Ob
São São Ob
Ob Ob

Operador de Subtração

O operador de subtração subtrai o segundo operando do primeiro operando.

SubtractionOperatorExpression
    : Expression '-' LineTerminator? Expression
    ;

O operador de subtração é definido para os seguintes tipos:

  • Byte, SByte, , UShort, Short, UInteger, ULongInteger, e Long. Se a verificação de estouro de número inteiro estiver ativada e a diferença estiver fora do intervalo do tipo de resultado, uma System.OverflowException exceção será lançada. Caso contrário, os estouros não são relatados e quaisquer bits significativos de alta ordem do resultado são descartados.

  • Single e Double. A diferença é calculada de acordo com as regras da aritmética IEEE 754.

  • Decimal. Se o valor resultante for muito grande para representar no formato decimal, uma System.OverflowException exceção será lançada. Se o valor do resultado for muito pequeno para ser representado no formato decimal, o resultado será 0.

  • Date. O System.DateTime tipo define operadores de subtração sobrecarregados. Por System.DateTime ser equivalente ao tipo intrínseco Date , esses operadores também estão disponíveis no Date tipo.

Tipo de operação:

Bo SB Por Sh EUA em Interface do usuário Lo UL De Si Fazer Da Ch São Ob
Bo Sh SB Sh Sh In In Lo Lo De De Si Faz Err Err Faz Ob
SB SB Sh Sh In In Lo Lo De De Si Faz Err Err Faz Ob
Por Por Sh EUA In IU Lo UL De Si Faz Err Err Faz Ob
Sh Sh In In Lo Lo De De Si Faz Err Err Faz Ob
EUA EUA In IU Lo UL De Si Faz Err Err Faz Ob
em In Lo Lo De De Si Faz Err Err Faz Ob
Interface do usuário IU Lo UL De Si Faz Err Err Faz Ob
Lo Lo De De Si Faz Err Err Faz Ob
UL UL De Si Faz Err Err Faz Ob
De De Si Faz Err Err Faz Ob
Si Si Faz Err Err Faz Ob
Fazer Faz Err Err Faz Ob
Da Err Err Err Err
Ch Err Err Err
São Faz Ob
Ob Ob

Operador de multiplicação

O operador de multiplicação calcula o produto de dois operandos.

MultiplicationOperatorExpression
    : Expression '*' LineTerminator? Expression
    ;

O operador de multiplicação é definido para os seguintes tipos:

  • Byte, SByte, , UShort, Short, UInteger, ULongInteger, e Long. Se a verificação de estouro de número inteiro estiver ativada e o produto estiver fora do intervalo do tipo de resultado, uma System.OverflowException exceção será lançada. Caso contrário, os estouros não são relatados e quaisquer bits significativos de alta ordem do resultado são descartados.

  • Single e Double. O produto é calculado de acordo com as regras da aritmética IEEE 754.

  • Decimal. Se o valor resultante for muito grande para representar no formato decimal, uma System.OverflowException exceção será lançada. Se o valor do resultado for muito pequeno para ser representado no formato decimal, o resultado será 0.

Tipo de operação:

Bo SB Por Sh EUA em Interface do usuário Lo UL De Si Fazer Da Ch São Ob
Bo Sh SB Sh Sh In In Lo Lo De De Si Faz Err Err Faz Ob
SB SB Sh Sh In In Lo Lo De De Si Faz Err Err Faz Ob
Por Por Sh EUA In IU Lo UL De Si Faz Err Err Faz Ob
Sh Sh In In Lo Lo De De Si Faz Err Err Faz Ob
EUA EUA In IU Lo UL De Si Faz Err Err Faz Ob
em In Lo Lo De De Si Faz Err Err Faz Ob
Interface do usuário IU Lo UL De Si Faz Err Err Faz Ob
Lo Lo De De Si Faz Err Err Faz Ob
UL UL De Si Faz Err Err Faz Ob
De De Si Faz Err Err Faz Ob
Si Si Faz Err Err Faz Ob
Fazer Faz Err Err Faz Ob
Da Err Err Err Err
Ch Err Err Err
São Faz Ob
Ob Ob

Operadores de Divisão

Os operadores de divisão calculam o quociente de dois operandos. Existem dois operadores de divisão: o operador de divisão regular (ponto flutuante) e o operador de divisão inteira.

DivisionOperatorExpression
    : FPDivisionOperatorExpression
    | IntegerDivisionOperatorExpression
    ;

FPDivisionOperatorExpression
    : Expression '/' LineTerminator? Expression
    ;

IntegerDivisionOperatorExpression
    : Expression '\\' LineTerminator? Expression
    ;

O operador de divisão regular é definido para os seguintes tipos:

  • Single e Double. O quociente é calculado de acordo com as regras da aritmética IEEE 754.

  • Decimal. Se o valor do operando direito for zero, uma System.DivideByZeroException exceção será lançada. Se o valor resultante for muito grande para representar no formato decimal, uma System.OverflowException exceção será lançada. Se o valor do resultado for muito pequeno para ser representado no formato decimal, o resultado será zero. A escala do resultado, antes de qualquer arredondamento, é a escala mais próxima da escala preferida que preservará um resultado igual ao resultado exato. A escala preferida é a escala do primeiro operando menos a escala do segundo operando.

De acordo com as regras normais de resolução do operador, a divisão regular puramente entre operandos de tipos como Byte, Short, Integere Long faria com que ambos os operandos fossem convertidos em tipo Decimal. No entanto, ao fazer a resolução do operador no operador de divisão, quando nenhum dos tipos é Decimal, Double é considerado mais estreito do que Decimal. Esta convenção é seguida porque Double a divisão é mais eficiente do que a Decimal divisão.

Tipo de operação:

Bo SB Por Sh EUA em Interface do usuário Lo UL De Si Fazer Da Ch São Ob
Bo Faz Faz Faz Faz Faz Faz Faz Faz Faz De Si Faz Err Err Faz Ob
SB Faz Faz Faz Faz Faz Faz Faz Faz De Si Faz Err Err Faz Ob
Por Faz Faz Faz Faz Faz Faz Faz De Si Faz Err Err Faz Ob
Sh Faz Faz Faz Faz Faz Faz De Si Faz Err Err Faz Ob
EUA Faz Faz Faz Faz Faz De Si Faz Err Err Faz Ob
em Faz Faz Faz Faz De Si Faz Err Err Faz Ob
Interface do usuário Faz Faz Faz De Si Faz Err Err Faz Ob
Lo Faz Faz De Si Faz Err Err Faz Ob
UL Faz De Si Faz Err Err Faz Ob
De De Si Faz Err Err Faz Ob
Si Si Faz Err Err Faz Ob
Fazer Faz Err Err Faz Ob
Da Err Err Err Err
Ch Err Err Err
São Faz Ob
Ob Ob

O operador de divisão inteira é definido para Byte, SByte, UShort, , Short, UIntegerInteger, ULong, e Long. Se o valor do operando direito for zero, uma System.DivideByZeroException exceção será lançada. A divisão arredonda o resultado para zero, e o valor absoluto do resultado é o maior inteiro possível que é menor do que o valor absoluto do quociente dos dois operandos. O resultado é zero ou positivo quando os dois operandos têm o mesmo sinal, e zero ou negativo quando os dois operandos têm sinais opostos. Se o operando esquerdo for o negativo SBytemáximo , Short, Integer, ou Long, e o operando direito for -1, ocorrerá um estouro, se a verificação de estouro de número inteiro estiver ativada, uma System.OverflowException exceção será lançada. Caso contrário, o estouro não é relatado e o resultado é, em vez disso, o valor do operando esquerdo.

Nota. Como os dois operandos para tipos não assinados serão sempre zero ou positivos, o resultado é sempre zero ou positivo. Como o resultado da expressão será sempre menor ou igual ao maior dos dois operandos, não é possível que ocorra um estouro. Como tal, a verificação de estouro de número inteiro não é executada para divisão de inteiros com dois inteiros não assinados. O resultado é o tipo como o do operando esquerdo.

Tipo de operação:

Bo SB Por Sh EUA em Interface do usuário Lo UL De Si Fazer Da Ch São Ob
Bo Sh SB Sh Sh In In Lo Lo Lo Lo Lo Lo Err Err Lo Ob
SB SB Sh Sh In In Lo Lo Lo Lo Lo Lo Err Err Lo Ob
Por Por Sh EUA In IU Lo UL Lo Lo Lo Err Err Lo Ob
Sh Sh In In Lo Lo Lo Lo Lo Lo Err Err Lo Ob
EUA EUA In IU Lo UL Lo Lo Lo Err Err Lo Ob
em In Lo Lo Lo Lo Lo Lo Err Err Lo Ob
Interface do usuário IU Lo UL Lo Lo Lo Err Err Lo Ob
Lo Lo Lo Lo Lo Lo Err Err Lo Ob
UL UL Lo Lo Lo Err Err Lo Ob
De Lo Lo Lo Err Err Lo Ob
Si Lo Lo Err Err Lo Ob
Fazer Lo Err Err Lo Ob
Da Err Err Err Err
Ch Err Err Err
São Lo Ob
Ob Ob

Operador Mod

O Mod operador (modulo) calcula o restante da divisão entre dois operandos.

ModuloOperatorExpression
    : Expression 'Mod' LineTerminator? Expression
    ;

O Mod operador é definido para os seguintes tipos:

  • Byte, SByte, , UShort, Short, IntegerUIntegerULong , e Long. O resultado de x Mod y é o valor produzido por x - (x \ y) * y. Se y for zero, uma System.DivideByZeroException exceção é lançada. O operador do módulo nunca causa um estouro.

  • Single e Double. O restante é calculado de acordo com as regras da aritmética IEEE 754.

  • Decimal. Se o valor do operando direito for zero, uma System.DivideByZeroException exceção será lançada. Se o valor resultante for muito grande para representar no formato decimal, uma System.OverflowException exceção será lançada. Se o valor do resultado for muito pequeno para ser representado no formato decimal, o resultado será zero.

Tipo de operação:

Bo SB Por Sh EUA em Interface do usuário Lo UL De Si Fazer Da Ch São Ob
Bo Sh SB Sh Sh In In Lo Lo De De Si Faz Err Err Faz Ob
SB SB Sh Sh In In Lo Lo De De Si Faz Err Err Faz Ob
Por Por Sh EUA In IU Lo UL De Si Faz Err Err Faz Ob
Sh Sh In In Lo Lo De De Si Faz Err Err Faz Ob
EUA EUA In IU Lo UL De Si Faz Err Err Faz Ob
em In Lo Lo De De Si Faz Err Err Faz Ob
Interface do usuário IU Lo UL De Si Faz Err Err Faz Ob
Lo Lo De De Si Faz Err Err Faz Ob
UL UL De Si Faz Err Err Faz Ob
De De Si Faz Err Err Faz Ob
Si Si Faz Err Err Faz Ob
Fazer Faz Err Err Faz Ob
Da Err Err Err Err
Ch Err Err Err
São Faz Ob
Ob Ob

Operador de Exponenciação

O operador de exponenciação calcula o primeiro operando elevado à potência do segundo operando.

ExponentOperatorExpression
    : Expression '^' LineTerminator? Expression
    ;

O operador de exponenciação é definido para o tipo Double. O valor é calculado de acordo com as regras da aritmética IEEE 754.

Tipo de operação:

Bo SB Por Sh EUA em Interface do usuário Lo UL De Si Fazer Da Ch São Ob
Bo Faz Faz Faz Faz Faz Faz Faz Faz Faz Faz Faz Faz Err Err Faz Ob
SB Faz Faz Faz Faz Faz Faz Faz Faz Faz Faz Faz Err Err Faz Ob
Por Faz Faz Faz Faz Faz Faz Faz Faz Faz Faz Err Err Faz Ob
Sh Faz Faz Faz Faz Faz Faz Faz Faz Faz Err Err Faz Ob
EUA Faz Faz Faz Faz Faz Faz Faz Faz Err Err Faz Ob
em Faz Faz Faz Faz Faz Faz Faz Err Err Faz Ob
Interface do usuário Faz Faz Faz Faz Faz Faz Err Err Faz Ob
Lo Faz Faz Faz Faz Faz Err Err Faz Ob
UL Faz Faz Faz Faz Err Err Faz Ob
De Faz Faz Faz Err Err Faz Ob
Si Faz Faz Err Err Faz Ob
Fazer Faz Err Err Faz Ob
Da Err Err Err Err
Ch Err Err Err
São Faz Ob
Ob Ob

Operadores Relacionais

Os operadores relacionais comparam valores entre si. Os operadores de comparação são =, <>, <, >, <=e >=.

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

Todos os operadores relacionais resultam em um Boolean valor.

Os operadores relacionais têm o seguinte significado geral:

  • O = operador testa se os dois operandos são iguais.

  • O <> operador testa se os dois operandos não são iguais.

  • O < operador testa se o primeiro operando é menor que o segundo operando.

  • O > operador testa se o primeiro operando é maior que o segundo operando.

  • O <= operador testa se o primeiro operando é menor ou igual ao segundo operando.

  • O >= operador testa se o primeiro operando é maior ou igual ao segundo operando.

Os operadores relacionais são definidos para os seguintes tipos:

  • Boolean. Os operadores comparam os valores de verdade dos dois operandos. True é considerada inferior Falsea , o que corresponde aos seus valores numéricos.

  • Byte, SByte, , UShort, Short, UInteger, ULongInteger, e Long. Os operadores comparam os valores numéricos dos dois operandos integrais.

  • Single e Double. Os operadores comparam os operandos de acordo com as regras do padrão IEEE 754.

  • Decimal. Os operadores comparam os valores numéricos dos dois operandos decimais.

  • Date. Os operadores retornam o resultado da comparação dos dois valores de data/hora.

  • Char. Os operadores retornam o resultado da comparação dos dois valores Unicode.

  • String. Os operadores retornam o resultado da comparação dos dois valores usando uma comparação binária ou uma comparação de texto. A comparação utilizada é determinada pelo ambiente de compilação e pela Option Compare instrução. Uma comparação binária determina se o valor numérico Unicode de cada caractere em cada cadeia de caracteres é o mesmo. Uma comparação de texto faz uma comparação de texto Unicode com base na cultura atual em uso no .NET Framework. Ao fazer uma comparação de cadeia de caracteres, um valor nulo é equivalente ao literal ""de cadeia de caracteres.

Tipo de operação:

Bo SB Por Sh EUA em Interface do usuário Lo UL De Si Fazer Da Ch São Ob
Bo Bo SB Sh Sh In In Lo Lo De De Si Faz Err Err Bo Ob
SB SB Sh Sh In In Lo Lo De De Si Faz Err Err Faz Ob
Por Por Sh EUA In IU Lo UL De Si Faz Err Err Faz Ob
Sh Sh In In Lo Lo De De Si Faz Err Err Faz Ob
EUA EUA In IU Lo UL De Si Faz Err Err Faz Ob
em In Lo Lo De De Si Faz Err Err Faz Ob
Interface do usuário IU Lo UL De Si Faz Err Err Faz Ob
Lo Lo De De Si Faz Err Err Faz Ob
UL UL De Si Faz Err Err Faz Ob
De De Si Faz Err Err Faz Ob
Si Si Faz Err Err Faz Ob
Fazer Faz Err Err Faz Ob
Da Da Err Da Ob
Ch Ch São Ob
São São Ob
Ob Ob

Como Operador

O Like operador determina se uma cadeia de caracteres corresponde a um determinado padrão.

LikeOperatorExpression
    : Expression 'Like' LineTerminator? Expression
    ;

O Like operador é definido para o String tipo. O primeiro operando é a cadeia de caracteres que está sendo correspondida, e o segundo operando é o padrão contra o qual corresponder. O padrão é composto de caracteres Unicode. As seguintes sequências de caracteres têm significados especiais:

  • O personagem ? corresponde a qualquer caractere único.

  • O caractere * corresponde a zero ou mais caracteres.

  • O caractere # corresponde a qualquer dígito (0-9).

  • Uma lista de caracteres entre colchetes ([ab...]) corresponde a qualquer caractere na lista.

  • Uma lista de caracteres entre parênteses e prefixada por um ponto de exclamação ([!ab...]) corresponde a qualquer caractere que não esteja na lista de caracteres.

  • Dois caracteres em uma lista de caracteres separados por um hífen (-) especificam um intervalo de caracteres Unicode começando com o primeiro caractere e terminando com o segundo caractere. Se o segundo caractere não for posterior na ordem de classificação do que o primeiro caractere, ocorrerá uma exceção em tempo de execução. Um hífen que aparece no início ou no final de uma lista de caracteres especifica a si mesmo.

Para corresponder aos caracteres especiais colchetes esquerdos ([), ponto de interrogação (?), sinal numérico (#) e asterisco (*), os colchetes devem incluí-los. O colchete direito (]) não pode ser usado dentro de um grupo para corresponder a si mesmo, mas pode ser usado fora de um grupo como um caractere individual. A sequência de caracteres é considerada a cadeia [] de caracteres literal "".

Observe que as comparações de caracteres e a ordenação das listas de caracteres dependem do tipo de comparações que estão sendo usadas. Se comparações binárias estiverem sendo usadas, as comparações de caracteres e a ordenação serão baseadas nos valores numéricos Unicode. Se comparações de texto estão sendo usadas, comparações de caracteres e ordenação são baseadas na localidade atual que está sendo usada no .NET Framework.

Em algumas línguas, os carateres especiais do alfabeto representam dois carateres separados e vice-versa. Por exemplo, várias línguas usam o caractere æ para representar os caracteres a e quando eles aparecem juntos, enquanto os caracteres ^ e pode ser usado para representar o caractere OeÔ. Ao usar comparações de texto, o Like operador reconhece tais equivalências culturais. Nesse caso, uma ocorrência do único caractere especial em qualquer padrão ou cadeia de caracteres corresponde à sequência equivalente de dois caracteres na outra cadeia de caracteres. Da mesma forma, um único caractere especial no padrão entre colchetes (por si só, em uma lista ou em um intervalo) corresponde à sequência equivalente de dois caracteres na cadeia de caracteres e vice-versa.

Em uma Like expressão onde ambos os operandos são Nothing ou um operando tem uma conversão intrínseca para String e o outro operando é Nothing, Nothing é tratado como se fosse a cadeia de caracteres vazia literal "".

Tipo de operação:

Bo SB Por Sh EUA em Interface do usuário Lo UL De Si Fazer Da Ch São Ob
Bo São São São São São São São São São São São São São São São Ob
SB São São São São São São São São São São São São São São Ob
Por São São São São São São São São São São São São São Ob
Sh São São São São São São São São São São São São Ob
EUA São São São São São São São São São São São Ob
em São São São São São São São São São São Ob
Interface do usuário São São São São São São São São São Ob
Lo São São São São São São São São Ob
UL São São São São São São São Ob
De São São São São São São Ob
Si São São São São São Ob
Fazer São São São São Ob
Da São São São Ob
Ch São São Ob
São São Ob
Ob Ob

Operador de concatenação

ConcatenationOperatorExpression
    : Expression '&' LineTerminator? Expression
    ;

O operador de concatenação é definido para todos os tipos intrínsecos, incluindo as versões anuláveis dos tipos de valor intrínseco. Também é definido para concatenação entre os tipos mencionados acima e System.DBNull, que é tratado como uma Nothing cadeia de caracteres. O operador de concatenação converte todos os seus operandos em String; na expressão, todas as conversões são String consideradas como ampliando, independentemente de a semântica estrita ser usada. Um System.DBNull valor é convertido para o literal Nothing digitado como String. Um tipo de valor anulável cujo valor também é Nothing convertido para o literal Nothing digitado como String, em vez de lançar um erro em tempo de execução.

Uma operação de concatenação resulta em uma cadeia de caracteres que é a concatenação dos dois operandos em ordem da esquerda para a direita. O valor Nothing é tratado como se fosse a cadeia de caracteres vazia literal "".

Tipo de operação:

Bo SB Por Sh EUA em Interface do usuário Lo UL De Si Fazer Da Ch São Ob
Bo São São São São São São São São São São São São São São São Ob
SB São São São São São São São São São São São São São São Ob
Por São São São São São São São São São São São São São Ob
Sh São São São São São São São São São São São São Ob
EUA São São São São São São São São São São São Ob
em São São São São São São São São São São Ob
Interface do usuário São São São São São São São São São Ob
Lo São São São São São São São São Ob
UL São São São São São São São Ob
De São São São São São São Ob
Si São São São São São Ob
Fazer São São São São Ob
Da São São São Ob
Ch São São Ob
São São Ob
Ob Ob

Operadores lógicos

Os Andoperadores , Not, Or, e são Xor chamados de operadores lógicos.

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

Os operadores lógicos são avaliados da seguinte forma:

  • Para o Boolean tipo:

    • Uma operação lógica And é executada em seus dois operandos.

    • Uma operação lógica Not é executada em seu operando.

    • Uma operação lógica Or é executada em seus dois operandos.

    • Uma operação lógica exclusivaOr é executada em seus dois operandos.

  • Para Byte, SByte, UShort, Short, UInteger, Integer, ULong, , Longe todos os tipos enumerados, a operação especificada é executada em cada bit da representação binária do(s) dois(s) operando(s):

    • And: O bit de resultado é 1 se ambos os bits forem 1; caso contrário, o bit de resultado é 0.

    • Not: O bit de resultado é 1 se o bit for 0; caso contrário, o bit de resultado é 1.

    • Or: O bit de resultado é 1 se qualquer bit for 1; caso contrário, o bit de resultado é 0.

    • Xor: O bit de resultado é 1 se qualquer bit for 1, mas não os dois bits; caso contrário, o bit de resultado é 0 (ou seja, 1 Xor 0 = 1, 1 Xor 1 = 0).

  • Quando os operadores And lógicos e Or são levantados para o tipo Boolean?, eles são estendidos para abranger a lógica booleana de três valores como tal:

    • And avalia como true se ambos os operandos são verdadeiros; false se um dos operandos for false; Nothing caso contrário.

    • Or avalia como true se qualquer operando é true; false é que ambos os operandos são falsos; Nothing caso contrário.

Por exemplo:

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, os operadores And lógicos e Or seria levantado usando lógica de três valores para qualquer tipo que pode ser usado em uma expressão booleana (ou seja, um tipo que implementa IsTrue e IsFalse), da mesma forma que AndAlso e OrElse curto-circuito em qualquer tipo que pode ser usado em uma expressão booleana. Infelizmente, o levantamento de três valores só é aplicado ao , portanto, os tipos definidos pelo Boolean?usuário que desejam lógica de três valores devem fazê-lo manualmente, definindo And e Or operadores para sua versão anulável.

Não é possível haver qualquer transbordamento a partir destas operações. Os operadores de tipo enumerados fazem a operação bit a bit no tipo subjacente do tipo enumerado, mas o valor de retorno é o tipo enumerado.

Não tipo de operação:

Bo SB Por Sh EUA em Interface do usuário Lo UL De Si Fazer Da Ch São Ob
Bo SB Por Sh EUA In IU Lo UL Lo Lo Lo Err Err Lo Ob

E, ou, Xor Tipo de operação:

Bo SB Por Sh EUA em Interface do usuário Lo UL De Si Fazer Da Ch São Ob
Bo Bo SB Sh Sh In In Lo Lo Lo Lo Lo Lo Err Err Bo Ob
SB SB Sh Sh In In Lo Lo Lo Lo Lo Lo Err Err Lo Ob
Por Por Sh EUA In IU Lo UL Lo Lo Lo Err Err Lo Ob
Sh Sh In In Lo Lo Lo Lo Lo Lo Err Err Lo Ob
EUA EUA In IU Lo UL Lo Lo Lo Err Err Lo Ob
em In Lo Lo Lo Lo Lo Lo Err Err Lo Ob
Interface do usuário IU Lo UL Lo Lo Lo Err Err Lo Ob
Lo Lo Lo Lo Lo Lo Err Err Lo Ob
UL UL Lo Lo Lo Err Err Lo Ob
De Lo Lo Lo Err Err Lo Ob
Si Lo Lo Err Err Lo Ob
Fazer Lo Err Err Lo Ob
Da Err Err Err Err
Ch Err Err Err
São Lo Ob
Ob Ob

Operadores lógicos de curto-circuito

Os AndAlso operadores e OrElse são as versões de curto-circuito dos And operadores lógicos Or .

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

Devido ao seu comportamento de curto-circuito, o segundo operando não é avaliado em tempo de execução se o resultado do operador for conhecido após a avaliação do primeiro operando.

Os operadores lógicos de curto-circuito são avaliados da seguinte forma:

  • Se o primeiro operando em uma AndAlso operação avaliar False ou retornar True de seu IsFalse operador, a expressão retornará seu primeiro operando. Caso contrário, o segundo operando é avaliado e uma operação lógica And é executada nos dois resultados.

  • Se o primeiro operando em uma OrElse operação avaliar True ou retornar True de seu IsTrue operador, a expressão retornará seu primeiro operando. Caso contrário, o segundo operando é avaliado e uma operação lógica Or é executada em seus dois resultados.

Os AndAlso operadores e OrElse são definidos para o tipo Boolean, ou para qualquer tipo T que sobrecarregue os seguintes operadores:

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

bem como sobrecarregar o correspondente And ou Or operador:

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

Ao avaliar os AndAlso operadores ou OrElse , o primeiro operando é avaliado apenas uma vez, e o segundo operando não é avaliado ou avaliado exatamente uma vez. Por exemplo, considere o seguinte código:

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

Imprime o seguinte resultado:

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

Na forma levantada dos AndAlso operadores e OrElse , se o primeiro operando era um nulo Boolean?, então o segundo operando é avaliado, mas o resultado é sempre um nulo Boolean?.

Tipo de operação:

Bo SB Por Sh EUA em Interface do usuário Lo UL De Si Fazer Da Ch São Ob
Bo Bo Bo Bo Bo Bo Bo Bo Bo Bo Bo Bo Bo Err Err Bo Ob
SB Bo Bo Bo Bo Bo Bo Bo Bo Bo Bo Bo Err Err Bo Ob
Por Bo Bo Bo Bo Bo Bo Bo Bo Bo Bo Err Err Bo Ob
Sh Bo Bo Bo Bo Bo Bo Bo Bo Bo Err Err Bo Ob
EUA Bo Bo Bo Bo Bo Bo Bo Bo Err Err Bo Ob
em Bo Bo Bo Bo Bo Bo Bo Err Err Bo Ob
Interface do usuário Bo Bo Bo Bo Bo Bo Err Err Bo Ob
Lo Bo Bo Bo Bo Bo Err Err Bo Ob
UL Bo Bo Bo Bo Err Err Bo Ob
De Bo Bo Bo Err Err Bo Ob
Si Bo Bo Err Err Bo Ob
Fazer Bo Err Err Bo Ob
Da Err Err Err Err
Ch Err Err Err
São Bo Ob
Ob Ob

Operadores de turnos

Os operadores << binários e >> executar operações de deslocamento de bits.

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

Os operadores são definidos para os Bytetipos , SByte, , ShortUShort, IntegerUIntegerULong , , e .Long Ao contrário dos outros operadores binários, o tipo de resultado de uma operação de deslocamento é determinado como se o operador fosse um operador unário com apenas o operando esquerdo. O tipo do operando direito deve ser implicitamente conversível e Integer não é usado para determinar o tipo de resultado da operação.

O << operador faz com que os bits no primeiro operando sejam deslocados para a esquerda do número de lugares especificado pela quantidade de deslocamento. Os bits de ordem alta fora do intervalo do tipo de resultado são descartados e as posições de bits desocupados de ordem baixa são preenchidas com zero.

O >> operador faz com que os bits no primeiro operando sejam deslocados para a direita no número de lugares especificado pela quantidade de deslocamento. Os bits de ordem baixa são descartados e as posições de bits desocupados de ordem alta são definidas como zero se o operando esquerdo for positivo ou para um se negativo. Se o operando esquerdo for do tipo Byte, UShort, UInteger, ou ULong os bits de ordem alta vazios serão preenchidos com zero.

Os operadores shift deslocam os bits da representação subjacente do primeiro operando pela quantidade do segundo operando. Se o valor do segundo operando for maior que o número de bits no primeiro operando ou for negativo, então a quantidade de deslocamento será calculada como RightOperand And SizeMask onde SizeMask está:

Tipo de LeftOperand SizeMask
Byte, SByte 7 (&H7)
UShort, Short 15 (&HF)
UInteger, Integer 31 (&H1F)
ULong, Long 63º (&H3F)

Se o valor do deslocamento for zero, o resultado da operação será idêntico ao valor do primeiro operando. Não é possível haver qualquer transbordamento a partir destas operações.

Tipo de operação:

Bo SB Por Sh EUA em Interface do usuário Lo UL De Si Fazer Da Ch São Ob
Sh SB Por Sh EUA In IU Lo UL Lo Lo Lo Err Err Lo Ob

Expressões Booleanas

Uma expressão booleana é uma expressão que pode ser testada para ver se é verdadeira ou se é falsa.

BooleanExpression
    : Expression
    ;

Um tipo T pode ser usado em uma expressão booleana se, em ordem de preferência:

  • T é Boolean ou Boolean?

  • T tem uma conversão alargada para Boolean

  • T tem uma conversão alargada para Boolean?

  • T define dois pseudooperadores IsTrue e IsFalse.

  • T tem uma conversão de estreitamento para Boolean? que não envolve uma conversão de Boolean para Boolean?.

  • T tem uma conversão de estreitamento para Boolean.

Nota. É interessante notar que, se Option Strict estiver desativada, uma expressão que tenha uma conversão de estreitamento para Boolean será aceita sem um erro em tempo de compilação, mas a linguagem ainda preferirá um IsTrue operador se existir. Isso ocorre porque Option Strict apenas muda o que é e o que não é aceito pela língua, e nunca muda o significado real de uma expressão. Assim, IsTrue tem que ser sempre preferido sobre uma conversão estreita, independentemente de Option Strict.

Por exemplo, a classe a seguir não define uma conversão de ampliação para Boolean. Como resultado, seu uso na If instrução causa uma chamada para o IsTrue operador.

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 uma expressão booleana for digitada como ou convertida em Boolean ou Boolean?, então ela será verdadeira se o valor for True e false caso contrário.

Caso contrário, uma expressão booleana chama o IsTrue operador e retorna True se o operador retornou True, caso contrário, é falsa (mas nunca chama o IsFalse operador).

No exemplo a seguir, Integer tem uma conversão de estreitamento para Boolean, então um null Integer? tem uma conversão de estreitamento para ambos Boolean? (produzindo um nulo Boolean) e para Boolean (que lança uma exceção). A conversão de estreitamento para Boolean? é preferível, e assim o valor de "i" como uma expressão booleana é, portanto, False.

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

Expressões do Lambda

Uma expressão lambda define um método anônimo chamado método lambda. Os métodos do Lambda facilitam a passagem de métodos "in-line" para outros métodos que usam tipos delegados.

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'
    ;

O exemplo:

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

irá imprimir:

2 4 6 8

Uma expressão lambda começa com os modificadores Async opcionais ou Iterator, seguida pela palavra-chave Function ou Sub e uma lista de parâmetros. Os parâmetros em uma expressão lambda não podem ser declarados Optional ou ParamArray não podem ter atributos. Ao contrário dos métodos regulares, omitir um tipo de parâmetro para um método lambda não infere Objectautomaticamente . Em vez disso, quando um método lambda é reclassificado, os tipos de parâmetros e ByRef modificadores omitidos são inferidos do tipo de destino. No exemplo anterior, a expressão lambda poderia ter sido escrita como Function(x) x * 2, e teria inferido o tipo de x ser Integer quando o método lambda foi usado para criar uma instância do IntFunc tipo delegado. Ao contrário da inferência de variáveis locais, se um parâmetro de método lambda omitir um tipo, mas tiver uma matriz ou um modificador de nome anulável, ocorrerá um erro em tempo de compilação.

Uma expressão lambda regular é aquela sem modificadores Async nem Iterator modificadores.

Uma expressão lambda iteradora é uma com o Iterator modificador e nenhum Async modificador. Tem de ser uma função. Quando ele é reclassificado para um valor, ele só pode ser reclassificado para um valor do tipo delegado cujo tipo de retorno é IEnumerator, ou IEnumerable, ou IEnumerable(Of T)IEnumerator(Of T) para alguns T, e que não tem parâmetros ByRef.

Uma expressão lambda assíncrona é uma com o Async modificador e nenhum Iterator modificador. Um sub lambda assíncrono só pode ser reclassificado para um valor de tipo subdelegado sem parâmetros ByRef. Uma lambda de função assíncrona só pode ser reclassificada para um valor de tipo de delegado de função cujo tipo de retorno é Task ou Task(Of T) para alguns Te que não tem parâmetros ByRef.

As expressões do Lambda podem ser de linha única ou de várias linhas. As expressões lambda de linha Function única contêm uma única expressão que representa o valor retornado do método lambda. As expressões lambda de linha Sub única contêm uma única instrução sem seu fechamento StatementTerminator. Por exemplo:

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

As construções lambda de linha única se ligam menos firmemente do que todas as outras expressões e instruções. Assim, por exemplo, "Function() x + 5" é equivalente a "Function() (x+5)" em vez de "(Function() x) + 5". Para evitar ambiguidade, uma expressão lambda de linha Sub única não pode conter uma instrução Dim ou uma declaração de rótulo. Além disso, a menos que esteja entre parênteses, uma expressão lambda de linha Sub única não pode ser imediatamente seguida por dois pontos ":", um operador de acesso de membro ".", um operador de acesso de membro de dicionário "!" ou um parêntese aberto "(". Não pode conter qualquer declaração de bloco (With, SyncLock, If...EndIf, , ForWhile, DoUsing, ) nem OnErrorResume.

Nota. Na expressão Function(i) x=ilambda, o corpo é interpretado como uma expressão (que testa se x e i são iguais). Mas na expressão Sub(i) x=ilambda, o corpo é interpretado como uma afirmação (que atribui i a x).

Uma expressão lambda de várias linhas contém um bloco de instrução e deve terminar com uma instrução apropriada End (ou seja, End Function ou End Sub). Tal como acontece com os métodos regulares, um método Function lambda de várias linhas ou instrução e SubEnd instruções devem estar em suas próprias linhas. Por exemplo:

' 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

As expressões lambda de várias linhas Function podem declarar um tipo de retorno, mas não podem colocar atributos nele. Se uma expressão lambda de várias linhas Function não declarar um tipo de retorno, mas o tipo de retorno puder ser inferido a partir do contexto no qual a expressão lambda é usada, esse tipo de retorno será usado. Caso contrário, o tipo de retorno da função é calculado da seguinte forma:

  • Em uma expressão lambda regular, o tipo de retorno é o tipo dominante das expressões em todas as Return instruções no bloco de instrução.

  • Em uma expressão lambda assíncrona, o tipo de retorno é Task(Of T) onde T é o tipo dominante das expressões em todas as Return instruções no bloco de instrução.

  • Em uma expressão lambda iteradora, o tipo de retorno é IEnumerable(Of T) onde T é o tipo dominante das expressões em todas as Yield instruções no bloco de instrução.

Por exemplo:

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

Em todos os casos, se não Return houver (respectivamente Yield) declarações, ou se não houver nenhum tipo dominante entre elas, e semânticas estritas estiverem sendo usadas, ocorre um erro em tempo de compilação, caso contrário, o tipo dominante é implicitamente Object.

Observe que o tipo de retorno é calculado a partir de todas as Return instruções, mesmo que elas não estejam acessíveis. Por exemplo:

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

Não há nenhuma variável de retorno implícita, pois não há nome para a variável.

Os blocos de instrução dentro de expressões lambda de várias linhas têm as seguintes restrições:

  • On Error e Resume declarações não são permitidas, embora Try declarações sejam permitidas.

  • Locais estáticos não podem ser declarados em expressões lambda de várias linhas.

  • Não é possível ramificar para dentro ou para fora do bloco de instrução de uma expressão lambda de várias linhas, embora as regras normais de ramificação se apliquem dentro dele. Por exemplo:

    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
    

Uma expressão lambda é aproximadamente equivalente a um método anônimo declarado no tipo que contém. O exemplo inicial é aproximadamente 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

Encerramentos

As expressões lambda têm acesso a todas as variáveis no escopo, incluindo variáveis locais ou parâmetros definidos no método de contenção e expressões lambda. Quando uma expressão lambda se refere a uma variável ou parâmetro local, a expressão lambda captura a variável que está sendo referida em um fechamento. Um fechamento é um objeto que vive na pilha em vez de na pilha e, quando uma variável é capturada, todas as referências à variável são redirecionadas para o fechamento. Isso permite que as expressões lambda continuem a se referir a variáveis e parâmetros locais mesmo depois que o método de contenção for concluído. Por exemplo:

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

é aproximadamente 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

Um fechamento captura uma nova cópia de uma variável local cada vez que ela entra no bloco no qual a variável local é declarada, mas a nova cópia é inicializada com o valor da cópia anterior, se houver. Por exemplo:

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

impressões

1 2 3 4 5 6 7 8 9 10

Em vez de

9 9 9 9 9 9 9 9 9 9

Como os fechamentos têm que ser inicializados ao entrar em um bloco, não é permitido GoTo entrar em um bloco com um fechamento de fora desse bloco, embora seja permitido Resume entrar em um bloco com um fechamento. Por exemplo:

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

Como eles não podem ser capturados em um fechamento, o seguinte não pode aparecer dentro de uma expressão lambda:

  • Parâmetros de referência.

  • Expressões de instância (Me, MyClass, MyBase), se o tipo de Me não for uma classe.

Os membros de uma expressão anônima de criação de tipo, se a expressão lambda fizer parte da expressão. Por exemplo:

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

ReadOnly variáveis de instância em construtores de instância ou ReadOnly variáveis compartilhadas em construtores compartilhados onde as variáveis são usadas em um contexto sem valor. Por exemplo:

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

Expressões de consulta

Uma expressão de consulta é uma expressão que aplica uma série de operadores de consulta aos elementos de uma coleção consultável . Por exemplo, a expressão a seguir usa uma coleção de Customer objetos e retorna os nomes de todos os clientes no estado de Washington:

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

Uma expressão de consulta deve começar com um From ou um Aggregate operador e pode terminar com qualquer operador de consulta. O resultado de uma expressão de consulta é classificado como um valor; O tipo de resultado da expressão depende do tipo de resultado do último operador de consulta na expressão.

QueryExpression
    : FromOrAggregateQueryOperator QueryOperator*
    ;

FromOrAggregateQueryOperator
    : FromQueryOperator
    | AggregateQueryOperator
    ;

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

JoinOrGroupJoinQueryOperator
    : JoinQueryOperator
    | GroupJoinQueryOperator
    ;

Variáveis de intervalo

Alguns operadores de consulta introduzem um tipo especial de variável chamada variável de intervalo. As variáveis de intervalo não são variáveis reais; em vez disso, eles representam os valores individuais durante a avaliação da consulta sobre as coleções de entrada.

CollectionRangeVariableDeclarationList
    : CollectionRangeVariableDeclaration ( Comma CollectionRangeVariableDeclaration )*
    ;

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

ExpressionRangeVariableDeclarationList
    : ExpressionRangeVariableDeclaration ( Comma ExpressionRangeVariableDeclaration )*
    ;

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

As variáveis de intervalo têm escopo desde o operador de consulta de introdução até o final de uma expressão de consulta ou para um operador de consulta como Select aquele que as oculta. Por exemplo, na consulta a seguir

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

O operador de consulta introduz uma variável cust de intervalo digitada FromCustomers como Customer que representa cada cliente na coleção. O operador de consulta a seguir Where refere-se à variável cust range na expressão de filtro para determinar se um cliente individual deve ser filtrado da coleção resultante.

Existem dois tipos de variáveis de intervalo: variáveis de intervalo de coleta e variáveis de intervalo de expressão. As variáveis do intervalo de coleta tomam seus valores dos elementos das coleções que estão sendo consultadas. A expressão de coleção em uma declaração de variável de intervalo de coleção deve ser classificada como um valor cujo tipo é consultável. Se o tipo de uma variável de intervalo de coleção for omitido, infere-se que seja o tipo de elemento da expressão de coleção ou Object se a expressão de coleção não tiver um tipo de elemento (ou seja, define apenas um Cast método). Se a expressão da coleção não for consultável (ou seja, o tipo de elemento da coleção não pode ser inferido), um erro em tempo de compilação resulta.

Uma variável de intervalo de expressão é uma variável de intervalo cujo valor é calculado por uma expressão em vez de uma coleção. No exemplo a seguir, o Select operador de consulta introduz uma variável de intervalo de expressões chamada cityState calculada a partir de dois campos:

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

Uma variável de intervalo de expressão não é necessária para fazer referência a outra variável de intervalo, embora tal variável possa ser de valor duvidoso. A expressão atribuída a uma variável de intervalo de expressões deve ser classificada como um valor e deve ser implicitamente convertível para o tipo da variável de intervalo, se fornecida.

Somente em um operador Let uma variável de intervalo de expressão pode ter seu tipo especificado. Em outros operadores, ou se seu tipo não for especificado, a inferência de tipo de variável local é usada para determinar o tipo da variável de intervalo.

Uma variável de intervalo deve seguir as regras para declarar variáveis locais em relação ao sombreamento. Assim, uma variável de intervalo não pode ocultar o nome de uma variável ou parâmetro local no método de delimitação ou outra variável de intervalo (a menos que o operador de consulta oculte especificamente todas as variáveis de intervalo atuais no escopo).

Tipos consultáveis

As expressões de consulta são implementadas convertendo a expressão em chamadas para métodos conhecidos em um tipo de coleção. Esses métodos bem definidos definem o tipo de elemento da coleção consultável, bem como os tipos de resultado dos operadores de consulta executados na coleção. Cada operador de consulta especifica o método ou métodos para os quais o operador de consulta é geralmente traduzido, embora a tradução específica dependa da implementação. Os métodos são fornecidos na especificação usando um formato geral que se parece com:

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

O seguinte aplica-se aos métodos:

  • O método deve ser um membro de instância ou extensão do tipo de coleção e deve ser acessível.

  • O método pode ser genérico, desde que seja possível inferir todos os argumentos de tipo.

  • O método pode estar sobrecarregado, caso em que a resolução de sobrecarga é usada para determinar o método exato a ser usado.

  • Outro tipo de delegado pode ser usado no lugar do tipo de delegado Func , desde que tenha a mesma assinatura, incluindo o tipo de retorno, que o tipo correspondente Func .

  • O tipo System.Linq.Expressions.Expression(Of D) pode ser usado no lugar do tipo delegado Func , desde que D seja um tipo delegado que tenha a mesma assinatura, incluindo o tipo de retorno, que o tipo correspondente Func .

  • O tipo T representa o tipo de elemento da coleção de entrada. Todos os métodos definidos por um tipo de coleção devem ter o mesmo tipo de elemento de entrada para que o tipo de coleção seja consultável.

  • O tipo S representa o tipo de elemento da segunda coleção de entrada no caso de operadores de consulta que executam junções.

  • O tipo K representa um tipo de chave no caso de operadores de consulta que têm um conjunto de variáveis de intervalo que atuam como chaves.

  • O tipo N representa um tipo que é usado como um tipo numérico (embora ainda possa ser um tipo definido pelo usuário e não um tipo numérico intrínseco).

  • O tipo B representa um tipo que pode ser usado em uma expressão booleana.

  • O tipo R representa o tipo de elemento da coleção de resultados, se o operador de consulta produzir uma coleção de resultados. R depende do número de variáveis de intervalo no escopo na conclusão do operador de consulta. Se uma única variável de intervalo estiver no escopo, então R é o tipo dessa variável de intervalo. No exemplo

    Dim custNames = From c In Customers
                    Select c.Name
    

    O resultado da consulta será um tipo de coleção com um tipo de elemento de String. Se várias variáveis de intervalo estiverem no escopo, então R é um tipo anônimo que contém todas as variáveis de intervalo no escopo como Key campos. No exemplo:

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

    O resultado da consulta será um tipo de coleção com um tipo de elemento de um tipo anônimo com uma propriedade somente leitura chamada Name de tipo String e uma propriedade somente leitura chamada ProductName de tipo String.

    Dentro de uma expressão de consulta, os tipos anônimos gerados para conter variáveis de intervalo são transparentes, o que significa que as variáveis de intervalo estão sempre disponíveis sem qualificação. Por exemplo, no exemplo anterior, as variáveis c de intervalo e o podiam ser acessadas sem qualificação no Select operador de consulta, mesmo que o tipo de elemento da coleção de entrada fosse um tipo anônimo.

  • O tipo CX representa um tipo de coleção, não necessariamente o tipo de coleção de entrada, cujo tipo de elemento é algum tipo X.

Um tipo de coleção consultável deve satisfazer uma das seguintes condições, em ordem de preferência:

  • Deve definir um método conforme Select .

  • Deve ter um dos seguintes métodos:

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

    que pode ser chamado para obter uma coleção consultável. Se ambos os métodos forem fornecidos, AsQueryable é preferível sobre AsEnumerable.

  • Deve ter um método

    Function Cast(Of T)() As CT
    

    que pode ser chamado com o tipo da variável range para produzir uma coleção consultável.

Como a determinação do tipo de elemento de uma coleção ocorre independentemente de uma invocação de método real, a aplicabilidade de métodos específicos não pode ser determinada. Assim, ao determinar o tipo de elemento de uma coleção se houver métodos de instância que correspondam a métodos conhecidos, todos os métodos de extensão que correspondam a métodos conhecidos serão ignorados.

A conversão do operador de consulta ocorre na ordem em que os operadores de consulta ocorrem na expressão. Não é necessário que um objeto de coleção implemente todos os métodos necessários para todos os operadores de consulta, embora cada objeto de coleção deva, pelo menos, suportar o Select operador de consulta. Se um método necessário não estiver presente, ocorrerá um erro em tempo de compilação. Ao vincular nomes de método conhecidos, os não-métodos são ignorados para fins de herança múltipla em interfaces e vinculação de método de extensão, embora a semântica de sombreamento ainda se aplique. Por exemplo:

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

Indexador de consulta padrão

Cada tipo de coleção consultável cujo tipo de elemento é T e ainda não tem uma propriedade padrão é considerado como tendo uma propriedade padrão da seguinte forma geral:

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

A propriedade padrão só pode ser referida usando a sintaxe de acesso à propriedade padrão; A propriedade padrão não pode ser referida pelo nome. Por exemplo:

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

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

Se o tipo de coleção não tiver um ElementAtOrDefault membro, ocorrerá um erro em tempo de compilação.

Do operador de consulta

O From operador de consulta introduz uma variável de intervalo de coleta que representa os membros individuais de uma coleção a ser consultada.

FromQueryOperator
    : LineTerminator? 'From' LineTerminator? CollectionRangeVariableDeclarationList
    ;

Por exemplo, a expressão de consulta:

From c As Customer In Customers ...

pode ser pensado como equivalente a

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

Quando um From operador de consulta declara várias variáveis de intervalo de coleta ou não é o primeiro From operador de consulta na expressão de consulta, cada nova variável de intervalo de coleta é cruzada com o conjunto existente de variáveis de intervalo. O resultado é que a consulta é avaliada sobre o produto cruzado de todos os elementos nas coleções associadas. Por exemplo, a expressão:

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

pode ser considerado equivalente a:

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

e é exatamente equivalente a:

From c In Customers, e In Employees ...

As variáveis de intervalo introduzidas em operadores de consulta anteriores podem ser usadas em um operador de consulta posterior From . Por exemplo, na expressão de consulta a seguir, o segundo From operador de consulta refere-se ao valor da primeira variável de intervalo:

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

Várias variáveis de intervalo em um From operador de consulta ou vários From operadores de consulta só são suportadas se o tipo de coleção contiver um ou ambos os seguintes métodos:

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

O código

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

é geralmente traduzido para

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 não é uma palavra reservada.

Operador de Consulta de Ingresso

O Join operador de consulta une variáveis de intervalo existentes com uma nova variável de intervalo de coleção, produzindo uma única coleção cujos elementos foram unidos com base em uma expressão de igualdade.

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

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

JoinCondition
    : Expression 'Equals' LineTerminator? Expression
    ;

Por exemplo:

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

A expressão de igualdade é mais restrita do que uma expressão de igualdade regular:

  • Ambas as expressões devem ser classificadas como um valor.

  • Ambas as expressões devem fazer referência a pelo menos uma variável de intervalo.

  • A variável range declarada no operador de consulta join deve ser referenciada por uma das expressões, e essa expressão não deve fazer referência a nenhuma outra variável de intervalo.

Se os tipos das duas expressões não forem exatamente do mesmo tipo, então

  • Se o operador de igualdade é definido para os dois tipos, ambas as expressões são implicitamente conversíveis para ele, e não Objecté , então converta ambas as expressões para esse tipo.

  • Caso contrário, se houver um tipo dominante para o qual ambas as expressões possam ser implicitamente convertidas, converta ambas as expressões para esse tipo.

  • Caso contrário, ocorrerá um erro em tempo de compilação.

As expressões são comparadas usando valores de hash (ou seja, chamando GetHashCode()) em vez de usar operadores de igualdade para eficiência. Um Join operador de consulta pode fazer várias junções ou condições de igualdade no mesmo operador. Um Join operador de consulta só é suportado se o tipo de coleção contiver um método:

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

O código

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

é geralmente traduzido para

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.Join, On e Equals não são palavras reservadas.

Permitir operador de consulta

O Let operador de consulta introduz uma variável de intervalo de expressão. Isso permite calcular um valor intermediário uma vez que será usado várias vezes em operadores de consulta posteriores.

LetQueryOperator
    : LineTerminator? 'Let' LineTerminator? ExpressionRangeVariableDeclarationList
    ;

Por exemplo:

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

pode ser considerado equivalente a:

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

Um Let operador de consulta só é suportado se o tipo de coleção contiver um método:

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

O código

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

é geralmente traduzido para

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

Selecionar operador de consulta

O Select operador de consulta é como o Let operador de consulta na medida em que introduz variáveis de intervalo de expressão, no entanto, um Select operador de consulta oculta as variáveis de intervalo atualmente disponíveis em vez de adicioná-las. Além disso, o tipo de uma variável de intervalo de expressão introduzida por um Select operador de consulta é sempre inferida usando regras de inferência de tipo de variável local, um tipo explícito não pode ser especificado e, se nenhum tipo pode ser inferido, ocorre um erro em tempo de compilação.

SelectQueryOperator
    : LineTerminator? 'Select' LineTerminator? ExpressionRangeVariableDeclarationList
    ;

Por exemplo, na consulta:

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

O Where operador de consulta só tem acesso à name variável de intervalo introduzida pelo operador, se o operador tivesse tentado SelectWhere fazer referência cust, teria ocorrido um erro em tempo de compilação.

Em vez de especificar explicitamente os nomes das variáveis de intervalo, um Select operador de consulta pode inferir os nomes das variáveis de intervalo, usando as mesmas regras que as expressões de criação de objeto de tipo anônimo. Por exemplo:

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

Se o nome da variável de intervalo não for fornecido e um nome não puder ser inferido, ocorrerá um erro em tempo de compilação. Se o Select operador de consulta contiver apenas uma única expressão, nenhum erro ocorrerá se um nome para essa variável de intervalo não puder ser inferido, mas a variável de intervalo não tiver nome. Por exemplo:

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

Se houver uma ambiguidade em um Select operador de consulta entre a atribuição de um nome a uma variável de intervalo e uma expressão de igualdade, a atribuição de nome será preferida. Por exemplo:

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)

Cada expressão no operador de consulta deve ser classificada Select como um valor. Um Select operador de consulta é suportado somente se o tipo de coleção contiver um método:

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

O código

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

é geralmente traduzido para

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

Operador de consulta distinto

O Distinct operador de consulta restringe os valores em uma coleção apenas àqueles com valores distintos, conforme determinado pela comparação do tipo de elemento para igualdade.

DistinctQueryOperator
    : LineTerminator? 'Distinct' LineTerminator?
    ;

Por exemplo, a consulta:

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

retornará apenas uma linha para cada par distinto de nome do cliente e preço do pedido, mesmo que o cliente tenha vários pedidos com o mesmo preço. Um Distinct operador de consulta é suportado somente se o tipo de coleção contiver um método:

Function Distinct() As CT

O código

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

é geralmente traduzido para

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

Nota. Distinct não é uma palavra reservada.

Onde Operador de Consulta

O Where operador de consulta restringe os valores em uma coleção àqueles que satisfazem uma determinada condição.

WhereQueryOperator
    : LineTerminator? 'Where' LineTerminator? BooleanExpression
    ;

Um Where operador de consulta usa uma expressão booleana que é avaliada para cada conjunto de valores de variáveis de intervalo, se o valor da expressão for true, os valores aparecerão na coleção de saída, caso contrário, os valores serão ignorados. Por exemplo, a expressão de consulta:

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

pode ser pensado como equivalente ao loop aninhado

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

Um Where operador de consulta é suportado somente se o tipo de coleção contiver um método:

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

O código

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

é geralmente traduzido para

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

Nota. Where não é uma palavra reservada.

Operadores de consulta de partição

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

O Take operador de consulta resulta nos primeiros n elementos de uma coleção. Quando usado com o While modificador, o Take operador resulta nos primeiros n elementos de uma coleção que satisfazem uma expressão booleana. O Skip operador ignora os primeiros n elementos de uma coleção e, em seguida, retorna o restante da coleção. Quando usado em conjunto com o While modificador, o Skip operador ignora os primeiros n elementos de uma coleção que satisfazem uma expressão booleana e, em seguida, retorna o restante da coleção. As expressões em um Take operador ou Skip consulta devem ser classificadas como um valor.

Um Take operador de consulta é suportado somente se o tipo de coleção contiver um método:

Function Take(count As N) As CT

Um Skip operador de consulta é suportado somente se o tipo de coleção contiver um método:

Function Skip(count As N) As CT

Um Take While operador de consulta é suportado somente se o tipo de coleção contiver um método:

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

Um Skip While operador de consulta é suportado somente se o tipo de coleção contiver um método:

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

O código

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

é geralmente traduzido para

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 não são palavras reservadas.

Ordenar por operador de consulta

O Order By operador de consulta ordena os valores que aparecem nas variáveis de intervalo.

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

OrderExpressionList
    : OrderExpression ( Comma OrderExpression )*
    ;

OrderExpression
    : Expression Ordering?
    ;

Ordering
    : 'Ascending' | 'Descending'
    ;

Um Order By operador de consulta usa expressões que especificam os valores-chave que devem ser usados para ordenar as variáveis de iteração. Por exemplo, a consulta a seguir retorna produtos classificados por preço:

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

Uma ordem pode ser marcada como Ascending, caso em que valores menores vêm antes de valores maiores, ou Descending, caso em que valores maiores vêm antes de valores menores. O padrão para um pedido, se nenhum for especificado, é Ascending. Por exemplo, a consulta a seguir retorna produtos classificados por preço com o produto mais caro primeiro:

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

O Order By operador de consulta pode especificar várias expressões para ordenação, caso em que a coleção é ordenada de forma aninhada. Por exemplo, a consulta a seguir ordena os clientes por estado, depois por cidade dentro de cada estado e, em seguida, por CEP dentro de cada cidade:

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

As expressões em um Order By operador de consulta devem ser classificadas como um valor. Um Order By operador de consulta é suportado somente se o tipo de coleção contiver um ou ambos os seguintes métodos:

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

O tipo CT de retorno deve ser uma coleção ordenada. Uma coleção ordenada é um tipo de coleção que contém um ou ambos os métodos:

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

O código

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

é geralmente traduzido para

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

Nota. Como os operadores de consulta simplesmente mapeiam a sintaxe para métodos que implementam uma operação de consulta específica, a preservação da ordem não é ditada pela linguagem e é determinada pela implementação do próprio operador. Isso é muito semelhante aos operadores definidos pelo usuário, pois a implementação para sobrecarregar o operador de adição para um tipo numérico definido pelo usuário pode não executar nada que se assemelhe a uma adição. É claro que, para preservar a previsibilidade, implementar algo que não corresponda às expectativas do usuário não é recomendado.

Nota. Order e By não são palavras reservadas.

Agrupar por operador de consulta

O Group By operador de consulta agrupa as variáveis de intervalo no escopo com base em uma ou mais expressões e, em seguida, produz novas variáveis de intervalo com base nesses agrupamentos.

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

Por exemplo, a consulta a seguir agrupa todos os clientes por e, em Stateseguida, calcula a contagem e a idade média de cada grupo:

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

O Group By operador de consulta tem três cláusulas: a cláusula opcional Group , a By cláusula e a Into cláusula. A Group cláusula tem a mesma sintaxe e efeito que um Select operador de consulta, exceto que afeta apenas as variáveis de intervalo disponíveis na Into cláusula e não a By cláusula. Por exemplo:

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

A By cláusula declara variáveis de intervalo de expressão que são usadas como valores-chave na operação de agrupamento. A Into cláusula permite a declaração de variáveis do intervalo de expressão que calculam agregações sobre cada um dos grupos formados pela By cláusula. Dentro da Into cláusula, a variável de intervalo de expressões só pode ser atribuída a uma expressão que é uma invocação de método de uma função agregada. Uma função de agregação é uma função no tipo de coleção do grupo (que pode não ser necessariamente o mesmo tipo de coleção da coleção original) que se parece com um dos seguintes métodos:

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

Se uma função agregada usa um argumento delegado, então a expressão de invocação pode ter uma expressão de argumento que deve ser classificada como um valor. A expressão de argumento pode usar as variáveis de intervalo que estão no escopo; Dentro da chamada para uma função agregada, essas variáveis de intervalo representam os valores no grupo que está sendo formado, não todos os valores na coleção. Por exemplo, no exemplo original desta seção, a Average função calcula a média das idades dos clientes por estado, em vez de para todos os clientes juntos.

Todos os tipos de coleção são considerados como tendo a função Group de agregação definida nele, que não usa parâmetros e simplesmente retorna o grupo. Outras funções agregadas padrão que um tipo de coleção pode fornecer são:

Count e LongCount, que retornam a contagem dos elementos no grupo ou a contagem dos elementos no grupo que satisfazem uma expressão booleana. Count e LongCount são suportados somente se o tipo de coleção contiver um dos métodos:

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, que retorna a soma de uma expressão em todos os elementos do grupo. Sum é suportado somente se o tipo de coleção contiver um dos métodos:

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

Min que retorna o valor mínimo de uma expressão em todos os elementos do grupo. Min é suportado somente se o tipo de coleção contiver um dos métodos:

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

Max, que retorna o valor máximo de uma expressão em todos os elementos do grupo. Max é suportado somente se o tipo de coleção contiver um dos métodos:

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

Average, que retorna a média de uma expressão em todos os elementos do grupo. Average é suportado somente se o tipo de coleção contiver um dos métodos:

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

Any, que determina se um grupo contém membros ou se uma expressão booleana é verdadeira para qualquer elemento do grupo. Any retorna um valor que pode ser usado em uma expressão booleana e é suportado somente se o tipo de coleção contém um dos métodos:

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

All, que determina se uma expressão booleana é verdadeira para todos os elementos do grupo. All retorna um valor que pode ser usado em uma expressão booleana e é suportado somente se o tipo de coleção contiver um método:

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

Após um Group By operador de consulta, as variáveis de intervalo anteriormente no escopo são ocultadas e as variáveis de intervalo introduzidas pelas By cláusulas e Into estão disponíveis. Um Group By operador de consulta é suportado somente se o tipo de coleção contiver o método:

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

As declarações de variáveis de intervalo na cláusula são suportadas Group somente se o tipo de coleção contiver o método:

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

O código

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) _
            ...

é geralmente traduzido para

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 não são palavras reservadas.

Operador de consulta agregado

O Aggregate operador de consulta executa uma função semelhante à Group By do operador, exceto que permite agregar sobre grupos que já foram formados. Como o grupo já foi formado, a Into cláusula de um Aggregate operador de consulta não oculta as variáveis de intervalo no escopo (desta forma, Aggregate é mais como um Let, e Group By é mais como um Select).

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

Por exemplo, a consulta a seguir agrega o total de todos os pedidos feitos pelos clientes em Washington:

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

O resultado dessa consulta é uma coleção cujo tipo de elemento é um tipo anônimo com uma propriedade chamada cust typed como Customer e uma propriedade chamada Sum typed como Integer.

Ao contrário Group Bydo , operadores de consulta adicionais podem ser colocados entre as Aggregate cláusulas e Into . Entre uma Aggregate cláusula e o final da Into cláusula, todas as variáveis de intervalo no âmbito, incluindo as declaradas pela Aggregate cláusula, podem ser utilizadas. Por exemplo, a consulta a seguir agrega a soma total de todos os pedidos feitos por clientes em Washington antes de 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)

O Aggregate operador também pode ser usado para iniciar uma expressão de consulta. Nesse caso, o resultado da expressão de consulta será o valor único calculado pela Into cláusula. Por exemplo, a consulta a seguir calcula a soma de todos os totais de pedidos antes de 1º de janeiro de 2006:

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

O resultado da consulta é um único Integer valor. Um Aggregate operador de consulta está sempre disponível (embora a função de agregação também deva estar disponível para que a expressão seja válida). O código

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

é geralmente traduzido para

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

Nota.  Aggregate e Into não são palavras reservadas.

Operador de Consulta de Ingresso em Grupo

O Group Join operador de consulta combina as funções dos operadores de Join consulta e Group By em um único operador. Group Join Junta duas coleções com base em chaves correspondentes extraídas dos elementos, agrupando todos os elementos no lado direito da junção que correspondem a um elemento específico no lado esquerdo da junção. Assim, o operador produz um conjunto de resultados hierárquicos.

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

Por exemplo, a consulta a seguir produz elementos que contêm o nome de um único cliente, um grupo de todos os seus pedidos e o valor total de todos esses pedidos:

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

O resultado da consulta é uma coleção cujo tipo de elemento é um tipo anônimo com três propriedades: Name, digitado como String, Orders digitado como uma coleção cujo tipo de elemento é Order, e OrdersTotal, digitado como Integer. Um Group Join operador de consulta é suportado somente se o tipo de coleção contiver o método:

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

O código

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 _
            ...

é geralmente traduzido para

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 não são palavras reservadas.

Expressões condicionais

Uma expressão condicional If testa uma expressão e retorna um valor.

ConditionalExpression
    : 'If' OpenParenthesis BooleanExpression Comma Expression Comma Expression CloseParenthesis
    | 'If' OpenParenthesis Expression Comma Expression CloseParenthesis
    ;

Ao contrário da IIF função de tempo de execução, no entanto, uma expressão condicional só avalia seus operandos se necessário. Assim, por exemplo, a expressão If(c Is Nothing, c.Name, "Unknown") não lançará uma exceção se o valor de c for Nothing. A expressão condicional tem duas formas: uma que usa dois operandos e outra que usa três operandos.

Se três operandos forem fornecidos, todas as três expressões devem ser classificadas como valores, e o primeiro operando deve ser uma expressão booleana. Se o resultado for da expressão for true, então a segunda expressão será o resultado do operador, caso contrário, a terceira expressão será o resultado do operador. O tipo de resultado da expressão é o tipo dominante entre os tipos da segunda e terceira expressão. Se não houver nenhum tipo dominante, ocorrerá um erro em tempo de compilação.

Se dois operandos forem fornecidos, ambos os operandos devem ser classificados como valores, e o primeiro operando deve ser um tipo de referência ou um tipo de valor anulável. A expressão If(x, y) é então avaliada como se fosse a expressão If(x IsNot Nothing, x, y), com duas exceções. Primeiro, a primeira expressão só é avaliada uma vez e, segundo, se o tipo do segundo operando for um tipo de valor não anulável e o tipo do primeiro operando for, o ? será removido do tipo do primeiro operando ao determinar o tipo dominante para o tipo de resultado da expressão. Por exemplo:

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

Em ambas as formas da expressão, se um operando é Nothing, seu tipo não é usado para determinar o tipo dominante. No caso da expressão If(<expression>, Nothing, Nothing), considera-se que o tipo dominante é Object.

Expressões literais XML

Uma expressão literal XML representa um valor XML (eXtensible Markup Language) 1.0.

XMLLiteralExpression
    : XMLDocument
    | XMLElement
    | XMLProcessingInstruction
    | XMLComment
    | XMLCDATASection
    ;

O resultado de uma expressão literal XML é um valor digitado como um dos tipos do System.Xml.Linq namespace. Se os tipos nesse namespace não estiverem disponíveis, uma expressão literal XML causará um erro em tempo de compilação. Os valores são gerados através de chamadas de construtor traduzidas da expressão literal XML. Por exemplo, o código:

Dim book As System.Xml.Linq.XElement = _
    <book title="My book"></book>

é aproximadamente equivalente ao código:

Dim book As System.Xml.Linq.XElement = _
    New System.Xml.Linq.XElement( _
        "book", _
        New System.Xml.Linq.XAttribute("title", "My book"))

Uma expressão literal XML pode assumir a forma de um documento XML, um elemento XML, uma instrução de processamento XML, um comentário XML ou uma seção CDATA.

Nota. Esta especificação contém apenas o suficiente de uma descrição de XML para descrever o comportamento da linguagem Visual Basic. Mais informações sobre XML podem ser encontradas em http://www.w3.org/TR/REC-xml/.

Regras lexicais

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>'
    ;

As expressões literais XML são interpretadas usando as regras lexicais do XML em vez das regras lexicais do código regular do Visual Basic. Os dois conjuntos de regras diferem geralmente das seguintes formas:

  • O espaço em branco é significativo em XML. Como resultado, a gramática para expressões literais XML declara explicitamente onde o espaço em branco é permitido. O espaço em branco não é preservado, exceto quando ocorre no contexto de dados de caracteres dentro de um elemento. Por exemplo:

    ' The following element preserves no whitespace
    Dim e1 = _
        <customer>
            <name>Bob</>
        </>
    
    ' The following element preserves all of the whitespace
    Dim e2 = _
        <customer>
            Bob
        </>
    
  • O espaço em branco de fim de linha XML é normalizado de acordo com a especificação XML.

  • O XML diferencia maiúsculas de minúsculas. As palavras-chave devem corresponder exatamente à caixa ou ocorrerá um erro em tempo de compilação.

  • Os terminadores de linha são considerados espaço em branco em XML. Como resultado, nenhum caractere de continuação de linha é necessário em expressões literais XML.

  • XML não aceita caracteres de largura total. Se forem usados caracteres de largura total, ocorrerá um erro em tempo de compilação.

Expressões incorporadas

As expressões literais XML podem conter expressões incorporadas. Uma expressão incorporada é uma expressão do Visual Basic que é avaliada e usada para preencher um ou mais valores no local da expressão incorporada.

XMLEmbeddedExpression
    : '<' '%' '=' LineTerminator? Expression LineTerminator? '%' '>'
    ;

Por exemplo, o código a seguir coloca a cadeia de caracteres John Smith como o valor do elemento XML:

Dim name as String = "John Smith"
Dim element As System.Xml.Linq.XElement = <customer><%= name %></customer>

As expressões podem ser incorporadas em vários contextos. Por exemplo, o código a seguir produz um elemento chamado customer:

Dim name As String = "customer"
Dim element As System.Xml.Linq.XElement = <<%= name %>>John Smith</>

Cada contexto em que uma expressão incorporada pode ser usada especifica os tipos que serão aceitos. Quando dentro do contexto da parte de expressão de uma expressão incorporada, as regras lexicais normais para o código do Visual Basic ainda se aplicam, portanto, por exemplo, continuações de linha devem ser usadas:

' Visual Basic expression uses line continuation, XML does not
Dim element As System.Xml.Linq.XElement = _
    <<%= name & _
          name %>>John 
                     Smith</>

Documentos 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
    ;

Um documento XML resulta em um valor digitado como System.Xml.Linq.XDocument. Ao contrário da especificação XML 1.0, documentos XML em expressões literais XML são necessários para especificar o prólogo do documento XML; As expressões literais XML sem o prólogo do documento XML são interpretadas como sua entidade individual. Por exemplo:

Dim doc As System.Xml.Linq.XDocument = _
    <?xml version="1.0"?>
    <?instruction?>
    <customer>Bob</>

Dim pi As System.Xml.Linq.XProcessingInstruction = _
    <?instruction?>

Um documento XML pode conter uma expressão incorporada cujo tipo pode ser qualquer tipo; No tempo de execução, no entanto, o objeto deve satisfazer os XDocument requisitos do construtor ou ocorrerá um erro em tempo de execução.

Ao contrário do XML normal, as expressões de documento XML não suportam DTDs (Document Type Declarations). Além disso, o atributo de codificação, se fornecido, será ignorado, uma vez que a codificação da expressão literal Xml é sempre a mesma que a codificação do próprio arquivo de origem.

Nota. Embora o atributo de codificação seja ignorado, ele ainda é um atributo válido para manter a capacidade de incluir quaisquer documentos Xml 1.0 válidos no código-fonte.

Elementos 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+ ';'
    ;

Um elemento XML resulta em um valor digitado como System.Xml.Linq.XElement. Ao contrário do XML normal, os elementos XML podem omitir o nome na tag de fechamento e o elemento mais aninhado atual será fechado. Por exemplo:

Dim name = <name>Bob</>

As declarações de atributo em um elemento XML resultam em valores digitados como System.Xml.Linq.XAttribute. Os valores de atributos são normalizados de acordo com a especificação XML. Quando o valor de um atributo é Nothing o atributo não será criado, portanto, a expressão do valor do atributo não precisará ser verificada para Nothing. Por exemplo:

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 %>/>

Os elementos e atributos XML podem conter expressões aninhadas nos seguintes locais:

O nome do elemento, caso em que a expressão incorporada deve ser um valor de um tipo implicitamente conversível em System.Xml.Linq.XName. Por exemplo:

Dim name = <<%= "name" %>>Bob</>

O nome de um atributo do elemento , caso em que a expressão incorporada deve ser um valor de um tipo implicitamente conversível em System.Xml.Linq.XName. Por exemplo:

Dim name = <name <%= "length" %>="3">Bob</>

O valor de um atributo do elemento , caso em que a expressão incorporada pode ser um valor de qualquer tipo. Por exemplo:

Dim name = <name length=<%= 3 %>>Bob</>

Um atributo do elemento , caso em que a expressão incorporada pode ser um valor de qualquer tipo. Por exemplo:

Dim name = <name <%= new XAttribute("length", 3) %>>Bob</>

O conteúdo do elemento, caso em que a expressão incorporada pode ser um valor de qualquer tipo. Por exemplo:

Dim name = <name><%= "Bob" %></>

Se o tipo da expressão incorporada for Object(), a matriz será passada como um paramarray para o XElement construtor.

XML Namespaces

Os elementos XML podem conter declarações de namespace XML, conforme definido pela especificação XML namespaces 1.0.

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
    ;

As restrições na definição dos namespaces xml são xmlns aplicadas e produzirão erros em tempo de compilação. As declarações de namespace XML não podem ter uma expressão incorporada para seu valor; O valor fornecido deve ser um literal de cadeia de caracteres não vazia. Por exemplo:

' 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. Esta especificação contém apenas o suficiente de uma descrição do namespace XML para descrever o comportamento da linguagem Visual Basic. Mais informações sobre namespaces XML podem ser encontradas em http://www.w3.org/TR/REC-xml-names/.

Os nomes de elementos e atributos XML podem ser qualificados usando nomes de namespace. Os namespaces são vinculados como em XML regular, com a exceção de que quaisquer importações de namespace declaradas no nível de arquivo são consideradas declaradas em um contexto que inclui a declaração, que é anexado por quaisquer importações de namespace declaradas pelo ambiente de compilação. Se um nome de namespace não puder ser encontrado, ocorrerá um erro em tempo de compilação. Por exemplo:

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

Os namespaces XML declarados em um elemento não se aplicam a literais XML dentro de expressões incorporadas. Por exemplo:

' Error: Namespace prefix 'db' is not declared
Dim customer = _
    <db:customer xmlns:db="http://example.org/database">
        <%= <db:customer>Bob</> %>
    </>

Nota. Isso ocorre porque a expressão incorporada pode ser qualquer coisa, incluindo uma chamada de função. Se a chamada de função contivesse uma expressão literal XML, não está claro se os programadores esperariam que o namespace XML fosse aplicado ou ignorado.

Instruções de processamento XML

Uma instrução de processamento XML resulta em um valor digitado como System.Xml.Linq.XProcessingInstruction. As instruções de processamento XML não podem conter expressões incorporadas, pois são sintaxe válida dentro da instrução de processamento.

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 ">">'
    ;

Comentários XML

Um comentário XML resulta em um valor digitado como System.Xml.Linq.XComment. Os comentários XML não podem conter expressões incorporadas, pois são sintaxe válida dentro do comentário.

XMLComment
    : '<' '!' '-' '-' XMLCommentCharacter* '-' '-' '>'
    ;

XMLCommentCharacter
    : '<Any XMLCharacter except dash (0x002D)>'
    | '-' '<Any XMLCharacter except dash (0x002D)>'
    ;

Secções CDATA

Uma seção CDATA resulta em um valor digitado como System.Xml.Linq.XCData. As seções CDATA não podem conter expressões incorporadas, pois são sintaxe válida dentro da seção CDATA.

XMLCDATASection
    : '<' '!' ( 'CDATA' '[' XMLCDATASectionString? ']' )? '>'
    ;

XMLCDATASectionString
    : '<Any XMLString that does not contain the string "]]>">'
    ;

Expressões de acesso de membro XML

Uma expressão de acesso de membro XML acessa os membros de um valor XML.

XMLMemberAccessExpression
    : Expression '.' LineTerminator? '<' XMLQualifiedName '>'
    | Expression '.' LineTerminator? '@' LineTerminator? '<' XMLQualifiedName '>'
    | Expression '.' LineTerminator? '@' LineTerminator? IdentifierOrKeyword
    | Expression '.' '.' '.' LineTerminator? '<' XMLQualifiedName '>'
    ;

Há três tipos de expressões de acesso de membro XML:

  • Acesso ao elemento, no qual um nome XML segue um único ponto. Por exemplo:

    Dim customer = _
        <customer>
            <name>Bob</>
        </>
    Dim customerName = customer.<name>.Value
    

    Mapas de acesso ao elemento para a função:

    Function Elements(name As System.Xml.Linq.XName) As _
        System.Collections.Generic.IEnumerable(Of _
            System.Xml.Linq.XNode)
    

    Assim, o exemplo acima é equivalente a:

    Dim customerName = customer.Elements("name").Value
    
  • Acesso a atributos, no qual um identificador do Visual Basic segue um ponto e um sinal de ar, ou um nome XML segue um ponto e um sinal de ar. Por exemplo:

    Dim customer = <customer age="30"/>
    Dim customerAge = customer.@age
    

    Mapas de acesso de atributo para a função:

    Function AttributeValue(name As System.Xml.Linq.XName) as String
    

    Assim, o exemplo acima é equivalente a:

    Dim customerAge = customer.AttributeValue("age")
    

    Nota. O AttributeValue método de extensão (bem como a propriedade Valuede extensão relacionada) não está atualmente definido em nenhum assembly. Se os membros da extensão forem necessários, eles são automaticamente definidos na montagem que está sendo produzida.

  • Acesso de descendentes, no qual um nome XML segue três pontos. Por exemplo:

    Dim company = _
        <company>
            <customers>
                <customer>Bob</>
                <customer>Mary</>
                <customer>Joe</>
            </>
        </>
    Dim customers = company...<customer>
    

    Os descendentes acessam mapas para a função:

    Function Descendents(name As System.Xml.Linq.XName) As _
        System.Collections.Generic.IEnumerable(Of _
            System.Xml.Linq.XElement)
    

    Assim, o exemplo acima é equivalente a:

    Dim customers = company.Descendants("customer")
    

A expressão base de uma expressão de acesso de membro XML deve ser um valor e deve ser do tipo:

  • Se um elemento ou descendentes acessam, System.Xml.Linq.XContainer ou um tipo derivado, ou System.Collections.Generic.IEnumerable(Of T) um tipo derivado, onde T é System.Xml.Linq.XContainer ou um tipo derivado.

  • Se um atributo acessa, System.Xml.Linq.XElement ou um tipo derivado, ou System.Collections.Generic.IEnumerable(Of T) um tipo derivado, onde T é System.Xml.Linq.XElement ou um tipo derivado.

Os nomes nas expressões de acesso de membro XML não podem estar vazios. Eles podem ser qualificados para namespace, usando quaisquer namespaces definidos por importações. Por exemplo:

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

O espaço em branco não é permitido após o(s) ponto(s) em uma expressão de acesso de membro XML ou entre os colchetes angulares e o nome. Por exemplo:

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 os tipos no System.Xml.Linq namespace não estiverem disponíveis, uma expressão de acesso de membro XML causará um erro em tempo de compilação.

Operador Await

O operador await está relacionado aos métodos assíncronos, que são descritos na Seção Métodos assíncronos.

AwaitOperatorExpression
    : 'Await' Expression
    ;

Await é uma palavra reservada se o método imediatamente fechado ou expressão lambda em que aparece tem um Async modificador, e se o Await aparece depois desse Async modificador, não é reservado em outro lugar. Também não é reservado nas diretivas de pré-processador. O operador await só é permitido no corpo de um método ou expressões lambda onde é uma palavra reservada. Dentro do método de fechamento imediato ou lambda, uma expressão de espera não pode ocorrer dentro do corpo de um Catch ou Finally bloco, nem dentro do corpo de uma SyncLock instrução, nem dentro de uma expressão de consulta.

O operador await usa uma única expressão que deve ser classificada como um valor e cujo tipo deve ser um tipo aguardado , ou Object. Se for do tipo Object , todo o processamento será adiado até o tempo de execução. Diz-se que um tipo C é aguardado se todos os itens a seguir forem verdadeiros:

  • C contém uma instância acessível ou um método de extensão nomeado GetAwaiter que não tem argumentos e que retorna algum tipo E;

  • E contém uma instância legível ou propriedade de extensão nomeada IsCompleted que não usa argumentos e tem o tipo Boolean;

  • E contém uma instância acessível ou um método de extensão denominado GetResult que não utiliza argumentos;

  • E implementa ou System.Runtime.CompilerServices.INotifyCompletionICriticalNotifyCompletion.

Se GetResult foi um Sub, então a expressão await é classificada como nula. Caso contrário, a expressão await é classificada como um valor e seu tipo é o tipo de retorno do GetResult método.

Aqui está um exemplo de uma classe que pode ser aguardada:

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. Recomenda-se que os autores da biblioteca sigam o padrão em que invocam o delegado de continuação no mesmo SynchronizationContext em OnCompleted que foram invocados. Além disso, o delegado de retomada não deve ser executado de forma síncrona dentro do OnCompleted método, pois isso pode levar ao estouro de pilha: em vez disso, o delegado deve ser enfileirado para execução subsequente.

Quando o fluxo de controle atinge um operador, o Await comportamento é o seguinte.

  1. O GetAwaiter método do operando await é invocado. O resultado desta invocação é denominado awaiter.

  2. A propriedade do IsCompleted awaiter é recuperada. Se o resultado for verdadeiro, então:

    1. O GetResult método do garçom é invocado. Se GetResult era uma função, então o valor da expressão await é o valor de retorno dessa função.
  3. Se a propriedade IsCompleted não for verdadeira, então:

    1. Ou ICriticalNotifyCompletion.UnsafeOnCompleted é invocado no garçom (se o tipo E do awaiter implementa ICriticalNotifyCompletion) ou INotifyCompletion.OnCompleted (caso contrário). Em ambos os casos, ele passa um delegado de retomada associado à instância atual do método assíncrono.

    2. O ponto de controle da instância atual do método assíncrono é suspenso e o fluxo de controle é retomado no chamador atual (definido na Seção Métodos Assíncronos).

    3. Se, posteriormente, o delegado de retomada for invocado,

      1. o delegado de retomada primeiro restaura System.Threading.Thread.CurrentThread.ExecutionContext o que era na época OnCompleted em que era chamado,
      2. em seguida, ele retoma o fluxo de controle no ponto de controle da instância do método assíncrono (consulte Seção Métodos assíncronos),
      3. onde chama o GetResult método do awaiter, como em 2.1 acima.

Se o operando await tiver o tipo Object, esse comportamento será adiado até o tempo de execução:

  • A etapa 1 é realizada chamando GetAwaiter() sem argumentos; pode, portanto, vincular-se em tempo de execução a métodos de instância que usam parâmetros opcionais.
  • A etapa 2 é realizada recuperando a propriedade IsCompleted() sem argumentos e tentando uma conversão intrínseca para Boolean.
  • A etapa 3.a é realizada tentando TryCast(awaiter, ICriticalNotifyCompletion)e, se isso falhar, então DirectCast(awaiter, INotifyCompletion).

O delegado de retomada aprovado em 3.a só pode ser invocado uma vez. Se for invocado mais de uma vez, o comportamento será indefinido.