Delen via


Instructies - Visual Basic

Instructies vertegenwoordigen uitvoerbare code.

Statement
    : LabelDeclarationStatement
    | LocalDeclarationStatement
    | WithStatement
    | SyncLockStatement
    | EventStatement
    | AssignmentStatement
    | InvocationStatement
    | ConditionalStatement
    | LoopStatement
    | ErrorHandlingStatement
    | BranchStatement
    | ArrayHandlingStatement
    | UsingStatement
	| AwaitStatement
	| YieldStatement
    ;

Opmerking. Microsoft Visual Basic Compiler staat alleen instructies toe die beginnen met een trefwoord of een id. De aanroepverklaring 'Call (Console).WriteLine' is dus toegestaan, maar de aanroepverklaring '(Console).WriteLine' is niet.

Controleflow

Controlestroom is de volgorde waarin instructies en expressies worden uitgevoerd. De uitvoeringsvolgorde is afhankelijk van de specifieke instructie of expressie.

Wanneer u bijvoorbeeld een optellingsoperator (Operator voor secties toevoegen) evalueert, wordt eerst de linkeroperand geëvalueerd, vervolgens de rechteroperand en vervolgens de operator zelf. Blokken worden uitgevoerd ( sectieblokken en labels) door eerst hun eerste substatement uit te voeren en vervolgens één voor één door te gaan via de instructies van het blok.

Impliciet in deze volgorde is het concept van een besturingspunt, dat de volgende bewerking is die moet worden uitgevoerd. Wanneer een methode wordt aangeroepen (of 'aangeroepen'), zeggen we dat er een exemplaar van de methode wordt gemaakt. Een methode-exemplaar bestaat uit een eigen kopie van de parameters en lokale variabelen van de methode en een eigen controlepunt.

Reguliere methoden

Hier volgt een voorbeeld van een reguliere methode

Function Test() As Integer
    Console.WriteLine("hello")
    Return 1
End Function

Dim x = Test()    ' invokes the function, prints "hello", assigns 1 to x

Wanneer een reguliere methode wordt aangeroepen,

  1. Eerst wordt een exemplaar van de methode gemaakt dat specifiek is voor die aanroep. Dit exemplaar bevat een kopie van alle parameters en lokale variabelen van de methode.
  2. Vervolgens worden alle parameters geïnitialiseerd op de opgegeven waarden en alle lokale variabelen ervan op de standaardwaarden van hun typen.
  3. In het geval van een Function, wordt een impliciete lokale variabele ook geïnitialiseerd met de naam van de functie retourvariabele waarvan de naam de naam is van de functie, waarvan het type het retourtype is en waarvan de oorspronkelijke waarde de standaardwaarde van het type is.
  4. Het controlepunt van het methode-exemplaar wordt vervolgens ingesteld op de eerste instructie van de hoofdtekst van de methode en de hoofdtekst van de methode begint onmiddellijk vanaf daar uit te voeren ( sectieblokken en labels).

Wanneer de besturingsstroom de hoofdtekst van de methode normaal verlaat, door het bereiken of End Sub markeren van het End Function einde, of via een expliciete Return of Exit instructie, keert de controlestroom terug naar de aanroeper van het methode-exemplaar. Als er een functie-retourvariabele is, is het resultaat van de aanroep de uiteindelijke waarde van deze variabele.

Wanneer de besturingsstroom de hoofdtekst van de methode verlaat via een niet-verwerkte uitzondering, wordt die uitzondering doorgegeven aan de aanroeper.

Nadat de controlestroom is afgesloten, zijn er geen live verwijzingen meer naar het methode-exemplaar. Als het methodeexemplaar de enige verwijzingen naar de kopie van lokale variabelen of parameters bevat, kunnen ze garbagecollection zijn.

Iterator-methoden

Iterator-methoden worden gebruikt als een handige manier om een reeks te genereren, een die door de For Each instructie kan worden gebruikt. Iterator-methoden gebruiken de Yield instructie (Section Yield Statement) om elementen van de reeks te bieden. (Een iteratormethode zonder Yield instructies produceert een lege reeks). Hier volgt een voorbeeld van een iteratormethode:

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

Wanneer een iteratormethode wordt aangeroepen waarvan het retourtype is IEnumerator(Of T),

  1. Eerst wordt een exemplaar van de iteratormethode gemaakt dat specifiek is voor die aanroep. Dit exemplaar bevat een kopie van alle parameters en lokale variabelen van de methode.
  2. Vervolgens worden alle parameters geïnitialiseerd op de opgegeven waarden en alle lokale variabelen ervan op de standaardwaarden van hun typen.
  3. Een impliciete lokale variabele wordt ook wel de huidige iteratorvariabele genoemd, waarvan het type is T en waarvan de oorspronkelijke waarde de standaardwaarde van het type is.
  4. Het besturingspunt van het methode-exemplaar wordt vervolgens ingesteld bij de eerste instructie van de hoofdtekst van de methode.
  5. Er wordt vervolgens een iterator-object gemaakt dat is gekoppeld aan dit methode-exemplaar. Het iterator-object implementeert het gedeclareerde retourtype en heeft gedrag zoals hieronder wordt beschreven.
  6. De besturingsstroom wordt vervolgens onmiddellijk in de aanroeper hervat en het resultaat van de aanroep is het iterator-object. Houd er rekening mee dat deze overdracht wordt uitgevoerd zonder het exemplaar van de iterator-methode af te sluiten en dat er geen handlers worden uitgevoerd. Het methode-exemplaar wordt nog steeds verwezen door het iterator-object en wordt niet garbage verzameld zolang er een live verwijzing naar het iterator-object bestaat.

Wanneer de eigenschap van Current het iterator-object wordt geopend, wordt de huidige variabele van de aanroep geretourneerd.

Wanneer de methode van MoveNext het iterator-object wordt aangeroepen, maakt de aanroep geen nieuw methode-exemplaar. In plaats daarvan wordt het bestaande methode-exemplaar gebruikt (en het bijbehorende besturingspunt en de lokale variabelen en parameters) - het exemplaar dat is gemaakt toen de iterator-methode voor het eerst werd aangeroepen. De controlestroom hervat de uitvoering op het besturingspunt van dat methode-exemplaar en doorloopt de hoofdtekst van de iteratormethode als normaal.

Wanneer de methode van Dispose het iterator-object wordt aangeroepen, wordt opnieuw het bestaande methode-exemplaar gebruikt. De controlestroom wordt hervat op het besturingspunt van dat methode-exemplaar, maar gedraagt zich onmiddellijk alsof een Exit Function instructie de volgende bewerking was.

De bovenstaande beschrijvingen van het gedrag voor het aanroepen van MoveNext of Dispose op een iteratorobject zijn alleen van toepassing als alle eerdere aanroepen van MoveNext of Dispose op dat iterator-object al zijn geretourneerd naar hun bellers. Als dat niet zo is, is het gedrag niet gedefinieerd.

Wanneer de controlestroom de hoofdtekst van de iteratormethode normaal verlaat, door het bereiken van het End Function einde of via een expliciete Return of Exit instructie, moet dit zijn gedaan in de context van een aanroep van MoveNext of Dispose functie op een iterator-object om het iterator-methodeexemplaren te hervatten. Deze heeft het methodeexemplaren gebruikt dat is gemaakt toen de iterator-methode voor het eerst werd aangeroepen. Het besturingspunt van dat exemplaar wordt achtergelaten bij de End Function instructie en de controlestroom wordt hervat in de beller. Als het is hervat door een aanroep naar MoveNext , wordt de waarde False teruggezet naar de beller.

Wanneer de controlestroom de hoofdtekst van de iteratormethode verlaat via een niet-verwerkte uitzondering, wordt de uitzondering doorgegeven aan de aanroeper, die opnieuw een aanroep van MoveNext of van Dispose.

Wat betreft de andere mogelijke retourtypen van een iterator-functie,

  • Wanneer een iterator-methode wordt aangeroepen waarvan het retourtype voor sommige TisIEnumerable(Of T), wordt er eerst een exemplaar gemaakt , specifiek voor die aanroep van de iterator-methode - van alle parameters in de methode en worden ze geïnitialiseerd met de opgegeven waarden. Het resultaat van de aanroep is een object dat het retourtype implementeert. Als de methode van GetEnumerator dit object moet worden aangeroepen, wordt er een exemplaar gemaakt dat specifiek is voor die aanroep van GetEnumerator alle parameters en lokale variabelen in de methode. Hiermee worden de parameters geïnitialiseerd voor de waarden die al zijn opgeslagen en wordt verdergegaan met de iterator-methode hierboven.
  • Wanneer een iteratormethode wordt aangeroepen waarvan het retourtype de niet-algemene interface IEnumeratoris, is het gedrag precies hetzelfde als voor IEnumerator(Of Object).
  • Wanneer een iteratormethode wordt aangeroepen waarvan het retourtype de niet-algemene interface IEnumerableis, is het gedrag precies hetzelfde als voor IEnumerable(Of Object).

Asynchrone methoden

Asynchrone methoden zijn een handige manier om langlopend werk uit te voeren zonder bijvoorbeeld de gebruikersinterface van een toepassing te blokkeren. Async staat voor Asynchroon . Dit betekent dat de aanroeper van de asynchrone methode de uitvoering onmiddellijk hervat, maar de uiteindelijke voltooiing van het exemplaar van de asynchrone methode kan later in de toekomst plaatsvinden. Op basis van conventie asynchrone methoden wordt het achtervoegsel 'Async' genoemd.

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"

Opmerking. Asynchrone methoden worden niet uitgevoerd op een achtergrondthread. In plaats daarvan kunnen ze een methode zichzelf onderbreken via de Await operator en zichzelf plannen om te worden hervat als reactie op een bepaalde gebeurtenis.

Wanneer een asynchrone methode wordt aangeroepen

  1. Eerst wordt een instantie van de asynchrone methode gemaakt die specifiek is voor die aanroep. Dit exemplaar bevat een kopie van alle parameters en lokale variabelen van de methode.
  2. Vervolgens worden alle parameters geïnitialiseerd op de opgegeven waarden en alle lokale variabelen ervan op de standaardwaarden van hun typen.
  3. In het geval van een asynchrone methode met een retourtype Task(Of T) voor sommige T, wordt een impliciete lokale variabele ook wel de variabele voor het retourneren van taken geïnitialiseerd, waarvan het type is T en waarvan de oorspronkelijke waarde de standaardwaarde Tis.
  4. Als de asynchrone methode een Function retourtype Task of Task(Of T) voor sommige Tis, wordt een object van dat type impliciet gemaakt, gekoppeld aan de huidige aanroep. Dit wordt een asynchroon object genoemd en vertegenwoordigt het toekomstige werk dat wordt uitgevoerd door het exemplaar van de asynchrone methode uit te voeren. Wanneer het besturingselement wordt hervat in de aanroeper van dit exemplaar van de asynchrone methode, ontvangt de aanroeper dit asynchrone object als gevolg van de aanroep.
  5. Het besturingspunt van het exemplaar wordt vervolgens ingesteld op de eerste instructie van de hoofdtekst van de asynchrone methode en begint onmiddellijk met het uitvoeren van de hoofdtekst van de methode ( sectieblokken en labels).

