Příkazy – Visual Basic

Příkazy představují spustitelný kód.

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

Poznámka. Microsoft Visual Basic Compiler umožňuje pouze příkazy, které začínají klíčovým slovem nebo identifikátorem. Příkaz vyvolání "Call (Console).WriteLine" je tedy povolený, ale vyvolání příkazu "(Console).WriteLine" není.

Řízení toku

Tok řízení je posloupnost, ve které se provádějí příkazy a výrazy. Pořadí provádění závisí na konkrétním příkazu nebo výrazu.

Například při vyhodnocování operátoru sčítání (operátor sčítání oddílů) se nejprve vyhodnotí levý operand, pak pravý operand a pak samotný operátor. Bloky se spouští ( bloky oddílů a popisky) tak, že nejprve spustíte první dílčístatek a pak jeden po druhém projdete příkazy bloku.

Implicitní v tomto pořadí je koncept řídicího bodu, což je další operace, která se má provést. Když je vyvolána metoda (nebo "volána"), říkáme, že vytvoří instanci metody. Instance metody se skládá z vlastní kopie parametrů metody a místních proměnných a vlastního řídicího bodu.

Běžné metody

Tady je příklad běžné metody.

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

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

Při vyvolání běžné metody

  1. První instance metody je vytvořena specifická pro toto vyvolání. Tato instance zahrnuje kopii všech parametrů a místních proměnných metody.
  2. Všechny jeho parametry se pak inicializují na zadané hodnoty a všechny jeho místní proměnné na výchozí hodnoty jejich typů.
  3. V případě Function, implicitní místní proměnná je také inicializována funkce návratová proměnná , jejíž název je název funkce, jehož typ je návratový typ funkce a jehož počáteční hodnota je výchozí hodnotou jeho typu.
  4. Řídicí bod instance metody se pak nastaví na prvním příkazu těla metody a tělo metody se okamžitě spustí odsud ( bloky a popisky oddílu).

Když tok řízení ukončí tělo metody normálně – tím, že dosáhnete End Function nebo End Sub tím, že označíte jeho konec, nebo prostřednictvím explicitního Return nebo Exit příkazu – tok řízení se vrátí volající instanci metody. Pokud existuje návratová proměnná funkce, výsledkem vyvolání je konečná hodnota této proměnné.

Když tok řízení ukončí tělo metody neošetřenou výjimkou, tato výjimka se rozšíří do volajícího.

Po ukončení toku řízení již neexistují žádné živé odkazy na instanci metody. Pokud instance metody uchovává pouze odkazy na jeho kopii místních proměnných nebo parametrů, mohou být uvolněny z paměti.

Metody iterátoru

Metody iterátoru se používají jako pohodlný způsob, jak vygenerovat sekvenci, která může být využívána příkazem For Each . Metody iterátoru Yield používají příkaz (Section Yield Statement) k poskytnutí prvků sekvence. (Metoda iterátoru bez Yield příkazů vytvoří prázdnou sekvenci). Tady je příklad metody iterátoru:

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

Při vyvolání metody iterátoru, jejíž návratový typ je IEnumerator(Of T),

  1. První instance metody iterátoru je vytvořena specifická pro toto vyvolání. Tato instance zahrnuje kopii všech parametrů a místních proměnných metody.
  2. Všechny jeho parametry se pak inicializují na zadané hodnoty a všechny jeho místní proměnné na výchozí hodnoty jejich typů.
  3. Implicitní místní proměnná se také inicializuje jako aktuální proměnná iterátoru, jejíž typ je T a jehož počáteční hodnota je výchozím typem.
  4. Řídicí bod instance metody je pak nastaven na prvním příkazu těla metody.
  5. Objekt iterátoru se pak vytvoří, přidružený k této instanci metody. Objekt iterátoru implementuje deklarovaný návratový typ a má chování, jak je popsáno níže.
  6. Tok řízení se pak okamžitě obnoví v volajícím a výsledkem vyvolání je objekt iterátoru. Všimněte si, že tento přenos se provádí bez ukončení instance metody iterátoru a nezpůsobí, že se nakonec obslužné rutiny spustí. Instance metody je stále odkazována objektem iterátoru a nebude uvolňování paměti, pokud existuje živý odkaz na objekt iterátoru.

Při přístupu k vlastnosti objektu Current iterátoru se vrátí aktuální proměnná vyvolání.

Když je vyvolána metoda iterátoru objektu MoveNext , vyvolání nevytvoří novou instanci metody. Místo toho se používá existující instance metody (a její řídicí bod a místní proměnné a parametry) – instance, která byla vytvořena při prvním vyvolání metody iterátoru. Tok řízení pokračuje v provádění v řídicím bodě této instance metody a pokračuje tělem metody iterátoru jako obvykle.

Při vyvolání metody objektu iterátoru Dispose se znovu použije existující instance metody. Tok řízení se obnoví v řídicím bodě této instance metody, ale okamžitě se chová, jako by Exit Function příkaz byl další operací.

Výše uvedené popisy chování pro vyvolání MoveNext objektu iterátoru nebo Dispose objektu iterátoru se vztahují pouze v případě, že se již všechny předchozí vyvolání objektu MoveNextDispose iterátoru nebo na něm již vrátily svým volajícím. Pokud ne, chování není definováno.

Když tok řízení ukončí tělo metody iterátoru normálně – prostřednictvím dosažení End Function této značky jeho konce nebo prostřednictvím explicitního Return nebo Exit příkazu – musí to provést v kontextu vyvolání MoveNext nebo Dispose funkce v objektu iterátoru obnovit instanci metody iterátoru a bude používat instanci metody, která byla vytvořena při prvním vyvolání metody iterátoru. Řídicí bod této instance je ponechán v End Function příkazu a tok řízení se obnoví v volajícím. Pokud bylo voláním MoveNext obnoveno, vrátí se volajícímu hodnota False .

Když tok řízení ukončí tělo metody iterátoru prostřednictvím neošetřené výjimky, pak se výjimka rozšíří do volajícího, který bude znovu vyvolána MoveNext nebo z Dispose.

Co se týče ostatních možných návratových typů funkce iterátoru,

  • Když je vyvolána metoda iterátoru, jejíž návratový typ je IEnumerable(Of T) pro některé T, instance je nejprve vytvořena -- specifická pro toto vyvolání metody iterátoru - všech parametrů v metodě a jsou inicializovány zadanými hodnotami. Výsledkem vyvolání je objekt, který implementuje návratový typ. Pokud by byla volána metoda tohoto objektu GetEnumerator , vytvoří instanci specifickou pro toto vyvolání GetEnumerator -- všech parametrů a místních proměnných v metodě. Inicializuje parametry na hodnoty, které jsou již uloženy, a pokračuje jako pro metodu iterátoru výše.
  • Když je vyvolána metoda iterátoru, jejíž návratový typ je ne generické rozhraní IEnumerator, chování je přesně tak jako IEnumerator(Of Object).
  • Když je vyvolána metoda iterátoru, jejíž návratový typ je ne generické rozhraní IEnumerable, chování je přesně tak jako IEnumerable(Of Object).

Asynchronní metody

Asynchronní metody představují pohodlný způsob, jak provádět dlouhotrvající práci, aniž byste například blokovali uživatelské rozhraní aplikace. Asynchronní je zkratka pro Asynchronní – znamená to, že volající asynchronní metody obnoví jeho provádění okamžitě, ale případné dokončení instance asynchronní metody může proběhnout později v budoucnu. Asynchronní metody konvence mají název s příponou "Async".

Async Function TestAsync() As Task(Of String)
    Console.WriteLine("hello")
    Await Task.Delay(100)
    Return "world"
End Function

Dim t = TestAsync()         ' prints "hello"
Console.WriteLine(Await t)  ' prints "world"

Poznámka. Asynchronní metody se nespouštějí ve vlákně na pozadí. Místo toho umožňují metodu pozastavit se prostřednictvím operátoru Await a naplánovat, aby se obnovila v reakci na určitou událost.

Při vyvolání asynchronní metody

  1. První instance asynchronní metody je vytvořena specifická pro toto vyvolání. Tato instance zahrnuje kopii všech parametrů a místních proměnných metody.
  2. Všechny jeho parametry se pak inicializují na zadané hodnoty a všechny jeho místní proměnné na výchozí hodnoty jejich typů.
  3. V případě asynchronní metody s návratovým typem Task(Of T) pro některé T, implicitní místní proměnná je také inicializována nazývá návratová proměnná úkolu, jehož typ je T a jehož počáteční hodnota je výchozí hodnota T.
  4. Pokud asynchronní metoda je návratový Function typ Task nebo Task(Of T) pro některé T, pak objekt tohoto typu implicitně vytvořen, přidružený k aktuálnímu vyvolání. To se nazývá asynchronní objekt a představuje budoucí práci, která bude provedena spuštěním instance asynchronní metody. Když ovládací prvek obnoví v volající instanci této asynchronní metody, volající obdrží tento asynchronní objekt jako výsledek jeho vyvolání.
  5. Řídicí bod instance se pak nastaví na první příkaz těla asynchronní metody a okamžitě začne spouštět tělo metody odsud ( Section Blocks and Labels).

