Instrukcje — Visual Basic

Instrukcje reprezentują kod wykonywalny.

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

Uwaga. Kompilator języka Microsoft Visual Basic zezwala tylko na instrukcje rozpoczynające się od słowa kluczowego lub identyfikatora. W związku z tym na przykład instrukcja wywołania "Call (Console).WriteLine" jest dozwolona, ale instrukcja wywołania "(Console).WriteLine" nie jest.

Sterowanie przepływem

Przepływ sterowania to sekwencja, w której są wykonywane instrukcje i wyrażenia. Kolejność wykonywania zależy od określonej instrukcji lub wyrażenia.

Na przykład podczas oceniania operatora dodawania ( Operator dodawania sekcji) najpierw jest obliczany lewy operand, a następnie prawy operand, a następnie sam operator. Bloki są wykonywane ( bloki sekcji i etykiety), najpierw wykonując ich pierwszy podstan, a następnie przechodząc jeden po jednym przez instrukcje bloku.

Niejawne w tej kolejności jest pojęcie punktu sterowania, który jest następną operacją do wykonania. Gdy metoda jest wywoływana (lub "wywoływana"), mówimy, że tworzy wystąpienie metody. Wystąpienie metody składa się z własnej kopii parametrów metody i zmiennych lokalnych oraz własnego punktu kontrolnego.

Metody regularne

Oto przykład metody regularnej

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

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

Po wywołaniu metody regularnej,

  1. Najpierw jest tworzone wystąpienie metody specyficzne dla tego wywołania. To wystąpienie zawiera kopię wszystkich parametrów i zmiennych lokalnych metody.
  2. Następnie wszystkie jego parametry są inicjowane do podanych wartości, a wszystkie jej zmienne lokalne do wartości domyślnych ich typów.
  3. W przypadku Functionzmiennej niejawnej lokalnej jest również inicjowana zmienna zwracana funkcji , której nazwa jest nazwą funkcji, której typem jest zwracany typ funkcji i którego wartość początkowa jest wartością domyślną jej typu.
  4. Punkt kontrolny wystąpienia metody jest następnie ustawiany w pierwszej instrukcji treści metody, a treść metody natychmiast rozpoczyna się od tego miejsca ( bloki sekcji i etykiety).

Gdy przepływ sterowania wychodzi z treści metody normalnie — przez dotarcie do End Function obiektu lub End Sub oznaczającego jego koniec, lub za pomocą jawnej Return instrukcji lub Exit — przepływ sterowania powraca do obiektu wywołującego wystąpienie metody. Jeśli istnieje zmienna zwracana przez funkcję, wynikiem wywołania jest końcowa wartość tej zmiennej.

Gdy przepływ sterowania zamyka treść metody za pomocą nieobsługiwanego wyjątku, ten wyjątek jest propagowany do elementu wywołującego.

Po zakończeniu przepływu sterowania nie ma już żadnych odwołań na żywo do wystąpienia metody. Jeśli wystąpienie metody przechowywało tylko odwołania do jego kopii zmiennych lokalnych lub parametrów, mogą być one zbierane jako bezużyteczne.

Metody iteracyjne

Metody iteracyjne są używane jako wygodny sposób generowania sekwencji, która może być używana przez instrukcję For Each . Metody iteracyjne używają instrukcji Yield (Section Yield Statement) do udostępniania elementów sekwencji. (Metoda iteratora bez Yield instrukcji nie spowoduje wygenerowania pustej sekwencji). Oto przykład metody iteratora:

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

Po wywołaniu metody iteratora, której zwracany typ to IEnumerator(Of T),

  1. Najpierw jest tworzone wystąpienie metody iteracyjnej specyficzne dla tego wywołania. To wystąpienie zawiera kopię wszystkich parametrów i zmiennych lokalnych metody.
  2. Następnie wszystkie jego parametry są inicjowane do podanych wartości, a wszystkie jej zmienne lokalne do wartości domyślnych ich typów.
  3. Niejawna zmienna lokalna jest również inicjowana jako bieżąca zmienna iteratora, której typ jest T i którego wartość początkowa jest wartością domyślną jej typu.
  4. Punkt kontrolny wystąpienia metody jest następnie ustawiany w pierwszej instrukcji treści metody.
  5. Następnie zostanie utworzony obiekt iteratora skojarzony z tym wystąpieniem metody. Obiekt iteratora implementuje zadeklarowany typ zwracany i zachowuje się zgodnie z poniższym opisem.
  6. Przepływ sterowania jest następnie natychmiast wznawiany w obiekcie wywołującym, a wynikiem wywołania jest obiekt iteratora. Należy pamiętać, że ten transfer odbywa się bez zamykania wystąpienia metody iteratora i nie powoduje wykonania procedury obsługi. Wystąpienie metody jest nadal przywoływane przez obiekt iteratora i nie będzie wyrzucane śmieci tak długo, jak istnieje odwołanie na żywo do obiektu iteratora.

Po korzystaniu z właściwości obiektu Current iteratora zwracana jest bieżąca zmienna wywołania.

Po wywołaniu metody obiektu MoveNext iteratora wywołanie nie tworzy nowego wystąpienia metody. Zamiast tego używane jest istniejące wystąpienie metody (oraz jego punkt kontrolny oraz lokalne zmienne i parametry) — wystąpienie, które zostało utworzone podczas pierwszego wywołania metody iteratora. Przepływ sterowania wznawia wykonywanie w punkcie kontrolnym tego wystąpienia metody i przechodzi przez treść metody iteratora w normalny sposób.

Po wywołaniu metody obiektu Dispose iteratora zostanie ponownie użyte istniejące wystąpienie metody. Przepływ sterowania jest wznawiany w punkcie kontrolnym tego wystąpienia metody, ale następnie natychmiast zachowuje się tak, jakby instrukcja była następną operacją Exit Function .

Powyższe opisy zachowania w przypadku wywołania obiektu iteratora MoveNext lub Dispose w obiekcie iteratora mają zastosowanie tylko wtedy, gdy wszystkie poprzednie wywołania obiektu iteratora MoveNext lub Dispose na tym obiekcie iteratora zostały już zwrócone do ich obiektów wywołujących. Jeśli tak nie jest, zachowanie jest niezdefiniowane.

Gdy przepływ sterowania kończy treść metody iteratora normalnie — przez dotarcie do End Function tego znaku końcowego lub za pomocą jawnej Return instrukcji lub Exit instrukcji — musi to zrobić w kontekście wywołania MoveNext funkcji lub Dispose obiektu iteratora, aby wznowić wystąpienie metody iteratora i będzie używać wystąpienia metody metody, które zostało utworzone podczas pierwszego wywołania metody iteratora. Punkt kontrolny tego wystąpienia jest pozostawiony w End Function instrukcji, a przepływ sterowania wznawia działanie obiektu wywołującego, a jeśli został wznowiony przez wywołanie metody MoveNext , wartość False jest zwracana do obiektu wywołującego.

Gdy przepływ sterowania kończy treść metody iteratora za pomocą nieobsługiwanego wyjątku, wyjątek jest propagowany do obiektu wywołującego, który ponownie będzie wywołaniem MoveNext elementu lub Dispose.

Jak w przypadku innych możliwych typów zwracanych funkcji iteratora,

  • Gdy wywoływana jest metoda iteratora, której typ zwracany jest IEnumerable(Of T) dla niektórych T, wystąpienie jest tworzone po raz pierwszy — specyficzne dla tej metody iteracyjnej — wszystkich parametrów w metodzie i są inicjowane przy użyciu podanych wartości. Wynikiem wywołania jest obiekt, który implementuje typ zwracany. Jeśli metoda tego obiektu GetEnumerator zostanie wywołana, tworzy wystąpienie — specyficzne dla tego wywołania GetEnumerator — wszystkich parametrów i zmiennych lokalnych w metodzie . Inicjuje parametry do wartości, które zostały już zapisane, i jest kontynuowane zgodnie z powyższymi metodami iteratora.
  • Gdy wywoływana jest metoda iteratora, której typ zwracany jest interfejsem IEnumeratorniegeneryjnym, zachowanie jest dokładnie takie samo jak w przypadku IEnumerator(Of Object)elementu .
  • Gdy wywoływana jest metoda iteratora, której typ zwracany jest interfejsem IEnumerableniegeneryjnym, zachowanie jest dokładnie takie samo jak w przypadku IEnumerable(Of Object)elementu .

Metody asynchroniczne

Metody asynchroniczne to wygodny sposób wykonywania długotrwałej pracy bez na przykład blokowania interfejsu użytkownika aplikacji. Asynchroniczna jest skrótem asynchronicznym — oznacza to, że obiekt wywołujący metody asynchronicznej wznowi jego wykonywanie natychmiast, ale ostateczne zakończenie wystąpienia metody asynchronicznej może nastąpić w późniejszym czasie w przyszłości. Zgodnie z konwencją metody asynchroniczne są nazywane sufiksem "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"

Uwaga. Metody asynchroniczne nie są uruchamiane w wątku w tle. Zamiast tego umożliwiają one wstrzymanie się Await przez operator metody i zaplanowanie jej wznowienia w odpowiedzi na jakieś zdarzenie.

Po wywołaniu metody asynchronicznej

  1. Najpierw jest tworzone wystąpienie metody asynchronicznej specyficzne dla tej wywołania. To wystąpienie zawiera kopię wszystkich parametrów i zmiennych lokalnych metody.
  2. Następnie wszystkie jego parametry są inicjowane do podanych wartości, a wszystkie jej zmienne lokalne do wartości domyślnych ich typów.
  3. W przypadku metody asynchronicznej z typem Task(Of T) zwrotnym dla niektórych Tparametrów niejawna zmienna lokalna jest również inicjowana jako zmienna zwracana zadania, której typ jest T i którego wartość początkowa jest wartością domyślną T.
  4. Jeśli metoda asynchronicznie jest typu zwracanego Function lub Task(Of T) dla niektórych T, obiekt tego typu jest niejawnie utworzony, skojarzony z Task bieżącym wywołaniem. Jest to nazywane obiektem asynchronizowania i reprezentuje przyszłą pracę, która zostanie wykonana przez wykonanie wystąpienia metody asynchronicznej. Gdy kontrolka zostanie wznowione w wywołaniu tego wystąpienia metody asynchronicznej, obiekt wywołujący otrzyma ten obiekt asynchroniczny w wyniku wywołania.
  5. Punkt kontrolny wystąpienia jest następnie ustawiany w pierwszej instrukcji treści metody asynchronicznej i natychmiast rozpoczyna wykonywanie treści metody z tego miejsca ( bloki sekcji i etykiety).