Hervatting gedelegeerde en huidige beller

Zoals beschreven in Section Await Operator, heeft de uitvoering van een Await expressie de mogelijkheid om het besturingspunt van het methode-exemplaar te onderbreken, zodat de controlestroom ergens anders kan worden uitgevoerd. De controlestroom kan later worden hervat op het besturingspunt van hetzelfde exemplaar door het aanroepen van een hervattingsdelegatie. Houd er rekening mee dat deze schorsing wordt uitgevoerd zonder de asynchrone methode af te sluiten en dat handlers uiteindelijk niet worden uitgevoerd. Er wordt nog steeds naar het methodeexemplaar verwezen door zowel de hervattingsdelegatie als het TaskTask(Of T) resultaat (indien aanwezig) en wordt er geen garbagecollection verzameld zolang er een liveverwijzing bestaat naar de gedelegeerde of het resultaat.

Het is handig om de instructie Dim x = Await WorkAsync() ongeveer als syntactische afkorting voor het volgende in te stellen:

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()

In het volgende wordt de huidige aanroeper van het methode-exemplaar gedefinieerd als de oorspronkelijke aanroeper of de aanroeper van de gemachtigde voor hervatting, afhankelijk van wat er recenter is.

Wanneer de besturingsstroom de hoofdtekst van de asynchrone methode verlaat, door het End SubEnd Function einde ervan te bereiken of door middel van een expliciete Return of Exit instructie, of via een niet-verwerkte uitzondering, wordt het besturingspunt van het exemplaar ingesteld op het einde van de methode. Gedrag is vervolgens afhankelijk van het retourtype van de asynchrone methode.

  • In het geval van een Async Function met retourtype Task:

    1. Als de controlestroom wordt afgesloten via een niet-verwerkte uitzondering, wordt de status van het asynchrone object ingesteld op en Exception.InnerException wordt de eigenschap ervan ingesteld TaskStatus.Faulted op de uitzondering (behalve: bepaalde door de implementatie gedefinieerde uitzonderingen, zoals OperationCanceledException wijzigen).TaskStatus.Canceled De controlestroom wordt hervat in de huidige beller.

    2. Anders wordt de status van het asynchrone object ingesteld op TaskStatus.Completed. De controlestroom wordt hervat in de huidige beller.

      (Opmerking. Het hele punt van taak en wat asynchrone methoden interessant maakt, is dat wanneer een taak wordt voltooid, alle methoden die erop wachten, hun hervattingsdelegeringen op dit moment zullen worden uitgevoerd, d.w.z. dat ze worden gedeblokkeerd.)

  • In het geval van een Async Function retourtype Task(Of T) voor sommigen T: het gedrag is zoals hierboven, behalve dat in niet-uitzonderingsgevallen de eigenschap van Result het asynchrone object ook is ingesteld op de uiteindelijke waarde van de taak retourvariabele.

  • In het geval van een Async Sub:

    1. Als de controlestroom wordt afgesloten via een niet-verwerkte uitzondering, wordt die uitzondering op een bepaalde implementatiespecifieke manier doorgegeven aan de omgeving. De controlestroom wordt hervat in de huidige beller.
    2. Anders wordt de controlestroom gewoon hervat in de huidige beller.

Asynchrone sub

Er is een specifiek Microsoft-gedrag van een Async Sub.

Als SynchronizationContext.Current deze zich Nothing aan het begin van de aanroep bevindt, worden eventuele onverwerkte uitzonderingen van een Async Sub naar de Threadpool geplaatst.

Als SynchronizationContext.Current deze zich niet Nothing aan het begin van de aanroep bevindt, wordt deze OperationStarted() aangeroepen voor die context vóór het begin van de methode en OperationCompleted() na het einde. Bovendien worden eventuele niet-verwerkte uitzonderingen geplaatst om opnieuw te worden geplaatst in de synchronisatiecontext.

Dit betekent dat in UI-toepassingen, voor een Async Sub die wordt aangeroepen op de UI-thread, eventuele uitzonderingen die niet kunnen worden verwerkt, de UI-thread opnieuw worden geplaatst.

Veranderlijke structuren in asynchrone en iteratormethoden

Veranderlijke structuren in het algemeen worden beschouwd als slechte praktijken en worden niet ondersteund door asynchrone of iteratormethoden. Met name elke aanroep van een asynchrone of iteratormethode in een structuur werkt impliciet op een kopie van die structuur die op het moment van aanroep wordt gekopieerd. Bijvoorbeeld:

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"

Blokken en labels

Een groep uitvoerbare instructies wordt een instructieblok genoemd. Uitvoering van een instructieblok begint met de eerste instructie in het blok. Zodra een instructie is uitgevoerd, wordt de volgende instructie in lexicale volgorde uitgevoerd, tenzij een instructie de uitvoering elders of een uitzondering overdraagt.

Binnen een instructieblok is de verdeling van instructies op logische regels niet significant met uitzondering van labeldeclaratie-instructies. Een label is een id die een bepaalde positie binnen het instructieblok identificeert die kan worden gebruikt als doel van een vertakkingsinstructie, zoals GoTo.

Block
    : Statements*
    ;

LabelDeclarationStatement
    : LabelName ':'
    ;

LabelName
    : Identifier
    | IntLiteral
    ;

Statements
    : Statement? ( ':' Statement? )*
    ;

Labeldeclaratie-instructies moeten worden weergegeven aan het begin van een logische regel en labels kunnen een id of een letterlijk geheel getal zijn. Omdat zowel labeldeclaratie-instructies als aanroepinstructies uit één id kunnen bestaan, wordt één id aan het begin van een lokale regel altijd beschouwd als een labeldeclaratie-instructie. Labeldeclaratie-instructies moeten altijd worden gevolgd door een dubbele punt, zelfs als er geen instructies op dezelfde logische lijn volgen.

Labels hebben hun eigen declaratieruimte en verstoren geen andere id's. Het volgende voorbeeld is geldig en gebruikt de naamvariabele x zowel als parameter als label.

Function F(x As Integer) As Integer
    If x >= 0 Then
        GoTo x
    End If
    x = -x
x: 
    Return x
End Function

Het bereik van een label is de hoofdtekst van de methode die het bevat.

In het belang van de leesbaarheid worden instructieproducties waarbij meerdere substaten betrokken zijn beschouwd als één productie in deze specificatie, ook al mogen de substatementen zich op een gelabelde regel bevinden.

Lokale variabelen en parameters

In de voorgaande secties wordt beschreven hoe en wanneer methode-exemplaren worden gemaakt en met deze exemplaren de kopieën van de lokale variabelen en parameters van een methode. Telkens wanneer de hoofdtekst van een lus wordt ingevoerd, wordt er bovendien een nieuwe kopie gemaakt van elke lokale variabele die in die lus wordt gedeclareerd, zoals beschreven in sectielusinstructies en bevat het methode-exemplaar nu deze kopie van de lokale variabele in plaats van de vorige kopie.

Alle lokale bevolking wordt geïnitialiseerd naar de standaardwaarde van hun type. Lokale variabelen en parameters zijn altijd openbaar toegankelijk. Het is een fout om te verwijzen naar een lokale variabele in een tekstuele positie die voorafgaat aan de declaratie, zoals in het volgende voorbeeld wordt geïllustreerd:

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

In de bovenstaande methode verwijst de eerste toewijzing naar F specifiek niet naar het veld dat in het buitenste bereik is gedeclareerd. In plaats daarvan verwijst deze naar de lokale variabele en is deze fout omdat deze tekstutisch voorafgaat aan de declaratie van de variabele. In de G methode verwijst een volgende variabeledeclaratie naar een lokale variabele die is gedeclareerd in een eerdere variabeledeclaratie binnen dezelfde lokale variabeledeclaratie.

Elk blok in een methode maakt een declaratieruimte voor lokale variabelen. Namen worden in deze declaratieruimte geïntroduceerd via declaraties van lokale variabelen in de hoofdtekst van de methode en via de parameterlijst van de methode, waarmee namen worden geïntroduceerd in de buitenste declaratieruimte van het blok. Blokken staan geen schaduw van namen toe via nesten: zodra een naam in een blok is gedeclareerd, wordt de naam mogelijk niet opnieuw gedeclareerd in geneste blokken.

In het volgende voorbeeld zijn de F en G methoden dus fout omdat de naam i wordt gedeclareerd in het buitenste blok en niet opnieuw kan worden gedeclareerd in het binnenste blok. H De en I methoden zijn echter geldig omdat de twee iworden gedeclareerd in afzonderlijke niet-geneste blokken.

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

Wanneer de methode een functie is, wordt een speciale lokale variabele impliciet gedeclareerd in de declaratieruimte van de methodebody met dezelfde naam als de methode die de retourwaarde van de functie vertegenwoordigt. De lokale variabele heeft speciale semantiek voor naamomzetting wanneer deze wordt gebruikt in expressies. Als de lokale variabele wordt gebruikt in een context waarin een expressie wordt verwacht die is geclassificeerd als een methodegroep, zoals een aanroepexpressie, wordt de naam omgezet in de functie in plaats van de lokale variabele. Voorbeeld:

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

Het gebruik van haakjes kan dubbelzinnige situaties veroorzaken (zoals F(1), waarbij F is een functie waarvan het retourtype een eendimensionale matrix is); in alle dubbelzinnige situaties wordt de naam omgezet in de functie in plaats van de lokale variabele. Voorbeeld:

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

Wanneer de besturingsstroom de hoofdtekst van de methode verlaat, wordt de waarde van de lokale variabele teruggegeven aan de aanroepexpressie. Als de methode een subroutine is, is er geen impliciete lokale variabele en wordt het besturingselement gewoon teruggeleid naar de aanroepexpressie.

Lokale declaratie-instructies