Resumption Delegate and Current Caller

Jak je podrobně popsáno v oddílu Await – operátor, má spuštění Await výrazu schopnost pozastavit řídicí bod instance metody, aby se tok řízení dostal jinam. Tok řízení může později pokračovat v řídicím bodu stejné instance prostřednictvím vyvolání delegáta obnovení. Všimněte si, že toto pozastavení se provádí bez ukončení asynchronní metody a nezpůsobí, že se nakonec obslužné rutiny spustí. Na instanci metody stále odkazuje jak delegát obnovení, Task tak Task(Of T) i výsledek (pokud existuje) a nebudou uvolněny z paměti, pokud existuje živý odkaz na delegáta nebo výsledek.

Je užitečné si představit příkaz Dim x = Await WorkAsync() přibližně jako syntaktickou zkratku pro následující:

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

V následujícím příkladu je aktuální volající instance metody definován jako původní volající nebo volající delegát obnovení podle toho, co je novější.

Když tok řízení ukončí tělo asynchronní metody – tím, že se dostanete na End Sub konec, End Function nebo přes explicitní Return nebo Exit příkaz nebo neošetřenou výjimku – řídicí bod instance se nastaví na konec metody. Chování pak závisí na návratovém typu asynchronní metody.

  • V případě návratového Async Function typu Task:

    1. Pokud tok řízení ukončí neošetřenou výjimku, stav asynchronního objektu je nastaven TaskStatus.Faulted na a jeho Exception.InnerException vlastnost je nastavena na výjimku (s výjimkou: určité výjimky definované implementací, například OperationCanceledException změnit na TaskStatus.Canceled). Tok řízení se obnoví v aktuálním volajícím.

    2. V opačném případě je stav asynchronního objektu nastaven na TaskStatus.Completedhodnotu . Tok řízení se obnoví v aktuálním volajícím.

      (Poznámka: Celý úkol a to, co dělá asynchronní metody zajímavé, je, že když se úkol změní na Dokončeno, všechny metody, které na ni čekají, budou mít nyní spuštěné své delegáty obnovení, tj. dojde k odblokování.)

  • V případě návratového Async Function typu Task(Of T) pro některé T: chování je uvedeno výše, s tím rozdílem, že v případech, kdy není výjimka, je vlastnost asynchronního objektu Result také nastavena na konečnou hodnotu návratové proměnné úkolu.

  • V případě Async Sub:

    1. Pokud se tok řízení ukončí neošetřenou výjimkou, pak se tato výjimka rozšíří do prostředí určitým způsobem specifickým pro implementaci. Tok řízení se obnoví v aktuálním volajícím.
    2. Jinak tok řízení jednoduše obnoví v aktuálním volajícím.

Asynchronní sub

Existuje určité chování specifické pro Microsoft .Async Sub

Pokud SynchronizationContext.Current je Nothing na začátku vyvolání, všechny neošetřené výjimky z asynchronní sub se publikují do fondu vláken.

Pokud SynchronizationContext.Current není Nothing na začátku vyvolání, je OperationStarted() vyvolána v tomto kontextu před zahájením metody a OperationCompleted() po ukončení. Kromě toho se všechny neošetřené výjimky publikují, aby se v kontextu synchronizace znovu zřetědily.

To znamená, že v aplikacích uživatelského rozhraní se pro Async Sub vlákno uživatelského rozhraní vyvolá všechny výjimky, které nezvládne, se znovu odešle vlákno uživatelského rozhraní.

Proměnlivé struktury v asynchronních a iterátorových metodách

Proměnlivé struktury jsou obecně považovány za špatnou praxi a nejsou podporovány asynchronními metodami ani metodami iterátoru. Konkrétně každé vyvolání asynchronní nebo iterátorové metody ve struktuře implicitně funguje na kopii této struktury, která se zkopíruje v okamžiku vyvolání. Příklad:

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"

Bloky a popisky

Skupina spustitelných příkazů se nazývá blok příkazu. Spuštění bloku příkazu začíná prvním příkazem v bloku. Po provedení příkazu se provede další příkaz v lexikálním pořadí, pokud příkaz přenese provádění jinde nebo dojde k výjimce.

V rámci bloku příkazu není dělení příkazů na logických řádcích významné s výjimkou příkazů deklarací popisků. Popisek je identifikátor, který identifikuje určitou pozici v rámci bloku příkazu, který lze použít jako cíl příkazu větve, například GoTo.

Block
    : Statements*
    ;

LabelDeclarationStatement
    : LabelName ':'
    ;

LabelName
    : Identifier
    | IntLiteral
    ;

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

Příkazy deklarace popisku se musí objevit na začátku logického řádku a popisky mohou být identifikátorem nebo celočíselnou literálem. Vzhledem k tomu, že příkazy deklarace popisků i vyvolání můžou obsahovat jeden identifikátor, jeden identifikátor na začátku místního řádku se vždy považuje za příkaz deklarace popisku. Příkazy deklarace popisků musí vždy následovat dvojtečka, a to i v případě, že žádné příkazy následují na stejném logickém řádku.

Popisky mají vlastní prostor deklarací a neruší jiné identifikátory. Následující příklad je platný a používá proměnnou x názvu jako parametr i jako popisek.

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

Obor popisku je tělo metody, která ho obsahuje.

Z důvodu čitelnosti jsou produkce příkazů, které zahrnují více dílčích stavů, považovány za jedinou produkci v této specifikaci, i když jednotlivé dílčí stavy mohou být samy o sobě na označeném řádku.

Místní proměnné a parametry

Předchozí části podrobně uvádějí, jak a kdy jsou instance metody vytvořeny, a s nimi kopie místních proměnných a parametrů metody. Kromě toho se při každém zadání textu smyčky vytvoří nová kopie každé místní proměnné deklarované uvnitř této smyčky, jak je popsáno v příkazech smyčky section, a instance metody teď obsahuje tuto kopii místní proměnné místo předchozí kopie.

Všechny místní hodnoty se inicializují na výchozí hodnotu jejich typu. Místní proměnné a parametry jsou vždy veřejně přístupné. Jedná se o chybu odkazující na místní proměnnou v textové pozici, která předchází jeho deklaraci, jak ukazuje následující příklad:

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

Ve výše uvedené F metodě první přiřazení i konkrétně neodkazuje na pole deklarované ve vnějším oboru. Spíše odkazuje na místní proměnnou a je chybná, protože textově předchází deklaraci proměnné. G V metodě následující deklarace proměnné odkazuje na místní proměnnou deklarovanou v dřívější deklaraci proměnné v rámci stejné deklarace místní proměnné.

Každý blok v metodě vytvoří prostor deklarace pro místní proměnné. Názvy se do tohoto prostoru deklarace zavádějí prostřednictvím deklarací místních proměnných v těle metody a prostřednictvím seznamu parametrů metody, který zavádí názvy do prostoru deklarace vnějšího bloku. Bloky neumožňují stínování názvů prostřednictvím vnoření: jakmile je název deklarován v bloku, název nemusí být předefinován v žádném vnořeném bloku.

V následujícím příkladu jsou tedy F metody chybné G , protože název i je deklarován ve vnějším bloku a nelze je předefinovat ve vnitřním bloku. Tyto H metody I jsou ale platné, protože tyto dvě ijsou deklarovány v samostatných nenořených blocích.

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

Pokud je metoda funkcí, je speciální místní proměnná implicitně deklarována v prostoru deklarace metody se stejným názvem jako metoda představující návratovou hodnotu funkce. Místní proměnná má při použití ve výrazech speciální sémantiku překladu názvů. Pokud se místní proměnná používá v kontextu, který očekává výraz klasifikovaný jako skupina metod, například vyvolání výrazu, pak se název přeloží na funkci, nikoli na místní proměnnou. Například:

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

Použití závorek může způsobit nejednoznačné situace (například F(1), kde F je funkce, jejíž návratový typ je jednorozměrné pole); ve všech nejednoznačných situacích se název překládá na funkci místo místní proměnné. Například:

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

Když tok řízení opustí tělo metody, hodnota místní proměnné se předá zpět do výrazu vyvolání. Pokud je metoda podprogram, neexistuje žádná taková implicitní místní proměnná a ovládací prvek se jednoduše vrátí do vyvolání výrazu.

Příkazy místní deklarace

Příkaz místní deklarace deklaruje novou místní proměnnou, místní konstantu nebo statickou proměnnou. Místní proměnné a místní konstanty jsou ekvivalentní proměnným instancí a konstantám vymezeným na metodu a jsou deklarovány stejným způsobem. Statické proměnné jsou podobné Shared proměnným a deklarují se pomocí modifikátoru Static .