Delegat wznowienia i bieżący obiekt wywołujący

Jak opisano w sekcji Await Operator, wykonanie Await wyrażenia ma możliwość zawieszenia punktu kontrolnego wystąpienia metody pozostawiając przepływ sterowania, aby przejść gdzie indziej. Przepływ sterowania może później wznawiać działanie w punkcie sterowania tego samego wystąpienia przez wywołanie delegata wznowienia. Należy pamiętać, że to zawieszenie jest wykonywane bez zamykania metody asynchronicznej i nie powoduje wykonania procedury obsługi. Wystąpienie metody jest nadal przywoływane zarówno przez delegata wznowienia, jak i Task wynik lub Task(Of T) (jeśli istnieje), i nie będzie wyrzucane śmieci tak długo, jak istnieje aktywne odwołanie do delegata lub wyniku.

Warto wyobrazić sobie instrukcję Dim x = Await WorkAsync() w przybliżeniu jako skrót składniowy dla następujących elementów:

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

W poniższym przykładzie bieżący obiekt wywołujący wystąpienie metody jest definiowany jako oryginalny obiekt wywołujący lub wywołujący delegata wznowienia, w zależności od tego, która z nich jest nowsza.

Gdy przepływ sterowania kończy treść metody asynchronicznej — przez dotarcie do End Sub obiektu lub End Function oznaczającego jego koniec albo za pomocą jawnego Return lub Exit instrukcji albo za pośrednictwem nieobsługiwanego wyjątku — punkt kontrolny wystąpienia jest ustawiony na koniec metody. Następnie zachowanie zależy od zwracanego typu metody asynchronicznej.

  • W przypadku obiektu Async Function o zwracanym typie Task:

    1. Jeśli przepływ sterowania kończy się przez nieobsługiwany wyjątek, stan obiektu asynchronicznego jest ustawiony na TaskStatus.Faulted wartość , a jego Exception.InnerException właściwość jest ustawiona na wyjątek (z wyjątkiem: niektórych wyjątków zdefiniowanych przez implementację, takich jak OperationCanceledException zmiana go na TaskStatus.Canceled). Przepływ sterowania jest wznawiany w bieżącym obiekcie wywołującym.

    2. W przeciwnym razie stan obiektu asynchronicznego ma wartość TaskStatus.Completed. Przepływ sterowania jest wznawiany w bieżącym obiekcie wywołującym.

      (Uwaga. Cały punkt zadania i co sprawia, że metody asynchroniczne są interesujące, jest to, że gdy zadanie stanie się Ukończone, wszystkie metody, które oczekiwały na to, będą miały wykonywane ich delegaty wznowienia, tj. staną się odblokowane.

  • W przypadku elementu z typem Async FunctionTask(Of T) zwrotnym dla niektórych Telementów : zachowanie jest tak samo jak powyżej, z tą różnicą, że w przypadkach innych niż wyjątki właściwość obiektu Result asynchronicznego jest również ustawiona na ostateczną wartość zmiennej zwracanej przez zadanie.

  • W przypadku elementu Async Sub:

    1. Jeśli przepływ sterowania kończy się przez nieobsługiwany wyjątek, ten wyjątek jest propagowany do środowiska w jakiś sposób specyficzny dla implementacji. Przepływ sterowania jest wznawiany w bieżącym obiekcie wywołującym.
    2. W przeciwnym razie przepływ sterowania po prostu wznawia działanie w bieżącym obiekcie wywołującym.

Podsynchroniczne

Istnieje pewne zachowanie specyficzne dla firmy Microsoft dotyczące elementu Async Sub.

Jeśli SynchronizationContext.Current znajduje Nothing się na początku wywołania, wszystkie nieobsługiwane wyjątki z podsynchronicznego zostaną opublikowane w puli Threadpool.

Jeśli SynchronizationContext.Current nie Nothing jest na początku wywołania, OperationStarted() jest wywoływany w tym kontekście przed rozpoczęciem metody i OperationCompleted() po końcu. Ponadto wszelkie nieobsługiwane wyjątki zostaną opublikowane, aby zostały ponownie dodane w kontekście synchronizacji.

Oznacza to, że w aplikacjach interfejsu użytkownika w przypadku elementu wywoływanego Async Sub w wątku interfejsu użytkownika wszelkie wyjątki, które nie można obsłużyć, zostaną ponownie wątek interfejsu użytkownika.

Struktury modyfikowalne w metodach asynchronicznych i iteratorowych

Struktury modyfikowalne są ogólnie uważane za złe rozwiązanie i nie są obsługiwane przez metody asynchroniczne ani iteracyjne. W szczególności każde wywołanie metody asynchronicznej lub iteracyjnej w strukturze będzie niejawnie działać na kopii tej struktury, która jest kopiowana w momencie wywołania. W związku z tym, na przykład,

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"

Bloki i etykiety

Grupa instrukcji wykonywalnych jest nazywana blokiem instrukcji. Wykonanie bloku instrukcji rozpoczyna się od pierwszej instrukcji w bloku. Po wykonaniu instrukcji następna instrukcja w kolejności leksykalnej jest wykonywana, chyba że wystąpi wykonywanie instrukcji w innym miejscu lub wyjątek.

W bloku instrukcji podział instrukcji w wierszach logicznych nie jest znaczący z wyjątkiem instrukcji deklaracji etykiety. Etykieta to identyfikator identyfikujący określoną pozycję w bloku instrukcji, który może być używany jako element docelowy instrukcji gałęzi, na przykład GoTo.

Block
    : Statements*
    ;

LabelDeclarationStatement
    : LabelName ':'
    ;

LabelName
    : Identifier
    | IntLiteral
    ;

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

Instrukcje deklaracji etykiety muszą być wyświetlane na początku wiersza logicznego, a etykiety mogą być identyfikatorem lub literałem całkowitym. Ponieważ obie instrukcje deklaracji etykiety i instrukcje wywołania mogą składać się z pojedynczego identyfikatora, pojedynczy identyfikator na początku wiersza lokalnego jest zawsze uważany za instrukcję deklaracji etykiety. Instrukcje deklaracji etykiet muszą zawsze być po dwukropku, nawet jeśli instrukcje nie są zgodne z tą samą linią logiczną.

Etykiety mają własną przestrzeń deklaracji i nie zakłócają innych identyfikatorów. Poniższy przykład jest prawidłowy i używa zmiennej x name zarówno jako parametru, jak i jako etykiety.

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

Zakres etykiety to treść metody zawierającej ją.

Ze względu na czytelność produkcje instrukcji, które obejmują wiele podstanów, są traktowane jako pojedyncza produkcja w tej specyfikacji, mimo że podstany mogą być same w wierszu oznaczonym etykietą.

Zmienne lokalne i parametry

W poprzednich sekcjach opisano sposób i czas tworzenia wystąpień metody oraz kopie lokalnych zmiennych i parametrów metody. Ponadto za każdym razem, gdy wprowadzona jest treść pętli, nowa kopia jest wykonana z każdej zmiennej lokalnej zadeklarowanej wewnątrz tej pętli zgodnie z opisem w instrukcjach pętli sekcji, a wystąpienie metody zawiera teraz tę kopię zmiennej lokalnej, a nie poprzedniej kopii.

Wszystkie lokalizacje lokalne są inicjowane do wartości domyślnej ich typu. Zmienne lokalne i parametry są zawsze dostępne publicznie. Jest to błąd podczas odwoływania się do zmiennej lokalnej w pozycji tekstowej, która poprzedza jego deklarację, jak pokazano w poniższym przykładzie:

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

W powyższej metodzie F pierwsze przypisanie nie i odnosi się do pola zadeklarowane w zakresie zewnętrznym. Zamiast tego odwołuje się do zmiennej lokalnej i jest w błędzie, ponieważ tekstowo poprzedza deklarację zmiennej. W metodzie G kolejna deklaracja zmiennej odwołuje się do zmiennej lokalnej zadeklarowanej we wcześniejszej deklaracji zmiennej w ramach tej samej deklaracji zmiennej lokalnej.

Każdy blok w metodzie tworzy przestrzeń deklaracji dla zmiennych lokalnych. Nazwy są wprowadzane do tej przestrzeni deklaracji za pośrednictwem deklaracji zmiennych lokalnych w treści metody i za pośrednictwem listy parametrów metody, która wprowadza nazwy do przestrzeni deklaracji najbardziej zewnętrznego bloku. Bloki nie zezwalają na cieniowanie nazw za pomocą zagnieżdżania: po zadeklarowaniu nazwy w bloku nazwa może nie być ponownie zadeklarowana w żadnych zagnieżdżonych blokach.

W związku z tym w poniższym przykładzie metody i G są błędne, F ponieważ nazwa i jest zadeklarowana w bloku zewnętrznym i nie może być ponownie zadeklarowana w bloku wewnętrznym. Metody i I są jednak prawidłowe, H ponieważ te dwie imetody są deklarowane w oddzielnych blokach niezagnieżdżonych.

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

Gdy metoda jest funkcją, specjalna zmienna lokalna jest niejawnie zadeklarowana w przestrzeni deklaracji treści metody o takiej samej nazwie jak metoda reprezentująca zwracaną wartość funkcji. Zmienna lokalna ma semantykę rozpoznawania nazw specjalnych, gdy jest używana w wyrażeniach. Jeśli zmienna lokalna jest używana w kontekście, który oczekuje wyrażenia sklasyfikowanego jako grupa metod, takiej jak wyrażenie wywołania, nazwa jest rozpoznawana jako funkcja, a nie do zmiennej lokalnej. Przykład:

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

Użycie nawiasów może powodować niejednoznaczne sytuacje (takie jak F(1), gdzie F jest funkcją, której typem zwracanym jest tablica jednowymiarowa); we wszystkich niejednoznacznych sytuacjach nazwa jest rozpoznawana jako funkcja, a nie zmienna lokalna. Przykład:

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

Gdy przepływ sterowania opuszcza treść metody, wartość zmiennej lokalnej jest przekazywana z powrotem do wyrażenia wywołania. Jeśli metoda jest podroutine, nie ma takiej niejawnej zmiennej lokalnej, a kontrolka po prostu powraca do wyrażenia wywołania.

Instrukcje deklaracji lokalnej

Instrukcja deklaracji lokalnej deklaruje nową zmienną lokalną, stałą lokalną lub zmienną statyczną. Zmienne lokalne i stałe lokalne są równoważne zmiennym wystąpienia i stałym zakresom metody i są deklarowane w taki sam sposób. Zmienne statyczne są podobne do Shared zmiennych i są deklarowane przy użyciu Static modyfikatora.

LocalDeclarationStatement
    : LocalModifier VariableDeclarators StatementTerminator
    ;

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

Zmienne statyczne to zmienne lokalne, które zachowują swoją wartość w wywołaniach metody. Zmienne statyczne zadeklarowane w metodach innych niż udostępnione są na wystąpienie: każde wystąpienie typu zawierającego metodę ma własną kopię zmiennej statycznej. Zmienne statyczne zadeklarowane w ramach Shared metod są na typ; istnieje tylko jedna kopia zmiennej statycznej dla wszystkich wystąpień. Podczas gdy zmienne lokalne są inicjowane do wartości domyślnej ich typu podczas każdego wpisu w metodzie, zmienne statyczne są inicjowane tylko do wartości domyślnej ich typu, gdy typ lub wystąpienie typu jest inicjowane. Zmienne statyczne nie mogą być deklarowane w strukturach ani metodach ogólnych.

Zmienne lokalne, stałe lokalne i zmienne statyczne zawsze mają publiczne ułatwienia dostępu i mogą nie określać modyfikatorów ułatwień dostępu. Jeśli w instrukcji deklaracji lokalnej nie określono żadnego typu, następujące kroki określają typ deklaracji lokalnej:

  1. Jeśli deklaracja ma znak typu, typ znaku typu jest typem deklaracji lokalnej.

  2. Jeśli deklaracja lokalna jest stałą lokalną lub lokalna deklaracja jest zmienną lokalną z inicjatorem, a wnioskowanie typu zmiennej lokalnej jest używane, typ deklaracji lokalnej jest wnioskowany z typu inicjatora. Jeśli inicjator odwołuje się do deklaracji lokalnej, wystąpi błąd czasu kompilacji. (Stałe lokalne są wymagane do inicjowania).

  3. Jeśli nie są używane ścisłe semantyka, typ instrukcji deklaracji lokalnej jest niejawnie Object.

  4. W przeciwnym razie wystąpi błąd czasu kompilacji.

Jeśli w instrukcji deklaracji lokalnej nie określono żadnego typu, który ma modyfikator rozmiaru tablicy lub typu tablicy, typ deklaracji lokalnej jest tablicą o określonej rangi, a poprzednie kroki są używane do określania typu elementu tablicy. Jeśli jest używana wnioskowanie typu zmiennej lokalnej, typ inicjatora musi być typem tablicy o tym samym kształcie tablicy (tj. modyfikatorami typów tablicy) co instrukcja deklaracji lokalnej. Należy pamiętać, że istnieje możliwość, że typ wywnioskowanych elementów może być nadal typem tablicy. Przykład:

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

Jeśli w instrukcji deklaracji lokalnej nie określono żadnego typu, który ma modyfikator typu dopuszczającego wartość null, typ deklaracji lokalnej jest wersją dopuszczającą wartość null typu wnioskowanego lub wywnioskowanym typem, jeśli jest to typ wartości dopuszczający wartość null. Jeśli wywnioskowany typ nie jest typem wartości, który może być dopuszczający wartość null, wystąpi błąd czasu kompilacji. Jeśli zarówno modyfikator typu dopuszczającego wartość null, jak i rozmiar tablicy lub modyfikator typu tablicy są umieszczane w instrukcji deklaracji lokalnej bez typu, modyfikator typu dopuszczającego wartość null jest uważany za stosowany do typu elementu tablicy, a poprzednie kroki są używane do określenia typu elementu.

Inicjatory zmiennych w instrukcjach deklaracji lokalnej są równoważne instrukcjom przypisania umieszczonym w tekstowej lokalizacji deklaracji. W związku z tym, jeśli wykonywanie gałęzi na instrukcji deklaracji lokalnej, inicjator zmiennej nie jest wykonywany. Jeśli instrukcja deklaracji lokalnej jest wykonywana więcej niż raz, inicjator zmiennej jest wykonywany równą liczbę razy. Zmienne statyczne wykonują tylko inicjator po raz pierwszy. Jeśli podczas inicjowania zmiennej statycznej wystąpi wyjątek, zmienna statyczna jest uznawana za zainicjowaną z wartością domyślną typu zmiennej statycznej.

W poniższym przykładzie pokazano użycie inicjatorów:

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

Ten program drukuje:

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

Inicjatory statycznych ustawień lokalnych są bezpieczne wątkowo i chronione przed wyjątkami podczas inicjowania. Jeśli podczas inicjatora statycznego lokalnego wystąpi wyjątek, statyczny lokalny będzie miał jego wartość domyślną i nie zostanie zainicjowany. Statyczny inicjator lokalny

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

jest równoważny

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

Zmienne lokalne, stałe lokalne i zmienne statyczne są ograniczone do bloku instrukcji, w którym są deklarowane. Zmienne statyczne są specjalne, że ich nazwy mogą być używane tylko raz w całej metodzie. Na przykład nie jest prawidłowe określenie dwóch deklaracji zmiennych statycznych o tej samej nazwie, nawet jeśli znajdują się w różnych blokach.

Niejawne deklaracje lokalne

Oprócz lokalnych instrukcji deklaracji zmienne lokalne można również zadeklarować niejawnie za pomocą. Proste wyrażenie nazwy, które używa nazwy, która nie rozpoznaje czegoś innego deklaruje zmienną lokalną o tej nazwie. Przykład:

Option Explicit Off

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

Niejawna deklaracja lokalna występuje tylko w kontekstach wyrażeń, które mogą akceptować wyrażenie sklasyfikowane jako zmienna. Wyjątkiem od tej reguły jest to, że zmienna lokalna może nie być niejawnie zadeklarowana, gdy jest ona celem wyrażenia wywołania funkcji, wyrażenia indeksowania lub wyrażenia dostępu elementu członkowskiego.

Niejawne ustawienia lokalne są traktowane tak, jakby były deklarowane na początku metody zawierającej. W związku z tym są one zawsze ograniczone do całej treści metody, nawet jeśli są zadeklarowane wewnątrz wyrażenia lambda. Na przykład następujący kod:

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

spowoduje wyświetlenie wartości 10. Niejawne ustawienia lokalne są wpisywane tak, jakby Object żaden znak typu nie został dołączony do nazwy zmiennej. W przeciwnym razie typ zmiennej jest typem znaku typu. Wnioskowanie typu zmiennej lokalnej nie jest używane dla niejawnych ustawień lokalnych.

Jeśli jawna deklaracja lokalna jest określona przez środowisko kompilacji lub przez Option Explicitprogram , wszystkie zmienne lokalne muszą być jawnie zadeklarowane, a niejawna deklaracja zmiennej jest niedozwolona.

With, instrukcja

Instrukcja With umożliwia wiele odwołań do elementów członkowskich wyrażenia bez wielokrotnego określania wyrażenia.

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

Wyrażenie musi być klasyfikowane jako wartość i jest obliczane raz po wejściu do bloku. W bloku instrukcji With wyrażenie dostępu członka lub wyrażenie dostępu do słownika rozpoczynające się od kropki lub wykrzyknika jest obliczane tak, jakby With wyrażenie poprzedzało je. Przykład:

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

Nie można rozgałęzić With bloku instrukcji spoza bloku.

SyncLock, instrukcja

Instrukcja SyncLock umożliwia synchronizowanie instrukcji w wyrażeniu, co gwarantuje, że wiele wątków wykonywania nie wykonuje jednocześnie tych samych instrukcji.

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

Wyrażenie musi być klasyfikowane jako wartość i jest obliczane raz po wpisie do bloku. Podczas wprowadzania bloku Shared metoda jest wywoływana w określonym wyrażeniu SyncLockSystem.Threading.Monitor.Enter, które blokuje, dopóki wątek wykonywania nie ma wyłącznej blokady obiektu zwróconego przez wyrażenie. Typ wyrażenia w instrukcji musi być typem SyncLock odwołania. Przykład:

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

Powyższy przykład synchronizuje się z określonym wystąpieniem klasy Test , aby upewnić się, że nie więcej niż jeden wątek wykonywania może dodawać lub odejmować od zmiennej count w danym momencie dla określonego wystąpienia.

Blok SyncLock jest niejawnie zawarty w Try instrukcji, której Finally blok wywołuje Shared metodę System.Threading.Monitor.Exit w wyrażeniu. Dzięki temu blokada zostanie zwolniona nawet wtedy, gdy zostanie zgłoszony wyjątek. W związku z tym nie można rozgałęzić SyncLock bloku spoza bloku, a SyncLock blok jest traktowany jako pojedyncza instrukcja dla celów Resume i Resume Next. Powyższy przykład jest odpowiednikiem następującego kodu:

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

Instrukcje zdarzenia

Instrukcje RaiseEvent, AddHandleri RemoveHandler zgłaszają zdarzenia i obsługują zdarzenia dynamicznie.

EventStatement
    : RaiseEventStatement
    | AddHandlerStatement
    | RemoveHandlerStatement
    ;

RaiseEvent, instrukcja

Instrukcja RaiseEvent powiadamia programy obsługi zdarzeń, że wystąpiło określone zdarzenie.

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

Proste wyrażenie nazwy w RaiseEvent instrukcji jest interpretowane jako odnośnik członkowski w elemencie Me. RaiseEvent x W związku z tym jest interpretowany tak, jakby to było RaiseEvent Me.x. Wynik wyrażenia musi być klasyfikowany jako dostęp do zdarzeń dla zdarzenia zdefiniowanego w samej klasie; w instrukcji nie można używać zdarzeń zdefiniowanych na typach bazowych RaiseEvent .

Instrukcja RaiseEvent jest przetwarzana jako wywołanie Invoke metody delegata zdarzenia przy użyciu podanych parametrów, jeśli istnieją. Jeśli wartość delegata to Nothing, nie jest zgłaszany żaden wyjątek. Jeśli nie ma argumentów, nawiasy mogą zostać pominięte. Przykład:

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

Powyższa klasa Raiser jest równoważna:

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

Instrukcje programu AddHandler i RemoveHandler

Chociaż większość programów obsługi zdarzeń jest automatycznie podłączona przez WithEvents zmienne, może być konieczne dynamiczne dodawanie i usuwanie programów obsługi zdarzeń w czasie wykonywania. AddHandler i RemoveHandler instrukcje to robią.

AddHandlerStatement
    : 'AddHandler' Expression Comma Expression StatementTerminator
    ;

RemoveHandlerStatement
    : 'RemoveHandler' Expression Comma Expression StatementTerminator
    ;

Każda instrukcja przyjmuje dwa argumenty: pierwszy argument musi być wyrażeniem sklasyfikowanym jako dostęp do zdarzeń, a drugi argument musi być wyrażeniem sklasyfikowanym jako wartość. Typ drugiego argumentu musi być typem delegata skojarzonym z dostępem do zdarzenia. Przykład:

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

Biorąc pod uwagę zdarzenie E, , instrukcja wywołuje odpowiednią add_E metodę lub remove_E w wystąpieniu, aby dodać lub usunąć delegata jako procedurę obsługi dla zdarzenia. W związku z tym powyższy kod jest odpowiednikiem:

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

Instrukcje przypisania

Instrukcja przypisania przypisuje wartość wyrażenia do zmiennej. Istnieje kilka typów przypisań.

AssignmentStatement
    : RegularAssignmentStatement
    | CompoundAssignmentStatement
    | MidAssignmentStatement
    ;

Regularne instrukcje przypisania

Prosta instrukcja przypisania przechowuje wynik wyrażenia w zmiennej.

RegularAssignmentStatement
    : Expression Equals Expression StatementTerminator
    ;

Wyrażenie po lewej stronie operatora przypisania musi być klasyfikowane jako zmienna lub dostęp do właściwości, a wyrażenie po prawej stronie operatora przypisania musi być klasyfikowane jako wartość. Typ wyrażenia musi być niejawnie konwertowany na typ zmiennej lub dostępu do właściwości.

Jeśli zmienna przypisana do elementu jest elementem tablicy typu odwołania, zostanie wykonane sprawdzanie czasu wykonywania w celu upewnienia się, że wyrażenie jest zgodne z typem elementu tablicy. W poniższym przykładzie ostatnie przypisanie powoduje System.ArrayTypeMismatchException zgłoszenie, ponieważ wystąpienie ArrayList nie może być przechowywane w elemecie String tablicy.

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.

Jeśli wyrażenie po lewej stronie operatora przypisania jest klasyfikowane jako zmienna, instrukcja przypisania przechowuje wartość w zmiennej. Jeśli wyrażenie jest klasyfikowane jako dostęp do właściwości, instrukcja przypisania zamienia dostęp do właściwości w wywołanie Set metody dostępu właściwości z wartością podstawioną dla parametru wartości. Przykład:

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

Jeśli element docelowy dostępu do zmiennej lub właściwości jest typ wartości, ale nie jest klasyfikowany jako zmienna, wystąpi błąd czasu kompilacji. Przykład:

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

Należy pamiętać, że semantyka przypisania zależy od typu zmiennej lub właściwości, do której jest przypisywana. Jeśli zmienna, do której jest przypisana, jest typem wartości, przypisanie kopiuje wartość wyrażenia do zmiennej. Jeśli zmienna, do której jest przypisana, jest typem odwołania, przypisanie kopiuje odwołanie, a nie samą wartość do zmiennej. Jeśli typ zmiennej to Object, semantyka przypisania jest określana przez to, czy typ wartości jest typem wartości, czy typem odwołania w czasie wykonywania.

Uwaga. W przypadku typów wewnętrznych, takich jak Integer i Date, semantyka przypisań odwołań i wartości jest taka sama, ponieważ typy są niezmienne. W związku z tym język jest bezpłatny do używania przypisania odwołań w typach wewnętrznych w polu jako optymalizacji. Z perspektywy wartości wynik jest taki sam.

Ponieważ znak równości (=) jest używany zarówno dla przypisania, jak i równości, istnieje niejednoznaczność między prostym przypisaniem a instrukcją wywołania w sytuacjach takich jak x = y.ToString(). We wszystkich takich przypadkach instrukcja przypisania ma pierwszeństwo przed operatorem równości. Oznacza to, że przykładowe wyrażenie jest interpretowane jako x = (y.ToString()) zamiast (x = y).ToString().

Instrukcje przypisania złożonego

Instrukcja przypisania złożonego przyjmuje formę V op= E (gdzie op jest prawidłowym operatorem binarnym).

CompoundAssignmentStatement
    : Expression CompoundBinaryOperator LineTerminator? Expression StatementTerminator
    ;

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

Wyrażenie po lewej stronie operatora przypisania musi być klasyfikowane jako zmienna lub dostęp do właściwości, a wyrażenie po prawej stronie operatora przypisania musi być klasyfikowane jako wartość. Instrukcja przypisania złożonego jest równoważna instrukcji V = V op E z różnicą, że zmienna po lewej stronie operatora przypisania złożonego jest obliczana tylko raz. W poniższym przykładzie pokazano tę różnicę:

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

Wyrażenie a(GetIndex()) jest obliczane dwa razy dla prostego przypisania, ale tylko raz w przypadku przypisania złożonego, więc kod drukuje:

Simple assignment
Getting index
Getting index
Compound assignment
Getting index

Mid Assignment, instrukcja

Instrukcja Mid przypisania przypisuje ciąg do innego ciągu. Lewa strona przypisania ma taką samą składnię jak wywołanie funkcji Microsoft.VisualBasic.Strings.Mid.

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

Pierwszy argument jest elementem docelowym przypisania i musi być klasyfikowany jako zmienna lub dostęp do właściwości, którego typ jest niejawnie konwertowany na i z String. Drugi parametr to 1 oparta na pozycji początkowej, która odpowiada miejscu, w którym przypisanie powinno rozpoczynać się w ciągu docelowym i musi być klasyfikowane jako wartość, której typ musi być niejawnie konwertowany na Integer. Opcjonalny trzeci parametr to liczba znaków z wartości po prawej stronie do przypisania do ciągu docelowego i musi być klasyfikowana jako wartość, której typ jest niejawnie konwertowany na Integerwartość . Prawa strona to ciąg źródłowy i musi być klasyfikowana jako wartość, której typ jest niejawnie konwertowany na Stringwartość . Prawa strona jest obcięta do parametru length, jeśli jest określona, i zastępuje znaki w ciągu po lewej stronie, zaczynając od pozycji początkowej. Jeśli prawy ciąg boczny zawiera mniej znaków niż trzeci parametr, zostaną skopiowane tylko znaki z prawego ciągu bocznego.

W poniższym przykładzie zostanie wyświetlony komunikat 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

Uwaga. Mid nie jest słowem zastrzeżonym.

Instrukcje wywołania

Instrukcja wywołania wywołuje metodę poprzedzoną opcjonalnym słowem kluczowym Call. Instrukcja wywołania jest przetwarzana w taki sam sposób, jak wyrażenie wywołania funkcji, z pewnymi różnicami opisanymi poniżej. Wyrażenie wywołania musi być klasyfikowane jako wartość lub void. Każda wartość wynikająca z oceny wyrażenia wywołania jest odrzucana.

Call Jeśli słowo kluczowe zostanie pominięte, wyrażenie wywołania musi zaczynać się od identyfikatora lub słowa kluczowego With lub wewnątrz . bloku. Na przykład "Call 1.ToString()" jest prawidłową instrukcją, ale "1.ToString()" nie jest. (Należy pamiętać, że w kontekście wyrażenia wyrażenia również wyrażenia wywołania nie muszą rozpoczynać się od identyfikatora. Na przykład "Dim x = 1.ToString()" jest prawidłową instrukcją).

Istnieje inna różnica między instrukcjami wywołania i wyrażeniami wywołania: jeśli instrukcja wywołania zawiera listę argumentów, to zawsze jest to traktowane jako lista argumentów wywołania. Poniższy przykład ilustruje różnicę:

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
    ;

Instrukcje warunkowe

Instrukcje warunkowe umożliwiają warunkowe wykonywanie instrukcji na podstawie wyrażeń ocenianych w czasie wykonywania.

ConditionalStatement
    : IfStatement
    | SelectStatement
    ;

Jeśli... Wtedy... Instrukcje Else

Instrukcja If...Then...Else jest podstawową instrukcją warunkową.

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żde wyrażenie w instrukcji musi być wyrażeniem logicznym If...Then...Else zgodnie z wyrażeniami logicznymi sekcji. (Uwaga: nie wymaga to, aby wyrażenie miało typ logiczny). Jeśli wyrażenie w instrukcji If ma wartość true, instrukcje ujęte w If blok są wykonywane. Jeśli wyrażenie jest fałszywe, każde z ElseIf wyrażeń jest oceniane. Jeśli jedno z ElseIf wyrażeń daje wartość true, zostanie wykonany odpowiedni blok. Jeśli żadne wyrażenie nie daje wartości true i istnieje Else blok, Else zostanie wykonany blok. Po zakończeniu wykonywania bloku wykonywanie wykonuje się na końcu instrukcji If...Then...Else .

Wersja wiersza instrukcji If zawiera pojedynczy zestaw instrukcji do wykonania, jeśli If wyrażenie jest True i opcjonalny zestaw instrukcji do wykonania, jeśli wyrażenie to False. Przykład:

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

Wersja wiersza instrukcji If wiąże się mniej ściśle niż ":", a jego Else powiązanie z leksykalicznie najbliższymi poprzednimi If , które jest dozwolone przez składnię. Na przykład następujące dwie wersje są równoważne:

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

Wszystkie instrukcje inne niż instrukcje deklaracji etykiety są dozwolone wewnątrz instrukcji wiersza If , w tym instrukcji block. Mogą jednak nie używać obiektów LineTerminators jako StatementTerminators z wyjątkiem wyrażeń lambda wielowierszowych. Przykład:

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

Wybieranie instrukcji wielkości liter

Instrukcja Select Case wykonuje instrukcje na podstawie wartości wyrażenia.

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

Wyrażenie musi być klasyfikowane jako wartość. Po wykonaniu Select CaseSelect instrukcji wyrażenie jest oceniane najpierw, a Case instrukcje są następnie oceniane w kolejności deklaracji tekstowej. Pierwsza Case instrukcja, która ocenia True , ma wykonany blok. Jeśli żadna instrukcja nie Case zwróci wartości True i istnieje Case Else instrukcja , zostanie wykonany ten blok. Po zakończeniu wykonywania bloku wykonywanie przechodzi na koniec instrukcji Select .

Case Wykonanie bloku nie jest dozwolone do "przejścia" do następnej sekcji przełącznika. Zapobiega to typowej klasie usterek występujących w innych językach, gdy Case instrukcja zakończenia zostanie przypadkowo pominięta. Poniższy przykład ilustruje to zachowanie:

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

Kod drukuje:

x = 10

Mimo że Case 10 i Case 20 - 10 wybrać tę samą wartość, jest wykonywana, Case 10 ponieważ poprzedza Case 20 - 10 tekstowo. Po osiągnięciu następnego Case wykonania będzie kontynuowane po instrukcji Select .

Klauzula Case może mieć dwie formy. Jeden formularz jest opcjonalnym Is słowem kluczowym, operatorem porównania i wyrażeniem. Wyrażenie jest konwertowane na typ Select wyrażenia. Jeśli wyrażenie nie jest niejawnie konwertowane na typ Select wyrażenia, wystąpi błąd czasu kompilacji. Select Jeśli wyrażenie to E, operator porównania to Op, a Case wyrażenie to E1, przypadek jest obliczany jako E OP E1. Operator musi być prawidłowy dla typów dwóch wyrażeń; w przeciwnym razie wystąpi błąd czasu kompilacji.

Drugi formularz jest wyrażeniem opcjonalnym, po którym następuje słowo kluczowe To i drugie wyrażenie. Oba wyrażenia są konwertowane na typ Select wyrażenia. Jeśli któreś z wyrażeń nie jest niejawnie konwertowane na typ Select wyrażenia, wystąpi błąd czasu kompilacji. Select Jeśli wyrażenie to E, pierwszym Case wyrażeniem jest E1, a drugie Case wyrażenie to E2, Case jest obliczane jako E = E1 (jeśli nie E2 określono) lub (E >= E1) And (E <= E2). Operatory muszą być prawidłowe dla typów dwóch wyrażeń; w przeciwnym razie wystąpi błąd czasu kompilacji.

Instrukcje pętli

Instrukcje pętli umożliwiają wielokrotne wykonywanie instrukcji w ich treści.

LoopStatement
    : WhileStatement
    | DoLoopStatement
    | ForStatement
    | ForEachStatement
    ;

Za każdym razem, gdy wprowadzona jest treść pętli, nowa kopia jest składana ze wszystkich zmiennych lokalnych zadeklarowanych w tej treści, zainicjowanych do poprzednich wartości zmiennych. Każde odwołanie do zmiennej w treści pętli będzie używać ostatnio wykonanej kopii. Ten kod przedstawia przykład:

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

Kod generuje dane wyjściowe:

31    32    33

Po wykonaniu treści pętli używana jest bieżąca kopia zmiennej. Na przykład instrukcja Dim y = x odnosi się do najnowszej kopii y i oryginalnej kopii .x A gdy zostanie utworzona funkcja lambda, pamięta, która kopia zmiennej była bieżąca w momencie jego utworzenia. W związku z tym każda lambda używa tej samej udostępnionej kopii xelementu , ale innej kopii y. Na końcu programu, kiedy wykonuje lambdy, że współużytkowany x kopię, do której się odwołują, znajduje się teraz w końcowej wartości 3.

Należy pamiętać, że jeśli nie ma wyrażeń lambd lub LINQ, nie można wiedzieć, że nowa kopia jest wykonana we wpisie pętli. W rzeczywistości optymalizacje kompilatora unikają tworzenia kopii w tym przypadku. Należy również pamiętać, że jest to niedozwolone do GoTo pętli zawierającej wyrażenia lambd lub LINQ.

Chwila... Zakończ czas i wykonaj... Instrukcje pętli

Pętla While instrukcji or Do oparta na wyrażeniu logicznym.

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

Pętla While instrukcji jest pętla, o ile wyrażenie logiczne daje wartość true. Do Instrukcja pętli może zawierać bardziej złożony warunek. Wyrażenie może zostać umieszczone po słowie Do kluczowym lub po słowie Loop kluczowym, ale nie po obu słowach. Wyrażenie warunkowe jest obliczane zgodnie z wyrażeniami logicznymi sekcji. (Uwaga: nie wymaga to, aby wyrażenie miało typ logiczny). Ważne jest również, aby w ogóle nie określić wyrażenia; w takim przypadku pętla nigdy nie zakończy się. Jeśli wyrażenie zostanie umieszczone po Do, zostanie obliczone przed wykonaniem bloku pętli w każdej iteracji. Jeśli wyrażenie zostanie umieszczone po Loop, zostanie obliczone po wykonaniu bloku pętli na każdej iteracji. Umieszczenie wyrażenia po Loop spowoduje wygenerowanie kolejnej pętli niż umieszczenie po Do. W poniższym przykładzie pokazano to zachowanie:

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

Kod generuje dane wyjściowe:

Second Loop

W przypadku pierwszej pętli warunek jest obliczany przed wykonaniem pętli. W przypadku drugiej pętli warunek jest wykonywany po wykonaniu pętli. Wyrażenie warunkowe musi być poprzedzone While słowem kluczowym lub Until słowem kluczowym. Pierwszy z nich przerywa pętlę, jeśli warunek daje wartość false, a drugi, gdy warunek ma wartość true.

Uwaga. Until nie jest słowem zastrzeżonym.

Dla... Następne instrukcje

Pętle For...Next instrukcji oparte na zestawie granic. Instrukcja For określa zmienną kontrolki pętli, dolną granicę wyrażenia, wyrażenie górnej granicy i opcjonalne wyrażenie wartości kroku. Zmienna sterowania pętli jest określana za pomocą identyfikatora, po którym następuje klauzula opcjonalna As lub wyrażenie.

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

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

NextExpressionList
    : Expression ( Comma Expression )*
    ;

Zgodnie z poniższymi regułami zmienna sterowania pętli odwołuje się do nowej zmiennej lokalnej specyficznej dla tej For...Next instrukcji lub do istniejącej zmiennej albo do wyrażenia.

  • Jeśli zmienna sterowania pętli jest identyfikatorem z klauzulą As , identyfikator definiuje nową zmienną lokalną typu określonego As w klauzuli, w zakresie do całej For pętli.

  • Jeśli zmienna sterowania pętli jest identyfikatorem bez As klauzuli, identyfikator jest najpierw rozpoznawany przy użyciu prostych reguł rozpoznawania nazw (zobacz Sekcję Proste wyrażenia nazw), z tą różnicą, że to wystąpienie identyfikatora nie będzie w sobie i powoduje utworzenie niejawnej zmiennej lokalnej (Sekcja Niejawne deklaracje lokalne).

    • Jeśli ta rozdzielczość powiedzie się i wynik zostanie sklasyfikowany jako zmienna, zmienna sterowania pętli jest to, że wstępnie istniejąca zmienna.

    • Jeśli rozwiązanie zakończy się niepowodzeniem lub rozwiązanie powiedzie się, a wynik zostanie sklasyfikowany jako typ, wówczas:

      • Jeśli jest używana wnioskowanie typu zmiennej lokalnej, identyfikator definiuje nową zmienną lokalną, której typ jest wnioskowany z wyrażeń powiązanych i kroków, w zakresie do całej For pętli;
      • Jeśli wnioskowanie typu zmiennej lokalnej nie jest używane, ale niejawna deklaracja lokalna jest tworzona, a niejawna zmienna lokalna jest tworzona, której zakres jest całą metodą (Sekcja niejawnych deklaracji lokalnych), a zmienna sterowania pętli odwołuje się do tej wstępnie istniejącej zmiennej;
      • jeśli nie są używane ani wnioskowanie typu zmiennej lokalnej, ani niejawne deklaracje lokalne, jest to błąd.
    • Jeśli rozwiązanie powiedzie się z czymś sklasyfikowanym jako typ ani zmienna, jest to błąd.

  • Jeśli zmienna sterowa pętli jest wyrażeniem, wyrażenie musi być klasyfikowane jako zmienna.

Zmienna sterowania pętli nie może być używana przez inną instrukcję otaczającą For...Next . Typ zmiennej For sterującej pętli instrukcji określa typ iteracji i musi być jednym z następujących elementów:

  • Byte, SByte, , , UIntegerLongSingleShortIntegerULongDecimalUShortDouble
  • Typ wyliczony
  • Object
  • Typ T , który ma następujące operatory, gdzie B jest typem, który może być używany w wyrażeniu logicznym:
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

Wyrażenia ograniczenia i kroku muszą być niejawnie konwertowane na typ zmiennej sterującej pętli i muszą być klasyfikowane jako wartości. W czasie kompilacji typ zmiennej sterującej pętli jest wnioskowany, wybierając najszerszy typ między dolną granicą, górną granicą i typami wyrażeń kroków. Jeśli nie ma konwersji rozszerzającej między dwoma typami, wystąpi błąd czasu kompilacji.

Jeśli w czasie wykonywania typ zmiennej sterującej pętli to Object, typ iteracji jest wywnioskowany tak samo jak w czasie kompilacji, z dwoma wyjątkami. Po pierwsze, jeśli wyrażenia powiązane i krokowe są wszystkimi typami całkowitymi, ale nie mają najszerszego typu, najszerszy typ, który obejmuje wszystkie trzy typy, zostanie wywnioskowany. Po drugie, jeśli typ zmiennej sterującej pętli zostanie wywnioskowany jako String, Double zostanie wywnioskowany zamiast tego. Jeśli w czasie wykonywania nie można określić żadnego typu kontrolki pętli lub jeśli nie można przekonwertować żadnego z wyrażeń na typ kontrolki pętli, wystąpi.System.InvalidCastException Po wybraniu typu kontrolki pętli na początku pętli ten sam typ będzie używany w całej iteracji, niezależnie od zmian wprowadzonych w wartości w zmiennej sterującej pętli.

Instrukcja For musi być zamknięta przez zgodną Next instrukcję. Instrukcja Next bez zmiennej pasuje do najbardziej wewnętrznej instrukcji otwierania For , podczas gdy Next instrukcja z co najmniej jedną zmienną kontrolki pętli od lewej do prawej dopasuje For pętle pasujące do każdej zmiennej. Jeśli zmienna pasuje do For pętli, która nie jest najbardziej zagnieżdżona pętlą w tym momencie, wynik błędu czasu kompilacji.

Na początku pętli trzy wyrażenia są oceniane w kolejności tekstowej, a do zmiennej sterującej pętli jest przypisywane do niższego ograniczenia wyrażenie. Jeśli wartość kroku zostanie pominięta, jest niejawnie literałem 1, przekonwertowanym na typ zmiennej sterującej pętli. Trzy wyrażenia są obliczane tylko na początku pętli.

Na początku każdej pętli zmienna kontrolna jest porównywana, aby sprawdzić, czy jest większa niż punkt końcowy, jeśli wyrażenie kroku jest dodatnie lub mniejsze niż punkt końcowy, jeśli wyrażenie kroku jest ujemne. Jeśli tak jest, pętla For kończy się; w przeciwnym razie blok pętli jest wykonywany. Jeśli zmienna sterowania pętli nie jest typem pierwotnym, operator porównania jest określany przez to, czy wyrażenie step >= step - step ma wartość true, czy false. W instrukcji Next wartość kroku jest dodawana do zmiennej sterującej, a wykonanie powraca do góry pętli.

Należy pamiętać, że nowa kopia zmiennej sterującej pętli nie jest tworzona w każdej iteracji bloku pętli. W tym względzie For oświadczenie różni się od For Each (sekcja Dla każdego... Następne instrukcje).

Nie można rozgałęzić For w pętli spoza pętli.

Dla każdego... Następne instrukcje

Pętle For Each...Next instrukcji oparte na elementach w wyrażeniu. Instrukcja For Each określa zmienną kontrolki pętli i wyrażenie modułu wyliczającego. Zmienna sterowania pętli jest określana za pomocą identyfikatora, po którym następuje klauzula opcjonalna As lub wyrażenie.

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

Postępując zgodnie z tymi samymi regułami co For...Next instrukcje (sekcja Dla... Następne instrukcje), zmienna kontrolki pętli odwołuje się do nowej zmiennej lokalnej specyficznej dla każdego... Następna instrukcja lub do istniejącej zmiennej lub wyrażenia.

Wyrażenie modułu wyliczającego musi być klasyfikowane jako wartość, a jego typ musi być typem kolekcji lub Object. Jeśli typ wyrażenia modułu wyliczającego to Object, wszystkie przetwarzanie jest odroczone do czasu wykonywania. W przeciwnym razie konwersja musi istnieć z typu elementu kolekcji na typ zmiennej sterującej pętli.

Zmienna sterowania pętli nie może być używana przez inną instrukcję otaczającą For Each . Instrukcja For Each musi być zamknięta przez zgodną Next instrukcję. Instrukcja Next bez zmiennej sterującej pętli jest zgodna z najbardziej otwartą wewnętrzną For Eachwartością . Instrukcja Next z co najmniej jedną zmienną kontrolki pętli będzie zgodna z For Each pętlami, które mają tę samą zmienną sterowania pętli. Jeśli zmienna pasuje do For Each pętli, która nie jest najbardziej zagnieżdżona pętlą w tym momencie, wystąpi błąd czasu kompilacji.

C Typ jest typ kolekcji, jeśli jest to jeden z następujących typów:

  • Wszystkie następujące elementy są spełnione:

    • C zawiera dostępne wystąpienie, metodę współużytkowania lub rozszerzenia z podpisem GetEnumerator() , który zwraca typ E.
    • E zawiera dostępne wystąpienie, udostępnioną lub metodę rozszerzenia z podpisem MoveNext() i zwracanym typem Boolean.
    • E zawiera dostępne wystąpienie lub właściwość udostępnioną o nazwie Current , która ma element getter. Typ tej właściwości jest typem elementu typu kolekcji.
  • Implementuje interfejs System.Collections.Generic.IEnumerable(Of T), w którym przypadku typ elementu kolekcji jest uważany za T.

  • Implementuje interfejs System.Collections.IEnumerable, w którym przypadku typ elementu kolekcji jest uważany za Object.

Oto przykład klasy, którą można wyliczyć:

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

Przed rozpoczęciem pętli obliczane jest wyrażenie modułu wyliczającego. Jeśli typ wyrażenia nie spełnia wymagań wzorca projektu, wyrażenie jest rzutowe na System.Collections.IEnumerable lub System.Collections.Generic.IEnumerable(Of T). Jeśli typ wyrażenia implementuje interfejs ogólny, interfejs ogólny jest preferowany w czasie kompilacji, ale interfejs niegeneryczny jest preferowany w czasie wykonywania. Jeśli typ wyrażenia implementuje interfejs ogólny wiele razy, instrukcja jest uznawana za niejednoznaczną i występuje błąd czasu kompilacji.

Uwaga. Interfejs niegeneryczny jest preferowany w późnym przypadku ograniczenia, ponieważ wybranie interfejsu ogólnego oznaczałoby, że wszystkie wywołania metod interfejsu będą obejmować parametry typu. Ponieważ nie można znać pasujących argumentów typu w czasie wykonywania, wszystkie takie wywołania musiałyby zostać wykonane przy użyciu wywołań związanych z opóźnieniem. Byłoby to wolniejsze niż wywoływanie interfejsu niegenerykowego, ponieważ interfejs niegeneryczny może być wywoływany przy użyciu wywołań czasu kompilacji.

GetEnumerator jest wywoływana na wynikowej wartości, a wartość zwracana funkcji jest przechowywana w tymczasowym. Następnie na początku każdej iteracji MoveNext wywoływana jest tymczasowa. Jeśli zwraca Falsewartość , pętla kończy się. W przeciwnym razie każda iteracja pętli jest wykonywana w następujący sposób:

  1. Jeśli zmienna sterowania pętli zidentyfikowała nową zmienną lokalną (a nie istniejącą wcześniej), zostanie utworzona nowa kopia tej zmiennej lokalnej. W przypadku bieżącej iteracji wszystkie odwołania w bloku pętli będą odwoływać się do tej kopii.
  2. Właściwość Current jest pobierana, coerced do typu zmiennej sterującej pętli (niezależnie od tego, czy konwersja jest niejawna, czy jawna) i przypisana do zmiennej sterującej pętli.
  3. Blok pętli jest wykonywany.

Uwaga. Istnieje niewielka zmiana zachowania między wersją 10.0 a 11.0 języka. Przed 11.0 nowa zmienna iteracji nie została utworzona dla każdej iteracji pętli. Ta różnica jest zauważalna tylko wtedy, gdy zmienna iteracji jest przechwytywana przez wyrażenie lambda lub LINQ, które jest następnie wywoływane po pętli:

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

Do wersji Visual Basic 10.0 pojawiło się ostrzeżenie w czasie kompilacji i wydrukowano "3" trzy razy. To dlatego, że istniała tylko jedna zmienna "x" współużytkowana przez wszystkie iteracje pętli, a wszystkie trzy lambdy przechwyciły tę samą wartość "x", a do czasu wykonania lambda trzymały liczbę 3. Od wersji Visual Basic 11.0 jest drukowana wartość "1, 2, 3". Wynika to z faktu, że każda lambda przechwytuje inną zmienną "x".

Uwaga. Bieżący element iteracji jest konwertowany na typ zmiennej sterującej pętli, nawet jeśli konwersja jest jawna, ponieważ nie ma wygodnego miejsca do wprowadzenia operatora konwersji w instrukcji . Stało się to szczególnie kłopotliwe podczas pracy z przestarzałym typem System.Collections.ArrayList, ponieważ jego typ elementu to Object. Wymagałoby to rzutów w wielu pętlach, co czuliśmy, że nie było idealne. Jak na ironię, typy ogólne umożliwiły tworzenie silnie typizowanej kolekcji, System.Collections.Generic.List(Of T)która mogła nas przemyśleć ten punkt projektowania, ale ze względów zgodności nie można go teraz zmienić.

Po osiągnięciu instrukcji Next wykonanie powraca do góry pętli. Jeśli zmienna jest określona po słowie Next kluczowym, musi być taka sama jak pierwsza zmienna po .For Each Rozważmy na przykład następujący kod:

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

Jest on odpowiednikiem następującego kodu:

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

Jeśli typ E modułu wyliczającego implementuje System.IDisposableelement , moduł wyliczający jest usuwany po wyjściu z pętli przez wywołanie Dispose metody . Dzięki temu zasoby przechowywane przez moduł wyliczający zostaną zwolnione. Jeśli metoda zawierająca For Each instrukcję nie używa obsługi błędów bez struktury, For Each instrukcja jest opakowana w instrukcję z Dispose metodą wywołaną Try w elemecie Finally w celu zapewnienia czyszczenia.

Uwaga. Typ System.Array jest typem kolekcji, a ponieważ wszystkie typy tablic pochodzą z System.Array, dowolne wyrażenie typu tablicy jest dozwolone w instrukcji For Each . W przypadku tablic jednowymiarowych For Each instrukcja wylicza elementy tablicy w kolejności rosnącej indeksu, począwszy od indeksu 0 i kończąc na długości indeksu - 1. W przypadku tablic wielowymiarowych indeksy wymiaru z prawej strony są najpierw zwiększane.

Na przykład poniższy kod drukuje 1 2 3 4polecenie :

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

Nie jest prawidłowa, aby rozgałęzić For Each blok instrukcji spoza bloku.

instrukcje Exception-Handling

Język Visual Basic obsługuje obsługę wyjątków strukturalnych i obsługę wyjątków bez struktury. W metodzie może być używany tylko jeden styl obsługi wyjątków, ale Error instrukcja może być używana w obsłudze wyjątków strukturalnych. Jeśli metoda używa obu stylów obsługi wyjątków, wyniki błędu czasu kompilacji.

ErrorHandlingStatement
    : StructuredErrorStatement
    | UnstructuredErrorStatement
    ;

Instrukcje Exception-Handling ustrukturyzowane

Obsługa wyjątków strukturalnych to metoda obsługi błędów przez deklarowanie jawnych bloków, w których będą obsługiwane pewne wyjątki. Obsługa wyjątków strukturalnych odbywa się za pomocą instrukcji Try .

StructuredErrorStatement
    : ThrowStatement
    | TryStatement
    ;

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

Przykład:

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

Instrukcja Try składa się z trzech rodzajów bloków: bloków try, bloków catch i wreszcie bloków. Blok try to blok instrukcji zawierający instrukcje do wykonania. Blok catch to blok instrukcji, który obsługuje wyjątek. Wreszcie blok to blok instrukcji zawierający instrukcje do uruchomienia po zakończeniu Try działania instrukcji, niezależnie od tego, czy wystąpił wyjątek i czy został obsłużony. Instrukcja Try , która może zawierać tylko jeden blok try i jeden blok na końcu, musi zawierać co najmniej jeden blok catch lub na koniec blok. Nie można jawnie przenieść wykonania do bloku try z wyjątkiem bloku catch w tej samej instrukcji.

Bloki na koniec

Blok Finally jest zawsze wykonywany, gdy wykonanie pozostawia dowolną część instrukcji Try . Do wykonania Finally bloku nie jest wymagana żadna jawna akcja. Gdy wykonanie opuści Try instrukcję, system automatycznie wykona Finally blok, a następnie przeniesie wykonanie do zamierzonego miejsca docelowego. Blok Finally jest wykonywany niezależnie od tego, w jaki sposób wykonanie pozostawia Try instrukcję: przez koniec bloku, przez koniec TryCatch bloku, za pośrednictwem Exit Try instrukcji, za pomocą GoTo instrukcji lub przez nieobsługiwowanie wyjątku zgłaszanego.

Należy pamiętać, że Await wyrażenie w metodzie asynchronicznej i Yield instrukcja w metodzie iteratora może spowodować wstrzymanie przepływu sterowania w wystąpieniu metody asynchronicznej lub iteratora i wznowienie w innym wystąpieniu metody. Jednak jest to jedynie zawieszenie wykonywania i nie wiąże się z wyjściem z odpowiedniej metody asynchronicznej ani wystąpienia metody iteratora, a więc nie powoduje Finally wykonania bloków.

Jawne przeniesienie wykonania do Finally bloku jest nieprawidłowe. Jest ono również nieprawidłowe w celu przeniesienia wykonania z Finally bloku z wyjątkiem wyjątku.

FinallyStatement
    : 'Finally' StatementTerminator
      Block?
    ;

Bloki przechwytywania błędów

Jeśli podczas przetwarzania bloku wystąpi wyjątek, każda Catch instrukcja jest badana w kolejności tekstowejTry, aby określić, czy obsługuje wyjątek.

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

Identyfikator określony w klauzuli Catch reprezentuje wyjątek, który został zgłoszony. Jeśli identyfikator zawiera klauzulę As , identyfikator jest uznawany za zadeklarowany w Catch przestrzeni deklaracji lokalnej bloku. W przeciwnym razie identyfikator musi być zmienną lokalną (nie zmienną statyczną), która została zdefiniowana w bloku zawierającym.

Klauzula Catch bez identyfikatora przechwyci wszystkie wyjątki pochodzące z System.Exceptionelementu . Klauzula Catch z identyfikatorem będzie przechwytywać tylko wyjątki, których typy są takie same jak lub pochodzące z typu identyfikatora. Typ musi mieć System.Exceptionwartość lub typ pochodzący z System.Exceptionklasy . Gdy zostanie przechwycony wyjątek pochodzący z System.Exceptionklasy , odwołanie do obiektu wyjątku jest przechowywane w obiekcie zwracanym przez funkcję Microsoft.VisualBasic.Information.Err.

Klauzula Catch z klauzulą When będzie przechwytywać wyjątki tylko wtedy, gdy wyrażenie zwróci wartość True; typ wyrażenia musi być wyrażeniem logicznym zgodnie z wyrażeniami logicznymi sekcji. Klauzula When jest stosowana tylko po sprawdzeniu typu wyjątku, a wyrażenie może odwoływać się do identyfikatora reprezentującego wyjątek, jak pokazano w tym przykładzie:

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

Ten przykład drukuje:

Third handler

Jeśli klauzula Catch obsługuje wyjątek, wykonanie jest transferem do Catch bloku. Na końcu Catch bloku wykonywanie jest transferem do pierwszej instrukcji po instrukcji Try . Instrukcja Try nie będzie obsługiwać żadnych wyjątków zgłaszanych w Catch bloku. Jeśli żadna klauzula nie Catch obsługuje wyjątku, wykonywanie jest transferowane do lokalizacji określonej przez system.

Jawne przeniesienie wykonania do Catch bloku jest nieprawidłowe.

Filtry w klauzulach When są zwykle oceniane przed zgłoszeniem wyjątku. Na przykład poniższy kod wyświetli tekst "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

Jednak metody asynchroniczne i iteracyjne powodują, że wszystkie bloki wewnątrz nich zostaną wykonane przed wszelkimi filtrami na zewnątrz. Jeśli na przykład powyższy kod miał Async Sub Foo()wartość , dane wyjściowe to "Finally, Filter, Catch".

Throw, instrukcja

Instrukcja Throw zgłasza wyjątek, który jest reprezentowany przez wystąpienie typu pochodzącego z System.Exceptionklasy .

ThrowStatement
    : 'Throw' Expression? StatementTerminator
    ;

Jeśli wyrażenie nie jest klasyfikowane jako wartość lub nie jest typem pochodzącym z System.Exceptionklasy , wystąpi błąd czasu kompilacji. Jeśli wyrażenie zwróci wartość null w czasie wykonywania, zostanie System.NullReferenceException zgłoszony wyjątek.

Instrukcja Throw może pominąć wyrażenie w bloku catch instrukcji Try , o ile nie ma interweniowania w końcu bloku. W takim przypadku instrukcja ponownie zwraca wyjątek, który jest obecnie obsługiwany w bloku catch. Przykład:

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

Instrukcje Exception-Handling bez struktury

Obsługa wyjątków bez struktury to metoda obsługi błędów wskazująca instrukcje do rozgałęzienia w przypadku wystąpienia wyjątku. Obsługa wyjątków bez struktury jest implementowana przy użyciu trzech instrukcji: Error instrukcji, On Error instrukcji i instrukcji Resume .

UnstructuredErrorStatement
    : ErrorStatement
    | OnErrorStatement
    | ResumeStatement
    ;

Przykład:

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

Gdy metoda używa obsługi wyjątków bez struktury, dla całej metody jest ustanawiana pojedyncza ustrukturyzowana procedura obsługi wyjątków, która przechwytuje wszystkie wyjątki. (Należy pamiętać, że w konstruktorach ta procedura obsługi nie rozszerza wywołania do wywołania na New początku konstruktora). Następnie metoda śledzi najnowszą lokalizację programu obsługi wyjątków i najnowszy wyjątek, który został zgłoszony. Podczas wprowadzania do metody lokalizacja programu obsługi wyjątków i wyjątek są ustawione na Nothingwartość . Gdy wyjątek jest zgłaszany w metodzie korzystającej z obsługi wyjątków bez struktury, odwołanie do obiektu wyjątku jest przechowywane w obiekcie zwracanym przez funkcję Microsoft.VisualBasic.Information.Err.

Instrukcje obsługi błędów bez struktury nie są dozwolone w metodach iteratora ani asynchronicznych.

Error, instrukcja

Instrukcja Error zgłasza System.Exception wyjątek zawierający numer wyjątku języka Visual Basic 6. Wyrażenie musi być klasyfikowane jako wartość, a jego typ musi być niejawnie konwertowany na Integer.

ErrorStatement
    : 'Error' Expression StatementTerminator
    ;

On Error, instrukcja

Instrukcja On Error modyfikuje najnowszy stan obsługi wyjątków.

OnErrorStatement
    : 'On' 'Error' ErrorClause StatementTerminator
    ;

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

Może być używany na jeden z czterech sposobów:

  • On Error GoTo -1 resetuje najnowszy wyjątek do Nothing.

  • On Error GoTo 0Resetuje najnowszą lokalizację programu obsługi wyjątków na .Nothing

  • On Error GoTo LabelName określa etykietę jako najnowszą lokalizację programu obsługi wyjątków. Tej instrukcji nie można użyć w metodzie zawierającej wyrażenie lambda lub zapytanie.

  • On Error Resume Next Resume Next program ustanawia zachowanie jako najnowszą lokalizację programu obsługi wyjątków.

Resume, instrukcja

Instrukcja Resume zwraca wykonanie do instrukcji, która spowodowała najnowszy wyjątek.

ResumeStatement
    : 'Resume' ResumeClause? StatementTerminator
    ;

ResumeClause
    : 'Next'
    | LabelName
    ;

Next Jeśli modyfikator zostanie określony, wykonanie powróci do instrukcji, która byłaby wykonywana po instrukcji, która spowodowała najnowszy wyjątek. Jeśli zostanie określona nazwa etykiety, wykonanie powróci do etykiety.

Ponieważ instrukcja SyncLock zawiera niejawny blok Resume obsługi błędów ustrukturyzowanych i Resume Next ma specjalne zachowania dla wyjątków występujących w SyncLock instrukcjach. Resume Zwraca wykonanie na początku instrukcji SyncLock , a funkcja Resume Next zwraca wykonanie do następnej instrukcji po instrukcji SyncLock . Rozważmy na przykład następujący kod:

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

Wyświetla następujący wynik.

Before exception
Before exception
After SyncLock

Po raz pierwszy za pomocą instrukcji SyncLockResume zwraca wykonanie na początku instrukcji SyncLock . Po raz drugi za pomocą instrukcji SyncLockResume Next funkcja zwraca wykonanie na końcu instrukcji SyncLock . Resume i Resume Next nie są dozwolone w instrukcji SyncLock .

We wszystkich przypadkach, gdy Resume instrukcja jest wykonywana, najnowszy wyjątek jest ustawiony na Nothingwartość . Resume Jeśli instrukcja jest wykonywana bez ostatniego wyjątku, instrukcja zgłasza System.Exception wyjątek zawierający numer 20 błędu języka Visual Basic (Wznów bez błędu).

Instrukcje gałęzi

Instrukcje gałęzi modyfikują przepływ wykonywania w metodzie . Istnieją sześć instrukcji gałęzi:

  1. Instrukcja GoTo powoduje, że wykonanie jest przenoszone do określonej etykiety w metodzie . Nie można wpuścić do GoToTrybloku pętli , , SyncLockUsing, WithForFor Each ani do żadnego bloku pętli, jeśli lokalna zmienna tego bloku jest przechwytywana w wyrażeniu lambda lub LINQ.
  2. Instrukcja Exit przenosi wykonywanie do następnej instrukcji po zakończeniu natychmiast zawierającej instrukcję bloku określonego rodzaju. Jeśli blok jest blokiem metody, przepływ sterowania kończy metodę zgodnie z opisem w sekcji Przepływ sterowania. Exit Jeśli instrukcja nie jest zawarta w rodzaju bloku określonego w instrukcji, wystąpi błąd czasu kompilacji.
  3. Instrukcja Continue przenosi wykonywanie na koniec natychmiast zawierającej instrukcję pętli bloków określonego rodzaju. Continue Jeśli instrukcja nie jest zawarta w rodzaju bloku określonego w instrukcji, wystąpi błąd czasu kompilacji.
  4. Instrukcja Stop powoduje wystąpienie wyjątku debugera.
  5. Instrukcja End kończy program. Finalizatory są uruchamiane przed zamknięciem, ale ostatecznie bloki wszystkich aktualnie wykonywanych Try instrukcji nie są wykonywane. Ta instrukcja może nie być używana w programach, które nie są wykonywalne (na przykład biblioteki DLL).
  6. Instrukcja Return bez wyrażenia jest równoważna instrukcji Exit Sub or Exit Function . Instrukcja Return z wyrażeniem jest dozwolona tylko w metodzie regularnej, która jest funkcją lub w metodzie asynchronicznej, która jest funkcją z typem Task(Of T) zwrotnym dla niektórych T. Wyrażenie musi być klasyfikowane jako wartość niejawnie konwertowana do zmiennej zwracanej funkcji (w przypadku metod regularnych) lub zmiennej zwracanej przez zadanie (w przypadku metod asynchronicznych). Jego zachowanie polega na ocenie wyrażenia, a następnie przechowywaniu go w zmiennej zwracanej, a następnie wykonaniu niejawnej Exit Function instrukcji.
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
    ;

instrukcje Array-Handling

Dwie instrukcje upraszczają pracę z tablicami: ReDim instrukcjami i Erase instrukcjami.

ArrayHandlingStatement
    : RedimStatement
    | EraseStatement
    ;

ReDim, instrukcja

Instrukcja ReDim tworzy wystąpienie nowych tablic.

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

RedimClauses
    : RedimClause ( Comma RedimClause )*
    ;

RedimClause
    : Expression ArraySizeInitializationModifier
    ;

Każda klauzula w instrukcji musi być klasyfikowana jako zmienna lub dostęp do właściwości, którego typ jest typem tablicy lub Object, a następnie listę granic tablicy. Liczba granic musi być spójna z typem zmiennej; dowolna liczba granic jest dozwolona dla elementu Object. W czasie wykonywania tablica jest tworzone dla każdego wyrażenia od lewej do prawej z określonymi granicami, a następnie przypisywana do zmiennej lub właściwości. Jeśli typem zmiennej jest Object, liczba wymiarów jest liczbą określonych wymiarów, a typ elementu tablicy to Object. Jeśli dana liczba wymiarów jest niezgodna ze zmienną lub właściwością w czasie wykonywania, wystąpi błąd czasu kompilacji. Przykład:

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 Jeśli słowo kluczowe jest określone, wyrażenia muszą być również klasyfikowalne jako wartość, a nowy rozmiar dla każdego wymiaru z wyjątkiem prawego musi być taki sam jak rozmiar istniejącej tablicy. Wartości w istniejącej tablicy są kopiowane do nowej tablicy: jeśli nowa tablica jest mniejsza, istniejące wartości zostaną odrzucone; Jeśli nowa tablica jest większa, dodatkowe elementy zostaną zainicjowane do wartości domyślnej typu elementu tablicy. Rozważmy na przykład następujący kod:

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

Wyświetla on następujący wynik:

3, 0

Jeśli istniejące odwołanie do tablicy jest wartością null w czasie wykonywania, nie zostanie podany błąd. Poza najbardziej odpowiednim wymiarem, jeśli rozmiar wymiaru ulegnie zmianie, System.ArrayTypeMismatchException zostanie zgłoszony element .

Uwaga. Preserve nie jest słowem zastrzeżonym.

Erase, instrukcja

Instrukcja Erase ustawia każdą ze zmiennych tablicy lub właściwości określonych w instrukcji na Nothing. Każde wyrażenie w instrukcji musi być klasyfikowane jako zmienna lub dostęp do właściwości, którego typ jest typem tablicy lub Object. Przykład:

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, instrukcja

Wystąpienia typów są automatycznie zwalniane przez moduł odśmiecający elementy bezużyteczne po uruchomieniu kolekcji i nie znaleziono żadnych odwołań na żywo do wystąpienia. Jeśli typ zawiera szczególnie cenny i ograniczony zasób (na przykład połączenia bazy danych lub uchwyty plików), może nie być pożądane poczekanie na następne odzyskiwanie pamięci w celu oczyszczenia określonego wystąpienia typu, które nie jest już używane. Aby zapewnić lekki sposób wydawania zasobów przed kolekcją, typ może zaimplementować System.IDisposable interfejs. Typ, który udostępnia metodę Dispose , którą można wywołać, aby wymusić natychmiastowe zwolnienie cennych zasobów, takich jak:

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

        ' Do some work
        ...

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

Instrukcja Using automatyzuje proces uzyskiwania zasobu, wykonywania zestawu instrukcji, a następnie usuwania zasobu. Instrukcja może przyjmować dwie formy: w jednym, zasób jest zmienną lokalną zadeklarowaną jako część instrukcji i traktowana jako zwykła instrukcja deklaracji zmiennej lokalnej; z drugiej strony zasób jest wynikiem wyrażenia.

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

UsingResources
    : VariableDeclarators
    | Expression
    ;

Jeśli zasób jest instrukcją deklaracji zmiennej lokalnej, typ deklaracji zmiennej lokalnej musi być typem, który może zostać niejawnie przekonwertowany na System.IDisposable. Zadeklarowane zmienne lokalne są tylko do odczytu, ograniczone do Using bloku instrukcji i muszą zawierać inicjator. Jeśli zasób jest wynikiem wyrażenia, wyrażenie musi być klasyfikowane jako wartość i musi być typu, który może zostać niejawnie przekonwertowany na System.IDisposable. Wyrażenie jest obliczane tylko raz na początku instrukcji.

Blok Using jest niejawnie zawarty przez instrukcję Try , której ostatecznie blok wywołuje metodę IDisposable.Dispose w zasobie. Gwarantuje to, że zasób zostanie usunięty nawet wtedy, gdy zostanie zgłoszony wyjątek. W związku z tym nie można rozgałęzić Using bloku spoza bloku, a Using blok jest traktowany jako pojedyncza instrukcja dla celów Resume i Resume Next. Jeśli zasób ma wartość Nothing, nie zostanie wykonane żadne wywołanie Dispose metody . W związku z tym przykład:

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

jest odpowiednikiem:

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

Instrukcja Using , która zawiera instrukcję deklaracji zmiennej lokalnej, może jednocześnie uzyskać wiele zasobów, co jest równoważne zagnieżdżonym Using instrukcjom. Na przykład instrukcja Using formularza:

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

jest odpowiednikiem:

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

Await, instrukcja

Instrukcja await ma taką samą składnię jak wyrażenie operatora await ( Operator Await sekcji), jest dozwolone tylko w metodach, które również zezwalają na wyrażenia await i mają takie samo zachowanie jak wyrażenie operatora await.

Można go jednak sklasyfikować jako wartość lub wartość void. Każda wartość wynikająca z oceny wyrażenia operatora await jest odrzucana.

AwaitStatement
    : AwaitOperatorExpression StatementTerminator
    ;

Yield, instrukcja

Instrukcje wydajności są powiązane z metodami iteratora, które opisano w sekcji Metody iteratora.

YieldStatement
    : 'Yield' Expression StatementTerminator
    ;

Yield jest słowem zarezerwowanym, jeśli natychmiast otaczającej metodę lub wyrażenie lambda, w którym się pojawia, ma Iterator modyfikator, a jeśli Yield pojawia się po tym Iterator modyfikatorze; nie jest zarezerwowany w innym miejscu. Nie jest również zastrzeżony w dyrektywach preprocesora. Instrukcja yield jest dozwolona tylko w treści wyrażenia lambda lub metody, gdzie jest słowem zarezerwowanym. W obrębie metody bezpośrednio otaczającej lub lambda instrukcja yield może nie występować wewnątrz treści Catch lub Finally bloku, ani wewnątrz treści instrukcji SyncLock .

Instrukcja yield przyjmuje pojedyncze wyrażenie, które musi być klasyfikowane jako wartość i którego typ jest niejawnie konwertowany na typ bieżącej zmiennej iteratora (Metody iteratora sekcji) jego otaczającej metody iteratora.

Przepływ sterowania osiąga instrukcję Yield tylko wtedy, gdy MoveNext metoda jest wywoływana na obiekcie iteratora. (Jest to spowodowane tym, że wystąpienie metody iteratora tylko kiedykolwiek wykonuje instrukcje z powodu MoveNext wywoływanych metod lub Dispose na obiekcie iteratora, a Dispose metoda będzie wykonywać tylko kod w Finally blokach, gdzie Yield jest niedozwolone).

Po wykonaniu Yield instrukcji wyrażenie jest obliczane i przechowywane w bieżącej zmiennej iteratora wystąpienia metody iteratora skojarzonego z tym obiektem iteratora. Wartość True jest zwracana do wywołania MoveNextobiektu , a punkt kontrolny tego wystąpienia przestaje przechodzić do następnego wywołania MoveNext obiektu iteratora.