Een lokale declaratie-instructie declareert een nieuwe lokale variabele, lokale constante of statische variabele. Lokale variabelen en lokale constanten zijn gelijk aan instantievariabelen en constanten binnen het bereik van de methode en worden op dezelfde manier gedeclareerd. Statische variabelen zijn vergelijkbaar met Shared variabelen en worden gedeclareerd met behulp van de Static wijzigingsfunctie.

LocalDeclarationStatement
    : LocalModifier VariableDeclarators StatementTerminator
    ;

LocalModifier
    : 'Static' | 'Dim' | 'Const'
    ;

Statische variabelen zijn lokale bevolking die hun waarde behouden in aanroepen van de methode. Statische variabelen die zijn gedeclareerd binnen niet-gedeelde methoden, zijn per exemplaar: elk exemplaar van het type dat de methode bevat, heeft een eigen kopie van de statische variabele. Statische variabelen die binnen Shared methoden zijn gedeclareerd, zijn per type. Er is slechts één kopie van de statische variabele voor alle exemplaren. Hoewel lokale variabelen worden geïnitialiseerd naar de standaardwaarde van het type bij elke vermelding in de methode, worden statische variabelen alleen geïnitialiseerd naar de standaardwaarde van het type wanneer het type of het type exemplaar wordt geïnitialiseerd. Statische variabelen kunnen niet worden gedeclareerd in structuren of algemene methoden.

Lokale variabelen, lokale constanten en statische variabelen hebben altijd openbare toegankelijkheid en kunnen geen toegankelijkheidsaanpassingen opgeven. Als er geen type is opgegeven voor een lokale declaratie-instructie, bepalen de volgende stappen het type lokale declaratie:

  1. Als de declaratie een typeteken heeft, is het type van het type het type van de lokale declaratie.

  2. Als de lokale declaratie een lokale constante is of als de lokale declaratie een lokale variabele is met een initialisatiefunctie en deductie van het lokale variabeletype, wordt het type lokale declaratie afgeleid van het type initialisatiefunctie. Als de initialisatiefunctie verwijst naar de lokale declaratie, treedt er een compilatietijdfout op. (Lokale constanten zijn vereist om initialisaties te hebben.)

  3. Als er geen strikte semantiek wordt gebruikt, is het type lokale declaratie-instructie impliciet Object.

  4. Anders treedt er een compilatietijdfout op.

Als er geen type is opgegeven in een lokale declaratie-instructie met een matrixgrootte of een aanpassing van het matrixtype, is het type van de lokale declaratie een matrix met de opgegeven rang en worden de vorige stappen gebruikt om het elementtype van de matrix te bepalen. Als deductie van het lokale variabeletype wordt gebruikt, moet het type initialisatiefunctie een matrixtype met dezelfde matrixvorm (dat wil zeggen modifiers voor matrixtypen) zijn als de instructie lokale declaratie. Houd er rekening mee dat het mogelijk is dat het type afgeleid element nog steeds een matrixtype is. Voorbeeld:

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

Als er geen type is opgegeven voor een lokale declaratie-instructie met een wijzigingsfunctie voor null-typen, is het type van de lokale declaratie de null-versie van het afgeleide type of het uitgestelde type zelf als het al een null-waardetype is. Als het uitgestelde type geen waardetype is dat nullable kan worden gemaakt, treedt er een compilatietijdfout op. Als zowel een wijzigingsfunctie voor null-typen als een matrixgrootte of een wijziging van het matrixtype wordt geplaatst op een lokale declaratie-instructie zonder type, wordt de wijziging van het null-type beschouwd als van toepassing op het elementtype van de matrix en worden de vorige stappen gebruikt om het elementtype te bepalen.

Variabele initializers voor lokale declaratie-instructies zijn gelijk aan toewijzingsinstructies die op de tekstlocatie van de declaratie worden geplaatst. Als de uitvoering vertakt over de lokale declaratie-instructie, wordt de initialisatiefunctie voor variabelen dus niet uitgevoerd. Als de lokale declaratie-instructie meer dan één keer wordt uitgevoerd, wordt de initialisatiefunctie van de variabele een gelijk aantal keren uitgevoerd. Statische variabelen voeren hun initialisatiefunctie alleen de eerste keer uit. Als er een uitzondering optreedt tijdens het initialiseren van een statische variabele, wordt de statische variabele beschouwd als geïnitialiseerd met de standaardwaarde van het type van de statische variabele.

In het volgende voorbeeld ziet u het gebruik van initialisatieprogramma's:

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

In dit programma wordt het volgende afgedrukt:

Static variable x = 5
Static variable x = 6
Static variable x = 7
Local variable y = 8
Local variable y = 8
Local variable y = 8

Initializers op statische lokale bevolking zijn thread-veilig en beschermd tegen uitzonderingen tijdens de initialisatie. Als er een uitzondering optreedt tijdens een statische lokale initialisatiefunctie, heeft de statische lokale standaardwaarde en wordt deze niet geïnitialiseerd. Een statische lokale initialisatiefunctie

Module Test
    Sub F()
        Static x As Integer = 5
    End Sub
End Module

komt overeen met

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

Lokale variabelen, lokale constanten en statische variabelen zijn gericht op het instructieblok waarin ze worden gedeclareerd. Statische variabelen zijn speciaal omdat hun namen slechts eenmaal in de hele methode mogen worden gebruikt. Het is bijvoorbeeld niet geldig om twee statische variabelendeclaraties met dezelfde naam op te geven, zelfs als ze zich in verschillende blokken bevinden.

Impliciete lokale declaraties

Naast lokale declaratie-instructies kunnen lokale variabelen ook impliciet worden gedeclareerd via gebruik. Een eenvoudige naamexpressie die gebruikmaakt van een naam die niet wordt omgezet in iets anders, declareert een lokale variabele met die naam. Voorbeeld:

Option Explicit Off

Module Test
    Sub Main()
        x = 10
        y = 20
        Console.WriteLine(x + y)
    End Sub
End Module

Impliciete lokale declaratie vindt alleen plaats in expressiecontexten die een expressie kunnen accepteren die is geclassificeerd als een variabele. De uitzondering op deze regel is dat een lokale variabele mogelijk niet impliciet wordt gedeclareerd wanneer dit het doel is van een functie-aanroepexpressie, indexeringsexpressie of een toegangsexpressie voor leden.

Impliciete lokale bevolking wordt behandeld alsof ze aan het begin van de metingsmethode worden gedeclareerd. Ze zijn dus altijd gericht op de gehele hoofdtekst van de methode, zelfs als ze binnen een lambda-expressie zijn gedeclareerd. Bijvoorbeeld de volgende code:

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

drukt de waarde 10af. Impliciete lokale bevolking wordt getypt alsof Object er geen typeteken is gekoppeld aan de naam van de variabele; anders is het type van het type teken. Lokale variabele type deductie wordt niet gebruikt voor impliciete lokale bevolking.

Als expliciete lokale declaratie wordt opgegeven door de compilatieomgeving of door Option Explicit, moeten alle lokale variabelen expliciet worden gedeclareerd en is impliciete variabeledeclaratie niet toegestaan.

Met instructie

Met een With instructie kunnen meerdere verwijzingen naar leden van een expressie worden gebruikt zonder de expressie meerdere keren op te geven.

WithStatement
    : 'With' Expression StatementTerminator
      Block?
      'End' 'With' StatementTerminator
    ;

De expressie moet worden geclassificeerd als een waarde en wordt eenmaal geëvalueerd na invoer in het blok. Binnen het With instructieblok wordt een toegangsexpressie of woordenlijsttoegangsexpressie die begint met een punt of een uitroepteken geëvalueerd alsof de With expressie eraan voorafging. Voorbeeld:

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

Het is ongeldig om te vertakken naar een With instructieblok van buiten het blok.

SyncLock-instructie

Met een SyncLock instructie kunnen instructies worden gesynchroniseerd op een expressie, waardoor meerdere threads van uitvoering niet tegelijkertijd dezelfde instructies uitvoeren.

SyncLockStatement
    : 'SyncLock' Expression StatementTerminator
      Block?
      'End' 'SyncLock' StatementTerminator
    ;

De expressie moet worden geclassificeerd als een waarde en wordt eenmaal geëvalueerd bij invoer in het blok. Bij het invoeren van het SyncLock blok wordt de Shared methode System.Threading.Monitor.Enter aangeroepen op de opgegeven expressie, die blokkeert totdat de thread van de uitvoering een exclusieve vergrendeling heeft voor het object dat door de expressie wordt geretourneerd. Het type expressie in een SyncLock instructie moet een verwijzingstype zijn. Voorbeeld:

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

In het bovenstaande voorbeeld wordt gesynchroniseerd op het specifieke exemplaar van de klasse Test om ervoor te zorgen dat niet meer dan één thread van uitvoering de tellingsvariabele tegelijk voor een bepaald exemplaar kan optellen of aftrekken.

Het SyncLock blok wordt impliciet opgenomen door een Try instructie waarvan Finally het blok de Shared methode System.Threading.Monitor.Exit voor de expressie aanroept. Dit zorgt ervoor dat de vergrendeling wordt vrijgemaakt, zelfs wanneer er een uitzondering wordt gegenereerd. Als gevolg hiervan is het ongeldig om te vertakken naar een SyncLock blok van buiten het blok en wordt een SyncLock blok beschouwd als één instructie voor de doeleinden Resume en Resume Next. Het bovenstaande voorbeeld is gelijk aan de volgende code:

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

Gebeurtenisinstructies

De RaiseEvent, AddHandleren RemoveHandler instructies genereren gebeurtenissen en verwerken gebeurtenissen dynamisch.

EventStatement
    : RaiseEventStatement
    | AddHandlerStatement
    | RemoveHandlerStatement
    ;

RaiseEvent-instructie

Een RaiseEvent instructie meldt gebeurtenis-handlers dat een bepaalde gebeurtenis heeft plaatsgevonden.

RaiseEventStatement
    : 'RaiseEvent' IdentifierOrKeyword
      ( OpenParenthesis ArgumentList? CloseParenthesis )? StatementTerminator
    ;

De eenvoudige naamexpressie in een RaiseEvent instructie wordt geïnterpreteerd als een opzoekactie Mevoor leden. RaiseEvent x Wordt dus geïnterpreteerd alsof het zou zijnRaiseEvent Me.x. Het resultaat van de expressie moet worden geclassificeerd als gebeurtenistoegang voor een gebeurtenis die is gedefinieerd in de klasse zelf; gebeurtenissen die zijn gedefinieerd op basistypen kunnen niet worden gebruikt in een RaiseEvent instructie.