LocalDeclarationStatement
    : LocalModifier VariableDeclarators StatementTerminator
    ;

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

Statické proměnné jsou místní hodnoty, které si zachovají jejich hodnotu napříč vyvoláním metody. Statické proměnné deklarované v rámci nesdílených metod jsou pro každou instanci: každá instance typu, která obsahuje metodu, má svou vlastní kopii statické proměnné. Statické proměnné deklarované v rámci Shared metod jsou na typ; pro všechny instance existuje pouze jedna kopie statické proměnné. Zatímco místní proměnné jsou inicializovány na výchozí hodnotu jejich typu při každé položce do metody, statické proměnné se inicializují pouze na výchozí hodnotu jejich typu při inicializaci typu nebo instance typu. Statické proměnné nemusí být deklarovány ve strukturách nebo obecných metodách.

Místní proměnné, místní konstanty a statické proměnné mají vždy veřejnou přístupnost a nemusí určovat modifikátory přístupnosti. Pokud není pro příkaz místní deklarace zadán žádný typ, určují následující kroky typ místní deklarace:

  1. Pokud deklarace obsahuje znak typu, typ znaku typu je typ místní deklarace.

  2. Je-li místní deklarace místní konstanta nebo pokud místní deklarace je místní proměnná s inicializátorem a používá se odvození typu místní proměnné, typ místní deklarace je odvozen z typu inicializátoru. Pokud inicializátor odkazuje na místní deklaraci, dojde k chybě v době kompilace. (Aby místní konstanty měly inicializátory.)

  3. Pokud se nepoužívá striktní sémantika, typ příkazu místní deklarace je implicitně Object.

  4. V opačném případě dojde k chybě kompilace.

Pokud není zadán žádný typ pro příkaz místní deklarace, který má modifikátor pole nebo typ pole, pak typ místní deklarace je pole se zadaným pořadím a předchozí kroky slouží k určení typu prvku pole. Pokud se použije odvození místního typu proměnné, musí být typ inicializátoru typ pole se stejným obrazcem matice (tj. modifikátory typu pole) jako příkaz místní deklarace. Všimněte si, že odvozený typ elementu může být stále typ pole. Například:

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

Pokud není zadán žádný typ v příkazu místní deklarace, který má modifikátor typu null, pak typ místní deklarace je nullable verze odvozeného typu nebo odvozený typ sám, pokud již typ hodnoty nullable. Pokud odvozený typ není typ hodnoty, který lze provést s možnou hodnotou null, dojde k chybě kompilace. Pokud jsou modifikátor typu s možnou hodnotou null i modifikátor pole nebo modifikátor typu matice umístěny do místního příkazu deklarace bez typu, je modifikátor typu null považován za použitý pro typ prvku pole a předchozí kroky slouží k určení typu prvku.

Inicializátory proměnných u místních deklaračních příkazů jsou ekvivalentní příkazům přiřazení umístěným v textovém umístění deklarace. Proto pokud provádění větve přes příkaz místní deklarace, proměnná inicializátor se nespustí. Pokud je příkaz místní deklarace proveden více než jednou, inicializátor proměnné se spustí stejný početkrát. Statické proměnné se spouští pouze při prvním spuštění inicializátoru. Pokud při inicializaci statické proměnné dojde k výjimce, statická proměnná se považuje za inicializovanou s výchozí hodnotou typu statické proměnné.

Následující příklad ukazuje použití inicializátorů:

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

Tento program vytiskne:

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

Inicializátory statických místních prostředí jsou bezpečné pro přístup z více vláken a jsou chráněné proti výjimkám během inicializace. Pokud během statického místního inicializátoru dojde k výjimce, bude mít statický místní výchozí hodnotu a nebude inicializován. Statický místní inicializátor

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

je to ekvivalentní

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

Místní proměnné, místní konstanty a statické proměnné jsou vymezeny na blok příkazů, ve kterém jsou deklarovány. Statické proměnné jsou speciální v tom, že jejich názvy mohou být použity pouze jednou v celé metodě. Například není platné zadat dvě deklarace statických proměnných se stejným názvem, i když jsou v různých blocích.

Implicitní místní deklarace

Kromě místních deklarací příkazů lze místní proměnné deklarovat také implicitně prostřednictvím použití. Jednoduchý výraz názvu, který používá název, který se nepřekládá na něco jiného, deklaruje místní proměnnou tímto názvem. Například:

Option Explicit Off

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

Implicitní místní deklarace se vyskytuje pouze v kontextech výrazů, které mohou přijmout výraz klasifikovaný jako proměnnou. Výjimkou tohoto pravidla je, že místní proměnná nemusí být implicitně deklarována, pokud je cílem výrazu vyvolání funkce, indexování výrazu nebo výrazu přístupu člena.

Implicitní místní hodnoty se považují za deklarované na začátku metody obsahující. Proto jsou vždy vymezeny na celý text metody, i když jsou deklarovány uvnitř výrazu lambda. Například následující kód:

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

vytiskne hodnotu 10. Implicitní místní hodnoty jsou zadány tak, jako Object kdyby nebyl k názvu proměnné připojen žádný znak typu. V opačném případě je typem znaku typu. Odvození typu místní proměnné se nepoužívá pro implicitní místní hodnoty.

Pokud je explicitní místní deklarace určena prostředím kompilace nebo Option Explicitadresou , musí být všechny místní proměnné explicitně deklarovány a implicitní deklarace proměnné je zakázána.

Příkaz With

Příkaz With umožňuje více odkazů na členy výrazu bez vícenásobného zadání výrazu.

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

Výraz musí být klasifikován jako hodnota a je vyhodnocen jednou při vstupu do bloku. With V rámci bloku příkazu se výraz přístupu člena nebo výraz přístupu slovníku začínající tečkou nebo vykřičníkem vyhodnotí, jako by With výraz před ním byl. Například:

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

Větev do With bloku příkazu mimo blok není platná.

Příkaz SyncLock

Příkaz SyncLock umožňuje synchronizaci příkazů ve výrazu, což zajišťuje, že více vláken provádění nespustí stejné příkazy současně.

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

Výraz musí být klasifikován jako hodnota a je vyhodnocen jednou po zadání do bloku. Při zadávání SyncLock bloku je Shared volána metoda System.Threading.Monitor.Enter pro zadaný výraz, který blokuje, dokud vlákno spuštění nemá výhradní zámek objektu vráceného výrazem. Typ výrazu v SyncLock příkazu musí být odkazovým typem. Například:

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

Výše uvedený příklad se synchronizuje s konkrétní instancí třídy Test , aby se zajistilo, že pro určitou instanci nemůže přičíst nebo odečíst více než jedno vlákno provádění.

Blok SyncLock je implicitně obsažen příkazem Try , jehož Finally blok volá metodu SharedSystem.Threading.Monitor.Exit ve výrazu. Tím se zajistí, že se zámek uvolní i v případě, že dojde k vyvolání výjimky. V důsledku toho je neplatné vytvořit větev do SyncLock bloku mimo blok a SyncLock blok je považován za jediný příkaz pro účely Resume a Resume Next. Výše uvedený příklad odpovídá následujícímu kódu:

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

Příkazy událostí

Příkazy RaiseEventa příkazy AddHandlerRemoveHandler vyvolávají události a zpracovávají události dynamicky.

EventStatement
    : RaiseEventStatement
    | AddHandlerStatement
    | RemoveHandlerStatement
    ;

RaiseEvent – příkaz

Příkaz RaiseEvent upozorní obslužné rutiny událostí, že došlo k určité události.

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

Jednoduchý výraz názvu v RaiseEvent příkazu je interpretován jako vyhledávání člena .Me Proto je interpretován tak, RaiseEvent x jako by to bylo RaiseEvent Me.x. Výsledek výrazu musí být klasifikován jako přístup k události pro událost definovanou v samotné třídě; události definované na základních typech nelze v RaiseEvent příkazu použít.

Příkaz RaiseEvent se zpracuje jako volání Invoke metody delegáta události pomocí zadaných parametrů, pokud existuje. Pokud je Nothinghodnota delegáta , není vyvolán žádná výjimka. Pokud neexistují žádné argumenty, mohou být závorky vynechány. Například:

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

Výše uvedená třída Raiser odpovídá:

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 a RemoveHandler – příkazy

I když se většina obslužných rutin událostí automaticky připojí prostřednictvím WithEvents proměnných, může být nutné dynamicky přidávat a odebírat obslužné rutiny událostí za běhu. AddHandler a RemoveHandler příkazy to dělají.

AddHandlerStatement
    : 'AddHandler' Expression Comma Expression StatementTerminator
    ;

RemoveHandlerStatement
    : 'RemoveHandler' Expression Comma Expression StatementTerminator
    ;

