Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
As instruções representam o código executável.
Statement
: LabelDeclarationStatement
| LocalDeclarationStatement
| WithStatement
| SyncLockStatement
| EventStatement
| AssignmentStatement
| InvocationStatement
| ConditionalStatement
| LoopStatement
| ErrorHandlingStatement
| BranchStatement
| ArrayHandlingStatement
| UsingStatement
| AwaitStatement
| YieldStatement
;
Nota. O compilador do Microsoft Visual Basic só permite instruções que começam com uma palavra-chave ou um identificador. Assim, por exemplo, a instrução de invocação "Call (Console).WriteLine" é permitida, mas a instrução de invocação "(Console).WriteLine" não é.
Fluxo de controle
Fluxo de controle é a sequência na qual instruções e expressões são executadas. A ordem de execução depende da declaração ou expressão em particular.
Por exemplo, ao avaliar um operador de adição ( Operador de Adição de Seção), primeiro o operando esquerdo é avaliado, depois o operando direito e, em seguida, o próprio operador. Os blocos são executados ( Blocos de Seção e Rótulos) primeiro executando sua primeira subdeclaração e, em seguida, procedendo um a um através das instruções do bloco.
Implícito nesta ordenação está o conceito de um ponto de controle, que é a próxima operação a ser executada. Quando um método é invocado (ou "chamado"), dizemos que ele cria uma instância do método. Uma instância de método consiste em sua própria cópia dos parâmetros do método e variáveis locais, e seu próprio ponto de controle.
Métodos regulares
Aqui está um exemplo de um método regular
Function Test() As Integer
Console.WriteLine("hello")
Return 1
End Function
Dim x = Test() ' invokes the function, prints "hello", assigns 1 to x
Quando um método regular é invocado,
- Primeiro, uma instância do método é criada específica para essa invocação. Esta instância inclui uma cópia de todos os parâmetros e variáveis locais do método.
- Em seguida, todos os seus parâmetros são inicializados para os valores fornecidos e todas as suas variáveis locais para os valores padrão de seus tipos.
- No caso de um
Function, uma variável local implícita também é inicializada chamada variável de retorno de função cujo nome é o nome da função, cujo tipo é o tipo de retorno da função e cujo valor inicial é o padrão de seu tipo. - O ponto de controle da instância do método é então definido na primeira instrução do corpo do método, e o corpo do método imediatamente começa a ser executado a partir daí ( Blocos de Seção e Rótulos).
Quando o fluxo de controle sai do corpo do método normalmente - alcançando o End Function ou End Sub que marca seu fim, ou através de uma instrução ou Exit explícita Return - o fluxo de controle retorna ao chamador da instância do método. Se houver uma variável de retorno de função, o resultado da invocação é o valor final dessa variável.
Quando o fluxo de controle sai do corpo do método por meio de uma exceção não tratada, essa exceção é propagada para o chamador.
Após a saída do fluxo de controle, não há mais referências dinâmicas à instância do método. Se a instância do método contiver as únicas referências à sua cópia de variáveis ou parâmetros locais, elas poderão ser coletadas por lixo.
Métodos Iterator
Os métodos iteradores são usados como uma maneira conveniente de gerar uma sequência, que pode ser consumida For Each pela instrução. Os métodos do iterador usam a Yield instrução (Section Yield Statement) para fornecer elementos da sequência. (Um método iterador sem Yield instruções produzirá uma sequência vazia). Aqui está um exemplo de um método iterador:
Iterator Function Test() As IEnumerable(Of Integer)
Console.WriteLine("hello")
Yield 1
Yield 2
End Function
Dim en = Test()
For Each x In en ' prints "hello" before the first x
Console.WriteLine(x) ' prints "1" and then "2"
Next
Quando um método iterador é invocado cujo tipo de retorno é IEnumerator(Of T),
- Primeiro, uma instância do método iterador é criada específica para essa invocação. Esta instância inclui uma cópia de todos os parâmetros e variáveis locais do método.
- Em seguida, todos os seus parâmetros são inicializados para os valores fornecidos e todas as suas variáveis locais para os valores padrão de seus tipos.
- Uma variável local implícita também é inicializada chamada variável atual do iterador, cujo tipo é
Te cujo valor inicial é o padrão de seu tipo. - O ponto de controle da instância do método é então definido na primeira instrução do corpo do método.
- Um objeto iterador é então criado, associado a essa instância de método. O objeto iterador implementa o tipo de retorno declarado e tem o comportamento descrito abaixo.
- O fluxo de controle é então retomado imediatamente no chamador, e o resultado da invocação é o objeto iterador. Observe que essa transferência é feita sem sair da instância do método iterador e não faz com que os manipuladores finalmente sejam executados. A instância do método ainda é referenciada pelo objeto iterador e não será coletada lixo enquanto existir uma referência ao objeto iterador.
Quando a propriedade do Current objeto iterador é acessada, a variável atual da invocação é retornada.
Quando o método do MoveNext objeto iterador é invocado, a invocação não cria uma nova instância do método. Em vez disso, a instância do método existente é usada (e seu ponto de controle e variáveis e parâmetros locais) - a instância que foi criada quando o método iterador foi invocado pela primeira vez. O fluxo de controle retoma a execução no ponto de controle dessa instância do método e prossegue pelo corpo do método iterador normalmente.
Quando o método do Dispose objeto iterador é invocado, novamente a instância do método existente é usada. O fluxo de controle é retomado no ponto de controle dessa instância do método, mas imediatamente se comporta como se uma Exit Function instrução fosse a próxima operação.
As descrições acima de comportamento para invocação de MoveNext ou Dispose em um objeto iterador só se aplicam se todas as invocações anteriores de MoveNext ou Dispose sobre esse objeto iterador já tiverem retornado aos seus chamadores. Se não o fizeram, então o comportamento é indefinido.
Quando o fluxo de controle sai do corpo do método iterador normalmente -- alcançando o End Function que marca seu fim, ou através de uma instrução ou Exit explícita Return -- ele deve ter feito isso no contexto de uma invocação ou Dispose função em um objeto iterador para retomar a instância do método iterador, e ele estará usando a instância do MoveNext método que foi criada quando o método iterador foi invocado pela primeira vez. O ponto de controle dessa instância é deixado na instrução e o End Function fluxo de controle é retomado no chamador, e se ele tiver sido retomado por uma chamada para MoveNext então o valor False é retornado ao chamador.
Quando o fluxo de controle sai do corpo do método iterador por meio de uma exceção não tratada, a exceção é propagada para o chamador, que novamente será uma invocação de MoveNext ou de Dispose.
Quanto aos outros tipos de retorno possíveis de uma função iteradora,
- Quando um método iterador é invocado cujo tipo de retorno é
IEnumerable(Of T)para algunsT, uma instância é criada primeiro -- específica para essa invocação do método iterador -- de todos os parâmetros no método, e eles são inicializados com os valores fornecidos. O resultado da invocação é um objeto que implementa o tipo de retorno. Se o método desseGetEnumeratorobjeto for chamado, ele criará uma instância -- específica para essa invocação deGetEnumerator-- de todos os parâmetros e variáveis locais no método. Ele inicializa os parâmetros para os valores já salvos e prossegue como para o método iterador acima. - Quando um método iterador é invocado cujo tipo de retorno é a interface
IEnumeratornão genérica, o comportamento é exatamente como paraIEnumerator(Of Object). - Quando um método iterador é invocado cujo tipo de retorno é a interface
IEnumerablenão genérica, o comportamento é exatamente como paraIEnumerable(Of Object).
Métodos assíncronos
Os métodos assíncronos são uma maneira conveniente de fazer um trabalho de longa duração sem, por exemplo, bloquear a interface do usuário de um aplicativo. Async é a abreviação de Asynchronous - significa que o chamador do método async retomará sua execução imediatamente, mas a eventual conclusão da instância do método async pode acontecer em algum momento posterior no futuro. Por convenção, os métodos assíncronos são nomeados com o sufixo "Async".
Async Function TestAsync() As Task(Of String)
Console.WriteLine("hello")
Await Task.Delay(100)
Return "world"
End Function
Dim t = TestAsync() ' prints "hello"
Console.WriteLine(Await t) ' prints "world"
Nota. Os métodos assíncronos não são executados em um thread em segundo plano. Em vez disso, eles permitem que um método se suspenda através do Await operador e se programe para ser retomado em resposta a algum evento.
Quando um método assíncrono é invocado
- Primeiro, uma instância do método assíncrono é criada específica para essa invocação. Esta instância inclui uma cópia de todos os parâmetros e variáveis locais do método.
- Em seguida, todos os seus parâmetros são inicializados para os valores fornecidos e todas as suas variáveis locais para os valores padrão de seus tipos.
- No caso de um método assíncrono com tipo
Task(Of T)de retorno para algunsT, uma variável local implícita também é inicializada chamada variável de retorno de tarefa, cujo tipo éTe cujo valor inicial é o padrão de .T - Se o método assíncrono for um
FunctiontipoTaskcom retorno ouTask(Of T)para algunsT, então um objeto desse tipo criado implicitamente, associado à chamada atual. Isso é chamado de objeto assíncrono e representa o trabalho futuro que será feito executando a instância do método assíncrono. Quando o controle for retomado no chamador dessa instância do método assíncrono, o chamador receberá esse objeto assíncrono como resultado de sua invocação. - O ponto de controle da instância é então definido na primeira instrução do corpo do método assíncrono e imediatamente começa a executar o corpo do método a partir daí ( Blocos de Seção e Rótulos).
Delegado de Retomada e Chamador Atual
Conforme detalhado na Seção Await Operator, a execução de uma Await expressão tem a capacidade de suspender o ponto de controle da instância do método, deixando o fluxo de controle para ir para outro lugar. O fluxo de controle pode ser retomado posteriormente no ponto de controle da mesma instância por meio da invocação de um delegado de retomada. Observe que essa suspensão é feita sem sair do método assíncrono e não faz com que os manipuladores sejam executados. A instância do método ainda é referenciada pelo delegado de retomada e pelo resultado ou Task(Of T) (se houver), e não será coletada Task enquanto houver uma referência ativa ao delegado ou resultado.
É útil imaginar a afirmação Dim x = Await WorkAsync() aproximadamente como uma abreviação sintática para o seguinte:
Dim temp = WorkAsync().GetAwaiter()
If Not temp.IsCompleted Then
temp.OnCompleted(resumptionDelegate)
Return [task]
CONT: ' invocation of 'resumptionDelegate' will resume here
End If
Dim x = temp.GetResult()
A seguir, o chamador atual da instância do método é definido como o chamador original ou o chamador do delegado de retomada, o que for mais recente.
Quando o fluxo de controle sai do corpo do método assíncrono -- alcançando o End Sub ou End Function que marca seu fim, ou por meio de uma instrução ou Exit explícitaReturn, ou por meio de uma exceção não tratada -- o ponto de controle da instância é definido como o final do método. O comportamento depende do tipo de retorno do método assíncrono.
No caso de um
Async FunctiontipoTaskcom retorno:Se o fluxo de controle sair por meio de uma exceção não tratada, o status do objeto assíncrono será definido como
TaskStatus.Faultede suaException.InnerExceptionpropriedade será definida como a exceção (exceto: certas exceções definidas pela implementação, comoOperationCanceledExceptionalterá-lo paraTaskStatus.Canceled). O fluxo de controle é retomado no chamador atual.Caso contrário, o status do objeto assíncrono será definido como
TaskStatus.Completed. O fluxo de controle é retomado no chamador atual.(Observação. O ponto principal de Task, e o que torna os métodos assíncronos interessantes, é que, quando uma tarefa se torna Concluída, todos os métodos que estavam aguardando ela terão seus delegados de retomada executados, ou seja, eles serão desbloqueados.)
No caso de um
Async FunctiontipoTask(Of T)com retorno para algunsT: o comportamento é como acima, exceto que, em casos sem exceção, a propriedade doResultobjeto assíncrono também é definida como o valor final da variável de retorno da tarefa.No caso de
Async Sub:- Se o fluxo de controle sair por meio de uma exceção não tratada, essa exceção será propagada para o ambiente de alguma maneira específica da implementação. O fluxo de controle é retomado no chamador atual.
- Caso contrário, o fluxo de controle simplesmente será retomado no chamador atual.
Sub assíncrono
Há algum comportamento específico da Microsoft de um Async Subarquivo .
Se SynchronizationContext.Current estiver Nothing no início da invocação, quaisquer exceções não tratadas de um Sub assíncrono serão postadas no Threadpool.
Se SynchronizationContext.Current não Nothing estiver no início da invocação, então OperationStarted() é invocado nesse contexto antes do início do método e OperationCompleted() após o fim. Além disso, quaisquer exceções não tratadas serão postadas para serem relançadas no contexto de sincronização.
Isso significa que, em aplicativos de interface do usuário, para um Async Sub que é invocado no thread da interface do usuário, quaisquer exceções que ele não conseguir manipular serão repostadas no thread da interface do usuário.
Estruturas mutáveis em métodos assíncronos e iteradores
Estruturas mutáveis em geral são consideradas más práticas e não são suportadas por métodos assíncronos ou iteradores. Em particular, cada invocação de um método assíncrono ou iterador em uma estrutura operará implicitamente em uma cópia dessa estrutura que é copiada em seu momento de invocação. Assim, por exemplo,
Structure S
Dim x As Integer
Async Sub Mutate()
x = 2
End Sub
End Structure
Dim s As New S With {.x = 1}
s.Mutate()
Console.WriteLine(s.x) ' prints "1"
Blocos e Etiquetas
Um grupo de instruções executáveis é chamado de bloco de instruções. A execução de um bloco de instrução começa com a primeira instrução no bloco. Uma vez que uma instrução tenha sido executada, a próxima instrução em ordem lexical é executada, a menos que uma instrução transfira a execução para outro lugar ou ocorra uma exceção.
Dentro de um bloco de instruções, a divisão de instruções em linhas lógicas não é significativa, com exceção das declarações de declaração de rótulo. Um rótulo é um identificador que identifica uma posição específica dentro do bloco de instrução que pode ser usada como o destino de uma instrução branch, como GoTo.
Block
: Statements*
;
LabelDeclarationStatement
: LabelName ':'
;
LabelName
: Identifier
| IntLiteral
;
Statements
: Statement? ( ':' Statement? )*
;
As instruções de declaração de rótulo devem aparecer no início de uma linha lógica e os rótulos podem ser um identificador ou um literal inteiro. Como as instruções de declaração de rótulo e as instruções de invocação podem consistir em um único identificador, um único identificador no início de uma linha local é sempre considerado uma instrução de declaração de rótulo. As declarações de declaração de rótulo devem ser sempre seguidas por dois pontos, mesmo que nenhuma instrução siga na mesma linha lógica.
As etiquetas têm o seu próprio espaço de declaração e não interferem com outros identificadores. O exemplo a seguir é válido e usa a variável x name como parâmetro e rótulo.
Function F(x As Integer) As Integer
If x >= 0 Then
GoTo x
End If
x = -x
x:
Return x
End Function
O âmbito de aplicação de um rótulo é o corpo do método que o contém.
Por uma questão de legibilidade, as produções de instruções que envolvem várias subdeclarações são tratadas como uma única produção nesta especificação, mesmo que as subdeclarações possam estar sozinhas em uma linha rotulada.
Variáveis e parâmetros locais
As seções anteriores detalham como e quando as instâncias de método são criadas e, com elas, as cópias das variáveis e parâmetros locais de um método. Além disso, cada vez que o corpo de um loop é inserido, uma nova cópia é feita de cada variável local declarada dentro desse loop, conforme descrito em Section Loop Statements, e a instância do método agora contém essa cópia de sua variável local em vez da cópia anterior.
Todos os locais são inicializados com o valor padrão do tipo. As variáveis e parâmetros locais estão sempre acessíveis ao público. É um erro referir-se a uma variável local em uma posição textual que precede sua declaração, como ilustra o exemplo a seguir:
Class A
Private i As Integer = 0
Sub F()
i = 1
Dim i As Integer ' Error, use precedes declaration.
i = 2
End Sub
Sub G()
Dim a As Integer = 1
Dim b As Integer = a ' This is valid.
End Sub
End Class
No método acima, a F primeira atribuição a i especificamente não se refere ao campo declarado no escopo externo. Em vez disso, refere-se à variável local, e está em erro porque precede textualmente a declaração da variável.
G No método, uma declaração de variável subsequente refere-se a uma variável local declarada em uma declaração de variável anterior dentro da mesma declaração de variável local.
Cada bloco em um método cria um espaço de declaração para variáveis locais. Os nomes são introduzidos neste espaço de declaração através de declarações de variáveis locais no corpo do método e através da lista de parâmetros do método, que introduz nomes no espaço de declaração do bloco mais externo. Os blocos não permitem sombreamento de nomes por meio de aninhamento: uma vez que um nome tenha sido declarado em um bloco, o nome não poderá ser redeclarado em nenhum bloco aninhado.
Assim, no exemplo a seguir, os F métodos e G estão em erro porque o nome i é declarado no bloco externo e não pode ser redeclarado no bloco interno. No entanto, os H métodos e I são válidos porque os dois i's são declarados em blocos separados não aninhados.
Class A
Sub F()
Dim i As Integer = 0
If True Then
Dim i As Integer = 1
End If
End Sub
Sub G()
If True Then
Dim i As Integer = 0
End If
Dim i As Integer = 1
End Sub
Sub H()
If True Then
Dim i As Integer = 0
End If
If True Then
Dim i As Integer = 1
End If
End Sub
Sub I()
For i As Integer = 0 To 9
H()
Next i
For i As Integer = 0 To 9
H()
Next i
End Sub
End Class
Quando o método é uma função, uma variável local especial é implicitamente declarada no espaço de declaração do corpo do método com o mesmo nome que o método que representa o valor de retorno da função. A variável local tem semântica de resolução de nome especial quando usada em expressões. Se a variável local for usada em um contexto que espera uma expressão classificada como um grupo de métodos, como uma expressão de invocação, o nome será resolvido para a função em vez de para a variável local. Por exemplo:
Function F(i As Integer) As Integer
If i = 0 Then
F = 1 ' Sets the return value.
Else
F = F(i - 1) ' Recursive call.
End If
End Function
O uso de parênteses pode causar situações ambíguas (como F(1), onde F é uma função cujo tipo de retorno é uma matriz unidimensional), em todas as situações ambíguas, o nome resolve para a função em vez da variável local. Por exemplo:
Function F(i As Integer) As Integer()
If i = 0 Then
F = new Integer(2) { 1, 2, 3 }
Else
F = F(i - 1) ' Recursive call, not an index.
End If
End Function
Quando o fluxo de controle deixa o corpo do método, o valor da variável local é passado de volta para a expressão de invocação. Se o método for uma sub-rotina, não há essa variável local implícita e o controle simplesmente retorna à expressão de invocação.
Declarações de Declaração Local
Uma instrução de declaração local declara uma nova variável local, constante local ou variável estática.
Variáveis locais e constantes locais são equivalentes a variáveis de instância e constantes com escopo para o método e são declaradas da mesma maneira.
As variáveis estáticas são semelhantes às Shared variáveis e são declaradas usando o Static modificador.
LocalDeclarationStatement
: LocalModifier VariableDeclarators StatementTerminator
;
LocalModifier
: 'Static' | 'Dim' | 'Const'
;
As variáveis estáticas são locais que retêm seu valor entre invocações do método. As variáveis estáticas declaradas dentro de métodos não compartilhados são por instância: cada instância do tipo que contém o método tem sua própria cópia da variável estática. As variáveis estáticas declaradas dentro Shared dos métodos são por tipo, há apenas uma cópia da variável estática para todas as instâncias. Enquanto as variáveis locais são inicializadas com o valor padrão de seu tipo em cada entrada no método, as variáveis estáticas só são inicializadas com o valor padrão de seu tipo quando o tipo ou instância de tipo é inicializado. As variáveis estáticas não podem ser declaradas em estruturas ou métodos genéricos.
Variáveis locais, constantes locais e variáveis estáticas sempre têm acessibilidade pública e podem não especificar modificadores de acessibilidade. Se nenhum tipo for especificado em uma instrução de declaração local, as etapas a seguir determinarão o tipo da declaração local:
Se a declaração tiver um caractere de tipo, o tipo do caractere de tipo será o tipo da declaração local.
Se a declaração local for uma constante local, ou se a declaração local for uma variável local com um inicializador e a inferência de tipo de variável local estiver sendo usada, o tipo da declaração local será inferido a partir do tipo do inicializador. Se o inicializador se referir à declaração local, ocorrerá um erro em tempo de compilação. (As constantes locais são necessárias para ter inicializadores.)
Se a semântica estrita não estiver sendo usada, o tipo da instrução de declaração local será implicitamente
Object.Caso contrário, ocorrerá um erro em tempo de compilação.
Se nenhum tipo for especificado em uma instrução de declaração local que tenha um modificador de tamanho ou tipo de matriz, o tipo da declaração local será uma matriz com a classificação especificada e as etapas anteriores serão usadas para determinar o tipo de elemento da matriz. Se a inferência de tipo de variável local for usada, o tipo do inicializador deve ser um tipo de matriz com a mesma forma de matriz (ou seja, modificadores de tipo de matriz) que a instrução de declaração local. Observe que é possível que o tipo de elemento inferido ainda seja um tipo de matriz. Por exemplo:
Option Infer On
Module Test
Sub Main()
' Error: initializer is not an array type
Dim x() = 1
' Type is Integer()
Dim y() = New Integer() {}
' Type is Integer()()
Dim z() = New Integer()() {}
' Type is Integer()()()
Dim a()() = New Integer()()() {}
' Error: initializer does not have same array shape
Dim b()() = New Integer(,)() {}
End Sub
End Module
Se nenhum tipo for especificado em uma instrução de declaração local que tenha um modificador de tipo anulável, o tipo da declaração local será a versão anulável do tipo inferido ou o próprio tipo inferido se já for um tipo de valor anulável. Se o tipo inferido não for um tipo de valor que pode ser tornado anulável, ocorrerá um erro em tempo de compilação. Se um modificador de tipo anulável e um modificador de tamanho ou tipo de matriz forem colocados em uma instrução de declaração local sem tipo, o modificador de tipo anulável será considerado como aplicável ao tipo de elemento da matriz e as etapas anteriores serão usadas para determinar o tipo de elemento.
Os inicializadores variáveis nas instruções de declaração local são equivalentes às instruções de atribuição colocadas no local textual da declaração. Assim, se a execução ramifica sobre a instrução de declaração local, o inicializador da variável não é executado. Se a instrução de declaração local for executada mais de uma vez, o inicializador da variável será executado um número igual de vezes. As variáveis estáticas só executam seu inicializador na primeira vez. Se ocorrer uma exceção durante a inicialização de uma variável estática, a variável estática será considerada inicializada com o valor padrão do tipo da variável estática.
O exemplo a seguir mostra o uso de inicializadores:
Module Test
Sub F()
Static x As Integer = 5
Console.WriteLine("Static variable x = " & x)
x += 1
End Sub
Sub Main()
Dim i As Integer
For i = 1 to 3
F()
Next i
i = 3
label:
Dim y As Integer = 8
If i > 0 Then
Console.WriteLine("Local variable y = " & y)
y -= 1
i -= 1
GoTo label
End If
End Sub
End Module
Este programa imprime:
Static variable x = 5
Static variable x = 6
Static variable x = 7
Local variable y = 8
Local variable y = 8
Local variable y = 8
Os inicializadores em locais estáticos são thread-safe e protegidos contra exceções durante a inicialização. Se ocorrer uma exceção durante um inicializador local estático, o local estático terá seu valor padrão e não será inicializado. Um inicializador local estático
Module Test
Sub F()
Static x As Integer = 5
End Sub
End Module
é equivalente a
Imports System.Threading
Imports Microsoft.VisualBasic.CompilerServices
Module Test
Class InitFlag
Public State As Short
End Class
Private xInitFlag As InitFlag = New InitFlag()
Sub F()
Dim x As Integer
If xInitFlag.State <> 1 Then
Monitor.Enter(xInitFlag)
Try
If xInitFlag.State = 0 Then
xInitFlag.State = 2
x = 5
Else If xInitFlag.State = 2 Then
Throw New IncompleteInitialization()
End If
Finally
xInitFlag.State = 1
Monitor.Exit(xInitFlag)
End Try
End If
End Sub
End Module
Variáveis locais, constantes locais e variáveis estáticas têm escopo para o bloco de instrução no qual são declaradas. As variáveis estáticas são especiais na medida em que os seus nomes só podem ser utilizados uma vez ao longo de todo o método. Por exemplo, não é válido especificar duas declarações de variáveis estáticas com o mesmo nome, mesmo que estejam em blocos diferentes.
Declarações locais implícitas
Além das instruções de declaração local, as variáveis locais também podem ser declaradas implicitamente através do uso. Uma expressão de nome simples que usa um nome que não resolve para outra coisa declara uma variável local por esse nome. Por exemplo:
Option Explicit Off
Module Test
Sub Main()
x = 10
y = 20
Console.WriteLine(x + y)
End Sub
End Module
A declaração local implícita só ocorre em contextos de expressão que podem aceitar uma expressão classificada como uma variável. A exceção a essa regra é que uma variável local pode não ser declarada implicitamente quando é o destino de uma expressão de invocação de função, expressão de indexação ou expressão de acesso de membro.
Os locais implícitos são tratados como se fossem declarados no início do método de contenção. Assim, eles são sempre direcionados para todo o corpo do método, mesmo que declarados dentro de uma expressão lambda. Por exemplo, o seguinte código:
Option Explicit Off
Module Test
Sub Main()
Dim x = Sub()
a = 10
End Sub
Dim y = Sub()
Console.WriteLine(a)
End Sub
x()
y()
End Sub
End Module
irá imprimir o valor 10. Os locais implícitos são digitados como Object se nenhum caractere de tipo estivesse anexado ao nome da variável, caso contrário, o tipo da variável é o tipo do caractere de tipo. A inferência de tipo de variável local não é usada para locais implícitos.
Se a declaração local explícita for especificada pelo ambiente de compilação ou pelo Option Explicit, todas as variáveis locais deverão ser explicitamente declaradas e a declaração de variável implícita não será permitida.
Com Declaração
Uma With instrução permite várias referências aos membros de uma expressão sem especificar a expressão várias vezes.
WithStatement
: 'With' Expression StatementTerminator
Block?
'End' 'With' StatementTerminator
;
A expressão deve ser classificada como um valor e é avaliada uma vez, aquando da entrada no bloco. Dentro do bloco de instrução, uma expressão de acesso de With membro ou expressão de acesso de dicionário começando com um ponto ou um ponto de exclamação é avaliada como se a With expressão a precedisse. Por exemplo:
Structure Test
Public x As Integer
Function F() As Integer
Return 10
End Function
End Structure
Module TestModule
Sub Main()
Dim y As Test
With y
.x = 10
Console.WriteLine(.x)
.x = .F()
End With
End Sub
End Module
É inválido ramificar em um With bloco de instrução de fora do bloco.
Instrução SyncLock
Uma SyncLock instrução permite que as instruções sejam sincronizadas em uma expressão, o que garante que vários threads de execução não executem as mesmas instruções ao mesmo tempo.
SyncLockStatement
: 'SyncLock' Expression StatementTerminator
Block?
'End' 'SyncLock' StatementTerminator
;
A expressão deve ser classificada como um valor e é avaliada uma vez, ao entrar no bloco. Ao inserir o SyncLock bloco, o Shared método System.Threading.Monitor.Enter é chamado na expressão especificada, que bloqueia até que o thread de execução tenha um bloqueio exclusivo no objeto retornado pela expressão. O tipo da expressão em uma SyncLock instrução deve ser um tipo de referência. Por exemplo:
Class Test
Private count As Integer = 0
Public Function Add() As Integer
SyncLock Me
count += 1
Add = count
End SyncLock
End Function
Public Function Subtract() As Integer
SyncLock Me
count -= 1
Subtract = count
End SyncLock
End Function
End Class
O exemplo acima sincroniza na instância específica da classe Test para garantir que não mais de um thread de execução possa adicionar ou subtrair da variável count de cada vez para uma instância específica.
O SyncLock bloco é implicitamente contido por uma Try instrução cujo Finally bloco chama o Shared método System.Threading.Monitor.Exit na expressão. Isso garante que o bloqueio seja liberado mesmo quando uma exceção é lançada. Como resultado, é inválido ramificar em um SyncLock bloco de fora do bloco, e um SyncLock bloco é tratado como uma única instrução para os fins de Resume e Resume Next. O exemplo acima é equivalente ao seguinte código:
Class Test
Private count As Integer = 0
Public Function Add() As Integer
Try
System.Threading.Monitor.Enter(Me)
count += 1
Add = count
Finally
System.Threading.Monitor.Exit(Me)
End Try
End Function
Public Function Subtract() As Integer
Try
System.Threading.Monitor.Enter(Me)
count -= 1
Subtract = count
Finally
System.Threading.Monitor.Exit(Me)
End Try
End Function
End Class
Declarações de Evento
As RaiseEventinstruções , AddHandler, e RemoveHandler geram eventos e manipulam eventos dinamicamente.
EventStatement
: RaiseEventStatement
| AddHandlerStatement
| RemoveHandlerStatement
;
Declaração RaiseEvent
Uma RaiseEvent instrução notifica os manipuladores de eventos de que um evento específico ocorreu.
RaiseEventStatement
: 'RaiseEvent' IdentifierOrKeyword
( OpenParenthesis ArgumentList? CloseParenthesis )? StatementTerminator
;
A expressão de nome simples em uma RaiseEvent instrução é interpretada como uma pesquisa de membro em Me. Assim, RaiseEvent x é interpretado como se fosse RaiseEvent Me.x. O resultado da expressão deve ser classificado como um acesso a eventos para um evento definido na própria classe; Os eventos definidos em tipos base não podem ser usados em uma RaiseEvent instrução.
A RaiseEvent instrução é processada como uma chamada para o Invoke método do delegado do evento, usando os parâmetros fornecidos, se houver. Se o valor do delegado for Nothing, nenhuma exceção será lançada. Se não houver argumentos, os parênteses podem ser omitidos. Por exemplo:
Class Raiser
Public Event E1(Count As Integer)
Public Sub Raise()
Static RaiseCount As Integer = 0
RaiseCount += 1
RaiseEvent E1(RaiseCount)
End Sub
End Class
Module Test
Private WithEvents x As Raiser
Private Sub E1Handler(Count As Integer) Handles x.E1
Console.WriteLine("Raise #" & Count)
End Sub
Public Sub Main()
x = New Raiser
x.Raise() ' Prints "Raise #1".
x.Raise() ' Prints "Raise #2".
x.Raise() ' Prints "Raise #3".
End Sub
End Module
A classe Raiser acima é equivalente a:
Class Raiser
Public Event E1(Count As Integer)
Public Sub Raise()
Static RaiseCount As Integer = 0
Dim TemporaryDelegate As E1EventHandler
RaiseCount += 1
' Use a temporary to avoid a race condition.
TemporaryDelegate = E1Event
If Not TemporaryDelegate Is Nothing Then
TemporaryDelegate.Invoke(RaiseCount)
End If
End Sub
End Class
Instruções AddHandler e RemoveHandler
Embora a maioria dos manipuladores de eventos sejam conectados automaticamente por meio WithEvents de variáveis, pode ser necessário adicionar e remover dinamicamente manipuladores de eventos em tempo de execução.
AddHandler e RemoveHandler as declarações fazem isso.
AddHandlerStatement
: 'AddHandler' Expression Comma Expression StatementTerminator
;
RemoveHandlerStatement
: 'RemoveHandler' Expression Comma Expression StatementTerminator
;
Cada instrução usa dois argumentos: o primeiro argumento deve ser uma expressão que é classificada como um acesso a eventos e o segundo argumento deve ser uma expressão que é classificada como um valor. O tipo do segundo argumento deve ser o tipo de delegado associado ao acesso ao evento. Por exemplo:
Public Class Form1
Public Sub New()
' Add Button1_Click as an event handler for Button1's Click event.
AddHandler Button1.Click, AddressOf Button1_Click
End Sub
Private Button1 As Button = New Button()
Sub Button1_Click(sender As Object, e As EventArgs)
Console.WriteLine("Button1 was clicked!")
End Sub
Public Sub Disconnect()
RemoveHandler Button1.Click, AddressOf Button1_Click
End Sub
End Class
Dado um evento E, , a instrução chama o método ou relevante add_Eremove_E na instância para adicionar ou remover o delegado como um manipulador para o evento. Assim, o código acima é equivalente a:
Public Class Form1
Public Sub New()
Button1.add_Click(AddressOf Button1_Click)
End Sub
Private Button1 As Button = New Button()
Sub Button1_Click(sender As Object, e As EventArgs)
Console.WriteLine("Button1 was clicked!")
End Sub
Public Sub Disconnect()
Button1.remove_Click(AddressOf Button1_Click)
End Sub
End Class
Declarações de Atribuição
Uma instrução de atribuição atribui o valor de uma expressão a uma variável. Existem vários tipos de atribuição.
AssignmentStatement
: RegularAssignmentStatement
| CompoundAssignmentStatement
| MidAssignmentStatement
;
Declarações de Atribuição Regular
Uma instrução de atribuição simples armazena o resultado de uma expressão em uma variável.
RegularAssignmentStatement
: Expression Equals Expression StatementTerminator
;
A expressão no lado esquerdo do operador de atribuição deve ser classificada como uma variável ou um acesso à propriedade, enquanto a expressão no lado direito do operador de atribuição deve ser classificada como um valor. O tipo da expressão deve ser implicitamente conversível para o tipo da variável ou acesso à propriedade.
Se a variável que está sendo atribuída for um elemento de matriz de um tipo de referência, uma verificação em tempo de execução será executada para garantir que a expressão seja compatível com o tipo de elemento de matriz. No exemplo a seguir, a última atribuição faz com que a System.ArrayTypeMismatchException seja lançada, porque uma instância de ArrayList não pode ser armazenada em um elemento de uma String matriz.
Dim sa(10) As String
Dim oa As Object() = sa
oa(0) = Nothing ' This is allowed.
oa(1) = "Hello" ' This is allowed.
oa(2) = New ArrayList() ' System.ArrayTypeMismatchException is thrown.
Se a expressão no lado esquerdo do operador de atribuição for classificada como uma variável, a instrução de atribuição armazenará o valor na variável. Se a expressão for classificada como um acesso à propriedade, a instrução de atribuição transformará o acesso à propriedade em uma invocação do Set acessador da propriedade com o valor substituído pelo parâmetro value. Por exemplo:
Module Test
Private PValue As Integer
Public Property P As Integer
Get
Return PValue
End Get
Set (Value As Integer)
PValue = Value
End Set
End Property
Sub Main()
' The following two lines are equivalent.
P = 10
set_P(10)
End Sub
End Module
Se o destino da variável ou acesso à propriedade for digitado como um tipo de valor, mas não classificado como uma variável, ocorrerá um erro em tempo de compilação. Por exemplo:
Structure S
Public F As Integer
End Structure
Class C
Private PValue As S
Public Property P As S
Get
Return PValue
End Get
Set (Value As S)
PValue = Value
End Set
End Property
End Class
Module Test
Sub Main()
Dim ct As C = New C()
Dim rt As Object = new C()
' Compile-time error: ct.P not classified as variable.
ct.P.F = 10
' Run-time exception.
rt.P.F = 10
End Sub
End Module
Observe que a semântica da atribuição depende do tipo da variável ou propriedade à qual ela está sendo atribuída. Se a variável à qual está sendo atribuída for um tipo de valor, a atribuição copiará o valor da expressão para a variável. Se a variável à qual está sendo atribuída for um tipo de referência, a atribuição copiará a referência, não o valor em si, para a variável. Se o tipo da variável for Object, a semântica da atribuição será determinada pelo fato de o tipo do valor ser um tipo de valor ou um tipo de referência em tempo de execução.
Nota. Para tipos intrínsecos como Integer e Date, a semântica de referência e atribuição de valor é a mesma porque os tipos são imutáveis. Como resultado, a linguagem é livre para usar a atribuição de referência em tipos intrínsecos em caixa como uma otimização. Do ponto de vista do valor, o resultado é o mesmo.
Como o caractere igual (=) é usado tanto para atribuição quanto para igualdade, há uma ambiguidade entre uma atribuição simples e uma instrução de invocação em situações como x = y.ToString(). Em todos esses casos, a declaração de atribuição tem precedência sobre o operador de igualdade. Isso significa que a expressão de exemplo é interpretada (x = y).ToString()como x = (y.ToString()) e não como .
Declarações de Atribuição Compostas
Uma instrução de atribuição composta assume a forma V op= E (onde op é um operador binário válido).
CompoundAssignmentStatement
: Expression CompoundBinaryOperator LineTerminator? Expression StatementTerminator
;
CompoundBinaryOperator
: '^' '=' | '*' '=' | '/' '=' | '\\' '=' | '+' '=' | '-' '='
| '&' '=' | '<' '<' '=' | '>' '>' '='
;
A expressão no lado esquerdo do operador de atribuição deve ser classificada como uma variável ou acesso à propriedade, enquanto a expressão no lado direito do operador de atribuição deve ser classificada como um valor. A instrução de atribuição composta é equivalente à instrução V = V op E com a diferença de que a variável no lado esquerdo do operador de atribuição composta é avaliada apenas uma vez. O exemplo a seguir demonstra essa diferença:
Module Test
Function GetIndex() As Integer
Console.WriteLine("Getting index")
Return 1
End Function
Sub Main()
Dim a(2) As Integer
Console.WriteLine("Simple assignment")
a(GetIndex()) = a(GetIndex()) + 1
Console.WriteLine("Compound assignment")
a(GetIndex()) += 1
End Sub
End Module
A expressão a(GetIndex()) é avaliada duas vezes para atribuição simples, mas apenas uma vez para atribuição composta, de modo que o código imprime:
Simple assignment
Getting index
Getting index
Compound assignment
Getting index
Declaração de Atribuição Média
Uma Mid instrução de atribuição atribui uma cadeia de caracteres a outra cadeia de caracteres. O lado esquerdo da atribuição tem a mesma sintaxe de uma chamada para a função Microsoft.VisualBasic.Strings.Mid.
MidAssignmentStatement
: 'Mid' '$'? OpenParenthesis Expression Comma Expression
( Comma Expression )? CloseParenthesis Equals Expression StatementTerminator
;
O primeiro argumento é o destino da atribuição e deve ser classificado como uma variável ou um acesso à Stringpropriedade cujo tipo é implicitamente conversível de e para . O segundo parâmetro é a posição inicial baseada em 1 que corresponde a onde a atribuição deve começar na cadeia de caracteres de destino e deve ser classificada como um valor cujo tipo deve ser implicitamente conversível em Integer. O terceiro parâmetro opcional é o número de caracteres do valor do lado direito a atribuir à cadeia de caracteres de destino e deve ser classificado como um valor cujo tipo é implicitamente conversível em Integer. O lado direito é a cadeia de caracteres de origem e deve ser classificado como um valor cujo tipo é implicitamente conversível em String. O lado direito é truncado para o parâmetro length, se especificado, e substitui os caracteres na cadeia de caracteres do lado esquerdo, começando na posição inicial. Se a cadeia de caracteres do lado direito contiver menos caracteres do que o terceiro parâmetro, somente os caracteres da cadeia do lado direito serão copiados.
O exemplo a seguir exibe ab123fg:
Module Test
Sub Main()
Dim s1 As String = "abcdefg"
Dim s2 As String = "1234567"
Mid$(s1, 3, 3) = s2
Console.WriteLine(s1)
End Sub
End Module
Nota.
Mid não é uma palavra reservada.
Declarações de invocação
Uma instrução de invocação invoca um método precedido pela palavra-chave Callopcional . A instrução de invocação é processada da mesma forma que a expressão de invocação de função, com algumas diferenças observadas abaixo. A expressão de invocação deve ser classificada como valor ou vazio. Qualquer valor resultante da avaliação da expressão de invocação é descartado.
Se a Call palavra-chave for omitida, a expressão de invocação deve começar com um identificador ou palavra-chave, ou com . dentro de um With bloco. Assim, por exemplo, "Call 1.ToString()" é uma afirmação válida, mas "1.ToString()" não é. (Observe que, em um contexto de expressão, as expressões de invocação também não precisam começar com um identificador. Por exemplo, "Dim x = 1.ToString()" é uma instrução válida).
Há outra diferença entre as instruções de invocação e as expressões de invocação: se uma instrução de invocação inclui uma lista de argumentos, então esta é sempre tomada como a lista de argumentos da invocação. O exemplo a seguir ilustra a diferença:
Module Test
Sub Main()
Call {Function() 15}(0)
' error: (0) is taken as argument list, but array is not invokable
Call ({Function() 15}(0))
' valid, since the invocation statement has no argument list
Dim x = {Function() 15}(0)
' valid as an expression, since (0) is taken as an array-indexing
Call f("a")
' error: ("a") is taken as argument list to the invocation of f
Call f()("a")
' valid, since () is the argument list for the invocation of f
Dim y = f("a")
' valid as an expression, since f("a") is interpreted as f()("a")
End Sub
Sub f() As Func(Of String,String)
Return Function(x) x
End Sub
End Module
InvocationStatement
: 'Call'? InvocationExpression StatementTerminator
;
Declarações condicionais
As instruções condicionais permitem a execução condicional de instruções com base em expressões avaliadas em tempo de execução.
ConditionalStatement
: IfStatement
| SelectStatement
;
Se... Então... Declarações Else
Uma If...Then...Else instrução é a instrução condicional básica.
IfStatement
: BlockIfStatement
| LineIfThenStatement
;
BlockIfStatement
: 'If' BooleanExpression 'Then'? StatementTerminator
Block?
ElseIfStatement*
ElseStatement?
'End' 'If' StatementTerminator
;
ElseIfStatement
: ElseIf BooleanExpression 'Then'? StatementTerminator
Block?
;
ElseStatement
: 'Else' StatementTerminator
Block?
;
LineIfThenStatement
: 'If' BooleanExpression 'Then' Statements ( 'Else' Statements )? StatementTerminator
;
ElseIf
: 'ElseIf'
| 'Else' 'If'
;
Cada expressão em uma If...Then...Else instrução deve ser uma expressão booleana, de acordo com Expressões Booleanas da Seção. (Nota: isso não requer que a expressão tenha tipo booleano). Se a expressão na If instrução for true, as instruções incluídas pelo If bloco serão executadas. Se a expressão for falsa, cada uma das ElseIf expressões é avaliada. Se uma das ElseIf expressões for avaliada como true, o bloco correspondente será executado. Se nenhuma expressão for avaliada como true e houver um Else bloco, o Else bloco será executado. Quando um bloco termina de ser executado, a If...Then...Else execução passa para o final da instrução.
A versão de linha da If instrução tem um único conjunto de instruções a serem executadas se a expressão for True e If um conjunto opcional de instruções a ser executado se a expressão for False. Por exemplo:
Module Test
Sub Main()
Dim a As Integer = 10
Dim b As Integer = 20
' Block If statement.
If a < b Then
a = b
Else
b = a
End If
' Line If statement
If a < b Then a = b Else b = a
End Sub
End Module
A versão de linha da instrução If liga-se menos firmemente do que ":", e liga-se Else ao precedente If lexicamente mais próximo que é permitido pela sintaxe. Por exemplo, as duas versões a seguir são equivalentes:
If True Then _
If True Then Console.WriteLine("a") Else Console.WriteLine("b") _
Else Console.WriteLine("c") : Console.WriteLine("d")
If True Then
If True Then
Console.WriteLine("a")
Else
Console.WriteLine("b")
End If
Console.WriteLine("c") : Console.WriteLine("d")
End If
Todas as instruções que não sejam instruções de declaração de rótulo são permitidas dentro de uma instrução de linha If , incluindo instruções de bloco. No entanto, eles não podem usar LineTerminators como StatementTerminators, exceto dentro de expressões lambda de várias linhas. Por exemplo:
' Allowed, since it uses : instead of LineTerminator to separate statements
If b Then With New String("a"(0),5) : Console.WriteLine(.Length) : End With
' Disallowed, since it uses a LineTerminator
If b then With New String("a"(0), 5)
Console.WriteLine(.Length)
End With
' Allowed, since it only uses LineTerminator inside a multi-line lambda
If b Then Call Sub()
Console.WriteLine("a")
End Sub.Invoke()
Selecionar declarações de caso
Uma Select Case instrução executa instruções com base no valor de uma expressão.
SelectStatement
: 'Select' 'Case'? Expression StatementTerminator
CaseStatement*
CaseElseStatement?
'End' 'Select' StatementTerminator
;
CaseStatement
: 'Case' CaseClauses StatementTerminator
Block?
;
CaseClauses
: CaseClause ( Comma CaseClause )*
;
CaseClause
: ( 'Is' LineTerminator? )? ComparisonOperator LineTerminator? Expression
| Expression ( 'To' Expression )?
;
ComparisonOperator
: '=' | '<' '>' | '<' | '>' | '>' '=' | '<' '='
;
CaseElseStatement
: 'Case' 'Else' StatementTerminator
Block?
;
A expressão deve ser classificada como um valor. Quando uma Select Case instrução é executada, a Select expressão é avaliada primeiro e, em seguida, as Case instruções são avaliadas em ordem de declaração textual. A primeira Case instrução que avalia tem True seu bloco executado. Se nenhuma Case instrução for avaliada e True houver uma Case Else instrução, esse bloco será executado. Uma vez que um bloco tenha terminado de executar, a execução passa para o final da Select instrução.
A execução de um Case bloco não pode "cair" para a próxima seção de switch. Isso evita uma classe comum de bugs que ocorrem em outros idiomas quando uma Case instrução de encerramento é omitida acidentalmente. O exemplo a seguir ilustra esse comportamento:
Module Test
Sub Main()
Dim x As Integer = 10
Select Case x
Case 5
Console.WriteLine("x = 5")
Case 10
Console.WriteLine("x = 10")
Case 20 - 10
Console.WriteLine("x = 20 - 10")
Case 30
Console.WriteLine("x = 30")
End Select
End Sub
End Module
O código imprime:
x = 10
Embora Case 10 e Case 20 - 10 selecione para o mesmo valor, Case 10 é executado porque precede textualmente Case 20 - 10 . Quando o próximo Case é atingido, a execução continua após a Select instrução.
Uma Case cláusula pode assumir duas formas. Um formulário é uma palavra-chave opcional Is , um operador de comparação e uma expressão. A expressão é convertida para o tipo da Select expressão, se a expressão não é implicitamente convertível para o tipo da expressão, ocorre um erro em tempo de Select compilação. Se a Select expressão for E, o operador de comparação for Op e a Case expressão for E1, o caso será avaliado como E OP E1. O operador deve ser válido para os tipos das duas expressões; caso contrário, ocorrerá um erro em tempo de compilação.
A outra forma é uma expressão opcionalmente seguida pela palavra-chave To e uma segunda expressão. Ambas as expressões são convertidas para o tipo da Select expressão, se uma das expressões não for implicitamente convertível para o tipo da expressão, ocorrerá um erro em tempo de Select compilação. Se a Select expressão for E, a primeira Case expressão for E1, e a segunda Case expressão for E2, a Case será avaliada como E = E1 (se não E2 for especificado) ou (E >= E1) And (E <= E2). Os operadores devem ser válidos para os tipos das duas expressões; caso contrário, ocorrerá um erro em tempo de compilação.
Instruções de loop
As instruções Loop permitem a execução repetida das instruções em seu corpo.
LoopStatement
: WhileStatement
| DoLoopStatement
| ForStatement
| ForEachStatement
;
Cada vez que um corpo de loop é inserido, uma nova cópia é feita de todas as variáveis locais declaradas nesse corpo, inicializadas para os valores anteriores das variáveis. Qualquer referência a uma variável dentro do corpo do loop usará a cópia feita mais recentemente. Este código mostra um exemplo:
Module Test
Sub Main()
Dim lambdas As New List(Of Action)
Dim x = 1
For i = 1 To 3
x = i
Dim y = x
lambdas.Add(Sub() Console.WriteLine(x & y))
Next
For Each lambda In lambdas
lambda()
Next
End Sub
End Module
O código produz a saída:
31 32 33
Quando o corpo do loop é executado, ele usa qualquer cópia da variável atual. Por exemplo, a declaração Dim y = x refere-se à última cópia e à cópia original do xy . E quando um lambda é criado, ele se lembra de qualquer cópia de uma variável que estava atual no momento em que foi criada. Portanto, cada lambda usa a mesma cópia compartilhada do x, mas uma cópia diferente do y. No final do programa, quando executa as lambdas, aquela cópia partilhada daquilo x a que todos se referem está agora no seu valor final 3.
Observe que, se não houver expressões lambdas ou LINQ, é impossível saber que uma nova cópia é feita na entrada de loop. De fato, as otimizações do compilador evitarão fazer cópias neste caso. Observe também que é ilegal GoTo entrar em um loop que contém expressões lambdas ou LINQ.
Enquanto... Termine enquanto e faça... Instruções de loop
Uma While instrução ou Do loop loops com base em uma expressão booleana.
WhileStatement
: 'While' BooleanExpression StatementTerminator
Block?
'End' 'While' StatementTerminator
;
DoLoopStatement
: DoTopLoopStatement
| DoBottomLoopStatement
;
DoTopLoopStatement
: 'Do' ( WhileOrUntil BooleanExpression )? StatementTerminator
Block?
'Loop' StatementTerminator
;
DoBottomLoopStatement
: 'Do' StatementTerminator
Block?
'Loop' WhileOrUntil BooleanExpression StatementTerminator
;
WhileOrUntil
: 'While' | 'Until'
;
Uma While instrução loop é executada desde que a expressão booleana seja avaliada como true, uma Do instrução loop pode conter uma condição mais complexa. Uma expressão pode ser colocada depois da Do palavra-chave ou após a palavra-chave, mas não depois de Loop ambas. A expressão booleana é avaliada de acordo com a seção Expressões booleanas. (Nota: isso não requer que a expressão tenha tipo booleano). Também é válido não especificar nenhuma expressão; nesse caso, o loop nunca será encerrado. Se a expressão for colocada após Do, ela será avaliada antes que o bloco de loop seja executado em cada iteração. Se a expressão for colocada após Loop, ela será avaliada após a execução do bloco de loop em cada iteração. Colocar a expressão depois Loop gerará, portanto, mais um loop do que o posicionamento após Do. O exemplo a seguir demonstra esse comportamento:
Module Test
Sub Main()
Dim x As Integer
x = 3
Do While x = 1
Console.WriteLine("First loop")
Loop
Do
Console.WriteLine("Second loop")
Loop While x = 1
End Sub
End Module
O código produz a saída:
Second Loop
No caso do primeiro loop, a condição é avaliada antes da execução do loop. No caso do segundo loop, a condição é executada após a execução do loop. A expressão condicional deve ser prefixada com uma While palavra-chave ou uma Until palavra-chave. O primeiro quebra o loop se a condição for avaliada como false, o segundo quando a condição for avaliada como true.
Nota.
Until não é uma palavra reservada.
Para... Próximas Declarações
Um For...Next loop de instrução com base em um conjunto de limites. Uma For instrução especifica uma variável de controle de loop, uma expressão de limite inferior, uma expressão de limite superior e uma expressão de valor de etapa opcional. A variável de controle de loop é especificada por meio de um identificador seguido por uma cláusula opcional As ou uma expressão.
ForStatement
: 'For' LoopControlVariable Equals Expression 'To' Expression
( 'Step' Expression )? StatementTerminator
Block?
( 'Next' NextExpressionList? StatementTerminator )?
;
LoopControlVariable
: Identifier ( IdentifierModifiers 'As' TypeName )?
| Expression
;
NextExpressionList
: Expression ( Comma Expression )*
;
De acordo com as regras a seguir, a variável de controle de loop refere-se a uma nova variável local específica para essa For...Next instrução, ou a uma variável pré-existente, ou a uma expressão.
Se a variável de controle de loop for um identificador com uma
Ascláusula, o identificador definirá uma nova variável local do tipo especificado naAscláusula, com escopo para todoForo loop.Se a variável de controle de loop for um identificador sem uma
Ascláusula, o identificador será primeiro resolvido usando as regras de resolução de nomes simples (consulte Expressões de nome simples da seção), exceto que essa ocorrência do identificador não causaria, por si só, a criação de uma variável local implícita ( Seção Declarações locais implícitas).Se essa resolução for bem-sucedida e o resultado for classificado como uma variável, a variável de controle de loop será essa variável pré-existente.
Se a resolução falhar ou se a resolução for bem-sucedida e o resultado for classificado como um tipo, então:
- se a inferência de tipo de variável local estiver sendo usada, o identificador define uma nova variável local cujo tipo é inferido a partir das expressões bound e step, com escopo para todo
Foro loop; - se a inferência de tipo de variável local não está sendo usada, mas a declaração local implícita está, então uma variável local implícita é criada cujo escopo é o método inteiro (Seção Declarações Locais Implícitas), e a variável de controle de loop refere-se a essa variável pré-existente;
- Se nem a inferência de tipo de variável local nem as declarações locais implícitas forem usadas, trata-se de um erro.
- se a inferência de tipo de variável local estiver sendo usada, o identificador define uma nova variável local cujo tipo é inferido a partir das expressões bound e step, com escopo para todo
Se a resolução for bem-sucedida com algo classificado como nem um tipo nem uma variável, é um erro.
Se a variável de controle de loop for uma expressão, a expressão deve ser classificada como uma variável.
Uma variável de controle de loop não pode ser usada por outra instrução de inclusão For...Next . O tipo da variável de controle de loop de uma For instrução determina o tipo da iteração e deve ser um de:
-
Byte,SByte, ,ShortUShort,UInteger,Integer,ULong,LongDecimalSingleDouble - Um tipo enumerado
Object- Um tipo
Tque tem os seguintes operadores, ondeBé um tipo que pode ser usado em uma expressão booleana:
Public Shared Operator >= (op1 As T, op2 As T) As B
Public Shared Operator <= (op1 As T, op2 As T) As B
Public Shared Operator - (op1 As T, op2 As T) As T
Public Shared Operator + (op1 As T, op2 As T) As T
As expressões bound e step devem ser implicitamente conversíveis para o tipo da variável de controle de loop e devem ser classificadas como valores. Em tempo de compilação, o tipo da variável de controle de loop é inferido escolhendo o tipo mais largo entre os tipos de expressão de limite inferior, limite superior e etapa. Se não houver nenhuma conversão de ampliação entre dois dos tipos, ocorrerá um erro em tempo de compilação.
Em tempo de execução, se o tipo da variável de controle de loop for Object, então o tipo da iteração é inferido da mesma forma que em tempo de compilação, com duas exceções. Primeiro, se as expressões bound e step forem todas de tipos integrais, mas não tiverem um tipo mais amplo, então o tipo mais amplo que engloba todos os três tipos será inferido. E segundo, se o tipo da variável de controle de loop é inferido como sendo String, Double será inferido em vez disso. Se, em tempo de execução, nenhum tipo de controle de loop puder ser determinado ou se alguma das expressões não puder ser convertida para o tipo de controle de loop, ocorrerá um System.InvalidCastException . Uma vez que um tipo de controle de loop tenha sido escolhido no início do loop, o mesmo tipo será usado durante toda a iteração, independentemente das alterações feitas no valor na variável de controle de loop.
Uma For instrução deve ser fechada por uma instrução correspondente Next . Uma Next instrução sem uma variável corresponde à instrução aberta For mais interna, enquanto uma Next instrução com uma ou mais variáveis de controle de loop corresponderá, da esquerda para a direita, aos For loops que correspondem a cada variável. Se uma variável corresponder a um For loop que não é o loop mais aninhado nesse ponto, um erro em tempo de compilação resulta.
No início do loop, as três expressões são avaliadas em ordem textual e a expressão de limite inferior é atribuída à variável de controle de loop. Se o valor step for omitido, ele será implicitamente o literal 1, convertido para o tipo da variável de controle de loop. As três expressões só são avaliadas no início do ciclo.
No início de cada loop, a variável de controle é comparada para ver se é maior do que o ponto final se a expressão de etapa for positiva, ou menor que o ponto final se a expressão de etapa for negativa. Se for, o For loop termina, caso contrário, o bloco de loop é executado. Se a variável de controle de loop não for um tipo primitivo, o operador de comparação será determinado se a expressão step >= step - step é true ou false.
Next Na instrução, o valor da etapa é adicionado à variável de controle e a execução retorna ao topo do loop.
Observe que uma nova cópia da variável de controle de loop não é criada em cada iteração do bloco de loop. A este respeito, a For afirmação difere de For Each (Secção Para Cada... Próximas Declarações).
Não é válido ramificar em um For loop de fora do loop.
Para cada um... Próximas Declarações
Um For Each...Next loop de instrução com base nos elementos de uma expressão. Uma For Each instrução especifica uma variável de controle de loop e uma expressão de enumerador. A variável de controle de loop é especificada por meio de um identificador seguido por uma cláusula opcional As ou uma expressão.
ForEachStatement
: 'For' 'Each' LoopControlVariable 'In' LineTerminator? Expression StatementTerminator
Block?
( 'Next' NextExpressionList? StatementTerminator )?
;
Seguindo as mesmas regras For...Next das declarações (Secção Para... Next Statements), a variável de controle de loop refere-se a uma nova variável local específica para este For Each... Próxima instrução, ou para uma variável pré-existente, ou para uma expressão.
A expressão do enumerador deve ser classificada como um valor e seu tipo deve ser um tipo de coleção ou Object. Se o tipo da expressão do enumerador for Object, todo o processamento será adiado até o tempo de execução. Caso contrário, deve existir uma conversão do tipo de elemento da coleção para o tipo da variável de controle de loop.
A variável de controle de loop não pode ser usada por outra instrução de inclusão For Each . Uma For Each instrução deve ser fechada por uma instrução correspondente Next . Uma Next instrução sem uma variável de controle de loop corresponde à abertura For Eachmais interna. Uma Next instrução com uma ou mais variáveis de controle de loop corresponderá, da esquerda para a direita, aos For Each loops que têm a mesma variável de controle de loop. Se uma variável corresponder a um For Each loop que não é o loop mais aninhado nesse ponto, ocorrerá um erro em tempo de compilação.
Diz-se que um tipo é um tipo C de coleção se:
Todos os itens a seguir são verdadeiros:
-
Ccontém uma instância acessível, método compartilhado ou extensão com a assinaturaGetEnumerator()que retorna um tipoE. -
Econtém uma instância acessível, método compartilhado ou extensão com a assinaturaMoveNext()e o tipoBooleande retorno. -
Econtém uma instância acessível ou propriedade compartilhada nomeadaCurrentque tem um getter. O tipo dessa propriedade é o tipo de elemento do tipo de coleção.
-
Ele implementa a interface
System.Collections.Generic.IEnumerable(Of T), caso em que o tipo de elemento da coleção é considerado comoT.Ele implementa a interface
System.Collections.IEnumerable, caso em que o tipo de elemento da coleção é considerado comoObject.
A seguir está um exemplo de uma classe que pode ser enumerada:
Public Class IntegerCollection
Private integers(10) As Integer
Public Class IntegerCollectionEnumerator
Private collection As IntegerCollection
Private index As Integer = -1
Friend Sub New(c As IntegerCollection)
collection = c
End Sub
Public Function MoveNext() As Boolean
index += 1
Return index <= 10
End Function
Public ReadOnly Property Current As Integer
Get
If index < 0 OrElse index > 10 Then
Throw New System.InvalidOperationException()
End If
Return collection.integers(index)
End Get
End Property
End Class
Public Sub New()
Dim i As Integer
For i = 0 To 10
integers(i) = I
Next i
End Sub
Public Function GetEnumerator() As IntegerCollectionEnumerator
Return New IntegerCollectionEnumerator(Me)
End Function
End Class
Antes do loop começar, a expressão do enumerador é avaliada. Se o tipo da expressão não satisfizer o padrão de design, a expressão será convertida em System.Collections.IEnumerable ou System.Collections.Generic.IEnumerable(Of T). Se o tipo de expressão implementa a interface genérica, a interface genérica é preferida em tempo de compilação, mas a interface não genérica é preferida em tempo de execução. Se o tipo de expressão implementa a interface genérica várias vezes, a instrução é considerada ambígua e ocorre um erro em tempo de compilação.
Nota. A interface não genérica é preferida no caso de ligação tardia, porque escolher a interface genérica significaria que todas as chamadas para os métodos de interface envolveriam parâmetros de tipo. Como não é possível conhecer os argumentos de tipo correspondentes em tempo de execução, todas essas chamadas teriam que ser feitas usando chamadas tardias. Isso seria mais lento do que chamar a interface não genérica porque a interface não genérica poderia ser chamada usando chamadas em tempo de compilação.
GetEnumerator é chamado no valor resultante e o valor de retorno da função é armazenado em um temporário. Então, no início de cada iteração, MoveNext é chamado no temporário. Se ele retornar False, o loop será encerrado. Caso contrário, cada iteração do loop é executada da seguinte maneira:
- Se a variável de controle de loop identificou uma nova variável local (em vez de uma pré-existente), então uma nova cópia dessa variável local será criada. Para a iteração atual, todas as referências dentro do bloco de loop farão referência a esta cópia.
- A
Currentpropriedade é recuperada, coagida ao tipo da variável de controle de loop (independentemente de a conversão ser implícita ou explícita) e atribuída à variável de controle de loop. - O bloco de loop é executado.
Nota. Há uma ligeira mudança no comportamento entre a versão 10.0 e 11.0 do idioma. Antes da 11.0, uma nova variável de iteração não era criada para cada iteração do loop. Essa diferença só é observável se a variável de iteração for capturada por uma expressão lambda ou LINQ que é então invocada após o loop:
Dim lambdas As New List(Of Action)
For Each x In {1,2,3}
lambdas.Add(Sub() Console.WriteLine(x)
Next
lambdas(0).Invoke()
lambdas(1).Invoke()
lambdas(2).Invoke()
Até o Visual Basic 10.0, isso produzia um aviso em tempo de compilação e imprimia "3" três vezes. Isso ocorreu porque havia apenas uma única variável "x" compartilhada por todas as iterações do loop, e todas as três lambdas capturaram o mesmo "x", e no momento em que as lambdas foram executadas, ela então detinha o número 3. A partir do Visual Basic 11.0, ele imprime "1, 2, 3". Isso porque cada lambda captura uma variável diferente "x".
Nota. O elemento atual da iteração é convertido para o tipo da variável de controle de loop, mesmo que a conversão seja explícita porque não há um local conveniente para introduzir um operador de conversão na instrução. Isso se tornou particularmente problemático ao trabalhar com o tipo System.Collections.ArrayListagora obsoleto, porque seu tipo de elemento é Object. Isso exigiria elencos em muitos loops, algo que achamos que não era o ideal. Ironicamente, os genéricos permitiram a criação de uma coleção fortemente tipada, System.Collections.Generic.List(Of T)o que poderia ter nos feito repensar esse ponto de design, mas por uma questão de compatibilidade, isso não pode ser mudado agora.
Quando a Next instrução é atingida, a execução retorna ao topo do loop. Se uma variável for especificada após a Next palavra-chave, ela deve ser a mesma que a primeira variável após o For Each. Por exemplo, considere o seguinte código:
Module Test
Sub Main()
Dim i As Integer
Dim c As IntegerCollection = New IntegerCollection()
For Each i In c
Console.WriteLine(i)
Next i
End Sub
End Module
É equivalente ao seguinte código:
Module Test
Sub Main()
Dim i As Integer
Dim c As IntegerCollection = New IntegerCollection()
Dim e As IntegerCollection.IntegerCollectionEnumerator
e = c.GetEnumerator()
While e.MoveNext()
i = e.Current
Console.WriteLine(i)
End While
End Sub
End Module
Se o tipo E do enumerador implementa System.IDisposable, então o enumerador é descartado ao sair do loop chamando o Dispose método. Isso garante que os recursos mantidos pelo enumerador sejam liberados. Se o método que contém a For Each instrução não usa manipulação de erros não estruturados, então a For Each instrução é encapsulada em uma Try instrução com o Dispose método chamado no para garantir a Finally limpeza.
Nota. O System.Array tipo é um tipo de coleção e, como todos os tipos de matriz derivam de , qualquer expressão de tipo de System.Arraymatriz é permitida em uma For Each instrução. Para matrizes unidimensionais, a For Each instrução enumera os elementos da matriz em ordem de índice crescente, começando com o índice 0 e terminando com o comprimento do índice - 1. Para matrizes multidimensionais, os índices da dimensão mais à direita são aumentados primeiro.
Por exemplo, o seguinte código imprime 1 2 3 4:
Module Test
Sub Main()
Dim x(,) As Integer = { { 1, 2 }, { 3, 4 } }
Dim i As Integer
For Each i In x
Console.Write(i & " ")
Next i
End Sub
End Module
Não é válido ramificar em um For Each bloco de instrução de fora do bloco.
Exception-Handling Declarações
Visual Basic oferece suporte ao tratamento de exceções estruturadas e tratamento de exceções não estruturadas. Apenas um estilo de tratamento de exceção pode ser usado em um método, mas a Error instrução pode ser usada no tratamento estruturado de exceções. Se um método usa ambos os estilos de tratamento de exceção, um erro em tempo de compilação resulta.
ErrorHandlingStatement
: StructuredErrorStatement
| UnstructuredErrorStatement
;
Declarações Exception-Handling estruturadas
O tratamento estruturado de exceções é um método de tratamento de erros declarando blocos explícitos dentro dos quais certas exceções serão tratadas. O tratamento estruturado de exceções é feito por meio de uma Try instrução.
StructuredErrorStatement
: ThrowStatement
| TryStatement
;
TryStatement
: 'Try' StatementTerminator
Block?
CatchStatement*
FinallyStatement?
'End' 'Try' StatementTerminator
;
Por exemplo:
Module Test
Sub ThrowException()
Throw New Exception()
End Sub
Sub Main()
Try
ThrowException()
Catch e As Exception
Console.WriteLine("Caught exception!")
Finally
Console.WriteLine("Exiting try.")
End Try
End Sub
End Module
Uma Try instrução é composta por três tipos de blocos: blocos try, catch blocks, e finalmente blocks. Um bloco try é um bloco de instrução que contém as instruções a serem executadas. Um bloco catch é um bloco de instrução que lida com uma exceção. Um bloco de última instância é um bloco de instrução que contém instruções a serem executadas quando a Try instrução é encerrada, independentemente de uma exceção ter ocorrido e sido tratada. Uma Try instrução, que só pode conter um bloco try e um bloco final, deve conter pelo menos um bloco catch ou finalmente block. É inválido transferir explicitamente a execução para um bloco try, exceto de dentro de um bloco catch na mesma instrução.
Finalmente Blocos
Um Finally bloco é sempre executado quando a execução deixa qualquer parte da Try instrução. Nenhuma ação explícita é necessária para executar o Finally bloco, quando a execução deixa a Try instrução, o sistema executará automaticamente o bloco e, em seguida, transferirá a Finally execução para o destino pretendido. O Finally bloco é executado independentemente de como a execução deixa a Try instrução: através do final do Try bloco, através do final de um Catch bloco, através de uma Exit Try instrução, através de uma GoTo instrução, ou por não lidar com uma exceção lançada.
Observe que a Await expressão em um método assíncrono e a Yield instrução em um método iterador podem fazer com que o fluxo de controle seja suspenso na instância do método async ou iterator e retomado em alguma outra instância do método. No entanto, isso é meramente uma suspensão da execução e não envolve sair do respetivo método assíncrono ou instância do método iterador e, portanto, não faz com que Finally os blocos sejam executados.
É inválido transferir explicitamente a execução para um Finally bloco, também é inválido transferir a execução para fora de um Finally bloco, exceto através de uma exceção.
FinallyStatement
: 'Finally' StatementTerminator
Block?
;
Blocos de captura
Se ocorrer uma exceção durante o processamento do Try bloco, cada Catch instrução será examinada em ordem textual para determinar se ela lida com a exceção.
CatchStatement
: 'Catch' ( Identifier ( 'As' NonArrayTypeName )? )?
( 'When' BooleanExpression )? StatementTerminator
Block?
;
O identificador especificado em uma Catch cláusula representa a exceção que foi lançada. Se o identificador contiver uma As cláusula, o identificador será considerado declarado dentro do Catch espaço de declaração local do bloco. Caso contrário, o identificador deve ser uma variável local (não uma variável estática) que foi definida em um bloco de contenção.
Uma Catch cláusula sem identificador capturará todas as exceções derivadas de System.Exception. Uma Catch cláusula com um identificador só capturará exceções cujos tipos sejam iguais ou derivados do tipo do identificador. O tipo deve ser System.Exception, ou um tipo derivado de System.Exception. Quando uma exceção é capturada que deriva de System.Exception, uma referência ao objeto de exceção é armazenada no objeto retornado pela função Microsoft.VisualBasic.Information.Err.
Uma Catch cláusula com uma When cláusula só pegará exceções quando a expressão for avaliada como True; o tipo da expressão deve ser uma expressão booleana de acordo com a Seção Expressões Booleanas. Uma When cláusula só é aplicada depois de verificar o tipo de exceção, e a expressão pode se referir ao identificador que representa a exceção, como este exemplo demonstra:
Module Test
Sub Main()
Dim i As Integer = 5
Try
Throw New ArgumentException()
Catch e As OverflowException When i = 5
Console.WriteLine("First handler")
Catch e As ArgumentException When i = 4
Console.WriteLine("Second handler")
Catch When i = 5
Console.WriteLine("Third handler")
End Try
End Sub
End Module
Este exemplo imprime:
Third handler
Se uma Catch cláusula lidar com a exceção, a execução será transferida para o Catch bloco. No final do bloco, a Catch execução é transferida para a primeira instrução após a Try instrução. A Try instrução não lidará com quaisquer exceções lançadas em um Catch bloco. Se nenhuma Catch cláusula lidar com a exceção, a execução será transferida para um local determinado pelo sistema.
É inválido transferir explicitamente a execução para um Catch bloco.
Os filtros nas cláusulas When são normalmente avaliados antes de a exceção ser lançada. Por exemplo, o código a seguir imprimirá "Filtrar, Finalmente, Capturar".
Sub Main()
Try
Foo()
Catch ex As Exception When F()
Console.WriteLine("Catch")
End Try
End Sub
Sub Foo()
Try
Throw New Exception
Finally
Console.WriteLine("Finally")
End Try
End Sub
Function F() As Boolean
Console.WriteLine("Filter")
Return True
End Function
No entanto, os métodos Async e Iterator fazem com que todos os blocos dentro deles sejam executados antes de quaisquer filtros externos. Por exemplo, se o código acima tivesse Async Sub Foo(), então a saída seria "Finalmente, Filtrar, Capturar".
Declaração de lançamento
A Throw instrução gera uma exceção, que é representada por uma instância de um tipo derivado de System.Exception.
ThrowStatement
: 'Throw' Expression? StatementTerminator
;
Se a expressão não for classificada como um valor ou não for um tipo derivado de , ocorrerá um erro em tempo de System.Exceptioncompilação. Se a expressão for avaliada como um valor nulo em tempo de execução, uma System.NullReferenceException exceção será gerada.
Uma Throw instrução pode omitir a expressão dentro de um bloco catch de uma Try declaração, desde que não haja um bloqueio interveniente. Nesse caso, a instrução relança a exceção que está sendo tratada atualmente dentro do bloco de captura. Por exemplo:
Sub Test(x As Integer)
Try
Throw New Exception()
Catch
If x = 0 Then
Throw ' OK, rethrows exception from above.
Else
Try
If x = 1 Then
Throw ' OK, rethrows exception from above.
End If
Finally
Throw ' Invalid, inside of a Finally.
End Try
End If
End Try
End Sub
Declarações Exception-Handling não estruturadas
O tratamento de exceções não estruturadas é um método de tratamento de erros indicando instruções para ramificação para quando ocorre uma exceção. O tratamento de exceções não estruturadas é implementado usando três instruções: a Error instrução, a On Error instrução e a Resume instrução.
UnstructuredErrorStatement
: ErrorStatement
| OnErrorStatement
| ResumeStatement
;
Por exemplo:
Module Test
Sub ThrowException()
Error 5
End Sub
Sub Main()
On Error GoTo GotException
ThrowException()
Exit Sub
GotException:
Console.WriteLine("Caught exception!")
Resume Next
End Sub
End Module
Quando um método usa o tratamento de exceções não estruturadas, um único manipulador de exceção estruturado é estabelecido para todo o método que captura todas as exceções. (Observe que, em construtores, esse manipulador não se estende sobre a chamada para a chamada no New início do construtor.) Em seguida, o método controla o local mais recente do manipulador de exceções e a exceção mais recente que foi lançada. Na entrada para o método, o local do manipulador de exceções e a exceção são definidos como Nothing. Quando uma exceção é lançada em um método que usa manipulação de exceção não estruturada, uma referência ao objeto de exceção é armazenada no objeto retornado pela função Microsoft.VisualBasic.Information.Err.
Instruções de tratamento de erros não estruturadas não são permitidas em métodos iteradores ou assíncronos.
Declaração de erro
Uma Error instrução lança uma System.Exception exceção que contém um número de exceção do Visual Basic 6. A expressão deve ser classificada como um valor e seu tipo deve ser implicitamente conversível em Integer.
ErrorStatement
: 'Error' Expression StatementTerminator
;
Sobre a instrução de erro
Uma On Error instrução modifica o estado de tratamento de exceções mais recente.
OnErrorStatement
: 'On' 'Error' ErrorClause StatementTerminator
;
ErrorClause
: 'GoTo' '-' '1'
| 'GoTo' '0'
| GoToStatement
| 'Resume' 'Next'
;
Pode ser utilizado de quatro formas:
On Error GoTo -1Redefine a exceção mais recente paraNothing.On Error GoTo 0Redefine o local mais recente do manipulador de exceções paraNothing.On Error GoTo LabelNameestabelece o rótulo como o local mais recente do manipulador de exceções. Essa instrução não pode ser usada em um método que contém uma expressão lambda ou query.On Error Resume NextEstabelece oResume Nextcomportamento como o local mais recente do manipulador de exceções.
Declaração de Currículo
Uma Resume instrução retorna a execução para a instrução que causou a exceção mais recente.
ResumeStatement
: 'Resume' ResumeClause? StatementTerminator
;
ResumeClause
: 'Next'
| LabelName
;
Se o Next modificador for especificado, a execução retornará à instrução que teria sido executada após a instrução que causou a exceção mais recente. Se um nome de rótulo for especificado, a execução retornará ao rótulo.
Porque a SyncLock instrução contém um bloco Resume de tratamento de erros estruturado implícito e Resume Next tem comportamentos especiais para exceções que ocorrem em SyncLock instruções.
Resume retorna a execução para o início da SyncLock instrução, enquanto Resume Next retorna a execução para a próxima instrução após a SyncLock instrução. Por exemplo, considere o seguinte código:
Class LockClass
End Class
Module Test
Sub Main()
Dim FirstTime As Boolean = True
Dim Lock As LockClass = New LockClass()
On Error GoTo Handler
SyncLock Lock
Console.WriteLine("Before exception")
Throw New Exception()
Console.WriteLine("After exception")
End SyncLock
Console.WriteLine("After SyncLock")
Exit Sub
Handler:
If FirstTime Then
FirstTime = False
Resume
Else
Resume Next
End If
End Sub
End Module
Ele imprime o seguinte resultado.
Before exception
Before exception
After SyncLock
A primeira vez através da SyncLock instrução, Resume retorna a execução para o início da SyncLock instrução. A segunda vez através da SyncLock instrução, Resume Next retorna a execução para o final da SyncLock instrução.
Resume e Resume Next não são permitidos dentro de uma SyncLock declaração.
Em todos os casos, quando uma Resume instrução é executada, a exceção mais recente é definida como Nothing. Se uma Resume instrução é executada sem exceção mais recente, a instrução gera uma System.Exception exceção contendo o número 20 de erro do Visual Basic (Retomar sem erro).
Declarações de sucursais
As instruções branch modificam o fluxo de execução em um método. Há seis declarações de ramo:
- Uma
GoToinstrução faz com que a execução seja transferida para o rótulo especificado no método. Não é permitido entrarGoToem umTry,Using,SyncLock,WithFor, ouFor Eachbloco, nem em qualquer bloco de loop se uma variável local desse bloco for capturada em uma expressão lambda ou LINQ. - Uma
Exitinstrução transfere a execução para a próxima instrução após o final da instrução de bloco imediatamente contendo do tipo especificado. Se o bloco for o bloco do método, o fluxo de controle sai do método conforme descrito em Fluxo de controle de seção. Se aExitinstrução não estiver contida no tipo de bloco especificado na instrução, ocorrerá um erro em tempo de compilação. - Uma
Continueinstrução transfere a execução para o final da instrução de loop de bloco imediatamente contendo do tipo especificado. Se aContinueinstrução não estiver contida no tipo de bloco especificado na instrução, ocorrerá um erro em tempo de compilação. - Uma
Stopinstrução faz com que ocorra uma exceção do depurador. - Uma
Endinstrução encerra o programa. Os finalizadores são executados antes do desligamento, mas os blocos finais de quaisquer instruções atualmente em execuçãoTrynão são executados. Esta instrução não pode ser usada em programas que não são executáveis (por exemplo, DLLs). - Uma
Returninstrução sem expressão é equivalente a umaExit Subinstrução ORExit Function. UmaReturninstrução com uma expressão só é permitida em um método regular que é uma função, ou em um método assíncrono que é uma função com tipoTask(Of T)de retorno para algunsT. A expressão deve ser classificada como um valor que é implicitamente conversível para a variável de retorno de função (no caso de métodos regulares) ou para a variável de retorno de tarefa (no caso de métodos assíncronos). Seu comportamento é avaliar sua expressão, armazená-la na variável de retorno e, em seguida, executar uma instrução implícitaExit Function.
BranchStatement
: GoToStatement
| ExitStatement
| ContinueStatement
| StopStatement
| EndStatement
| ReturnStatement
;
GoToStatement
: 'GoTo' LabelName StatementTerminator
;
ExitStatement
: 'Exit' ExitKind StatementTerminator
;
ExitKind
: 'Do' | 'For' | 'While' | 'Select' | 'Sub' | 'Function' | 'Property' | 'Try'
;
ContinueStatement
: 'Continue' ContinueKind StatementTerminator
;
ContinueKind
: 'Do' | 'For' | 'While'
;
StopStatement
: 'Stop' StatementTerminator
;
EndStatement
: 'End' StatementTerminator
;
ReturnStatement
: 'Return' Expression? StatementTerminator
;
Array-Handling Declarações
Duas instruções simplificam o trabalho com matrizes: ReDim instruções e Erase instruções.
ArrayHandlingStatement
: RedimStatement
| EraseStatement
;
Declaração ReDim
Uma ReDim instrução instancia novas matrizes.
RedimStatement
: 'ReDim' 'Preserve'? RedimClauses StatementTerminator
;
RedimClauses
: RedimClause ( Comma RedimClause )*
;
RedimClause
: Expression ArraySizeInitializationModifier
;
Cada cláusula na instrução deve ser classificada como uma variável ou um acesso à propriedade cujo tipo é um tipo de matriz ou Object, e ser seguida por uma lista de limites de matriz. O número dos limites deve ser coerente com o tipo da variável; qualquer número de limites é permitido para Object. Em tempo de execução, uma matriz é instanciada para cada expressão da esquerda para a direita com os limites especificados e, em seguida, atribuída à variável ou propriedade. Se o tipo de variável for Object, o número de dimensões será o número de dimensões especificadas e o tipo de elemento de matriz será Object. Se o número dado de dimensões for incompatível com a variável ou propriedade em tempo de execução, ocorrerá um erro em tempo de compilação. Por exemplo:
Module Test
Sub Main()
Dim o As Object
Dim b() As Byte
Dim i(,) As Integer
' The next two statements are equivalent.
ReDim o(10,30)
o = New Object(10, 30) {}
' The next two statements are equivalent.
ReDim b(10)
b = New Byte(10) {}
' Error: Incorrect number of dimensions.
ReDim i(10, 30, 40)
End Sub
End Module
Se a Preserve palavra-chave for especificada, as expressões também devem ser classificáveis como um valor, e o novo tamanho para cada dimensão, exceto para a mais à direita, deve ser o mesmo que o tamanho da matriz existente. Os valores na matriz existente são copiados para a nova matriz: se a nova matriz for menor, os valores existentes serão descartados; Se a nova matriz for maior, os elementos extras serão inicializados com o valor padrão do tipo de elemento da matriz. Por exemplo, considere o seguinte código:
Module Test
Sub Main()
Dim x(5, 5) As Integer
x(3, 3) = 3
ReDim Preserve x(5, 6)
Console.WriteLine(x(3, 3) & ", " & x(3, 6))
End Sub
End Module
Imprime o seguinte resultado:
3, 0
Se a referência de matriz existente for um valor nulo em tempo de execução, nenhum erro será fornecido. Além da dimensão mais à direita, se o tamanho de uma dimensão mudar, um System.ArrayTypeMismatchException será lançado.
Nota.
Preserve não é uma palavra reservada.
Apagar declaração
Uma Erase instrução define cada uma das variáveis de matriz ou propriedades especificadas na instrução como Nothing. Cada expressão na instrução deve ser classificada como uma variável ou acesso à propriedade cujo tipo é um tipo de matriz ou Object. Por exemplo:
Module Test
Sub Main()
Dim x() As Integer = New Integer(5) {}
' The following two statements are equivalent.
Erase x
x = Nothing
End Sub
End Module
EraseStatement
: 'Erase' EraseExpressions StatementTerminator
;
EraseExpressions
: Expression ( Comma Expression )*
;
Usando a instrução
Instâncias de tipos são liberadas automaticamente pelo coletor de lixo quando uma coleção é executada e nenhuma referência em tempo real à instância é encontrada. Se um tipo retiver um recurso particularmente valioso e escasso (como conexões de banco de dados ou identificadores de arquivo), pode não ser desejável esperar até a próxima coleta de lixo para limpar uma instância específica do tipo que não está mais em uso. Para fornecer uma maneira leve de liberar recursos antes de uma coleção, um tipo pode implementar a System.IDisposable interface. Um tipo que faz isso expõe um Dispose método que pode ser chamado para forçar recursos valiosos a serem liberados imediatamente, como tal:
Module Test
Sub Main()
Dim x As DBConnection = New DBConnection("...")
' Do some work
...
x.Dispose() ' Free the connection
End Sub
End Module
A Using instrução automatiza o processo de aquisição de um recurso, executando um conjunto de instruções e, em seguida, descartando o recurso. A instrução pode assumir duas formas: em uma, o recurso é uma variável local declarada como parte da instrução e tratada como uma instrução de declaração de variável local regular; no outro, o recurso é o resultado de uma expressão.
UsingStatement
: 'Using' UsingResources StatementTerminator
Block?
'End' 'Using' StatementTerminator
;
UsingResources
: VariableDeclarators
| Expression
;
Se o recurso for uma instrução de declaração de variável local, o tipo da declaração de variável local deverá ser um tipo que possa ser implicitamente convertido em System.IDisposable. As variáveis locais declaradas são somente leitura, com escopo para o Using bloco de instrução e devem incluir um inicializador. Se o recurso é o resultado de uma expressão, então a expressão deve ser classificada como um valor e deve ser de um tipo que pode ser implicitamente convertido em System.IDisposable. A expressão é avaliada apenas uma vez, no início do enunciado.
O Using bloco é implicitamente contido por uma Try instrução cujo bloco final chama o método IDisposable.Dispose no recurso. Isso garante que o recurso seja descartado mesmo quando uma exceção é lançada. Como resultado, é inválido ramificar em um Using bloco de fora do bloco, e um Using bloco é tratado como uma única instrução para os fins de Resume e Resume Next. Se o recurso for Nothing, nenhuma chamada será Dispose feita. Assim, o exemplo:
Using f As C = New C()
...
End Using
é equivalente a:
Dim f As C = New C()
Try
...
Finally
If f IsNot Nothing Then
f.Dispose()
End If
End Try
Uma Using instrução que tem uma instrução de declaração variável local pode adquirir vários recursos ao mesmo tempo, o que é equivalente a instruções aninhadas Using . Por exemplo, uma Using declaração do formulário:
Using r1 As R = New R(), r2 As R = New R()
r1.F()
r2.F()
End Using
é equivalente a:
Using r1 As R = New R()
Using r2 As R = New R()
r1.F()
r2.F()
End Using
End Using
Aguarda declaração
Uma instrução await tem a mesma sintaxe que uma expressão de operador await (Section Await Operator), é permitida apenas em métodos que também permitem expressões await e tem o mesmo comportamento que uma expressão de operador await.
No entanto, pode ser classificado como valor ou nulo. Qualquer valor resultante da avaliação da expressão do operador await é descartado.
AwaitStatement
: AwaitOperatorExpression StatementTerminator
;
Demonstração de Rendimento
As declarações de rendimento estão relacionadas aos métodos iteradores, que são descritos em Métodos de iterador de seção.
YieldStatement
: 'Yield' Expression StatementTerminator
;
Yield é uma palavra reservada se o método imediatamente fechado ou expressão lambda em que aparece tem um Iterator modificador, e se o Yield aparece depois desse Iterator modificador, não é reservado em outro lugar. Também não é reservado nas diretivas de pré-processador. A demonstração de rendimento só é permitida no corpo de um método ou expressão lambda quando é uma palavra reservada. Dentro do método de fechamento imediato ou lambda, a demonstração de rendimento não pode ocorrer dentro do corpo de um Catch ou Finally bloco, nem dentro do corpo de uma SyncLock declaração.
A demonstração de rendimento usa uma única expressão que deve ser classificada como um valor e cujo tipo é implicitamente conversível para o tipo da variável de corrente iteradora ( Métodos de iterador de seção) de seu método iterador de inclusão.
O fluxo de controle só atinge uma Yield instrução quando o método é invocado MoveNext em um objeto iterador. (Isso ocorre porque uma instância do método iterador só executa suas instruções devido aos MoveNext métodos ou Dispose serem chamados em um objeto iterador; e o Dispose método só executará código em Finally blocos, onde Yield não é permitido).
Quando uma Yield instrução é executada, sua expressão é avaliada e armazenada na variável atual do iterador da instância do método iterator associada a esse objeto iterador. O valor True é retornado ao invocador de MoveNext, e o ponto de controle desta instância para de avançar até a próxima invocação de MoveNext no objeto iterador.
Visual Basic language spec