De RaiseEvent instructie wordt verwerkt als een aanroep naar de methode van de Invoke gemachtigde van de gebeurtenis, met behulp van de opgegeven parameters, indien van toepassing. Als de waarde van de gemachtigde is Nothing, wordt er geen uitzondering gegenereerd. Als er geen argumenten zijn, kunnen de haakjes worden weggelaten. Voorbeeld:

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

De bovenstaande klasse Raiser is gelijk aan:

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

AddHandler- en RemoveHandler-instructies

Hoewel de meeste gebeurtenis-handlers automatisch via variabelen worden gekoppeld WithEvents , kan het nodig zijn om tijdens runtime dynamisch gebeurtenis-handlers toe te voegen en te verwijderen. AddHandler en RemoveHandler instructies doen dit.

AddHandlerStatement
    : 'AddHandler' Expression Comma Expression StatementTerminator
    ;

RemoveHandlerStatement
    : 'RemoveHandler' Expression Comma Expression StatementTerminator
    ;

Elke instructie heeft twee argumenten: het eerste argument moet een expressie zijn die is geclassificeerd als gebeurtenistoegang en het tweede argument moet een expressie zijn die is geclassificeerd als een waarde. Het type van het tweede argument moet het gemachtigde type zijn dat is gekoppeld aan de gebeurtenistoegang. Voorbeeld:

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

Gezien een gebeurtenis E, roept de instructie de relevante add_E of remove_E methode op het exemplaar aan om de gedelegeerde toe te voegen of te verwijderen als handler voor de gebeurtenis. De bovenstaande code is dus gelijk aan:

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

Toewijzingsinstructies

Met een toewijzingsinstructie wordt de waarde van een expressie toegewezen aan een variabele. Er zijn verschillende typen toewijzingen.

AssignmentStatement
    : RegularAssignmentStatement
    | CompoundAssignmentStatement
    | MidAssignmentStatement
    ;

Reguliere toewijzingsinstructies

Met een eenvoudige toewijzingsinstructie wordt het resultaat van een expressie in een variabele opgeslagen.

RegularAssignmentStatement
    : Expression Equals Expression StatementTerminator
    ;

De expressie aan de linkerkant van de toewijzingsoperator moet worden geclassificeerd als een variabele of een eigenschapstoegang, terwijl de expressie aan de rechterkant van de toewijzingsoperator moet worden geclassificeerd als een waarde. Het type expressie moet impliciet worden omgezet in het type variabele of eigenschapstoegang.

Als de variabele waaraan wordt toegewezen een matrixelement van een verwijzingstype is, wordt er een runtimecontrole uitgevoerd om ervoor te zorgen dat de expressie compatibel is met het type matrixelement. In het volgende voorbeeld wordt de laatste toewijzing veroorzaakt System.ArrayTypeMismatchException doordat een exemplaar van ArrayList een matrix niet kan worden opgeslagen in een element van een String matrix.

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.

Als de expressie aan de linkerkant van de toewijzingsoperator is geclassificeerd als een variabele, slaat de toewijzingsinstructie de waarde in de variabele op. Als de expressie is geclassificeerd als eigenschapstoegang, wordt met de toewijzingsinstructie de eigenschapstoegang omgezet in een aanroep van de Set accessor van de eigenschap met de waarde die wordt vervangen door de waardeparameter. Voorbeeld:

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

Als het doel van de toegang tot variabelen of eigenschappen wordt getypt als een waardetype, maar niet is geclassificeerd als een variabele, treedt er een compilatietijdfout op. Voorbeeld:

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

Houd er rekening mee dat de semantiek van de toewijzing afhankelijk is van het type variabele of eigenschap waaraan deze wordt toegewezen. Als de variabele waaraan deze wordt toegewezen een waardetype is, kopieert de toewijzing de waarde van de expressie naar de variabele. Als de variabele waaraan deze wordt toegewezen een verwijzingstype is, kopieert de toewijzing de verwijzing, niet de waarde zelf, naar de variabele. Als het type van de variabele is Object, worden de semantiek van de toewijzing bepaald door of het type waarde een waardetype is of een verwijzingstype tijdens runtime.

Opmerking. Voor intrinsieke typen zoals Integer en Date, semantiek voor verwijzings- en waardetoewijzingen zijn hetzelfde omdat de typen onveranderbaar zijn. Als gevolg hiervan is de taal vrij om verwijzingstoewijzing te gebruiken voor intrinsieke typen in vakken als optimalisatie. Vanuit een waardeperspectief is het resultaat hetzelfde.

Omdat het gelijkteken (=) zowel voor toewijzing als voor gelijkheid wordt gebruikt, is er een dubbelzinnigheid tussen een eenvoudige toewijzing en een aanroepinstructie in situaties zoals x = y.ToString(). In dergelijke gevallen heeft de toewijzingsinstructie voorrang op de gelijkheidsoperator. Dit betekent dat de voorbeeldexpressie wordt geïnterpreteerd als x = (y.ToString()) in plaats (x = y).ToString()van .

Samengestelde toewijzingsinstructies

Een samengestelde toewijzingsinstructie heeft de vorm V op= E (waar op is een geldige binaire operator).

CompoundAssignmentStatement
    : Expression CompoundBinaryOperator LineTerminator? Expression StatementTerminator
    ;

CompoundBinaryOperator
    : '^' '=' | '*' '=' | '/' '=' | '\\' '=' | '+' '=' | '-' '='
    | '&' '=' | '<' '<' '=' | '>' '>' '='
    ;

De expressie aan de linkerkant van de toewijzingsoperator moet worden geclassificeerd als een variabele of eigenschapstoegang, terwijl de expressie aan de rechterkant van de toewijzingsoperator moet worden geclassificeerd als een waarde. De samengestelde toewijzingsinstructie is gelijk aan de instructie V = V op E met het verschil dat de variabele aan de linkerkant van de samengestelde toewijzingsoperator slechts eenmaal wordt geëvalueerd. In het volgende voorbeeld ziet u dit verschil:

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

De expressie a(GetIndex()) wordt tweemaal geëvalueerd voor eenvoudige toewijzing, maar slechts één keer voor samengestelde toewijzing, zodat de code wordt afgedrukt:

Simple assignment
Getting index
Getting index
Compound assignment
Getting index

Mid-toewijzingsinstructie

Een Mid toewijzingsinstructie wijst een tekenreeks toe aan een andere tekenreeks. De linkerkant van de toewijzing heeft dezelfde syntaxis als een aanroep naar de functie Microsoft.VisualBasic.Strings.Mid.

MidAssignmentStatement
    : 'Mid' '$'? OpenParenthesis Expression Comma Expression
      ( Comma Expression )? CloseParenthesis Equals Expression StatementTerminator
    ;

Het eerste argument is het doel van de toewijzing en moet worden geclassificeerd als een variabele of een eigenschapstoegang waarvan het type impliciet converteerbaar is naar en van String. De tweede parameter is de op 1 gebaseerde beginpositie die overeenkomt met waar de toewijzing moet beginnen in de doeltekenreeks en moet worden geclassificeerd als een waarde waarvan het type impliciet moet worden omgezet in Integer. De optionele derde parameter is het aantal tekens uit de rechterwaarde dat moet worden toegewezen aan de doeltekenreeks en moet worden geclassificeerd als een waarde waarvan het type impliciet kan worden omgezet in Integer. De rechterkant is de brontekenreeks en moet worden geclassificeerd als een waarde waarvan het type impliciet converteerbaar is naar String. De rechterkant wordt afgekapt tot de lengteparameter, indien opgegeven, en vervangt de tekens in de tekenreeks aan de linkerkant, beginnend bij de beginpositie. Als de tekenreeks aan de rechterkant minder tekens bevat dan de derde parameter, worden alleen de tekens uit de rechtertekenreeks gekopieerd.

In het volgende voorbeeld wordt het volgende voorbeeld weergegeven 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

Opmerking. Mid is geen gereserveerd woord.

Aanroepinstructies

Een aanroepinstructie roept een methode aan die voorafgaat door het optionele trefwoord Call. De aanroepinstructie wordt op dezelfde manier verwerkt als de functie-aanroepexpressie, met enkele verschillen die hieronder worden vermeld. De aanroepexpressie moet worden geclassificeerd als een waarde of ongeldigheid. Elke waarde die het resultaat is van de evaluatie van de aanroepexpressie, wordt verwijderd.

Als het Call trefwoord wordt weggelaten, moet de aanroepexpressie beginnen met een id of trefwoord of met . binnen een With blok. Zo is 'Call 1.ToString()' bijvoorbeeld een geldige instructie, maar '1.ToString()' niet. (Houd er rekening mee dat in een expressiecontext ook aanroepexpressies niet hoeven te beginnen met een id. 'Dim x = 1.ToString()' is bijvoorbeeld een geldige instructie).

Er is nog een verschil tussen de aanroepinstructies en aanroepexpressies: als een aanroepinstructie een lijst met argumenten bevat, wordt dit altijd beschouwd als de argumentenlijst van de aanroep. In het volgende voorbeeld ziet u het verschil:

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
    ;

Voorwaardelijke uitspraken

Met voorwaardelijke instructies kunt u voorwaardelijke uitvoering van instructies toestaan op basis van expressies die tijdens runtime worden geëvalueerd.

ConditionalStatement
    : IfStatement
    | SelectStatement
    ;

Als... Dan... Else-instructies

Een If...Then...Else instructie is de basisvoorwaardelijke instructie.

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

Elke expressie in een If...Then...Else instructie moet een Boole-expressie zijn, volgens booleaanse sectieexpressies. (Opmerking: hiervoor is niet vereist dat de expressie een Booleaanse waarde heeft). Als de expressie in de If instructie waar is, worden de instructies ingesloten door het If blok uitgevoerd. Als de expressie onwaar is, wordt elk van de ElseIf expressies geëvalueerd. Als een van de ElseIf expressies waar oplevert, wordt het bijbehorende blok uitgevoerd. Als er geen expressie waar is en er een Else blok is, wordt het Else blok uitgevoerd. Zodra een blok is uitgevoerd, wordt de uitvoering doorgegeven aan het einde van de If...Then...Else instructie.

De regelversie van de If instructie heeft één set instructies die moeten worden uitgevoerd als de If expressie is True en een optionele set instructies die moeten worden uitgevoerd als de expressie is False. Voorbeeld:

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

De regelversie van de If-instructie bindt minder strak dan ':', en de bijbehorende Else bindingen met het lexisch dichtstbijzijnde voorafgaande If dat is toegestaan door de syntaxis. De volgende twee versies zijn bijvoorbeeld gelijkwaardig:

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