Každý příkaz má dva argumenty: první argument musí být výraz klasifikovaný jako přístup k události a druhý argument musí být výraz klasifikovaný jako hodnota. Druhým typem argumentu musí být typ delegáta přidružený k přístupu k události. Například:

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

Vzhledem k události E, příkaz volá relevantní add_E nebo remove_E metodu instance přidat nebo odebrat delegáta jako obslužnou rutinu události. Výše uvedený kód je tedy ekvivalentní:

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

Příkazy přiřazení

Příkaz přiřazení přiřadí hodnotu výrazu proměnné. Existuje několik typů přiřazení.

AssignmentStatement
    : RegularAssignmentStatement
    | CompoundAssignmentStatement
    | MidAssignmentStatement
    ;

Regulární příkazy přiřazení

Jednoduchý příkaz přiřazení ukládá výsledek výrazu do proměnné.

RegularAssignmentStatement
    : Expression Equals Expression StatementTerminator
    ;

Výraz na levé straně operátoru přiřazení musí být klasifikován jako proměnná nebo přístup k vlastnosti, zatímco výraz na pravé straně operátoru přiřazení musí být klasifikován jako hodnota. Typ výrazu musí být implicitně konvertibilní na typ proměnné nebo přístupu k vlastnosti.

Pokud je proměnná přiřazená do prvku pole typu odkazu, provede se kontrola doby běhu, která zajistí, aby byl výraz kompatibilní s typem prvku pole. V následujícím příkladu poslední přiřazení způsobí System.ArrayTypeMismatchException vyvolání, protože instance ArrayList nemůže být uložena v prvku String pole.

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.

Pokud je výraz na levé straně operátoru přiřazení klasifikován jako proměnná, příkaz přiřazení uloží hodnotu do proměnné. Pokud je výraz klasifikovaný jako přístup k vlastnosti, příkaz přiřazení změní přístup vlastnosti na vyvolání Set přístupového objektu vlastnosti s hodnotou nahrazenou parametrem value. Například:

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

Pokud je cíl přístupu k proměnné nebo vlastnosti zadán jako typ hodnoty, ale není klasifikován jako proměnná, dojde k chybě v době kompilace. Například:

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

Všimněte si, že sémantika přiřazení závisí na typu proměnné nebo vlastnosti, ke které se přiřazuje. Pokud je proměnná, ke které je přiřazena, typ hodnoty, přiřazení zkopíruje hodnotu výrazu do proměnné. Pokud je proměnná, ke které se přiřazuje, referenční typ, přiřazení zkopíruje odkaz, nikoli samotnou hodnotu, do proměnné. Pokud je Objecttyp proměnné , je sémantika přiřazení určena, zda je typ hodnoty typ hodnoty nebo referenční typ za běhu.

Poznámka. U vnitřních typů, jako Integer jsou a Date, odkaz a sémantika přiřazení hodnot jsou stejné, protože typy jsou neměnné. V důsledku toho je jazyk zdarma používat referenční přiřazení u krabicových vnitřních typů jako optimalizace. Z hlediska hodnoty je výsledek stejný.

Vzhledem k tomu, že znak rovná se (=) se používá jak pro přiřazení, tak pro rovnost, existuje nejednoznačnost mezi jednoduchým přiřazením a vyvoláním příkazu v situacích, jako x = y.ToString()je například . Ve všech takových případech má příkaz přiřazení přednost před operátorem rovnosti. To znamená, že ukázkový výraz je interpretován spíše než x = (y.ToString())(x = y).ToString().

Složené příkazy přiřazení

Složený příkaz přiřazení má tvar V op= E (kde op je platný binární operátor).

CompoundAssignmentStatement
    : Expression CompoundBinaryOperator LineTerminator? Expression StatementTerminator
    ;

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

Výraz na levé straně operátoru přiřazení musí být klasifikován jako přístup k proměnné nebo vlastnosti, zatímco výraz na pravé straně operátoru přiřazení musí být klasifikován jako hodnota. Složený příkaz přiřazení je ekvivalentní příkazu V = V op E s rozdílem, že proměnná na levé straně operátoru složeného přiřazení je vyhodnocena pouze jednou. Následující příklad ukazuje tento rozdíl:

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

Výraz a(GetIndex()) se vyhodnotí dvakrát pro jednoduché přiřazení, ale pouze jednou pro složené přiřazení, takže kód se vytiskne:

Simple assignment
Getting index
Getting index
Compound assignment
Getting index

Příkaz Mid Assignment

Příkaz Mid přiřazení přiřadí řetězec k jinému řetězci. Levá strana přiřazení má stejnou syntaxi jako volání funkce Microsoft.VisualBasic.Strings.Mid.

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

Prvním argumentem je cíl přiřazení a musí být klasifikován jako proměnná nebo přístup k vlastnosti, jehož typ je implicitně převoditelný na a z String. Druhý parametr je počáteční pozice založená na 1, která odpovídá místu, kde má přiřazení začínat v cílovém řetězci a musí být klasifikováno jako hodnota, jejíž typ musí být implicitně konvertibilní na Integer. Volitelný třetí parametr je počet znaků z hodnoty pravé strany, která se má přiřadit k cílovému řetězci, a musí být klasifikována jako hodnota, jejíž typ je implicitně převoditelný na Integer. Pravá strana je zdrojový řetězec a musí být klasifikována jako hodnota, jejíž typ je implicitně konvertibilní na String. Pravá strana se zkrátí na parametr délky, pokud je zadána, a nahradí znaky v řetězci na levé straně počínaje počáteční pozicí. Pokud řetězec pravé strany obsahoval méně znaků než třetí parametr, zkopírují se pouze znaky z pravého řetězce.

Následující příklad zobrazí 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

Poznámka. Mid není rezervované slovo.

Vyvolání příkazů

Vyvolání příkazu vyvolá metodu, která předchází volitelnému klíčovému slovu Call. Vyvolání příkazu se zpracuje stejným způsobem jako výraz vyvolání funkce s některými rozdíly uvedenými níže. Výraz vyvolání musí být klasifikován jako hodnota nebo void. Jakákoli hodnota vyplývající z vyhodnocení výrazu vyvolání se zahodí.

Call Pokud je klíčové slovo vynecháno, musí výraz vyvolání začínat identifikátorem nebo klíčovým slovem . nebo uvnitř With bloku. Proto například "Call 1.ToString()" je platný příkaz, ale1.ToString() "" není. (Všimněte si, že v kontextu výrazu nemusí vyvolání výrazů začínat identifikátorem. Například "Dim x = 1.ToString()" je platný příkaz).

Mezi příkazy volání a vyvoláním výrazů existuje další rozdíl: pokud příkaz vyvolání obsahuje seznam argumentů, pak se vždy považuje za seznam argumentů vyvolání. Následující příklad znázorňuje rozdíl:

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
    ;

Podmíněné příkazy

Podmíněné příkazy umožňují podmíněné spouštění příkazů na základě výrazů vyhodnocených za běhu.

ConditionalStatement
    : IfStatement
    | SelectStatement
    ;

Když... Potom... Příkazy Else

Příkaz If...Then...Else je základní podmíněný příkaz.

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

Každý výraz v If...Then...Else příkazu musí být logický výraz, jak je uvedeno v logických výrazech oddílu. (Poznámka: Tento výraz nevyžaduje, aby měl logický typ). Pokud je výraz v If příkazu pravdivý, příkazy uzavřené blokem If se spustí. Pokud je výraz nepravda, vyhodnotí se každý výraz ElseIf . Pokud se některý z ElseIf výrazů vyhodnotí jako true, provede se odpovídající blok. Pokud se žádný výraz nevyhodnotí jako pravdivý a blok existuje Else , Else provede se blok. Po dokončení provádění bloku se provádění předá na konec If...Then...Else příkazu.

Verze řádku If příkazu má jednu sadu příkazů, které se mají spustit, pokud If je True výraz a volitelná sada příkazů, které se mají spustit, pokud je Falsevýraz . Například:

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

Verze řádku příkazu If je vázána méně těsně než ":" a její Else vazby na lexicky nejbližší předcházející If , které je povoleno syntaxí. Například následující dvě verze jsou ekvivalentní:

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

Všechny příkazy kromě příkazů deklarací popisků jsou povoleny uvnitř příkazu řádku If , včetně příkazů bloku. Nemusí však používat LineTerminators jako StatementTerminators s výjimkou výrazů lambda s více řádky. Například:

' 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 – příkazy

Příkaz Select Case spouští příkazy na základě hodnoty výrazu.

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

Výraz musí být klasifikován jako hodnota. Select Case Při spuštění Select příkazu se výraz vyhodnotí jako první a Case příkazy se pak vyhodnocují v pořadí od textové deklarace. První Case příkaz, který se vyhodnotí jako True , má spuštěný blok. Pokud se žádný Case příkaz nevyhodnotí True a existuje příkaz Case Else , spustí se tento blok. Po dokončení provádění bloku se provádění předá na konec Select příkazu.

