Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
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,
- Najpierw jest tworzone wystąpienie metody specyficzne dla tego wywołania. To wystąpienie zawiera kopię wszystkich parametrów i zmiennych lokalnych metody.
- Następnie wszystkie jego parametry są inicjowane do podanych wartości, a wszystkie jej zmienne lokalne do wartości domyślnych ich typów.
- 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. - 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),
- Najpierw jest tworzone wystąpienie metody iteracyjnej specyficzne dla tego wywołania. To wystąpienie zawiera kopię wszystkich parametrów i zmiennych lokalnych metody.
- Następnie wszystkie jego parametry są inicjowane do podanych wartości, a wszystkie jej zmienne lokalne do wartości domyślnych ich typów.
- Niejawna zmienna lokalna jest również inicjowana jako bieżąca zmienna iteratora, której typ jest
Ti którego wartość początkowa jest wartością domyślną jej typu. - Punkt kontrolny wystąpienia metody jest następnie ustawiany w pierwszej instrukcji treści metody.
- 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.
- 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órychT, 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 obiektuGetEnumeratorzostanie wywołana, tworzy wystąpienie — specyficzne dla tego wywołaniaGetEnumerator— 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 przypadkuIEnumerator(Of Object)elementu . - Gdy wywoływana jest metoda iteratora, której typ zwracany jest interfejsem
IEnumerableniegeneryjnym, zachowanie jest dokładnie takie samo jak w przypadkuIEnumerable(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
- Najpierw jest tworzone wystąpienie metody asynchronicznej specyficzne dla tej wywołania. To wystąpienie zawiera kopię wszystkich parametrów i zmiennych lokalnych metody.
- Następnie wszystkie jego parametry są inicjowane do podanych wartości, a wszystkie jej zmienne lokalne do wartości domyślnych ich typów.
- W przypadku metody asynchronicznej z typem
Task(Of T)zwrotnym dla niektórychTparametrów niejawna zmienna lokalna jest również inicjowana jako zmienna zwracana zadania, której typ jestTi którego wartość początkowa jest wartością domyślnąT. - Jeśli metoda asynchronicznie jest typu zwracanego
FunctionlubTask(Of T)dla niektórychT, obiekt tego typu jest niejawnie utworzony, skojarzony zTaskbieżą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. - 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 Functiono zwracanym typieTask:Jeśli przepływ sterowania kończy się przez nieobsługiwany wyjątek, stan obiektu asynchronicznego jest ustawiony na
TaskStatus.Faultedwartość , a jegoException.InnerExceptionwłaściwość jest ustawiona na wyjątek (z wyjątkiem: niektórych wyjątków zdefiniowanych przez implementację, takich jakOperationCanceledExceptionzmiana go naTaskStatus.Canceled). Przepływ sterowania jest wznawiany w bieżącym obiekcie wywołującym.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órychTelementów : zachowanie jest tak samo jak powyżej, z tą różnicą, że w przypadkach innych niż wyjątki właściwość obiektuResultasynchronicznego jest również ustawiona na ostateczną wartość zmiennej zwracanej przez zadanie.W przypadku elementu
Async Sub:- 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.
- 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:
Jeśli deklaracja ma znak typu, typ znaku typu jest typem deklaracji lokalnej.
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).
Jeśli nie są używane ścisłe semantyka, typ instrukcji deklaracji lokalnej jest niejawnie
Object.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ślonegoAsw klauzuli, w zakresie do całejForpętli.Jeśli zmienna sterowania pętli jest identyfikatorem bez
Asklauzuli, 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
Forpę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 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
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, gdzieBjest 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:
-
Czawiera dostępne wystąpienie, metodę współużytkowania lub rozszerzenia z podpisemGetEnumerator(), który zwraca typE. -
Ezawiera dostępne wystąpienie, udostępnioną lub metodę rozszerzenia z podpisemMoveNext()i zwracanym typemBoolean. -
Ezawiera dostępne wystąpienie lub właściwość udostępnioną o nazwieCurrent, 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 zaT.Implementuje interfejs
System.Collections.IEnumerable, w którym przypadku typ elementu kolekcji jest uważany zaObject.
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:
- 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.
- Właściwość
Currentjest 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. - 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 -1resetuje najnowszy wyjątek doNothing.On Error GoTo 0Resetuje najnowszą lokalizację programu obsługi wyjątków na .NothingOn Error GoTo LabelNameokreś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 NextResume Nextprogram 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:
- Instrukcja
GoTopowoduje, że wykonanie jest przenoszone do określonej etykiety w metodzie . Nie można wpuścić doGoToTrybloku pętli , ,SyncLockUsing,WithForFor Eachani do żadnego bloku pętli, jeśli lokalna zmienna tego bloku jest przechwytywana w wyrażeniu lambda lub LINQ. - Instrukcja
Exitprzenosi 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.ExitJeśli instrukcja nie jest zawarta w rodzaju bloku określonego w instrukcji, wystąpi błąd czasu kompilacji. - Instrukcja
Continueprzenosi wykonywanie na koniec natychmiast zawierającej instrukcję pętli bloków określonego rodzaju.ContinueJeśli instrukcja nie jest zawarta w rodzaju bloku określonego w instrukcji, wystąpi błąd czasu kompilacji. - Instrukcja
Stoppowoduje wystąpienie wyjątku debugera. - Instrukcja
Endkończy program. Finalizatory są uruchamiane przed zamknięciem, ale ostatecznie bloki wszystkich aktualnie wykonywanychTryinstrukcji nie są wykonywane. Ta instrukcja może nie być używana w programach, które nie są wykonywalne (na przykład biblioteki DLL). - Instrukcja
Returnbez wyrażenia jest równoważna instrukcjiExit SuborExit Function. InstrukcjaReturnz wyrażeniem jest dozwolona tylko w metodzie regularnej, która jest funkcją lub w metodzie asynchronicznej, która jest funkcją z typemTask(Of T)zwrotnym dla niektórychT. 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 niejawnejExit Functioninstrukcji.
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.
Visual Basic language spec