Alle andere instructies dan labeldeclaratie-instructies zijn toegestaan binnen een regelinstructie If , inclusief blokinstructies. Ze mogen echter niet LineTerminators als StatementTerminators gebruiken, behalve binnen lambda-expressies met meerdere regels. Voorbeeld:

' 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()

Select Case-instructies

Een Select Case instructie voert instructies uit op basis van de waarde van een expressie.

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

De expressie moet worden geclassificeerd als een waarde. Wanneer een Select Case instructie wordt uitgevoerd, wordt de Select expressie eerst geëvalueerd en worden de Case instructies vervolgens geëvalueerd in volgorde van tekstdeclaratie. De eerste Case instructie die evalueert True , wordt het blok uitgevoerd. Als er geen Case instructie wordt geëvalueerd True en er een Case Else instructie is, wordt dat blok uitgevoerd. Zodra een blok is uitgevoerd, wordt de uitvoering doorgegeven aan het einde van de Select instructie.

Uitvoering van een Case blok is niet toegestaan om door te vallen naar de volgende switchsectie. Dit voorkomt een veelvoorkomende klasse bugs die zich in andere talen voordoen wanneer een Case afsluitinstructie per ongeluk wordt weggelaten. In het volgende voorbeeld ziet u dit gedrag:

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

De code wordt afgedrukt:

x = 10

Hoewel Case 10 en Case 20 - 10 selecteert voor dezelfde waarde, Case 10 wordt deze uitgevoerd omdat deze tekst voorafgaat Case 20 - 10 . Wanneer de volgende Case is bereikt, wordt de uitvoering voortgezet na de Select instructie.

Een Case component kan twee vormen aannemen. Eén formulier is een optioneel Is trefwoord, een vergelijkingsoperator en een expressie. De expressie wordt geconverteerd naar het type expressie Select . Als de expressie niet impliciet wordt geconverteerd naar het type Select expressie, treedt er een compilatietijdfout op. Als de Select expressie E is, is de vergelijkingsoperator Op en de Case expressie E1, wordt de case geëvalueerd als E OP E1. De operator moet geldig zijn voor de typen van de twee expressies; anders treedt er een compilatietijdfout op.

Het andere formulier is een expressie die optioneel wordt gevolgd door het trefwoord To en een tweede expressie. Beide expressies worden geconverteerd naar het type expressie. Als een van Select beide expressies niet impliciet wordt geconverteerd naar het type Select expressie, treedt er een compilatietijdfout op. Als de Select expressie is E, is de eerste Case expressie E1en de tweede Case expressie is E2, wordt de Case waarde geëvalueerd als E = E1 (als er geen E2 is opgegeven) of (E >= E1) And (E <= E2). De operators moeten geldig zijn voor de typen van de twee expressies; anders treedt er een compilatietijdfout op.

Lusinstructies

Lusinstructies maken herhaalde uitvoering van de instructies in hun hoofdtekst mogelijk.

LoopStatement
    : WhileStatement
    | DoLoopStatement
    | ForStatement
    | ForEachStatement
    ;

Telkens wanneer een lustekst wordt ingevoerd, wordt er een nieuwe kopie gemaakt van alle lokale variabelen die in die hoofdtekst zijn gedeclareerd, geïnitialiseerd naar de vorige waarden van de variabelen. Elke verwijzing naar een variabele in de hoofdtekst van de lus maakt gebruik van de laatst gemaakte kopie. Deze code toont een voorbeeld:

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

De code produceert de uitvoer:

31    32    33

Wanneer de hoofdtekst van de lus wordt uitgevoerd, wordt gebruikt welke kopie van de variabele actueel is. De instructie Dim y = x verwijst bijvoorbeeld naar de meest recente kopie van y en de oorspronkelijke kopie van x. En wanneer een lambda wordt gemaakt, onthoudt deze welke kopie van een variabele actueel was op het moment dat deze werd gemaakt. Daarom gebruikt elke lambda dezelfde gedeelde kopie van x, maar een andere kopie van y. Aan het einde van het programma, wanneer het de lambdas uitvoert, die gedeelde kopie van x die allemaal verwijst, bevindt zich nu op de uiteindelijke waarde 3.

Houd er rekening mee dat als er geen lambdas- of LINQ-expressies zijn, het onmogelijk is om te weten dat er een nieuwe kopie wordt gemaakt bij lusvermelding. Compileroptimalisaties voorkomen inderdaad dat er kopieën worden gemaakt in dit geval. Houd er ook rekening mee dat het illegaal is om een lus te GoTo maken die lambdas- of LINQ-expressies bevat.

Terwijl... End While and Do... Lusinstructies

Een While of Do lus-instructielussen op basis van een Boole-expressie.

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

Een While lusinstructie wordt herhaald zolang de Boole-expressie resulteert in waar. Een lusinstructie Do kan een complexere voorwaarde bevatten. Een expressie kan na het Do trefwoord of na het Loop trefwoord worden geplaatst, maar niet na beide. De Boole-expressie wordt geëvalueerd volgens booleaanse expressies van secties. (Opmerking: hiervoor is niet vereist dat de expressie een Booleaanse waarde heeft). Het is ook geldig om helemaal geen expressie op te geven; In dat geval zal de lus nooit afsluiten. Als de expressie hierna Dowordt geplaatst, wordt deze geëvalueerd voordat het lusblok wordt uitgevoerd op elke iteratie. Als de expressie hierna Loopwordt geplaatst, wordt deze geëvalueerd nadat het lusblok op elke iteratie is uitgevoerd. Als u de expressie erna Loop plaatst, wordt daarom nog één lus gegenereerd dan na Doplaatsing. In het volgende voorbeeld ziet u dit gedrag:

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

De code produceert de uitvoer:

Second Loop

In het geval van de eerste lus wordt de voorwaarde geëvalueerd voordat de lus wordt uitgevoerd. In het geval van de tweede lus wordt de voorwaarde uitgevoerd nadat de lus is uitgevoerd. De voorwaardelijke expressie moet worden voorafgegaan door een While trefwoord of een Until trefwoord. De vorige breekt de lus als de voorwaarde onwaar oplevert, de laatste wanneer de voorwaarde waar is.

Opmerking. Until is geen gereserveerd woord.

Voor... Volgende instructies

Een For...Next instructielussen op basis van een set grenzen. Een For instructie geeft een lusbesturingselementvariabele, een ondergrensexpressie, een bovengrensexpressie en een optionele stapwaarde-expressie op. De lusbesturingselementvariabele wordt opgegeven via een id, gevolgd door een optionele As component of een expressie.

ForStatement
    : 'For' LoopControlVariable Equals Expression 'To' Expression
      ( 'Step' Expression )? StatementTerminator
      Block?
      ( 'Next' NextExpressionList? StatementTerminator )?
    ;

LoopControlVariable
    : Identifier ( IdentifierModifiers 'As' TypeName )?
    | Expression
    ;

NextExpressionList
    : Expression ( Comma Expression )*
    ;

Volgens de volgende regels verwijst de lusbesturingselementvariabele naar een nieuwe lokale variabele die specifiek is voor deze For...Next instructie, of naar een bestaande variabele of naar een expressie.

  • Als de lusbesturingsvariabele een id is met een As component, definieert de id een nieuwe lokale variabele van het type dat is opgegeven in de As component, die is afgestemd op de hele For lus.

  • Als de lusbesturingselementvariabele een id As zonder component is, wordt de id eerst omgezet met behulp van de eenvoudige naamomzettingsregels (zie Sectie Eenvoudige naamexpressies), behalve dat deze instantie van de id niet op en van zichzelf ertoe zou leiden dat een impliciete lokale variabele wordt gemaakt (sectie impliciete lokale declaraties).

    • Als deze oplossing slaagt en het resultaat wordt geclassificeerd als een variabele, is de lusbesturingselementvariabele die bestaande variabele.

    • Als de oplossing mislukt of als de oplossing slaagt en het resultaat wordt geclassificeerd als een type, dan:

      • als deductie van het lokale variabeletype wordt gebruikt, definieert de id een nieuwe lokale variabele waarvan het type wordt afgeleid van de afhankelijke en stapexpressies, binnen het bereik van de hele For lus;
      • als deductie van het lokale variabeletype niet wordt gebruikt, maar impliciete lokale declaratie is, wordt een impliciete lokale variabele gemaakt waarvan het bereik de volledige methode is (Sectie impliciete lokale declaraties) en de lusbesturingselementvariabele verwijst naar deze bestaande variabele;
      • als deductie van het lokale variabeletype of impliciete lokale declaraties niet worden gebruikt, is dit een fout.
    • Als de oplossing slaagt met iets dat is geclassificeerd als een type of een variabele, is het een fout.

  • Als de lusbesturingselementvariabele een expressie is, moet de expressie worden geclassificeerd als een variabele.

Een lusbesturingsvariabele kan niet worden gebruikt door een andere insluitinstructie For...Next . Het type lusbesturingselementvariabele van een For instructie bepaalt het type iteratie en moet een van de volgende zijn:

  • Byte, , SByteUShort, Short, UInteger, , Integer, , DecimalLongSingleULongDouble
  • Een geïnventariseerd type
  • Object
  • Een type T met de volgende operators, waarbij B een type is dat kan worden gebruikt in een Boole-expressie:
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

De afhankelijke en stapexpressies moeten impliciet worden omgezet in het type lusbesturingselementvariabele en moeten worden geclassificeerd als waarden. Tijdens het compileren wordt het type lusbesturingselementvariabele afgeleid door het breedste type te kiezen tussen de typen ondergrens, bovengrens en stapexpressie. Als er geen widening conversie tussen twee van de typen is, treedt er een compilatietijdfout op.

Als het type lusbesturingselementvariabele tijdens runtime is Object, wordt het type iteratie afgeleid van hetzelfde als tijdens het compileren, met twee uitzonderingen. Als de afhankelijke en stapexpressies allemaal integrale typen zijn, maar geen breedst type hebben, wordt het breedste type dat alle drie de typen omvat, afgeleid. En ten tweede, als het type lusbesturingsvariabele wordt afgeleid String, Double wordt in plaats daarvan afgeleid. Als tijdens runtime geen lusbesturingselementtype kan worden bepaald of als een van de expressies niet kan worden geconverteerd naar het lusbesturingselementtype, treedt er een System.InvalidCastException op. Zodra een lusbesturingselementtype aan het begin van de lus is gekozen, wordt hetzelfde type tijdens de iteratie gebruikt, ongeacht de wijzigingen in de waarde in de lusbesturingselementvariabele.