Case Spuštění bloku není povoleno "projít" do dalšího oddílu přepínače. To zabraňuje běžné třídě chyb, ke kterým dochází v jiných jazycích, když Case je ukončovací příkaz omylem vynechán. Následující příklad ukazuje toto chování:

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

Kód se vytiskne:

x = 10

I když Case 10 a Case 20 - 10 vyberte stejnou hodnotu, spustí se, Case 10 protože předchází Case 20 - 10 textově. Jakmile dojde k dalšímu CaseSelect dosažení, provádění pokračuje po příkazu.

Case Klauzule může mít dvě formy. Jeden formulář je volitelné Is klíčové slovo, relační operátor a výraz. Výraz se převede na typ výrazu Select . Pokud se výraz implicitně nepřevádí na typ výrazu Select , dojde k chybě v době kompilace. Select Pokud je výraz E, relační operátor je Op a Case výraz je E1, případ se vyhodnotí jako E OP E1. Operátor musí být platný pro typy dvou výrazů; jinak dojde k chybě v době kompilace.

Druhý formulář je výraz volitelně následovaný klíčovým slovem To a druhým výrazem. Oba výrazy se převedou na typ výrazu Select . Pokud některý z výrazů není implicitně konvertibilní na typ výrazu Select , dojde k chybě v době kompilace. Select Pokud je Evýraz , první Case výraz je E1a druhý Case výraz je E2, Case je vyhodnocen buď jako E = E1 (pokud není zadán) E2 nebo (E >= E1) And (E <= E2). Operátory musí být platné pro typy těchto dvou výrazů; jinak dojde k chybě v době kompilace.

Příkazy smyčky

Příkazy smyčky umožňují opakované spuštění příkazů v těle.

LoopStatement
    : WhileStatement
    | DoLoopStatement
    | ForStatement
    | ForEachStatement
    ;

Při každém zadání textu smyčky se nová kopie vytvoří ze všech místních proměnných deklarovaných v těle, inicializované na předchozí hodnoty proměnných. Všechny odkazy na proměnnou v těle smyčky budou používat naposledy vytvořené kopie. Tento kód ukazuje příklad:

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

Kód vytvoří výstup:

31    32    33

Při spuštění těla smyčky se použije jakákoli kopie proměnné, která je aktuální. Například příkaz Dim y = x odkazuje na nejnovější kopii y a původní kopii x. A když se vytvoří lambda, pamatuje si, která kopie proměnné byla aktuální v době, kdy byla vytvořena. Proto každá lambda používá stejnou sdílenou kopii x, ale jinou kopii y. Na konci programu, když spustí lambda, že sdílená kopie x , které všechny odkazují, je nyní na své konečné hodnotě 3.

Všimněte si, že pokud neexistují žádné výrazy lambda nebo LINQ, není možné vědět, že při zadávání smyčky se vytvoří nová kopie. Optimalizace kompilátoru se tedy v tomto případě nebudou vytvářet kopie. Všimněte si také, že není možné přejít do GoTo smyčky, která obsahuje výrazy lambda nebo LINQ.

Zatímco... Ukončit while a do... Příkazy smyčky

Smyčky While příkazů nebo Do smyčky založené na logickém výrazu.

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

Smyčka While příkazů smyčky se smyček za předpokladu, že logický výraz vyhodnotí jako pravdivý. Do Příkaz smyčky může obsahovat složitější podmínku. Výraz může být umístěn za Do klíčové slovo nebo za Loop klíčové slovo, ale ne za obojí. Logický výraz se vyhodnotí jako logický výraz oddílu. (Poznámka: Tento výraz nevyžaduje, aby měl logický typ). Je také platné zadat žádný výraz vůbec; v takovém případě smyčka nikdy neodejde. Pokud je výraz umístěn za Do, bude vyhodnocen před spuštěním bloku smyčky v každé iteraci. Pokud je výraz umístěn za Loop, bude vyhodnocen po spuštění bloku smyčky pro každou iteraci. Umístění výrazu za Loop vygeneruje jednu více smyček než umístění za Do. Následující příklad ukazuje toto chování:

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

Kód vytvoří výstup:

Second Loop

V případě první smyčky se podmínka vyhodnotí před spuštěním smyčky. V případě druhé smyčky se podmínka spustí po spuštění smyčky. Podmíněný výraz musí být předponou klíčového While slova nebo klíčového Until slova. První přeruší smyčku, pokud se podmínka vyhodnotí jako nepravda, druhá, když se podmínka vyhodnotí jako true.

Poznámka. Until není rezervované slovo.

Pro... Další příkazy

Smyčka For...Next příkazů založená na sadě hranic. Příkaz For určuje proměnnou ovládacího prvku smyčky, výraz s dolní vazbou, výraz s horní vazbou a volitelný výraz hodnoty kroku. Řídicí proměnná smyčky je určena buď prostřednictvím identifikátoru následovaného volitelnou As klauzulí nebo výrazem.

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

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

NextExpressionList
    : Expression ( Comma Expression )*
    ;

Podle následujících pravidel řídicí proměnná smyčky odkazuje buď na novou místní proměnnou specifickou pro tento For...Next příkaz, nebo na existující proměnnou nebo na výraz.

  • Pokud je řídicí proměnná smyčky identifikátor s As klauzulí, identifikátor definuje novou místní proměnnou typu zadaného v As klauzuli, která je vymezena na celou For smyčku.

  • Pokud je řídicí proměnná smyčky identifikátor bez As klauzule, identifikátor se nejprve přeloží pomocí jednoduchých pravidel překladu názvů (viz Výrazy jednoduchého názvu oddílu), s tím rozdílem, že tento výskyt identifikátoru by nebyl a sám o sobě způsobil vytvoření implicitní místní proměnné ( implicitní místní deklarace oddílu).

    • Pokud toto řešení proběhne úspěšně a výsledek se klasifikuje jako proměnná, je řídicí proměnná smyčky taková, která už existuje.

    • Pokud se řešení nezdaří nebo pokud je řešení úspěšné a výsledek se klasifikuje jako typ, postupujte takto:

      • pokud se používá odvození typu místní proměnné, identifikátor definuje novou místní proměnnou, jejíž typ je odvozen z vázaných a krokových výrazů, vymezený na celou For smyčku;
      • pokud se nepoužívá odvození typu místní proměnné, ale implicitní místní deklarace je, vytvoří se implicitní místní proměnná, jejíž obor je celá metoda (Section Implicit Local Declarations) a řídicí proměnná smyčky odkazuje na tuto před existující proměnnou;
      • Pokud se nepoužívá odvození typu místní proměnné ani implicitní místní deklarace, jedná se o chybu.
    • Pokud je řešení úspěšné s něčím klasifikovaným jako typ ani proměnnou, jedná se o chybu.

  • Pokud je řídicí proměnná smyčky výrazem, musí být výraz klasifikován jako proměnná.

Řídicí proměnnou smyčky nelze použít jiným nadřazeným příkazem For...Next . Typ řídicí proměnné For smyčky příkazu určuje typ iterace a musí být jedním z:

  • Byte, SByte, , UShort, UIntegerDecimalShortIntegerULongLong, SingleDouble
  • Výčtový typ
  • Object
  • Typ T , který má následující operátory, kde B je typ, který lze použít v logickém výrazu:
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

Vázané a krokové výrazy musí být implicitně konvertibilní na typ řídicí proměnné smyčky a musí být klasifikovány jako hodnoty. V době kompilace je typ řídicí proměnné smyčky odvozen výběrem nejširšího typu mezi dolní mez, horní mez a typy výrazů kroku. Pokud mezi dvěma typy neexistuje rozšiřující převod, dojde k chybě v době kompilace.

Pokud je Objecttyp řídicí proměnné smyčky v době běhu , je typ iterace odvozen stejně jako v době kompilace se dvěma výjimkami. Nejprve platí, že pokud jsou vázané a krokové výrazy všechny celočíselné typy, ale nemají žádný široký typ, bude odvozen nejširší typ, který zahrnuje všechny tři typy. A za druhé, pokud je typ řídicí proměnné smyčky odvozen, Stringbude odvozen místo toho Double . Pokud nelze v době běhu určit žádný typ ovládacího prvku smyčky nebo pokud některý z výrazů nelze převést na typ ovládacího prvku smyčky, System.InvalidCastException dojde k chybě. Jakmile se na začátku smyčky vybere typ ovládacího prvku smyčky, použije se stejný typ v průběhu iterace bez ohledu na změny hodnoty v řídicí proměnné smyčky.

Příkaz For musí být uzavřen odpovídajícím Next příkazem. Příkaz Next bez proměnné odpovídá nejvnitřnějšímu otevřenému For příkazu, zatímco Next příkaz s jednou nebo více řídicími proměnnými smyčky bude zleva doprava odpovídat For smyčkám, které odpovídají jednotlivým proměnným. Pokud proměnná odpovídá smyčce For , která není v tomto okamžiku nejvíce vnořenou smyčkou, dojde k chybě kompilace.

Na začátku smyčky se tři výrazy vyhodnocují v textovém pořadí a dolní mez výrazu je přiřazena k řídicí proměnné smyčky. Pokud je hodnota kroku vynechána, je implicitně literál 1převeden na typ řídicí proměnné smyčky. Tři výrazy se vyhodnocují pouze na začátku smyčky.

Na začátku každé smyčky se řídicí proměnná porovná a zjistí, jestli je větší než koncový bod, pokud je výraz kroku kladný nebo menší než koncový bod, pokud je výraz kroku záporný. Pokud ano, smyčka For se ukončí, jinak se blok smyčky spustí. Pokud řídicí proměnná smyčky není primitivním typem, je relační operátor určen, zda je výraz step >= step - step pravdivý nebo nepravda. Next V příkazu se hodnota kroku přidá do řídicí proměnné a provádění se vrátí na začátek smyčky.

Všimněte si, že při každé iteraci bloku smyčky se nevytvořila nová kopie řídicí proměnné smyčky. V tomto ohledu For se prohlášení liší od For Each (oddíl pro každý... Další příkazy).

Větev do smyčky mimo smyčku For není platná.

Pro každý... Další příkazy

Smyčka For Each...Next příkazů založená na prvcích ve výrazu. Příkaz For Each určuje řídicí proměnnou smyčky a výraz enumerátoru. Řídicí proměnná smyčky je určena buď prostřednictvím identifikátoru následovaného volitelnou As klauzulí nebo výrazem.

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

Následují stejná pravidla jako For...Next příkazy (oddíl for... Další příkazy), řídicí proměnná smyčky odkazuje buď na novou místní proměnnou specifickou pro tuto pro každý... Další příkaz nebo před existující proměnnou nebo výraz.

Výraz enumerátoru musí být klasifikován jako hodnota a jeho typ musí být typ kolekce nebo Object. Pokud je Objecttyp výrazu enumerátoru , veškeré zpracování je odloženo do doby běhu. Jinak musí existovat převod z typu prvku kolekce na typ řídicí proměnné smyčky.

Řídicí proměnnou smyčky nelze použít jiným uzavřeným příkazem For Each . Příkaz For Each musí být uzavřen odpovídajícím Next příkazem. Příkaz Next bez řídicí proměnné smyčky odpovídá nejvnitřnější otevřené For Eachproměnné . Příkaz Next s jednou nebo více řídicími proměnnými smyčky bude zleva doprava odpovídat For Each smyčkám, které mají stejnou řídicí proměnnou smyčky. Pokud proměnná odpovídá smyčce For Each , která není v tomto okamžiku nejvíce vnořenou smyčkou, dojde k chybě v době kompilace.

C Typ se říká, že se jedná o typ kolekce, pokud jeden z těchto typů:

  • Platí všechny následující skutečnosti:

    • C obsahuje přístupnou instanci, sdílenou metodu nebo metodu rozšíření s podpisem GetEnumerator() , který vrací typ E.
    • E obsahuje přístupnou instanci, sdílenou metodu nebo metodu rozšíření s podpisem MoveNext() a návratovým typem Boolean.
    • E obsahuje přístupnou instanci nebo sdílenou vlastnost s názvem Current getter. Typ této vlastnosti je typ elementu typu kolekce.
  • Implementuje rozhraní System.Collections.Generic.IEnumerable(Of T), v takovém případě typ prvku kolekce je považován za T.

  • Implementuje rozhraní System.Collections.IEnumerable, v takovém případě typ prvku kolekce je považován za Object.

Následuje příklad třídy, která se dá vyčíslit:

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

Před zahájením smyčky se vyhodnotí výraz enumerátoru. Pokud typ výrazu nevyhovuje vzoru návrhu, přetypuje se výraz na System.Collections.IEnumerable nebo System.Collections.Generic.IEnumerable(Of T). Pokud typ výrazu implementuje obecné rozhraní, je obecné rozhraní upřednostňované v době kompilace, ale ne generické rozhraní je upřednostňované za běhu. Pokud typ výrazu implementuje obecné rozhraní vícekrát, příkaz je považován za nejednoznačný a dojde k chybě kompilace.

Poznámka. Ne generické rozhraní je upřednostňované v pozdním vázaném případě, protože výběr obecného rozhraní by znamenal, že všechna volání metod rozhraní by zahrnovala parametry typu. Vzhledem k tomu, že není možné znát odpovídající argumenty typu za běhu, musí být všechna taková volání provedena pomocí pozdních volání. To by bylo pomalejší než volání jiného než obecné rozhraní, protože jiné než obecné rozhraní by mohlo být volána pomocí volání v době kompilace.

GetEnumerator se volá na výslednou hodnotu a návratová hodnota funkce je uložena v dočasném stavu. Na začátku každé iterace MoveNext se pak volá na dočasné. Pokud se vrátí False, smyčka se ukončí. V opačném případě se každá iterace smyčky provede takto:

  1. Pokud řídicí proměnná smyčky identifikovala novou místní proměnnou (nikoli existující proměnnou), vytvoří se nová kopie této místní proměnné. U aktuální iterace budou všechny odkazy v bloku smyčky odkazovat na tuto kopii.
  2. Vlastnost Current se načte, převede na typ řídicí proměnné smyčky (bez ohledu na to, zda je převod implicitní nebo explicitní) a přiřazen k řídicí proměnné smyčky.
  3. Blok smyčky se spustí.

Poznámka. Mezi verzí 10.0 a 11.0 jazyka se mírně mění chování. Před verzí 11.0 se pro každou iteraci smyčky nevytvořila nová iterační proměnná. Tento rozdíl je pozorovatelný pouze v případě, že je proměnná iterace zachycena lambda nebo výrazEM LINQ, který se pak vyvolá po smyčce:

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

Až do jazyka Visual Basic 10.0 se vygeneroval upozornění při kompilaci a třikrát se vytisklo "3". Důvodem bylo, že všechny iterace smyčky sdílely pouze jednu proměnnou "x" a všechny tři výrazy lambda zachytily stejné "x" a v době, kdy se lambdas spustily, pak drželo číslo 3. Od verze Visual Basic 11.0 se vytiskne 1, 2, 3. Je to proto, že každá lambda zachycuje jinou proměnnou "x".

Poznámka. Aktuální prvek iterace je převeden na typ řídicí proměnné smyčky, i když převod je explicitní, protože neexistuje vhodné místo pro zavedení operátoru převodu v příkazu. To se stalo obzvláště problematické při práci s nyní zastaralým typem System.Collections.ArrayList, protože jeho typ prvku je Object. To by vyžadovalo přetypování ve velké řadě smyček, něco, co jsme cítili nebyli ideální. Je ironií, že generické typy umožnily vytvoření kolekce silného typu, System.Collections.Generic.List(Of T)která nám mohla změnit návrhový bod, ale kvůli kompatibilitě to teď nejde změnit.

Next Po dosažení příkazu se spuštění vrátí na začátek smyčky. Pokud je proměnná za klíčovým slovem Next zadaná, musí být stejná jako první proměnná za hodnotou For Each. Představte si například následující kód:

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

Je ekvivalentní následujícímu kódu:

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

Pokud typ E enumerátoru implementuje System.IDisposable, enumerátor je uvolněn při ukončení smyčky voláním Dispose metody. Tím zajistíte, že budou uvolněny prostředky, které jsou uloženy enumerátorem. Pokud metoda obsahující For Each příkaz nepoužívá nestrukturované zpracování chyb, příkaz For Each se zabalí do Try příkazu s metodou Dispose volanou v Finally rámci vyčištění.

Poznámka. Typ System.Array je typ kolekce a vzhledem k tomu, že všechny typy pole jsou odvozeny od System.Array, je v příkazu povolen For Each libovolný výraz typu pole. U jednorozměrných polí příkaz vyčísluje For Each prvky pole ve vzestupném pořadí indexu, počínaje indexem 0 a končí indexem Length - 1. U multidimenzionálních polí se nejprve zvýší indexy pravé dimenze.

Například následující kód vytiskne 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

Větev do For Each bloku příkazu mimo blok není platná.

příkazy Exception-Handling

Visual Basic podporuje strukturované zpracování výjimek a nestrukturované zpracování výjimek. V metodě lze použít pouze jeden styl zpracování výjimek, ale Error příkaz lze použít ve strukturovaném zpracování výjimek. Pokud metoda používá oba styly zpracování výjimek, výsledky chyby v době kompilace.

ErrorHandlingStatement
    : StructuredErrorStatement
    | UnstructuredErrorStatement
    ;