Een For instructie moet worden gesloten door een overeenkomende Next instructie. Een Next instructie zonder variabele komt overeen met de binnenste open For instructie, terwijl een instructie met een Next of meer lusbesturingsvariabelen van links naar rechts overeenkomt met de For lussen die overeenkomen met elke variabele. Als een variabele overeenkomt met een For lus die op dat moment niet de meest geneste lus is, treedt er een compilatiefout op.

Aan het begin van de lus worden de drie expressies geëvalueerd in tekstvolgorde en wordt de ondergrensexpressie toegewezen aan de lusbesturingselementvariabele. Als de stapwaarde wordt weggelaten, wordt deze impliciet de letterlijke 1waarde geconverteerd naar het type lusbesturingsvariabele. De drie expressies worden alleen aan het begin van de lus geëvalueerd.

Aan het begin van elke lus wordt de besturingsvariabele vergeleken om te zien of deze groter is dan het eindpunt als de stapexpressie positief is of kleiner is dan het eindpunt als de stapexpressie negatief is. Als dat zo is, wordt de For lus beëindigd. Anders wordt het lusblok uitgevoerd. Als de lusbesturingselementvariabele geen primitief type is, wordt de vergelijkingsoperator bepaald door of de expressie step >= step - step waar of onwaar is. In de Next instructie wordt de stapwaarde toegevoegd aan de besturingsvariabele en de uitvoering keert terug naar de bovenkant van de lus.

Houd er rekening mee dat er bij elke herhaling van het lusblok geen nieuwe kopie van de lusbesturingselementvariabele wordt gemaakt. In dit opzicht verschilt de For verklaring van For Each (Sectie voor elke... Volgende instructies).

Het is niet geldig om een For lus van buiten de lus te vertakken.

Voor elke... Volgende instructies

Een For Each...Next instructielussen op basis van de elementen in een expressie. Een For Each instructie geeft een lusbesturingselementvariabele en een opsommingsexpressie op. De lusbesturingselementvariabele wordt opgegeven via een id, gevolgd door een optionele As component of een expressie.

ForEachStatement
    : 'For' 'Each' LoopControlVariable 'In' LineTerminator? Expression StatementTerminator
      Block?
      ( 'Next' NextExpressionList? StatementTerminator )?
    ;

Volg dezelfde regels als For...Next instructies (Sectie voor... Volgende instructies), verwijst de lusbesturingsvariabele naar een nieuwe lokale variabele die specifiek is voor elke variabele... Volgende instructie, of naar een bestaande variabele of naar een expressie.

De enumerator-expressie moet worden geclassificeerd als een waarde en het bijbehorende type moet een verzamelingstype zijn of Object. Als het type van de enumerator-expressie is Object, wordt alle verwerking uitgesteld tot de runtime. Anders moet er een conversie bestaan van het elementtype van de verzameling naar het type lusbesturingsvariabele.

De lusbesturingsvariabele kan niet worden gebruikt door een andere insluitinstructie For Each . Een For Each instructie moet worden gesloten door een overeenkomende Next instructie. Een Next instructie zonder lusbesturingsvariabele komt overeen met de binnenste open For Each. Een Next instructie met een of meer lusbesturingsvariabelen komt van links naar rechts overeen met de For Each lussen met dezelfde lusbesturingsvariabele. Als een variabele overeenkomt met een For Each lus die op dat moment niet de meest geneste lus is, treedt er een compileertijdfout op.

Een type C wordt als een verzamelingstype beschouwd als een van de volgende:

  • Alle volgende zaken zijn waar:

    • C bevat een toegankelijke instantie, gedeelde of extensiemethode met de handtekening GetEnumerator() die een type Eretourneert.
    • E bevat een toegankelijke instantie, gedeelde of extensiemethode met de handtekening MoveNext() en het retourtype Boolean.
    • E bevat een toegankelijk exemplaar of een gedeelde eigenschap met de naam Current een getter. Het type van deze eigenschap is het elementtype van het verzamelingstype.
  • Het implementeert de interface System.Collections.Generic.IEnumerable(Of T), in welk geval het elementtype van de verzameling wordt beschouwd als T.

  • Het implementeert de interface System.Collections.IEnumerable, in welk geval het elementtype van de verzameling wordt beschouwd als Object.

Hier volgt een voorbeeld van een klasse die kan worden geïnventariseerd:

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

Voordat de lus begint, wordt de opsommingsexpressie geëvalueerd. Als het type van de expressie niet voldoet aan het ontwerppatroon, wordt de expressie omgezet naar System.Collections.IEnumerable of System.Collections.Generic.IEnumerable(Of T). Als het expressietype de algemene interface implementeert, heeft de algemene interface de voorkeur tijdens het compileren, maar de niet-algemene interface heeft de voorkeur tijdens runtime. Als het expressietype de algemene interface meerdere keren implementeert, wordt de instructie beschouwd als dubbelzinnig en treedt er een compilatiefout op.

Opmerking. De niet-algemene interface heeft de voorkeur in het late gebonden geval, omdat het kiezen van de algemene interface zou betekenen dat alle aanroepen naar de interfacemethoden typeparameters zouden omvatten. Omdat het niet mogelijk is om de overeenkomende typeargumenten tijdens runtime te kennen, moeten al deze aanroepen worden gedaan met late gebonden aanroepen. Dit zou langzamer zijn dan het aanroepen van de niet-algemene interface, omdat de niet-algemene interface kan worden aangeroepen met behulp van compileertijdaanroepen.

GetEnumerator wordt aangeroepen op de resulterende waarde en de retourwaarde van de functie wordt opgeslagen in een tijdelijke waarde. Vervolgens wordt aan het begin van elke iteratie MoveNext de tijdelijke aangeroepen. Als deze wordt geretourneerd False, wordt de lus beëindigd. Anders wordt elke iteratie van de lus als volgt uitgevoerd:

  1. Als de lusbesturingselementvariabele een nieuwe lokale variabele heeft geïdentificeerd (in plaats van een bestaande variabele), wordt er een nieuwe kopie van deze lokale variabele gemaakt. Voor de huidige iteratie verwijzen alle verwijzingen binnen het lusblok naar deze kopie.
  2. De Current eigenschap wordt opgehaald, afgedwongen op het type lusbesturingselementvariabele (ongeacht of de conversie impliciet of expliciet is) en toegewezen aan de lusbesturingselementvariabele.
  3. Het lusblok wordt uitgevoerd.

Opmerking. Er is een kleine wijziging in het gedrag tussen versie 10.0 en 11.0 van de taal. Vóór 11.0 is er geen nieuwe iteratievariabele gemaakt voor elke iteratie van de lus. Dit verschil is alleen waarneembaar als de iteratievariabele wordt vastgelegd door een lambda of een LINQ-expressie die vervolgens wordt aangeroepen na de lus:

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()

Tot Visual Basic 10.0 heeft dit drie keer een waarschuwing gegenereerd tijdens het compileren en '3' afgedrukt. Dat was omdat er slechts één variabele 'x' werd gedeeld door alle iteraties van de lus, en alle drie lambdas hebben dezelfde "x" vastgelegd, en toen de lambdas werden uitgevoerd, hield het nummer 3. Vanaf Visual Basic 11.0 wordt '1, 2, 3' afgedrukt. Dat komt doordat elke lambda een andere variabele 'x' vastlegt.

Opmerking. Het huidige element van de iteratie wordt geconverteerd naar het type lusbesturingselementvariabele, zelfs als de conversie expliciet is omdat er geen handige plaats is om een conversieoperator in de instructie te introduceren. Dit werd bijzonder lastig bij het werken met het nu verouderde type System.Collections.ArrayList, omdat het elementtype is Object. Dit zou casts in een groot aantal lussen nodig hebben, iets wat we voelden was niet ideaal. Ironisch genoeg hebben generics het creëren van een sterk getypeerde verzameling mogelijk gemaakt, System.Collections.Generic.List(Of T)waardoor we mogelijk dit ontwerppunt opnieuw hebben herzien, maar in het belang van compatibiliteit kan dit nu niet worden gewijzigd.

Wanneer de Next instructie is bereikt, keert de uitvoering terug naar de bovenkant van de lus. Als een variabele na het Next trefwoord is opgegeven, moet deze hetzelfde zijn als de eerste variabele na de For Each. Denk bijvoorbeeld aan de volgende code:

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

Deze is gelijk aan de volgende code:

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

Als het type E enumerator wordt geïmplementeerd System.IDisposable, wordt de enumerator verwijderd bij het afsluiten van de lus door de Dispose methode aan te roepen. Dit zorgt ervoor dat resources die door de enumerator worden bewaard, worden vrijgegeven. Als de methode met de For Each instructie geen ongestructureerde foutafhandeling gebruikt, wordt de For Each instructie verpakt in een Try instructie met de Dispose methode die in de Finally instructie wordt aangeroepen om opschoning te garanderen.

Opmerking. Het System.Array type is een verzamelingstype en omdat alle matrixtypen zijn afgeleid van System.Array, is elke expressie van het matrixtype toegestaan in een For Each instructie. Voor enkelvoudige matrices bevat de For Each instructie de matrixelementen in toenemende indexvolgorde, beginnend met index 0 en eindigend met indexlengte - 1. Voor multidimensionale matrices worden eerst de indexen van de meest rechtse dimensie verhoogd.

De volgende code wordt bijvoorbeeld afgedrukt 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

Het is niet geldig om te vertakken naar een For Each instructieblok van buiten het blok.

Exception-Handling-instructies

Visual Basic biedt ondersteuning voor gestructureerde verwerking van uitzonderingen en ongestructureerde uitzonderingsafhandeling. Er kan slechts één stijl voor het verwerken van uitzonderingen worden gebruikt in een methode, maar de Error instructie kan worden gebruikt bij de verwerking van gestructureerde uitzonderingen. Als bij een methode beide stijlen voor het verwerken van uitzonderingen worden gebruikt, treedt er een compilatiefout op.

ErrorHandlingStatement
    : StructuredErrorStatement
    | UnstructuredErrorStatement
    ;

Gestructureerde Exception-Handling-instructies

Gestructureerde uitzonderingsafhandeling is een methode voor het afhandelen van fouten door expliciete blokken te declareren waarin bepaalde uitzonderingen worden afgehandeld. De verwerking van gestructureerde uitzonderingen wordt uitgevoerd via een Try instructie.

StructuredErrorStatement
    : ThrowStatement
    | TryStatement
    ;

TryStatement
    : 'Try' StatementTerminator
      Block?
      CatchStatement*
      FinallyStatement?
      'End' 'Try' StatementTerminator
    ;