Příkazy strukturovaného Exception-Handling

Strukturované zpracování výjimek je metoda zpracování chyb deklarováním explicitních bloků, ve kterých se budou zpracovávat určité výjimky. Strukturované zpracování výjimek se provádí prostřednictvím Try příkazu.

StructuredErrorStatement
    : ThrowStatement
    | TryStatement
    ;

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

Například:

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

Příkaz Try se skládá ze tří druhů bloků: try blocks, catch blocks a finally blocks. Blok try je blok příkazu, který obsahuje příkazy, které se mají spustit. Blok catch je blok příkazu, který zpracovává výjimku. Konečně blok je blok příkazu, který obsahuje příkazy, které se mají spustit při Try ukončení příkazu, bez ohledu na to, jestli došlo k výjimce a byla zpracována. Příkaz Try , který může obsahovat pouze jeden blok try a jeden blok finally, musí obsahovat alespoň jeden blok catch nebo nakonec blok. Není platné explicitně přenést spuštění do bloku try s výjimkou bloku catch ve stejném příkazu.

Konečné bloky

Finally Blok se vždy spustí, když provádění opustí jakoukoli část Try příkazu. K provedení Finally bloku není nutná žádná explicitní akce. Když spuštění opustí Try příkaz, systém automaticky spustí Finally blok a pak přenese spuštění do zamýšleného cíle. Blok Finally se spustí bez ohledu na to, jak provádění příkaz opustí Try : až na konec Try bloku, přes konec Catch bloku, příkazem Exit Try , příkazem GoTo nebo nezpracováním vyvolané výjimky.

Všimněte si, že Await výraz v asynchronní metodě a Yield příkaz v metodě iterátoru může způsobit pozastavení toku řízení v instanci asynchronní nebo iterátorové metody a obnovení v některé jiné instanci metody. Jedná se však pouze o pozastavení provádění a nezahrnuje ukončení příslušné asynchronní metody nebo instance metody iterátoru, a proto nezpůsobí Finally spuštění bloků.

Explicitní přenos spuštění do Finally bloku není platný. Je také neplatný pro přenos z Finally bloku s výjimkou výjimky.

FinallyStatement
    : 'Finally' StatementTerminator
      Block?
    ;

Zachytávání bloků

Pokud při zpracování Try bloku dojde k výjimce, je každý Catch příkaz zkoumán v textovém pořadí, aby se zjistilo, zda zpracovává výjimku.

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

Identifikátor zadaný v Catch klauzuli představuje výjimku, která byla vyvolána. Pokud identifikátor obsahuje As klauzuli, považuje se identifikátor za deklarovaný v Catch místním prostoru deklarace bloku. Jinak musí být identifikátor místní proměnnou (nikoli statickou proměnnou), která byla definována v bloku obsahujícím.

Klauzule Catch bez identifikátoru zachytí všechny výjimky odvozené z System.Exception. Catch Klauzule s identifikátorem zachytí pouze výjimky, jejichž typy jsou stejné jako nebo odvozené od typu identifikátoru. Typ musí být System.Exceptionnebo typ odvozený z System.Exception. Je-li zachycena výjimka, která je odvozena z System.Exception, odkaz na objekt výjimky je uložen v objektu vrácené funkcí Microsoft.VisualBasic.Information.Err.

Catch Klauzule s When klauzulí zachytí pouze výjimky, když se výraz vyhodnotí jako True; typ výrazu musí být logický výraz podle logických výrazů oddílu. When Klauzule se použije pouze po kontrole typu výjimky a výraz může odkazovat na identifikátor představující výjimku, jak ukazuje tento příklad:

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

Tento příklad vytiskne:

Third handler

Catch Pokud klauzule zpracovává výjimku, provádění se přenese do Catch bloku. Na konci Catch bloku se provádění přenese na první příkaz za příkazem Try . Příkaz Try nezpracuje žádné výjimky vyvolané v Catch bloku. Pokud žádná Catch klauzule nezpracuje výjimku, provádění se přenese do umístění určeného systémem.

Explicitní přenos spuštění do Catch bloku není platný.

Filtry v klauzulích When se obvykle vyhodnocují před vyvolání výjimky. Například následující kód vytiskne "Filter, Finally, Catch".

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

Metody Async a Iterator však způsobují, že se všechny bloky uvnitř nich spustí před všemi filtry mimo ně. Pokud by například výše uvedený kód měl Async Sub Foo(), výstup by byl "Finally, Filter, Catch".

Throw – příkaz

Příkaz Throw vyvolá výjimku, která je reprezentována instancí typu odvozeného z System.Exception.

ThrowStatement
    : 'Throw' Expression? StatementTerminator
    ;

Pokud výraz není klasifikovaný jako hodnota nebo není typem odvozeným z System.Exception, dojde k chybě v době kompilace. Pokud se výraz vyhodnotí jako hodnota null za běhu, System.NullReferenceException vyvolá se místo toho výjimka.

Příkaz Throw může vynechat výraz v bloku Try catch příkazu, pokud neexistuje žádný blok posledního zásady. V takovém případě příkaz znovu zvětšuje výjimku, která se právě zpracovává v rámci bloku catch. Například:

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

Nestrukturované příkazy Exception-Handling

Nestrukturované zpracování výjimek je metoda zpracování chyb tím, že indikuje příkazy pro větvení v případě, že dojde k výjimce. Nestrukturované zpracování výjimek se implementuje pomocí tří příkazů: Error příkaz, On Error příkaz a Resume příkaz.

UnstructuredErrorStatement
    : ErrorStatement
    | OnErrorStatement
    | ResumeStatement
    ;

Například:

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

Pokud metoda používá nestrukturované zpracování výjimek, vytvoří se jedna obslužná rutina strukturované výjimky pro celou metodu, která zachytí všechny výjimky. (Všimněte si, že v konstruktorech tato obslužná rutina nepřesahuje volání New volání na začátku konstruktoru.) Metoda pak sleduje poslední umístění obslužné rutiny výjimky a poslední výjimku, která byla vyvolána. Při vstupu do metody je umístění obslužné rutiny výjimky a výjimka obě nastaveny na Nothing. Při vyvolání výjimky v metodě, která používá nestrukturované zpracování výjimek, je odkaz na objekt výjimky uložen v objektu vrácený funkcí Microsoft.VisualBasic.Information.Err.

Nestrukturované příkazy zpracování chyb nejsou povoleny v iterátoru nebo asynchronních metodách.

Error – příkaz

Příkaz Error vyvolá System.Exception výjimku obsahující číslo výjimky jazyka Visual Basic 6. Výraz musí být klasifikován jako hodnota a jeho typ musí být implicitně konvertibilní na Integer.

ErrorStatement
    : 'Error' Expression StatementTerminator
    ;

Příkaz On Error

Příkaz On Error upraví nejnovější stav zpracování výjimek.

OnErrorStatement
    : 'On' 'Error' ErrorClause StatementTerminator
    ;

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

Může se použít jedním ze čtyř způsobů:

  • On Error GoTo -1 resetuje nejnovější výjimku na Nothing.

  • On Error GoTo 0 resetuje nejnovější umístění obslužné rutiny výjimky na Nothing.

  • On Error GoTo LabelName vytvoří popisek jako poslední umístění obslužné rutiny výjimky. Tento příkaz nelze použít v metodě, která obsahuje výraz lambda nebo dotaz.

  • On Error Resume Next vytvoří chování jako poslední umístění obslužné rutiny Resume Next výjimky.

Resume – příkaz

Příkaz Resume vrátí spuštění příkazu, který způsobil poslední výjimku.

ResumeStatement
    : 'Resume' ResumeClause? StatementTerminator
    ;

ResumeClause
    : 'Next'
    | LabelName
    ;

Next Pokud je zadán modifikátor, spuštění se vrátí do příkazu, který by byl proveden po příkazu, který způsobil poslední výjimku. Pokud je zadán název popisku, spuštění se vrátí k popisku.

Vzhledem k tomu, že příkaz SyncLock obsahuje implicitní blok pro zpracování chyb a Resume Next má zvláštní chování pro výjimky, ke kterým dochází v SyncLock příkazech. Resume Resume vrátí spuštění na začátek SyncLock příkazu, zatímco Resume Next vrátí provádění na další příkaz následující příkaz SyncLock . Představte si například následující kód:

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

Vytiskne následující výsledek.

Before exception
Before exception
After SyncLock

Při prvním provedení příkazu SyncLockResume vrátí spuštění na začátek SyncLock příkazu. Druhý čas prostřednictvím SyncLock příkazu Resume Next vrátí spuštění na konec SyncLock příkazu. Resume a Resume Next nejsou povoleny SyncLock v rámci příkazu.

Ve všech případech je při Resume spuštění příkazu poslední výjimka nastavena na Nothing. Pokud je Resume příkaz proveden bez nejnovější výjimky, příkaz vyvolá System.Exception výjimku obsahující číslo 20 chyby jazyka Visual Basic (Resume bez chyby).