Voorbeeld:

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

Een Try instructie bestaat uit drie soorten blokken: try blocks, catch blocks en finally blocks. Een try-blok is een instructieblok dat de instructies bevat die moeten worden uitgevoerd. Een catch-blok is een instructieblok dat een uitzondering afhandelt. Een laatste blok is een instructieblok dat instructies bevat die moeten worden uitgevoerd wanneer de Try instructie wordt afgesloten, ongeacht of er een uitzondering is opgetreden en verwerkt. Een Try instructie, die slechts één try-blok en één blok kan bevatten, moet ten minste één catch-blok of ten slotte blok bevatten. Het is ongeldig om de uitvoering expliciet over te dragen naar een try-blok, behalve vanuit een catch-blok in dezelfde instructie.

Finally-blokken

Een Finally blok wordt altijd uitgevoerd wanneer de uitvoering een deel van de Try instructie verlaat. Er is geen expliciete actie vereist om het Finally blok uit te voeren. Wanneer de uitvoering de Try instructie verlaat, wordt het Finally blok automatisch uitgevoerd en wordt de uitvoering vervolgens overgedragen naar de beoogde bestemming. Het Finally blok wordt uitgevoerd, ongeacht de manier waarop de uitvoering de Try instructie verlaat: via het einde van het Try blok, door het einde van een Catch blok, via een Exit Try instructie, via een GoTo instructie of door het niet verwerken van een gegenereerde uitzondering.

Houd er rekening mee dat de Await expressie in een asynchrone methode en de Yield instructie in een iterator-methode ertoe kan leiden dat de controlestroom wordt onderbroken in het exemplaar van de asynchrone of iterator-methode en in een ander exemplaar van de methode wordt hervat. Dit is echter slechts een opschorting van de uitvoering en houdt niet in dat de respectieve asynchrone methode of het exemplaar van de iterator-methode wordt afgesloten, en dat er dus geen Finally blokken worden uitgevoerd.

Het is ongeldig om de uitvoering expliciet over te dragen naar een Finally blok; het is ook ongeldig om de uitvoering uit een Finally blok over te dragen, behalve via een uitzondering.

FinallyStatement
    : 'Finally' StatementTerminator
      Block?
    ;

Catch-blokken

Als er een uitzondering optreedt tijdens het verwerken van het Try blok, wordt elke Catch instructie in tekstuele volgorde onderzocht om te bepalen of deze de uitzondering verwerkt.

CatchStatement
    : 'Catch' ( Identifier ( 'As' NonArrayTypeName )? )?
	  ( 'When' BooleanExpression )? StatementTerminator
      Block?
    ;

De id die is opgegeven in een Catch component vertegenwoordigt de uitzondering die is gegenereerd. Als de id een As component bevat, wordt de id beschouwd als gedeclareerd binnen de lokale declaratieruimte van het Catch blok. Anders moet de id een lokale variabele (niet een statische variabele) zijn die is gedefinieerd in een bevattend blok.

Een Catch component zonder id ondervangt alle uitzonderingen die zijn afgeleid van System.Exception. Een Catch component met een id onderschept alleen uitzonderingen waarvan de typen hetzelfde zijn als of zijn afgeleid van het type van de id. Het type moet zijn System.Exception, of een type dat is afgeleid van System.Exception. Wanneer een uitzondering wordt afgevangen die is afgeleid van System.Exception, wordt een verwijzing naar het uitzonderingsobject opgeslagen in het object dat door de functie Microsoft.VisualBasic.Information.Errwordt geretourneerd.

Een Catch component met een When component ondervangt alleen uitzonderingen wanneer de expressie evalueert; Truehet type expressie moet een Boole-expressie zijn volgens de Boole-expressies van sectie. Er wordt alleen een When component toegepast nadat het type uitzondering is gecontroleerd en de expressie kan verwijzen naar de id die de uitzondering vertegenwoordigt, zoals in dit voorbeeld wordt gedemonstreert:

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

In dit voorbeeld wordt afgedrukt:

Third handler

Als een Catch component de uitzondering afhandelt, wordt de uitvoering overgedragen naar het Catch blok. Aan het einde van het Catch blok wordt de uitvoering overgedragen naar de eerste instructie na de Try instructie. De Try instructie verwerkt geen uitzonderingen die in een Catch blok worden gegenereerd. Als er geen Catch component de uitzondering afhandelt, wordt de uitvoering overgedragen naar een locatie die door het systeem wordt bepaald.

Het is ongeldig om de uitvoering expliciet over te dragen naar een Catch blok.

De filters in When-componenten worden normaal gesproken geëvalueerd voordat de uitzondering wordt gegenereerd. Met de volgende code wordt bijvoorbeeld 'Filter, Finally, Catch' afgedrukt.

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

Async- en Iterator-methoden zorgen er echter voor dat alle blokken in deze worden uitgevoerd voordat filters buiten worden uitgevoerd. Als de bovenstaande code bijvoorbeeld had, Async Sub Foo()zou de uitvoer 'Eindelijk Filter, Catch' zijn.

Werpinstructie

De Throw instructie genereert een uitzondering, die wordt vertegenwoordigd door een exemplaar van een type dat is afgeleid van System.Exception.

ThrowStatement
    : 'Throw' Expression? StatementTerminator
    ;

Als de expressie niet is geclassificeerd als een waarde of geen type is dat is afgeleid van System.Exception, treedt er een compilatietijdfout op. Als de expressie tijdens runtime een null-waarde oplevert, wordt in plaats daarvan een System.NullReferenceException uitzondering gegenereerd.

Een Throw instructie kan de expressie weglaten binnen een catch-blok van een Try instructie, zolang er geen tussenliggend eindblok is. In dat geval wordt met de instructie de uitzondering die momenteel in het catch-blok wordt verwerkt, opnieuw uitgevoerd. Voorbeeld:

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

Ongestructureerde Exception-Handling-instructies

Niet-gestructureerde uitzonderingsafhandeling is een methode voor het afhandelen van fouten door instructies aan te geven die moeten worden vertakt wanneer er een uitzondering optreedt. Ongestructureerde verwerking van uitzonderingen wordt geïmplementeerd met behulp van drie instructies: de Error instructie, de On Error instructie en de Resume instructie.

UnstructuredErrorStatement
    : ErrorStatement
    | OnErrorStatement
    | ResumeStatement
    ;

Voorbeeld:

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

Wanneer een methode gebruikmaakt van niet-gestructureerde uitzonderingsafhandeling, wordt één gestructureerde uitzonderingshandler ingesteld voor de hele methode waarmee alle uitzonderingen worden onderschept. (Houd er rekening mee dat in constructors deze handler niet wordt uitgebreid over de aanroep naar de aanroep aan New het begin van de constructor.) De methode houdt vervolgens de meest recente locatie van de uitzonderingshandler en de meest recente uitzondering bij die is gegenereerd. Bij het invoeren van de methode worden de locatie van de uitzonderingshandler en de uitzondering beide ingesteld op Nothing. Wanneer een uitzondering wordt gegenereerd in een methode die gebruikmaakt van niet-gestructureerde uitzonderingsverwerking, wordt een verwijzing naar het uitzonderingsobject opgeslagen in het object dat door de functie Microsoft.VisualBasic.Information.Errwordt geretourneerd.

Niet-gestructureerde instructies voor foutafhandeling zijn niet toegestaan in iterator- of asynchrone methoden.

Foutinstructie

Een Error instructie genereert een System.Exception uitzondering met een Visual Basic 6-uitzonderingsnummer. De expressie moet worden geclassificeerd als een waarde en het type moet impliciet worden omgezet in Integer.

ErrorStatement
    : 'Error' Expression StatementTerminator
    ;

Bij foutinstructie

Een On Error instructie wijzigt de meest recente status voor het verwerken van uitzonderingen.

OnErrorStatement
    : 'On' 'Error' ErrorClause StatementTerminator
    ;

ErrorClause
    : 'GoTo' '-' '1'
    | 'GoTo' '0'
    | GoToStatement
    | 'Resume' 'Next'
    ;

Het kan op vier manieren worden gebruikt:

  • On Error GoTo -1 stelt de meest recente uitzondering opnieuw in op Nothing.

  • On Error GoTo 0 stelt de meest recente locatie van de uitzonderingshandler opnieuw in op Nothing.

  • On Error GoTo LabelName stelt het label in als de meest recente locatie van de uitzonderingshandler. Deze instructie kan niet worden gebruikt in een methode die een lambda- of query-expressie bevat.

  • On Error Resume Next stelt het Resume Next gedrag vast als de meest recente locatie van de uitzonderingshandler.

Cv-instructie

Een Resume instructie retourneert de uitvoering van de instructie die de meest recente uitzondering heeft veroorzaakt.

ResumeStatement
    : 'Resume' ResumeClause? StatementTerminator
    ;

ResumeClause
    : 'Next'
    | LabelName
    ;

Als de Next wijzigingsfunctie is opgegeven, keert de uitvoering terug naar de instructie die zou zijn uitgevoerd na de instructie die de meest recente uitzondering heeft veroorzaakt. Als er een labelnaam is opgegeven, keert de uitvoering terug naar het label.

Omdat de SyncLock instructie een impliciet gestructureerd blok Resume voor foutafhandeling bevat en Resume Next speciaal gedrag heeft voor uitzonderingen die voorkomen in SyncLock instructies. Resume retourneert de uitvoering aan het begin van de SyncLock instructie, terwijl Resume Next de uitvoering wordt geretourneerd naar de volgende instructie na de SyncLock instructie. Denk bijvoorbeeld aan de volgende code:

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

Het volgende resultaat wordt afgedrukt.

Before exception
Before exception
After SyncLock

De eerste keer dat u de instructie doorloopt SyncLock , Resume retourneert de uitvoering aan het begin van de SyncLock instructie. De tweede keer door de SyncLock instructie retourneert Resume Next de uitvoering aan het einde van de SyncLock instructie. Resume en Resume Next zijn niet toegestaan binnen een SyncLock instructie.

Wanneer een Resume instructie wordt uitgevoerd, wordt de meest recente uitzondering ingesteld op Nothing. Als een Resume instructie wordt uitgevoerd zonder de meest recente uitzondering, genereert de instructie een System.Exception uitzondering met het Visual Basic-foutnummer 20 (hervatten zonder fout).

Vertakkingsinstructies