Branch – příkazy

Příkazy branch upravují tok provádění v metodě. K dispozici je šest příkazů větve:

  1. Příkaz GoTo způsobí, že provádění se přenese na zadaný popisek v metodě. Není povoleno do bloku , , , WithForSyncLockUsing, ani For Each do bloku, ani do bloku smyčky, pokud je místní proměnná tohoto bloku zachycena ve výrazu lambda nebo LINQ.TryGoTo
  2. Příkaz Exit přenese provádění na další příkaz za koncem bezprostředně obsahujícího blokový příkaz zadaného typu. Pokud je blokem metody, pak tok řízení ukončí metodu, jak je popsáno v části Řízení toku. Pokud příkaz Exit není obsažen v rámci typu bloku zadaného v příkazu, dojde k chybě v době kompilace.
  3. Příkaz Continue přenese provádění na konec bezprostředně obsahující příkaz blokové smyčky zadaného typu. Pokud příkaz Continue není obsažen v rámci typu bloku zadaného v příkazu, dojde k chybě v době kompilace.
  4. Příkaz Stop způsobí, že dojde k výjimce ladicího programu.
  5. Příkaz End program ukončí. Finalizátory se spouští před vypnutím, ale nakonec se nespustí bloky všech aktuálně spouštěných Try příkazů. Tento příkaz nelze použít v programech, které nejsou spustitelné (například knihovny DLL).
  6. Příkaz Return bez výrazu je ekvivalentem příkazu Exit Sub nebo Exit Function příkazu. Příkaz Return s výrazem je povolen pouze v běžné metodě, která je funkcí, nebo v asynchronní metodě, která je funkcí s návratovým typem Task(Of T) pro některé T. Výraz musí být klasifikován jako hodnota, která je implicitně konvertibilní na návratovou proměnnou funkce (v případě běžných metod) nebo na návratovou proměnnou úkolu (v případě asynchronních metod). Jeho chování je vyhodnotit jeho výraz, pak ho uložit do návratové proměnné a pak spustit implicitní Exit Function příkaz.
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
    ;

příkazy Array-Handling

Dva příkazy zjednodušují práci s poli: ReDim příkazy a Erase příkazy.

ArrayHandlingStatement
    : RedimStatement
    | EraseStatement
    ;

Příkaz ReDim

Příkaz ReDim vytvoří instanci nových polí.

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

RedimClauses
    : RedimClause ( Comma RedimClause )*
    ;

RedimClause
    : Expression ArraySizeInitializationModifier
    ;

Každá klauzule v příkazu musí být klasifikována jako proměnná nebo přístup k vlastnosti, jejíž typ je typ pole nebo Objecta za nimi následuje seznam maticových hranic. Počet hranic musí být konzistentní s typem proměnné; pro libovolný počet hranic je povolen Object. V době běhu se vytvoří instance pole pro každý výraz zleva doprava se zadanými hranicemi a pak je přiřazen k proměnné nebo vlastnosti. Pokud je Objecttyp proměnné , počet dimenzí je počet zadaných dimenzí a typ prvku pole je Object. Pokud je daný počet dimenzí nekompatibilní s proměnnou nebo vlastností za běhu, dojde k chybě kompilace. Například:

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

Preserve Pokud je klíčové slovo zadáno, musí být výrazy také klasifikovatelné jako hodnota a nová velikost pro každou dimenzi s výjimkou pravé strany musí být stejná jako velikost existujícího pole. Hodnoty v existujícím poli se zkopírují do nového pole: pokud je nová matice menší, stávající hodnoty se zahodí; pokud je nové pole větší, extra prvky budou inicializovány na výchozí hodnotu typu prvku pole. Představte si například následující kód:

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

Vytiskne následující výsledek:

3, 0

Pokud je existující odkaz na pole hodnotou null za běhu, nezobrazí se žádná chyba. Pokud se velikost dimenze změní, System.ArrayTypeMismatchException vyvolá se jiná dimenze, než je dimenze úplně vpravo.

Poznámka. Preserve není rezervované slovo.

Příkaz Erase

Příkaz Erase nastaví každou z proměnných pole nebo vlastností zadaných v příkazu na Nothing. Každý výraz v příkazu musí být klasifikován jako proměnná nebo vlastnost přístup, jehož typ je typ pole nebo Object. Například:

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

Using – příkaz

Instance typů jsou automaticky uvolněny uvolňováním paměti při spuštění kolekce a nejsou nalezeny žádné živé odkazy na instanci. Pokud typ obsahuje zvláště cenný a vzácný prostředek (například připojení k databázi nebo popisovače souborů), nemusí být žádoucí počkat na další uvolňování paměti, aby se vyčistil konkrétní instance typu, který se už nepoužívá. Pokud chcete poskytnout jednoduchý způsob uvolnění prostředků před kolekcí, může typ implementovat System.IDisposable rozhraní. Typ, který tak zpřístupní metodu, kterou lze volat k vynucení okamžitého Dispose uvolnění cenných prostředků, například:

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

        ' Do some work
        ...

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

Tento Using příkaz automatizuje proces získání prostředku, spuštění sady příkazů a následného odstranění prostředku. Příkaz může mít dvě formy: v jedné je prostředek místní proměnnou deklarovanou jako součást příkazu a považována za regulární příkaz deklarace místní proměnné; v druhém je prostředek výsledkem výrazu.

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

UsingResources
    : VariableDeclarators
    | Expression
    ;

Pokud je prostředek příkaz deklarace místní proměnné, pak typ deklarace místní proměnné musí být typ, který lze implicitně převést na System.IDisposable. Deklarované místní proměnné jsou jen pro čtení, vymezeny na Using blok příkazu a musí obsahovat inicializátor. Pokud je prostředek výsledkem výrazu, musí být výraz klasifikován jako hodnota a musí být typu, který lze implicitně převést na System.IDisposable. Výraz se vyhodnocuje pouze jednou na začátku příkazu.

Blok Using je implicitně obsažen příkazem Try , jehož nakonec blok volá metodu IDisposable.Dispose prostředku. Tím se zajistí, že se prostředek odstraní i v případě, že dojde k vyvolání výjimky. V důsledku toho je neplatné vytvořit větev do Using bloku mimo blok a Using blok je považován za jediný příkaz pro účely Resume a Resume Next. Pokud je Nothingprostředek , nebude provedeno žádné volání Dispose . Příklad:

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

odpovídá:

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

Příkaz Using , který má příkaz deklarace místní proměnné, může získat více prostředků najednou, což je ekvivalentem vnořených Using příkazů. Například Using příkaz formuláře:

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

odpovídá:

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

Příkaz Await

Příkaz await má stejnou syntaxi jako výraz operátoru await (Section Await Operator), je povolen pouze v metodách, které také umožňují výrazy await a mají stejné chování jako výraz operátoru await.

Může se ale klasifikovat jako hodnota nebo void. Jakákoli hodnota vyplývající z vyhodnocení výrazu operátoru await se zahodí.

AwaitStatement
    : AwaitOperatorExpression StatementTerminator
    ;

Příkaz Yield

Příkazy yield se vztahují k metodám iterátoru, které jsou popsány v části Iterator Methods.

YieldStatement
    : 'Yield' Expression StatementTerminator
    ;

Yield je vyhrazené slovo, pokud bezprostředně uzavřená metoda nebo výraz lambda, ve kterém se zobrazí, má Iterator modifikátor, a pokud Yield se zobrazí za tímto Iterator modifikátorem; není rezervován jinde. Není také rezervován v direktivách preprocesoru. Příkaz yield je povolen pouze v těle metody nebo výrazu lambda, kde se jedná o rezervované slovo. V rámci bezprostředně uzavřené metody nebo lambda nemusí příkaz výnosu nastat uvnitř těla Catch nebo Finally bloku ani uvnitř těla SyncLock příkazu.

Příkaz yield přebírá jeden výraz, který musí být klasifikován jako hodnota a jehož typ je implicitně konvertibilní na typ aktuální proměnné iterátoru (Section Iterator Methods) jeho uzavřené metody iterátoru.

Tok řízení dosáhne příkazu pouze Yield při MoveNext vyvolání metody v objektu iterátoru. (Důvodem je to, že instance metody iterátoru spouští pouze své příkazy z důvodu MoveNext nebo Dispose metod, které jsou volány na objektu iterátoru; a Dispose metoda bude provádět pouze kód v Finally blocích, kde Yield není povoleno).

Yield Při spuštění příkazu se jeho výraz vyhodnotí a uloží v aktuální proměnné iterátoru instance metody iterátoru přidružené k danému objektu iterátoru. Hodnota True je vrácena do invoker of MoveNext, a řídicí bod této instance se zastaví až do další vyvolání MoveNext objektu iterátoru.