Vertakkingsinstructies wijzigen de uitvoeringsstroom in een methode. Er zijn zes vertakkingsinstructies:

  1. Een GoTo instructie zorgt ervoor dat de uitvoering wordt overgedragen naar het opgegeven label in de methode. Het is niet toegestaan om GoTo een lusblok in een Trylambda WithForUsingSyncLock- of For Each LINQ-expressie te plaatsen, noch in een lusblok als een lokale variabele van dat blok wordt vastgelegd in een lambda- of LINQ-expressie.
  2. Met een Exit instructie wordt de uitvoering overgedragen naar de volgende instructie na het einde van de onmiddellijk met blokinstructie van het opgegeven type. Als het blok het methodeblok is, sluit de controlestroom de methode af, zoals beschreven in sectiebeheerstroom. Als de Exit instructie niet is opgenomen in het type blok dat is opgegeven in de instructie, treedt er een compilatietijdfout op.
  3. Met een Continue instructie wordt de uitvoering overgedragen naar het einde van de instructie van de bloklus van het opgegeven type. Als de Continue instructie niet is opgenomen in het type blok dat is opgegeven in de instructie, treedt er een compilatietijdfout op.
  4. Een Stop instructie zorgt ervoor dat er een foutopsporingsprogramma-uitzondering optreedt.
  5. Een End instructie beëindigt het programma. Finalizers worden uitgevoerd voordat ze worden afgesloten, maar de laatste blokken van eventuele momenteel uitgevoerde Try instructies worden niet uitgevoerd. Deze instructie kan niet worden gebruikt in programma's die niet uitvoerbaar zijn (bijvoorbeeld DLL's).
  6. Een Return instructie zonder expressie is gelijk aan een Exit Sub of Exit Function instructie. Een Return instructie met een expressie is alleen toegestaan in een reguliere methode die een functie is, of in een asynchrone methode die een functie is met een retourtype Task(Of T) voor sommige T. De expressie moet worden geclassificeerd als een waarde die impliciet wordt geconverteerd naar de functie-retourvariabele (in het geval van reguliere methoden) of naar de taak retourvariabele (in het geval van asynchrone methoden). Het gedrag is om de expressie te evalueren en deze vervolgens op te slaan in de retourvariabele en vervolgens een impliciete Exit Function instructie uit te voeren.
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-instructies

Twee instructies vereenvoudigen het werken met matrices: ReDim instructies en Erase instructies.

ArrayHandlingStatement
    : RedimStatement
    | EraseStatement
    ;

ReDim-instructie

Met een ReDim instructie worden nieuwe matrices geïnstitueert.

RedimStatement
    : 'ReDim' 'Preserve'? RedimClauses StatementTerminator
    ;

RedimClauses
    : RedimClause ( Comma RedimClause )*
    ;

RedimClause
    : Expression ArraySizeInitializationModifier
    ;

Elke component in de instructie moet worden geclassificeerd als een variabele of een eigenschapstoegang waarvan het type een matrixtype is, Objecten moet worden gevolgd door een lijst met matrixgrenzen. Het aantal grenzen moet consistent zijn met het type variabele; een willekeurig aantal grenzen is toegestaan voor Object. Tijdens runtime wordt een matrix geïnstantieerd voor elke expressie van links naar rechts met de opgegeven grenzen en vervolgens toegewezen aan de variabele of eigenschap. Als het variabeletype is Object, is het aantal dimensies het opgegeven aantal dimensies en het type Objectmatrixelement. Als het opgegeven aantal dimensies niet compatibel is met de variabele of eigenschap tijdens runtime, treedt er een compilatietijdfout op. Voorbeeld:

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

Als het Preserve trefwoord is opgegeven, moeten de expressies ook worden classificeerbaar als een waarde en moet de nieuwe grootte voor elke dimensie, met uitzondering van de meest rechtse, gelijk zijn aan de grootte van de bestaande matrix. De waarden in de bestaande matrix worden gekopieerd naar de nieuwe matrix: als de nieuwe matrix kleiner is, worden de bestaande waarden verwijderd; als de nieuwe matrix groter is, worden de extra elementen geïnitialiseerd tot de standaardwaarde van het elementtype van de matrix. Denk bijvoorbeeld aan de volgende code:

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

Het volgende resultaat wordt afgedrukt:

3, 0

Als de bestaande matrixreferentie een null-waarde is tijdens runtime, wordt er geen fout weergegeven. Afgezien van de meest rechtse dimensie, als de grootte van een dimensie verandert, wordt er een System.ArrayTypeMismatchException gegenereerd.

Opmerking. Preserve is geen gereserveerd woord.

Wisinstructie

Met een Erase instructie wordt elk van de matrixvariabelen of eigenschappen ingesteld die in de instructie zijn Nothingopgegeven. Elke expressie in de instructie moet worden geclassificeerd als een variabele of eigenschapstoegang waarvan het type een matrixtype is of Object. Voorbeeld:

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 )*
    ;

Instructie gebruiken

Exemplaren van typen worden automatisch vrijgegeven door de garbagecollector wanneer een verzameling wordt uitgevoerd en er geen live verwijzingen naar het exemplaar worden gevonden. Als een type een bijzonder waardevolle en schaarse resource bevat (zoals databaseverbindingen of bestandsingangen), is het mogelijk niet wenselijk te wachten totdat de volgende garbagecollection een bepaald exemplaar van het type opschoont dat niet meer wordt gebruikt. Om een lichtgewicht manier te bieden om resources vrij te geven voordat een verzameling wordt verzameld, kan een type de System.IDisposable interface implementeren. Een type dat dit doet, maakt een Dispose methode beschikbaar die kan worden aangeroepen om waardevolle resources onmiddellijk vrij te geven, zoals:

Module Test
    Sub Main()
        Dim x As DBConnection = New DBConnection("...")

        ' Do some work
        ...

        x.Dispose()        ' Free the connection
    End Sub
End Module

De Using instructie automatiseert het proces van het verkrijgen van een resource, het uitvoeren van een set instructies en het verwijderen van de resource. De instructie kan twee vormen aannemen: in één vorm is de resource een lokale variabele die is gedeclareerd als onderdeel van de instructie en wordt behandeld als een reguliere declaratie-instructie voor lokale variabelen; in het andere is de resource het resultaat van een expressie.

UsingStatement
    : 'Using' UsingResources StatementTerminator
      Block?
      'End' 'Using' StatementTerminator
    ;

UsingResources
    : VariableDeclarators
    | Expression
    ;

Als de resource een instructie voor lokale variabeledeclaratie is, moet het type lokale variabeledeclaratie een type zijn waarnaar impliciet kan worden geconverteerd System.IDisposable. De gedeclareerde lokale variabelen zijn alleen-lezen, die zijn afgestemd op het Using instructieblok en moeten een initialisatiefunctie bevatten. Als de resource het resultaat is van een expressie, moet de expressie worden geclassificeerd als een waarde en moet deze van een type zijn dat impliciet kan worden geconverteerd naar System.IDisposable. De expressie wordt slechts eenmaal geëvalueerd, aan het begin van de instructie.

Het Using blok is impliciet opgenomen in een Try instructie waarvan uiteindelijk de methode IDisposable.Dispose voor de resource wordt aangeroepen. Dit zorgt ervoor dat de resource wordt verwijderd, zelfs wanneer er een uitzondering wordt gegenereerd. Als gevolg hiervan is het ongeldig om te vertakken naar een Using blok van buiten het blok en wordt een Using blok beschouwd als één instructie voor de doeleinden Resume en Resume Next. Als de resource is Nothing, wordt er geen aanroep gedaan Dispose . Het voorbeeld is dus:

Using f As C = New C()
    ...
End Using

is gelijk aan:

Dim f As C = New C()
Try
    ...
Finally
    If f IsNot Nothing Then
        f.Dispose()
    End If
End Try

Een Using instructie met een lokale variabeledeclaratie-instructie kan meerdere resources tegelijk verkrijgen, wat gelijk is aan geneste Using instructies. Bijvoorbeeld een Using instructie van het formulier:

Using r1 As R = New R(), r2 As R = New R()
    r1.F()
    r2.F()
End Using

is gelijk aan:

Using r1 As R = New R()
    Using r2 As R = New R()
        r1.F()
        r2.F()
    End Using
End Using

Await-instructie

Een await-instructie heeft dezelfde syntaxis als een await-operatorexpressie (Section Await Operator), is alleen toegestaan in methoden die ook await-expressies toestaan en hetzelfde gedrag hebben als een await-operatorexpressie.

Het kan echter worden geclassificeerd als een waarde of ongeldigheid. Elke waarde die het gevolg is van de evaluatie van de await-operatorexpressie, wordt verwijderd.

AwaitStatement
    : AwaitOperatorExpression StatementTerminator
    ;

Rendementsoverzicht

Rendementsinstructies zijn gerelateerd aan iteratormethoden, die worden beschreven in Sectie Iterator-methoden.

YieldStatement
    : 'Yield' Expression StatementTerminator
    ;

Yield is een gereserveerd woord als de methode of lambda-expressie waarin deze wordt weergegeven, onmiddellijk een wijzigingsfunctie heeft Iterator , en als de Yield expressie na die Iterator wijziging wordt weergegeven; deze is elders niet gereserveerd. Het is ook niet gereserveerd in preprocessorrichtlijnen. De rendementsinstructie is alleen toegestaan in de hoofdtekst van een methode of lambda-expressie waarin het een gereserveerd woord is. Binnen de onmiddellijk omsluitende methode of lambda kan de rendementsinstructie niet voorkomen in de hoofdtekst van een Catch of Finally blok, noch in de hoofdtekst van een SyncLock instructie.

De rendementsinstructie neemt één expressie die moet worden geclassificeerd als een waarde en waarvan het type impliciet wordt omgezet in het type van de huidige iteratorvariabele (Section Iterator Methods) van de bijbehorende iterator-methode.

Controlestroom bereikt alleen ooit een Yield instructie wanneer de MoveNext methode wordt aangeroepen op een iterator-object. (Dit komt doordat een iteratormethode-exemplaar alleen de instructies uitvoert vanwege de MoveNext of Dispose methoden die worden aangeroepen op een iterator-object; en de Dispose methode alleen code in Finally blokken uitvoert, waar Yield niet is toegestaan).

Wanneer een Yield instructie wordt uitgevoerd, wordt de expressie geëvalueerd en opgeslagen in de huidige iteratorvariabele van het iterator-methodeexemplaar dat is gekoppeld aan dat iterator-object. De waarde True wordt geretourneerd naar de aanroeper van MoveNexten het besturingspunt van dit exemplaar stopt totdat de volgende aanroep van MoveNext het iterator-object wordt aangeroepen.