Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Příkazy představují spustitelný kód.
Statement
: LabelDeclarationStatement
| LocalDeclarationStatement
| WithStatement
| SyncLockStatement
| EventStatement
| AssignmentStatement
| InvocationStatement
| ConditionalStatement
| LoopStatement
| ErrorHandlingStatement
| BranchStatement
| ArrayHandlingStatement
| UsingStatement
| AwaitStatement
| YieldStatement
;
Poznámka. Microsoft Visual Basic Compiler umožňuje pouze příkazy, které začínají klíčovým slovem nebo identifikátorem. Příkaz vyvolání "Call (Console).WriteLine" je tedy povolený, ale vyvolání příkazu "(Console).WriteLine" není.
Řízení toku
Tok řízení je posloupnost, ve které se provádějí příkazy a výrazy. Pořadí provádění závisí na konkrétním příkazu nebo výrazu.
Například při vyhodnocování operátoru sčítání (operátor sčítání oddílů) se nejprve vyhodnotí levý operand, pak pravý operand a pak samotný operátor. Bloky se spouští ( bloky oddílů a popisky) tak, že nejprve spustíte první dílčístatek a pak jeden po druhém projdete příkazy bloku.
Implicitní v tomto pořadí je koncept řídicího bodu, což je další operace, která se má provést. Když je vyvolána metoda (nebo "volána"), říkáme, že vytvoří instanci metody. Instance metody se skládá z vlastní kopie parametrů metody a místních proměnných a vlastního řídicího bodu.
Běžné metody
Tady je příklad běžné metody.
Function Test() As Integer
Console.WriteLine("hello")
Return 1
End Function
Dim x = Test() ' invokes the function, prints "hello", assigns 1 to x
Při vyvolání běžné metody
- První instance metody je vytvořena specifická pro toto vyvolání. Tato instance zahrnuje kopii všech parametrů a místních proměnných metody.
- Všechny jeho parametry se pak inicializují na zadané hodnoty a všechny jeho místní proměnné na výchozí hodnoty jejich typů.
- V případě
Function, implicitní místní proměnná je také inicializována funkce návratová proměnná , jejíž název je název funkce, jehož typ je návratový typ funkce a jehož počáteční hodnota je výchozí hodnotou jeho typu. - Řídicí bod instance metody se pak nastaví na prvním příkazu těla metody a tělo metody se okamžitě spustí odsud ( bloky a popisky oddílu).
Když tok řízení ukončí tělo metody normálně – tím, že dosáhnete End Function nebo End Sub tím, že označíte jeho konec, nebo prostřednictvím explicitního Return nebo Exit příkazu – tok řízení se vrátí volající instanci metody. Pokud existuje návratová proměnná funkce, výsledkem vyvolání je konečná hodnota této proměnné.
Když tok řízení ukončí tělo metody neošetřenou výjimkou, tato výjimka se rozšíří do volajícího.
Po ukončení toku řízení již neexistují žádné živé odkazy na instanci metody. Pokud instance metody uchovává pouze odkazy na jeho kopii místních proměnných nebo parametrů, mohou být uvolněny z paměti.
Metody iterátoru
Metody iterátoru se používají jako pohodlný způsob, jak vygenerovat sekvenci, která může být využívána příkazem For Each . Metody iterátoru Yield používají příkaz (Section Yield Statement) k poskytnutí prvků sekvence. (Metoda iterátoru bez Yield příkazů vytvoří prázdnou sekvenci). Tady je příklad metody iterátoru:
Iterator Function Test() As IEnumerable(Of Integer)
Console.WriteLine("hello")
Yield 1
Yield 2
End Function
Dim en = Test()
For Each x In en ' prints "hello" before the first x
Console.WriteLine(x) ' prints "1" and then "2"
Next
Při vyvolání metody iterátoru, jejíž návratový typ je IEnumerator(Of T),
- První instance metody iterátoru je vytvořena specifická pro toto vyvolání. Tato instance zahrnuje kopii všech parametrů a místních proměnných metody.
- Všechny jeho parametry se pak inicializují na zadané hodnoty a všechny jeho místní proměnné na výchozí hodnoty jejich typů.
- Implicitní místní proměnná se také inicializuje jako aktuální proměnná iterátoru, jejíž typ je
Ta jehož počáteční hodnota je výchozím typem. - Řídicí bod instance metody je pak nastaven na prvním příkazu těla metody.
- Objekt iterátoru se pak vytvoří, přidružený k této instanci metody. Objekt iterátoru implementuje deklarovaný návratový typ a má chování, jak je popsáno níže.
- Tok řízení se pak okamžitě obnoví v volajícím a výsledkem vyvolání je objekt iterátoru. Všimněte si, že tento přenos se provádí bez ukončení instance metody iterátoru a nezpůsobí, že se nakonec obslužné rutiny spustí. Instance metody je stále odkazována objektem iterátoru a nebude uvolňování paměti, pokud existuje živý odkaz na objekt iterátoru.
Při přístupu k vlastnosti objektu Current iterátoru se vrátí aktuální proměnná vyvolání.
Když je vyvolána metoda iterátoru objektu MoveNext , vyvolání nevytvoří novou instanci metody. Místo toho se používá existující instance metody (a její řídicí bod a místní proměnné a parametry) – instance, která byla vytvořena při prvním vyvolání metody iterátoru. Tok řízení pokračuje v provádění v řídicím bodě této instance metody a pokračuje tělem metody iterátoru jako obvykle.
Při vyvolání metody objektu iterátoru Dispose se znovu použije existující instance metody. Tok řízení se obnoví v řídicím bodě této instance metody, ale okamžitě se chová, jako by Exit Function příkaz byl další operací.
Výše uvedené popisy chování pro vyvolání MoveNext objektu iterátoru nebo Dispose objektu iterátoru se vztahují pouze v případě, že se již všechny předchozí vyvolání objektu MoveNextDispose iterátoru nebo na něm již vrátily svým volajícím. Pokud ne, chování není definováno.
Když tok řízení ukončí tělo metody iterátoru normálně – prostřednictvím dosažení End Function této značky jeho konce nebo prostřednictvím explicitního Return nebo Exit příkazu – musí to provést v kontextu vyvolání MoveNext nebo Dispose funkce v objektu iterátoru obnovit instanci metody iterátoru a bude používat instanci metody, která byla vytvořena při prvním vyvolání metody iterátoru. Řídicí bod této instance je ponechán v End Function příkazu a tok řízení se obnoví v volajícím. Pokud bylo voláním MoveNext obnoveno, vrátí se volajícímu hodnota False .
Když tok řízení ukončí tělo metody iterátoru prostřednictvím neošetřené výjimky, pak se výjimka rozšíří do volajícího, který bude znovu vyvolána MoveNext nebo z Dispose.
Co se týče ostatních možných návratových typů funkce iterátoru,
- Když je vyvolána metoda iterátoru, jejíž návratový typ je
IEnumerable(Of T)pro některéT, instance je nejprve vytvořena -- specifická pro toto vyvolání metody iterátoru - všech parametrů v metodě a jsou inicializovány zadanými hodnotami. Výsledkem vyvolání je objekt, který implementuje návratový typ. Pokud by byla volána metoda tohoto objektuGetEnumerator, vytvoří instanci specifickou pro toto vyvoláníGetEnumerator-- všech parametrů a místních proměnných v metodě. Inicializuje parametry na hodnoty, které jsou již uloženy, a pokračuje jako pro metodu iterátoru výše. - Když je vyvolána metoda iterátoru, jejíž návratový typ je ne generické rozhraní
IEnumerator, chování je přesně tak jakoIEnumerator(Of Object). - Když je vyvolána metoda iterátoru, jejíž návratový typ je ne generické rozhraní
IEnumerable, chování je přesně tak jakoIEnumerable(Of Object).
Asynchronní metody
Asynchronní metody představují pohodlný způsob, jak provádět dlouhotrvající práci, aniž byste například blokovali uživatelské rozhraní aplikace. Asynchronní je zkratka pro Asynchronní – znamená to, že volající asynchronní metody obnoví jeho provádění okamžitě, ale případné dokončení instance asynchronní metody může proběhnout později v budoucnu. Asynchronní metody konvence mají název s příponou "Async".
Async Function TestAsync() As Task(Of String)
Console.WriteLine("hello")
Await Task.Delay(100)
Return "world"
End Function
Dim t = TestAsync() ' prints "hello"
Console.WriteLine(Await t) ' prints "world"
Poznámka. Asynchronní metody se nespouštějí ve vlákně na pozadí. Místo toho umožňují metodu pozastavit se prostřednictvím operátoru Await a naplánovat, aby se obnovila v reakci na určitou událost.
Při vyvolání asynchronní metody
- První instance asynchronní metody je vytvořena specifická pro toto vyvolání. Tato instance zahrnuje kopii všech parametrů a místních proměnných metody.
- Všechny jeho parametry se pak inicializují na zadané hodnoty a všechny jeho místní proměnné na výchozí hodnoty jejich typů.
- V případě asynchronní metody s návratovým typem
Task(Of T)pro některéT, implicitní místní proměnná je také inicializována nazývá návratová proměnná úkolu, jehož typ jeTa jehož počáteční hodnota je výchozí hodnotaT. - Pokud asynchronní metoda je návratový
FunctiontypTaskneboTask(Of T)pro některéT, pak objekt tohoto typu implicitně vytvořen, přidružený k aktuálnímu vyvolání. To se nazývá asynchronní objekt a představuje budoucí práci, která bude provedena spuštěním instance asynchronní metody. Když ovládací prvek obnoví v volající instanci této asynchronní metody, volající obdrží tento asynchronní objekt jako výsledek jeho vyvolání. - Řídicí bod instance se pak nastaví na první příkaz těla asynchronní metody a okamžitě začne spouštět tělo metody odsud ( Section Blocks and Labels).
Resumption Delegate and Current Caller
Jak je podrobně popsáno v oddílu Await – operátor, má spuštění Await výrazu schopnost pozastavit řídicí bod instance metody, aby se tok řízení dostal jinam. Tok řízení může později pokračovat v řídicím bodu stejné instance prostřednictvím vyvolání delegáta obnovení. Všimněte si, že toto pozastavení se provádí bez ukončení asynchronní metody a nezpůsobí, že se nakonec obslužné rutiny spustí. Na instanci metody stále odkazuje jak delegát obnovení, Task tak Task(Of T) i výsledek (pokud existuje) a nebudou uvolněny z paměti, pokud existuje živý odkaz na delegáta nebo výsledek.
Je užitečné si představit příkaz Dim x = Await WorkAsync() přibližně jako syntaktickou zkratku pro následující:
Dim temp = WorkAsync().GetAwaiter()
If Not temp.IsCompleted Then
temp.OnCompleted(resumptionDelegate)
Return [task]
CONT: ' invocation of 'resumptionDelegate' will resume here
End If
Dim x = temp.GetResult()
V následujícím příkladu je aktuální volající instance metody definován jako původní volající nebo volající delegát obnovení podle toho, co je novější.
Když tok řízení ukončí tělo asynchronní metody – tím, že se dostanete na End Sub konec, End Function nebo přes explicitní Return nebo Exit příkaz nebo neošetřenou výjimku – řídicí bod instance se nastaví na konec metody. Chování pak závisí na návratovém typu asynchronní metody.
V případě návratového
Async FunctiontypuTask:Pokud tok řízení ukončí neošetřenou výjimku, stav asynchronního objektu je nastaven
TaskStatus.Faultedna a jehoException.InnerExceptionvlastnost je nastavena na výjimku (s výjimkou: určité výjimky definované implementací, napříkladOperationCanceledExceptionzměnit naTaskStatus.Canceled). Tok řízení se obnoví v aktuálním volajícím.V opačném případě je stav asynchronního objektu nastaven na
TaskStatus.Completedhodnotu . Tok řízení se obnoví v aktuálním volajícím.(Poznámka: Celý úkol a to, co dělá asynchronní metody zajímavé, je, že když se úkol změní na Dokončeno, všechny metody, které na ni čekají, budou mít nyní spuštěné své delegáty obnovení, tj. dojde k odblokování.)
V případě návratového
Async FunctiontypuTask(Of T)pro některéT: chování je uvedeno výše, s tím rozdílem, že v případech, kdy není výjimka, je vlastnost asynchronního objektuResulttaké nastavena na konečnou hodnotu návratové proměnné úkolu.V případě
Async Sub:- Pokud se tok řízení ukončí neošetřenou výjimkou, pak se tato výjimka rozšíří do prostředí určitým způsobem specifickým pro implementaci. Tok řízení se obnoví v aktuálním volajícím.
- Jinak tok řízení jednoduše obnoví v aktuálním volajícím.
Asynchronní sub
Existuje určité chování specifické pro Microsoft .Async Sub
Pokud SynchronizationContext.Current je Nothing na začátku vyvolání, všechny neošetřené výjimky z asynchronní sub se publikují do fondu vláken.
Pokud SynchronizationContext.Current není Nothing na začátku vyvolání, je OperationStarted() vyvolána v tomto kontextu před zahájením metody a OperationCompleted() po ukončení. Kromě toho se všechny neošetřené výjimky publikují, aby se v kontextu synchronizace znovu zřetědily.
To znamená, že v aplikacích uživatelského rozhraní se pro Async Sub vlákno uživatelského rozhraní vyvolá všechny výjimky, které nezvládne, se znovu odešle vlákno uživatelského rozhraní.
Proměnlivé struktury v asynchronních a iterátorových metodách
Proměnlivé struktury jsou obecně považovány za špatnou praxi a nejsou podporovány asynchronními metodami ani metodami iterátoru. Konkrétně každé vyvolání asynchronní nebo iterátorové metody ve struktuře implicitně funguje na kopii této struktury, která se zkopíruje v okamžiku vyvolání. Příklad:
Structure S
Dim x As Integer
Async Sub Mutate()
x = 2
End Sub
End Structure
Dim s As New S With {.x = 1}
s.Mutate()
Console.WriteLine(s.x) ' prints "1"
Bloky a popisky
Skupina spustitelných příkazů se nazývá blok příkazu. Spuštění bloku příkazu začíná prvním příkazem v bloku. Po provedení příkazu se provede další příkaz v lexikálním pořadí, pokud příkaz přenese provádění jinde nebo dojde k výjimce.
V rámci bloku příkazu není dělení příkazů na logických řádcích významné s výjimkou příkazů deklarací popisků. Popisek je identifikátor, který identifikuje určitou pozici v rámci bloku příkazu, který lze použít jako cíl příkazu větve, například GoTo.
Block
: Statements*
;
LabelDeclarationStatement
: LabelName ':'
;
LabelName
: Identifier
| IntLiteral
;
Statements
: Statement? ( ':' Statement? )*
;
Příkazy deklarace popisku se musí objevit na začátku logického řádku a popisky mohou být identifikátorem nebo celočíselnou literálem. Vzhledem k tomu, že příkazy deklarace popisků i vyvolání můžou obsahovat jeden identifikátor, jeden identifikátor na začátku místního řádku se vždy považuje za příkaz deklarace popisku. Příkazy deklarace popisků musí vždy následovat dvojtečka, a to i v případě, že žádné příkazy následují na stejném logickém řádku.
Popisky mají vlastní prostor deklarací a neruší jiné identifikátory. Následující příklad je platný a používá proměnnou x názvu jako parametr i jako popisek.
Function F(x As Integer) As Integer
If x >= 0 Then
GoTo x
End If
x = -x
x:
Return x
End Function
Obor popisku je tělo metody, která ho obsahuje.
Z důvodu čitelnosti jsou produkce příkazů, které zahrnují více dílčích stavů, považovány za jedinou produkci v této specifikaci, i když jednotlivé dílčí stavy mohou být samy o sobě na označeném řádku.
Místní proměnné a parametry
Předchozí části podrobně uvádějí, jak a kdy jsou instance metody vytvořeny, a s nimi kopie místních proměnných a parametrů metody. Kromě toho se při každém zadání textu smyčky vytvoří nová kopie každé místní proměnné deklarované uvnitř této smyčky, jak je popsáno v příkazech smyčky section, a instance metody teď obsahuje tuto kopii místní proměnné místo předchozí kopie.
Všechny místní hodnoty se inicializují na výchozí hodnotu jejich typu. Místní proměnné a parametry jsou vždy veřejně přístupné. Jedná se o chybu odkazující na místní proměnnou v textové pozici, která předchází jeho deklaraci, jak ukazuje následující příklad:
Class A
Private i As Integer = 0
Sub F()
i = 1
Dim i As Integer ' Error, use precedes declaration.
i = 2
End Sub
Sub G()
Dim a As Integer = 1
Dim b As Integer = a ' This is valid.
End Sub
End Class
Ve výše uvedené F metodě první přiřazení i konkrétně neodkazuje na pole deklarované ve vnějším oboru. Spíše odkazuje na místní proměnnou a je chybná, protože textově předchází deklaraci proměnné.
G V metodě následující deklarace proměnné odkazuje na místní proměnnou deklarovanou v dřívější deklaraci proměnné v rámci stejné deklarace místní proměnné.
Každý blok v metodě vytvoří prostor deklarace pro místní proměnné. Názvy se do tohoto prostoru deklarace zavádějí prostřednictvím deklarací místních proměnných v těle metody a prostřednictvím seznamu parametrů metody, který zavádí názvy do prostoru deklarace vnějšího bloku. Bloky neumožňují stínování názvů prostřednictvím vnoření: jakmile je název deklarován v bloku, název nemusí být předefinován v žádném vnořeném bloku.
V následujícím příkladu jsou tedy F metody chybné G , protože název i je deklarován ve vnějším bloku a nelze je předefinovat ve vnitřním bloku. Tyto H metody I jsou ale platné, protože tyto dvě ijsou deklarovány v samostatných nenořených blocích.
Class A
Sub F()
Dim i As Integer = 0
If True Then
Dim i As Integer = 1
End If
End Sub
Sub G()
If True Then
Dim i As Integer = 0
End If
Dim i As Integer = 1
End Sub
Sub H()
If True Then
Dim i As Integer = 0
End If
If True Then
Dim i As Integer = 1
End If
End Sub
Sub I()
For i As Integer = 0 To 9
H()
Next i
For i As Integer = 0 To 9
H()
Next i
End Sub
End Class
Pokud je metoda funkcí, je speciální místní proměnná implicitně deklarována v prostoru deklarace metody se stejným názvem jako metoda představující návratovou hodnotu funkce. Místní proměnná má při použití ve výrazech speciální sémantiku překladu názvů. Pokud se místní proměnná používá v kontextu, který očekává výraz klasifikovaný jako skupina metod, například vyvolání výrazu, pak se název přeloží na funkci, nikoli na místní proměnnou. Například:
Function F(i As Integer) As Integer
If i = 0 Then
F = 1 ' Sets the return value.
Else
F = F(i - 1) ' Recursive call.
End If
End Function
Použití závorek může způsobit nejednoznačné situace (například F(1), kde F je funkce, jejíž návratový typ je jednorozměrné pole); ve všech nejednoznačných situacích se název překládá na funkci místo místní proměnné. Například:
Function F(i As Integer) As Integer()
If i = 0 Then
F = new Integer(2) { 1, 2, 3 }
Else
F = F(i - 1) ' Recursive call, not an index.
End If
End Function
Když tok řízení opustí tělo metody, hodnota místní proměnné se předá zpět do výrazu vyvolání. Pokud je metoda podprogram, neexistuje žádná taková implicitní místní proměnná a ovládací prvek se jednoduše vrátí do vyvolání výrazu.
Příkazy místní deklarace
Příkaz místní deklarace deklaruje novou místní proměnnou, místní konstantu nebo statickou proměnnou.
Místní proměnné a místní konstanty jsou ekvivalentní proměnným instancí a konstantám vymezeným na metodu a jsou deklarovány stejným způsobem.
Statické proměnné jsou podobné Shared proměnným a deklarují se pomocí modifikátoru Static .
LocalDeclarationStatement
: LocalModifier VariableDeclarators StatementTerminator
;
LocalModifier
: 'Static' | 'Dim' | 'Const'
;
Statické proměnné jsou místní hodnoty, které si zachovají jejich hodnotu napříč vyvoláním metody. Statické proměnné deklarované v rámci nesdílených metod jsou pro každou instanci: každá instance typu, která obsahuje metodu, má svou vlastní kopii statické proměnné. Statické proměnné deklarované v rámci Shared metod jsou na typ; pro všechny instance existuje pouze jedna kopie statické proměnné. Zatímco místní proměnné jsou inicializovány na výchozí hodnotu jejich typu při každé položce do metody, statické proměnné se inicializují pouze na výchozí hodnotu jejich typu při inicializaci typu nebo instance typu. Statické proměnné nemusí být deklarovány ve strukturách nebo obecných metodách.
Místní proměnné, místní konstanty a statické proměnné mají vždy veřejnou přístupnost a nemusí určovat modifikátory přístupnosti. Pokud není pro příkaz místní deklarace zadán žádný typ, určují následující kroky typ místní deklarace:
Pokud deklarace obsahuje znak typu, typ znaku typu je typ místní deklarace.
Je-li místní deklarace místní konstanta nebo pokud místní deklarace je místní proměnná s inicializátorem a používá se odvození typu místní proměnné, typ místní deklarace je odvozen z typu inicializátoru. Pokud inicializátor odkazuje na místní deklaraci, dojde k chybě v době kompilace. (Aby místní konstanty měly inicializátory.)
Pokud se nepoužívá striktní sémantika, typ příkazu místní deklarace je implicitně
Object.V opačném případě dojde k chybě kompilace.
Pokud není zadán žádný typ pro příkaz místní deklarace, který má modifikátor pole nebo typ pole, pak typ místní deklarace je pole se zadaným pořadím a předchozí kroky slouží k určení typu prvku pole. Pokud se použije odvození místního typu proměnné, musí být typ inicializátoru typ pole se stejným obrazcem matice (tj. modifikátory typu pole) jako příkaz místní deklarace. Všimněte si, že odvozený typ elementu může být stále typ pole. Například:
Option Infer On
Module Test
Sub Main()
' Error: initializer is not an array type
Dim x() = 1
' Type is Integer()
Dim y() = New Integer() {}
' Type is Integer()()
Dim z() = New Integer()() {}
' Type is Integer()()()
Dim a()() = New Integer()()() {}
' Error: initializer does not have same array shape
Dim b()() = New Integer(,)() {}
End Sub
End Module
Pokud není zadán žádný typ v příkazu místní deklarace, který má modifikátor typu null, pak typ místní deklarace je nullable verze odvozeného typu nebo odvozený typ sám, pokud již typ hodnoty nullable. Pokud odvozený typ není typ hodnoty, který lze provést s možnou hodnotou null, dojde k chybě kompilace. Pokud jsou modifikátor typu s možnou hodnotou null i modifikátor pole nebo modifikátor typu matice umístěny do místního příkazu deklarace bez typu, je modifikátor typu null považován za použitý pro typ prvku pole a předchozí kroky slouží k určení typu prvku.
Inicializátory proměnných u místních deklaračních příkazů jsou ekvivalentní příkazům přiřazení umístěným v textovém umístění deklarace. Proto pokud provádění větve přes příkaz místní deklarace, proměnná inicializátor se nespustí. Pokud je příkaz místní deklarace proveden více než jednou, inicializátor proměnné se spustí stejný početkrát. Statické proměnné se spouští pouze při prvním spuštění inicializátoru. Pokud při inicializaci statické proměnné dojde k výjimce, statická proměnná se považuje za inicializovanou s výchozí hodnotou typu statické proměnné.
Následující příklad ukazuje použití inicializátorů:
Module Test
Sub F()
Static x As Integer = 5
Console.WriteLine("Static variable x = " & x)
x += 1
End Sub
Sub Main()
Dim i As Integer
For i = 1 to 3
F()
Next i
i = 3
label:
Dim y As Integer = 8
If i > 0 Then
Console.WriteLine("Local variable y = " & y)
y -= 1
i -= 1
GoTo label
End If
End Sub
End Module
Tento program vytiskne:
Static variable x = 5
Static variable x = 6
Static variable x = 7
Local variable y = 8
Local variable y = 8
Local variable y = 8
Inicializátory statických místních prostředí jsou bezpečné pro přístup z více vláken a jsou chráněné proti výjimkám během inicializace. Pokud během statického místního inicializátoru dojde k výjimce, bude mít statický místní výchozí hodnotu a nebude inicializován. Statický místní inicializátor
Module Test
Sub F()
Static x As Integer = 5
End Sub
End Module
je to ekvivalentní
Imports System.Threading
Imports Microsoft.VisualBasic.CompilerServices
Module Test
Class InitFlag
Public State As Short
End Class
Private xInitFlag As InitFlag = New InitFlag()
Sub F()
Dim x As Integer
If xInitFlag.State <> 1 Then
Monitor.Enter(xInitFlag)
Try
If xInitFlag.State = 0 Then
xInitFlag.State = 2
x = 5
Else If xInitFlag.State = 2 Then
Throw New IncompleteInitialization()
End If
Finally
xInitFlag.State = 1
Monitor.Exit(xInitFlag)
End Try
End If
End Sub
End Module
Místní proměnné, místní konstanty a statické proměnné jsou vymezeny na blok příkazů, ve kterém jsou deklarovány. Statické proměnné jsou speciální v tom, že jejich názvy mohou být použity pouze jednou v celé metodě. Například není platné zadat dvě deklarace statických proměnných se stejným názvem, i když jsou v různých blocích.
Implicitní místní deklarace
Kromě místních deklarací příkazů lze místní proměnné deklarovat také implicitně prostřednictvím použití. Jednoduchý výraz názvu, který používá název, který se nepřekládá na něco jiného, deklaruje místní proměnnou tímto názvem. Například:
Option Explicit Off
Module Test
Sub Main()
x = 10
y = 20
Console.WriteLine(x + y)
End Sub
End Module
Implicitní místní deklarace se vyskytuje pouze v kontextech výrazů, které mohou přijmout výraz klasifikovaný jako proměnnou. Výjimkou tohoto pravidla je, že místní proměnná nemusí být implicitně deklarována, pokud je cílem výrazu vyvolání funkce, indexování výrazu nebo výrazu přístupu člena.
Implicitní místní hodnoty se považují za deklarované na začátku metody obsahující. Proto jsou vždy vymezeny na celý text metody, i když jsou deklarovány uvnitř výrazu lambda. Například následující kód:
Option Explicit Off
Module Test
Sub Main()
Dim x = Sub()
a = 10
End Sub
Dim y = Sub()
Console.WriteLine(a)
End Sub
x()
y()
End Sub
End Module
vytiskne hodnotu 10. Implicitní místní hodnoty jsou zadány tak, jako Object kdyby nebyl k názvu proměnné připojen žádný znak typu. V opačném případě je typem znaku typu. Odvození typu místní proměnné se nepoužívá pro implicitní místní hodnoty.
Pokud je explicitní místní deklarace určena prostředím kompilace nebo Option Explicitadresou , musí být všechny místní proměnné explicitně deklarovány a implicitní deklarace proměnné je zakázána.
Příkaz With
Příkaz With umožňuje více odkazů na členy výrazu bez vícenásobného zadání výrazu.
WithStatement
: 'With' Expression StatementTerminator
Block?
'End' 'With' StatementTerminator
;
Výraz musí být klasifikován jako hodnota a je vyhodnocen jednou při vstupu do bloku.
With V rámci bloku příkazu se výraz přístupu člena nebo výraz přístupu slovníku začínající tečkou nebo vykřičníkem vyhodnotí, jako by With výraz před ním byl. Například:
Structure Test
Public x As Integer
Function F() As Integer
Return 10
End Function
End Structure
Module TestModule
Sub Main()
Dim y As Test
With y
.x = 10
Console.WriteLine(.x)
.x = .F()
End With
End Sub
End Module
Větev do With bloku příkazu mimo blok není platná.
Příkaz SyncLock
Příkaz SyncLock umožňuje synchronizaci příkazů ve výrazu, což zajišťuje, že více vláken provádění nespustí stejné příkazy současně.
SyncLockStatement
: 'SyncLock' Expression StatementTerminator
Block?
'End' 'SyncLock' StatementTerminator
;
Výraz musí být klasifikován jako hodnota a je vyhodnocen jednou po zadání do bloku. Při zadávání SyncLock bloku je Shared volána metoda System.Threading.Monitor.Enter pro zadaný výraz, který blokuje, dokud vlákno spuštění nemá výhradní zámek objektu vráceného výrazem. Typ výrazu v SyncLock příkazu musí být odkazovým typem. Například:
Class Test
Private count As Integer = 0
Public Function Add() As Integer
SyncLock Me
count += 1
Add = count
End SyncLock
End Function
Public Function Subtract() As Integer
SyncLock Me
count -= 1
Subtract = count
End SyncLock
End Function
End Class
Výše uvedený příklad se synchronizuje s konkrétní instancí třídy Test , aby se zajistilo, že pro určitou instanci nemůže přičíst nebo odečíst více než jedno vlákno provádění.
Blok SyncLock je implicitně obsažen příkazem Try , jehož Finally blok volá metodu SharedSystem.Threading.Monitor.Exit ve výrazu. Tím se zajistí, že se zámek uvolní i v případě, že dojde k vyvolání výjimky. V důsledku toho je neplatné vytvořit větev do SyncLock bloku mimo blok a SyncLock blok je považován za jediný příkaz pro účely Resume a Resume Next. Výše uvedený příklad odpovídá následujícímu kódu:
Class Test
Private count As Integer = 0
Public Function Add() As Integer
Try
System.Threading.Monitor.Enter(Me)
count += 1
Add = count
Finally
System.Threading.Monitor.Exit(Me)
End Try
End Function
Public Function Subtract() As Integer
Try
System.Threading.Monitor.Enter(Me)
count -= 1
Subtract = count
Finally
System.Threading.Monitor.Exit(Me)
End Try
End Function
End Class
Příkazy událostí
Příkazy RaiseEventa příkazy AddHandlerRemoveHandler vyvolávají události a zpracovávají události dynamicky.
EventStatement
: RaiseEventStatement
| AddHandlerStatement
| RemoveHandlerStatement
;
RaiseEvent – příkaz
Příkaz RaiseEvent upozorní obslužné rutiny událostí, že došlo k určité události.
RaiseEventStatement
: 'RaiseEvent' IdentifierOrKeyword
( OpenParenthesis ArgumentList? CloseParenthesis )? StatementTerminator
;
Jednoduchý výraz názvu v RaiseEvent příkazu je interpretován jako vyhledávání člena .Me Proto je interpretován tak, RaiseEvent x jako by to bylo RaiseEvent Me.x. Výsledek výrazu musí být klasifikován jako přístup k události pro událost definovanou v samotné třídě; události definované na základních typech nelze v RaiseEvent příkazu použít.
Příkaz RaiseEvent se zpracuje jako volání Invoke metody delegáta události pomocí zadaných parametrů, pokud existuje. Pokud je Nothinghodnota delegáta , není vyvolán žádná výjimka. Pokud neexistují žádné argumenty, mohou být závorky vynechány. Například:
Class Raiser
Public Event E1(Count As Integer)
Public Sub Raise()
Static RaiseCount As Integer = 0
RaiseCount += 1
RaiseEvent E1(RaiseCount)
End Sub
End Class
Module Test
Private WithEvents x As Raiser
Private Sub E1Handler(Count As Integer) Handles x.E1
Console.WriteLine("Raise #" & Count)
End Sub
Public Sub Main()
x = New Raiser
x.Raise() ' Prints "Raise #1".
x.Raise() ' Prints "Raise #2".
x.Raise() ' Prints "Raise #3".
End Sub
End Module
Výše uvedená třída Raiser odpovídá:
Class Raiser
Public Event E1(Count As Integer)
Public Sub Raise()
Static RaiseCount As Integer = 0
Dim TemporaryDelegate As E1EventHandler
RaiseCount += 1
' Use a temporary to avoid a race condition.
TemporaryDelegate = E1Event
If Not TemporaryDelegate Is Nothing Then
TemporaryDelegate.Invoke(RaiseCount)
End If
End Sub
End Class
AddHandler a RemoveHandler – příkazy
I když se většina obslužných rutin událostí automaticky připojí prostřednictvím WithEvents proměnných, může být nutné dynamicky přidávat a odebírat obslužné rutiny událostí za běhu.
AddHandler a RemoveHandler příkazy to dělají.
AddHandlerStatement
: 'AddHandler' Expression Comma Expression StatementTerminator
;
RemoveHandlerStatement
: 'RemoveHandler' Expression Comma Expression StatementTerminator
;
Každý příkaz má dva argumenty: první argument musí být výraz klasifikovaný jako přístup k události a druhý argument musí být výraz klasifikovaný jako hodnota. Druhým typem argumentu musí být typ delegáta přidružený k přístupu k události. Například:
Public Class Form1
Public Sub New()
' Add Button1_Click as an event handler for Button1's Click event.
AddHandler Button1.Click, AddressOf Button1_Click
End Sub
Private Button1 As Button = New Button()
Sub Button1_Click(sender As Object, e As EventArgs)
Console.WriteLine("Button1 was clicked!")
End Sub
Public Sub Disconnect()
RemoveHandler Button1.Click, AddressOf Button1_Click
End Sub
End Class
Vzhledem k události E, příkaz volá relevantní add_E nebo remove_E metodu instance přidat nebo odebrat delegáta jako obslužnou rutinu události. Výše uvedený kód je tedy ekvivalentní:
Public Class Form1
Public Sub New()
Button1.add_Click(AddressOf Button1_Click)
End Sub
Private Button1 As Button = New Button()
Sub Button1_Click(sender As Object, e As EventArgs)
Console.WriteLine("Button1 was clicked!")
End Sub
Public Sub Disconnect()
Button1.remove_Click(AddressOf Button1_Click)
End Sub
End Class
Příkazy přiřazení
Příkaz přiřazení přiřadí hodnotu výrazu proměnné. Existuje několik typů přiřazení.
AssignmentStatement
: RegularAssignmentStatement
| CompoundAssignmentStatement
| MidAssignmentStatement
;
Regulární příkazy přiřazení
Jednoduchý příkaz přiřazení ukládá výsledek výrazu do proměnné.
RegularAssignmentStatement
: Expression Equals Expression StatementTerminator
;
Výraz na levé straně operátoru přiřazení musí být klasifikován jako proměnná nebo přístup k vlastnosti, zatímco výraz na pravé straně operátoru přiřazení musí být klasifikován jako hodnota. Typ výrazu musí být implicitně konvertibilní na typ proměnné nebo přístupu k vlastnosti.
Pokud je proměnná přiřazená do prvku pole typu odkazu, provede se kontrola doby běhu, která zajistí, aby byl výraz kompatibilní s typem prvku pole. V následujícím příkladu poslední přiřazení způsobí System.ArrayTypeMismatchException vyvolání, protože instance ArrayList nemůže být uložena v prvku String pole.
Dim sa(10) As String
Dim oa As Object() = sa
oa(0) = Nothing ' This is allowed.
oa(1) = "Hello" ' This is allowed.
oa(2) = New ArrayList() ' System.ArrayTypeMismatchException is thrown.
Pokud je výraz na levé straně operátoru přiřazení klasifikován jako proměnná, příkaz přiřazení uloží hodnotu do proměnné. Pokud je výraz klasifikovaný jako přístup k vlastnosti, příkaz přiřazení změní přístup vlastnosti na vyvolání Set přístupového objektu vlastnosti s hodnotou nahrazenou parametrem value. Například:
Module Test
Private PValue As Integer
Public Property P As Integer
Get
Return PValue
End Get
Set (Value As Integer)
PValue = Value
End Set
End Property
Sub Main()
' The following two lines are equivalent.
P = 10
set_P(10)
End Sub
End Module
Pokud je cíl přístupu k proměnné nebo vlastnosti zadán jako typ hodnoty, ale není klasifikován jako proměnná, dojde k chybě v době kompilace. Například:
Structure S
Public F As Integer
End Structure
Class C
Private PValue As S
Public Property P As S
Get
Return PValue
End Get
Set (Value As S)
PValue = Value
End Set
End Property
End Class
Module Test
Sub Main()
Dim ct As C = New C()
Dim rt As Object = new C()
' Compile-time error: ct.P not classified as variable.
ct.P.F = 10
' Run-time exception.
rt.P.F = 10
End Sub
End Module
Všimněte si, že sémantika přiřazení závisí na typu proměnné nebo vlastnosti, ke které se přiřazuje. Pokud je proměnná, ke které je přiřazena, typ hodnoty, přiřazení zkopíruje hodnotu výrazu do proměnné. Pokud je proměnná, ke které se přiřazuje, referenční typ, přiřazení zkopíruje odkaz, nikoli samotnou hodnotu, do proměnné. Pokud je Objecttyp proměnné , je sémantika přiřazení určena, zda je typ hodnoty typ hodnoty nebo referenční typ za běhu.
Poznámka. U vnitřních typů, jako Integer jsou a Date, odkaz a sémantika přiřazení hodnot jsou stejné, protože typy jsou neměnné. V důsledku toho je jazyk zdarma používat referenční přiřazení u krabicových vnitřních typů jako optimalizace. Z hlediska hodnoty je výsledek stejný.
Vzhledem k tomu, že znak rovná se (=) se používá jak pro přiřazení, tak pro rovnost, existuje nejednoznačnost mezi jednoduchým přiřazením a vyvoláním příkazu v situacích, jako x = y.ToString()je například . Ve všech takových případech má příkaz přiřazení přednost před operátorem rovnosti. To znamená, že ukázkový výraz je interpretován spíše než x = (y.ToString())(x = y).ToString().
Složené příkazy přiřazení
Složený příkaz přiřazení má tvar V op= E (kde op je platný binární operátor).
CompoundAssignmentStatement
: Expression CompoundBinaryOperator LineTerminator? Expression StatementTerminator
;
CompoundBinaryOperator
: '^' '=' | '*' '=' | '/' '=' | '\\' '=' | '+' '=' | '-' '='
| '&' '=' | '<' '<' '=' | '>' '>' '='
;
Výraz na levé straně operátoru přiřazení musí být klasifikován jako přístup k proměnné nebo vlastnosti, zatímco výraz na pravé straně operátoru přiřazení musí být klasifikován jako hodnota. Složený příkaz přiřazení je ekvivalentní příkazu V = V op E s rozdílem, že proměnná na levé straně operátoru složeného přiřazení je vyhodnocena pouze jednou. Následující příklad ukazuje tento rozdíl:
Module Test
Function GetIndex() As Integer
Console.WriteLine("Getting index")
Return 1
End Function
Sub Main()
Dim a(2) As Integer
Console.WriteLine("Simple assignment")
a(GetIndex()) = a(GetIndex()) + 1
Console.WriteLine("Compound assignment")
a(GetIndex()) += 1
End Sub
End Module
Výraz a(GetIndex()) se vyhodnotí dvakrát pro jednoduché přiřazení, ale pouze jednou pro složené přiřazení, takže kód se vytiskne:
Simple assignment
Getting index
Getting index
Compound assignment
Getting index
Příkaz Mid Assignment
Příkaz Mid přiřazení přiřadí řetězec k jinému řetězci. Levá strana přiřazení má stejnou syntaxi jako volání funkce Microsoft.VisualBasic.Strings.Mid.
MidAssignmentStatement
: 'Mid' '$'? OpenParenthesis Expression Comma Expression
( Comma Expression )? CloseParenthesis Equals Expression StatementTerminator
;
Prvním argumentem je cíl přiřazení a musí být klasifikován jako proměnná nebo přístup k vlastnosti, jehož typ je implicitně převoditelný na a z String. Druhý parametr je počáteční pozice založená na 1, která odpovídá místu, kde má přiřazení začínat v cílovém řetězci a musí být klasifikováno jako hodnota, jejíž typ musí být implicitně konvertibilní na Integer. Volitelný třetí parametr je počet znaků z hodnoty pravé strany, která se má přiřadit k cílovému řetězci, a musí být klasifikována jako hodnota, jejíž typ je implicitně převoditelný na Integer. Pravá strana je zdrojový řetězec a musí být klasifikována jako hodnota, jejíž typ je implicitně konvertibilní na String. Pravá strana se zkrátí na parametr délky, pokud je zadána, a nahradí znaky v řetězci na levé straně počínaje počáteční pozicí. Pokud řetězec pravé strany obsahoval méně znaků než třetí parametr, zkopírují se pouze znaky z pravého řetězce.
Následující příklad zobrazí ab123fg:
Module Test
Sub Main()
Dim s1 As String = "abcdefg"
Dim s2 As String = "1234567"
Mid$(s1, 3, 3) = s2
Console.WriteLine(s1)
End Sub
End Module
Poznámka.
Mid není rezervované slovo.
Vyvolání příkazů
Vyvolání příkazu vyvolá metodu, která předchází volitelnému klíčovému slovu Call. Vyvolání příkazu se zpracuje stejným způsobem jako výraz vyvolání funkce s některými rozdíly uvedenými níže. Výraz vyvolání musí být klasifikován jako hodnota nebo void. Jakákoli hodnota vyplývající z vyhodnocení výrazu vyvolání se zahodí.
Call Pokud je klíčové slovo vynecháno, musí výraz vyvolání začínat identifikátorem nebo klíčovým slovem . nebo uvnitř With bloku. Proto například "Call 1.ToString()" je platný příkaz, ale1.ToString() "" není. (Všimněte si, že v kontextu výrazu nemusí vyvolání výrazů začínat identifikátorem. Například "Dim x = 1.ToString()" je platný příkaz).
Mezi příkazy volání a vyvoláním výrazů existuje další rozdíl: pokud příkaz vyvolání obsahuje seznam argumentů, pak se vždy považuje za seznam argumentů vyvolání. Následující příklad znázorňuje rozdíl:
Module Test
Sub Main()
Call {Function() 15}(0)
' error: (0) is taken as argument list, but array is not invokable
Call ({Function() 15}(0))
' valid, since the invocation statement has no argument list
Dim x = {Function() 15}(0)
' valid as an expression, since (0) is taken as an array-indexing
Call f("a")
' error: ("a") is taken as argument list to the invocation of f
Call f()("a")
' valid, since () is the argument list for the invocation of f
Dim y = f("a")
' valid as an expression, since f("a") is interpreted as f()("a")
End Sub
Sub f() As Func(Of String,String)
Return Function(x) x
End Sub
End Module
InvocationStatement
: 'Call'? InvocationExpression StatementTerminator
;
Podmíněné příkazy
Podmíněné příkazy umožňují podmíněné spouštění příkazů na základě výrazů vyhodnocených za běhu.
ConditionalStatement
: IfStatement
| SelectStatement
;
Když... Potom... Příkazy Else
Příkaz If...Then...Else je základní podmíněný příkaz.
IfStatement
: BlockIfStatement
| LineIfThenStatement
;
BlockIfStatement
: 'If' BooleanExpression 'Then'? StatementTerminator
Block?
ElseIfStatement*
ElseStatement?
'End' 'If' StatementTerminator
;
ElseIfStatement
: ElseIf BooleanExpression 'Then'? StatementTerminator
Block?
;
ElseStatement
: 'Else' StatementTerminator
Block?
;
LineIfThenStatement
: 'If' BooleanExpression 'Then' Statements ( 'Else' Statements )? StatementTerminator
;
ElseIf
: 'ElseIf'
| 'Else' 'If'
;
Každý výraz v If...Then...Else příkazu musí být logický výraz, jak je uvedeno v logických výrazech oddílu. (Poznámka: Tento výraz nevyžaduje, aby měl logický typ). Pokud je výraz v If příkazu pravdivý, příkazy uzavřené blokem If se spustí. Pokud je výraz nepravda, vyhodnotí se každý výraz ElseIf . Pokud se některý z ElseIf výrazů vyhodnotí jako true, provede se odpovídající blok. Pokud se žádný výraz nevyhodnotí jako pravdivý a blok existuje Else , Else provede se blok. Po dokončení provádění bloku se provádění předá na konec If...Then...Else příkazu.
Verze řádku If příkazu má jednu sadu příkazů, které se mají spustit, pokud If je True výraz a volitelná sada příkazů, které se mají spustit, pokud je Falsevýraz . Například:
Module Test
Sub Main()
Dim a As Integer = 10
Dim b As Integer = 20
' Block If statement.
If a < b Then
a = b
Else
b = a
End If
' Line If statement
If a < b Then a = b Else b = a
End Sub
End Module
Verze řádku příkazu If je vázána méně těsně než ":" a její Else vazby na lexicky nejbližší předcházející If , které je povoleno syntaxí. Například následující dvě verze jsou ekvivalentní:
If True Then _
If True Then Console.WriteLine("a") Else Console.WriteLine("b") _
Else Console.WriteLine("c") : Console.WriteLine("d")
If True Then
If True Then
Console.WriteLine("a")
Else
Console.WriteLine("b")
End If
Console.WriteLine("c") : Console.WriteLine("d")
End If
Všechny příkazy kromě příkazů deklarací popisků jsou povoleny uvnitř příkazu řádku If , včetně příkazů bloku. Nemusí však používat LineTerminators jako StatementTerminators s výjimkou výrazů lambda s více řádky. Například:
' Allowed, since it uses : instead of LineTerminator to separate statements
If b Then With New String("a"(0),5) : Console.WriteLine(.Length) : End With
' Disallowed, since it uses a LineTerminator
If b then With New String("a"(0), 5)
Console.WriteLine(.Length)
End With
' Allowed, since it only uses LineTerminator inside a multi-line lambda
If b Then Call Sub()
Console.WriteLine("a")
End Sub.Invoke()
Select Case – příkazy
Příkaz Select Case spouští příkazy na základě hodnoty výrazu.
SelectStatement
: 'Select' 'Case'? Expression StatementTerminator
CaseStatement*
CaseElseStatement?
'End' 'Select' StatementTerminator
;
CaseStatement
: 'Case' CaseClauses StatementTerminator
Block?
;
CaseClauses
: CaseClause ( Comma CaseClause )*
;
CaseClause
: ( 'Is' LineTerminator? )? ComparisonOperator LineTerminator? Expression
| Expression ( 'To' Expression )?
;
ComparisonOperator
: '=' | '<' '>' | '<' | '>' | '>' '=' | '<' '='
;
CaseElseStatement
: 'Case' 'Else' StatementTerminator
Block?
;
Výraz musí být klasifikován jako hodnota.
Select Case Při spuštění Select příkazu se výraz vyhodnotí jako první a Case příkazy se pak vyhodnocují v pořadí od textové deklarace. První Case příkaz, který se vyhodnotí jako True , má spuštěný blok. Pokud se žádný Case příkaz nevyhodnotí True a existuje příkaz Case Else , spustí se tento blok. Po dokončení provádění bloku se provádění předá na konec Select příkazu.
Case Spuštění bloku není povoleno "projít" do dalšího oddílu přepínače. To zabraňuje běžné třídě chyb, ke kterým dochází v jiných jazycích, když Case je ukončovací příkaz omylem vynechán. Následující příklad ukazuje toto chování:
Module Test
Sub Main()
Dim x As Integer = 10
Select Case x
Case 5
Console.WriteLine("x = 5")
Case 10
Console.WriteLine("x = 10")
Case 20 - 10
Console.WriteLine("x = 20 - 10")
Case 30
Console.WriteLine("x = 30")
End Select
End Sub
End Module
Kód se vytiskne:
x = 10
I když Case 10 a Case 20 - 10 vyberte stejnou hodnotu, spustí se, Case 10 protože předchází Case 20 - 10 textově. Jakmile dojde k dalšímu CaseSelect dosažení, provádění pokračuje po příkazu.
Case Klauzule může mít dvě formy. Jeden formulář je volitelné Is klíčové slovo, relační operátor a výraz. Výraz se převede na typ výrazu Select . Pokud se výraz implicitně nepřevádí na typ výrazu Select , dojde k chybě v době kompilace.
Select Pokud je výraz E, relační operátor je Op a Case výraz je E1, případ se vyhodnotí jako E OP E1. Operátor musí být platný pro typy dvou výrazů; jinak dojde k chybě v době kompilace.
Druhý formulář je výraz volitelně následovaný klíčovým slovem To a druhým výrazem. Oba výrazy se převedou na typ výrazu Select . Pokud některý z výrazů není implicitně konvertibilní na typ výrazu Select , dojde k chybě v době kompilace.
Select Pokud je Evýraz , první Case výraz je E1a druhý Case výraz je E2, Case je vyhodnocen buď jako E = E1 (pokud není zadán) E2 nebo (E >= E1) And (E <= E2). Operátory musí být platné pro typy těchto dvou výrazů; jinak dojde k chybě v době kompilace.
Příkazy smyčky
Příkazy smyčky umožňují opakované spuštění příkazů v těle.
LoopStatement
: WhileStatement
| DoLoopStatement
| ForStatement
| ForEachStatement
;
Při každém zadání textu smyčky se nová kopie vytvoří ze všech místních proměnných deklarovaných v těle, inicializované na předchozí hodnoty proměnných. Všechny odkazy na proměnnou v těle smyčky budou používat naposledy vytvořené kopie. Tento kód ukazuje příklad:
Module Test
Sub Main()
Dim lambdas As New List(Of Action)
Dim x = 1
For i = 1 To 3
x = i
Dim y = x
lambdas.Add(Sub() Console.WriteLine(x & y))
Next
For Each lambda In lambdas
lambda()
Next
End Sub
End Module
Kód vytvoří výstup:
31 32 33
Při spuštění těla smyčky se použije jakákoli kopie proměnné, která je aktuální. Například příkaz Dim y = x odkazuje na nejnovější kopii y a původní kopii x. A když se vytvoří lambda, pamatuje si, která kopie proměnné byla aktuální v době, kdy byla vytvořena. Proto každá lambda používá stejnou sdílenou kopii x, ale jinou kopii y. Na konci programu, když spustí lambda, že sdílená kopie x , které všechny odkazují, je nyní na své konečné hodnotě 3.
Všimněte si, že pokud neexistují žádné výrazy lambda nebo LINQ, není možné vědět, že při zadávání smyčky se vytvoří nová kopie. Optimalizace kompilátoru se tedy v tomto případě nebudou vytvářet kopie. Všimněte si také, že není možné přejít do GoTo smyčky, která obsahuje výrazy lambda nebo LINQ.
Zatímco... Ukončit while a do... Příkazy smyčky
Smyčky While příkazů nebo Do smyčky založené na logickém výrazu.
WhileStatement
: 'While' BooleanExpression StatementTerminator
Block?
'End' 'While' StatementTerminator
;
DoLoopStatement
: DoTopLoopStatement
| DoBottomLoopStatement
;
DoTopLoopStatement
: 'Do' ( WhileOrUntil BooleanExpression )? StatementTerminator
Block?
'Loop' StatementTerminator
;
DoBottomLoopStatement
: 'Do' StatementTerminator
Block?
'Loop' WhileOrUntil BooleanExpression StatementTerminator
;
WhileOrUntil
: 'While' | 'Until'
;
Smyčka While příkazů smyčky se smyček za předpokladu, že logický výraz vyhodnotí jako pravdivý. Do Příkaz smyčky může obsahovat složitější podmínku. Výraz může být umístěn za Do klíčové slovo nebo za Loop klíčové slovo, ale ne za obojí. Logický výraz se vyhodnotí jako logický výraz oddílu. (Poznámka: Tento výraz nevyžaduje, aby měl logický typ). Je také platné zadat žádný výraz vůbec; v takovém případě smyčka nikdy neodejde. Pokud je výraz umístěn za Do, bude vyhodnocen před spuštěním bloku smyčky v každé iteraci. Pokud je výraz umístěn za Loop, bude vyhodnocen po spuštění bloku smyčky pro každou iteraci. Umístění výrazu za Loop vygeneruje jednu více smyček než umístění za Do. Následující příklad ukazuje toto chování:
Module Test
Sub Main()
Dim x As Integer
x = 3
Do While x = 1
Console.WriteLine("First loop")
Loop
Do
Console.WriteLine("Second loop")
Loop While x = 1
End Sub
End Module
Kód vytvoří výstup:
Second Loop
V případě první smyčky se podmínka vyhodnotí před spuštěním smyčky. V případě druhé smyčky se podmínka spustí po spuštění smyčky. Podmíněný výraz musí být předponou klíčového While slova nebo klíčového Until slova. První přeruší smyčku, pokud se podmínka vyhodnotí jako nepravda, druhá, když se podmínka vyhodnotí jako true.
Poznámka.
Until není rezervované slovo.
Pro... Další příkazy
Smyčka For...Next příkazů založená na sadě hranic. Příkaz For určuje proměnnou ovládacího prvku smyčky, výraz s dolní vazbou, výraz s horní vazbou a volitelný výraz hodnoty kroku. Řídicí proměnná smyčky je určena buď prostřednictvím identifikátoru následovaného volitelnou As klauzulí nebo výrazem.
ForStatement
: 'For' LoopControlVariable Equals Expression 'To' Expression
( 'Step' Expression )? StatementTerminator
Block?
( 'Next' NextExpressionList? StatementTerminator )?
;
LoopControlVariable
: Identifier ( IdentifierModifiers 'As' TypeName )?
| Expression
;
NextExpressionList
: Expression ( Comma Expression )*
;
Podle následujících pravidel řídicí proměnná smyčky odkazuje buď na novou místní proměnnou specifickou pro tento For...Next příkaz, nebo na existující proměnnou nebo na výraz.
Pokud je řídicí proměnná smyčky identifikátor s
Asklauzulí, identifikátor definuje novou místní proměnnou typu zadaného vAsklauzuli, která je vymezena na celouForsmyčku.Pokud je řídicí proměnná smyčky identifikátor bez
Asklauzule, identifikátor se nejprve přeloží pomocí jednoduchých pravidel překladu názvů (viz Výrazy jednoduchého názvu oddílu), s tím rozdílem, že tento výskyt identifikátoru by nebyl a sám o sobě způsobil vytvoření implicitní místní proměnné ( implicitní místní deklarace oddílu).Pokud toto řešení proběhne úspěšně a výsledek se klasifikuje jako proměnná, je řídicí proměnná smyčky taková, která už existuje.
Pokud se řešení nezdaří nebo pokud je řešení úspěšné a výsledek se klasifikuje jako typ, postupujte takto:
- pokud se používá odvození typu místní proměnné, identifikátor definuje novou místní proměnnou, jejíž typ je odvozen z vázaných a krokových výrazů, vymezený na celou
Forsmyčku; - pokud se nepoužívá odvození typu místní proměnné, ale implicitní místní deklarace je, vytvoří se implicitní místní proměnná, jejíž obor je celá metoda (Section Implicit Local Declarations) a řídicí proměnná smyčky odkazuje na tuto před existující proměnnou;
- Pokud se nepoužívá odvození typu místní proměnné ani implicitní místní deklarace, jedná se o chybu.
- pokud se používá odvození typu místní proměnné, identifikátor definuje novou místní proměnnou, jejíž typ je odvozen z vázaných a krokových výrazů, vymezený na celou
Pokud je řešení úspěšné s něčím klasifikovaným jako typ ani proměnnou, jedná se o chybu.
Pokud je řídicí proměnná smyčky výrazem, musí být výraz klasifikován jako proměnná.
Řídicí proměnnou smyčky nelze použít jiným nadřazeným příkazem For...Next . Typ řídicí proměnné For smyčky příkazu určuje typ iterace a musí být jedním z:
-
Byte,SByte, ,UShort,UIntegerDecimalShortIntegerULongLong,SingleDouble - Výčtový typ
Object- Typ
T, který má následující operátory, kdeBje typ, který lze použít v logickém výrazu:
Public Shared Operator >= (op1 As T, op2 As T) As B
Public Shared Operator <= (op1 As T, op2 As T) As B
Public Shared Operator - (op1 As T, op2 As T) As T
Public Shared Operator + (op1 As T, op2 As T) As T
Vázané a krokové výrazy musí být implicitně konvertibilní na typ řídicí proměnné smyčky a musí být klasifikovány jako hodnoty. V době kompilace je typ řídicí proměnné smyčky odvozen výběrem nejširšího typu mezi dolní mez, horní mez a typy výrazů kroku. Pokud mezi dvěma typy neexistuje rozšiřující převod, dojde k chybě v době kompilace.
Pokud je Objecttyp řídicí proměnné smyčky v době běhu , je typ iterace odvozen stejně jako v době kompilace se dvěma výjimkami. Nejprve platí, že pokud jsou vázané a krokové výrazy všechny celočíselné typy, ale nemají žádný široký typ, bude odvozen nejširší typ, který zahrnuje všechny tři typy. A za druhé, pokud je typ řídicí proměnné smyčky odvozen, Stringbude odvozen místo toho Double . Pokud nelze v době běhu určit žádný typ ovládacího prvku smyčky nebo pokud některý z výrazů nelze převést na typ ovládacího prvku smyčky, System.InvalidCastException dojde k chybě. Jakmile se na začátku smyčky vybere typ ovládacího prvku smyčky, použije se stejný typ v průběhu iterace bez ohledu na změny hodnoty v řídicí proměnné smyčky.
Příkaz For musí být uzavřen odpovídajícím Next příkazem. Příkaz Next bez proměnné odpovídá nejvnitřnějšímu otevřenému For příkazu, zatímco Next příkaz s jednou nebo více řídicími proměnnými smyčky bude zleva doprava odpovídat For smyčkám, které odpovídají jednotlivým proměnným. Pokud proměnná odpovídá smyčce For , která není v tomto okamžiku nejvíce vnořenou smyčkou, dojde k chybě kompilace.
Na začátku smyčky se tři výrazy vyhodnocují v textovém pořadí a dolní mez výrazu je přiřazena k řídicí proměnné smyčky. Pokud je hodnota kroku vynechána, je implicitně literál 1převeden na typ řídicí proměnné smyčky. Tři výrazy se vyhodnocují pouze na začátku smyčky.
Na začátku každé smyčky se řídicí proměnná porovná a zjistí, jestli je větší než koncový bod, pokud je výraz kroku kladný nebo menší než koncový bod, pokud je výraz kroku záporný. Pokud ano, smyčka For se ukončí, jinak se blok smyčky spustí. Pokud řídicí proměnná smyčky není primitivním typem, je relační operátor určen, zda je výraz step >= step - step pravdivý nebo nepravda.
Next V příkazu se hodnota kroku přidá do řídicí proměnné a provádění se vrátí na začátek smyčky.
Všimněte si, že při každé iteraci bloku smyčky se nevytvořila nová kopie řídicí proměnné smyčky. V tomto ohledu For se prohlášení liší od For Each (oddíl pro každý... Další příkazy).
Větev do smyčky mimo smyčku For není platná.
Pro každý... Další příkazy
Smyčka For Each...Next příkazů založená na prvcích ve výrazu. Příkaz For Each určuje řídicí proměnnou smyčky a výraz enumerátoru. Řídicí proměnná smyčky je určena buď prostřednictvím identifikátoru následovaného volitelnou As klauzulí nebo výrazem.
ForEachStatement
: 'For' 'Each' LoopControlVariable 'In' LineTerminator? Expression StatementTerminator
Block?
( 'Next' NextExpressionList? StatementTerminator )?
;
Následují stejná pravidla jako For...Next příkazy (oddíl for... Další příkazy), řídicí proměnná smyčky odkazuje buď na novou místní proměnnou specifickou pro tuto pro každý... Další příkaz nebo před existující proměnnou nebo výraz.
Výraz enumerátoru musí být klasifikován jako hodnota a jeho typ musí být typ kolekce nebo Object. Pokud je Objecttyp výrazu enumerátoru , veškeré zpracování je odloženo do doby běhu. Jinak musí existovat převod z typu prvku kolekce na typ řídicí proměnné smyčky.
Řídicí proměnnou smyčky nelze použít jiným uzavřeným příkazem For Each . Příkaz For Each musí být uzavřen odpovídajícím Next příkazem. Příkaz Next bez řídicí proměnné smyčky odpovídá nejvnitřnější otevřené For Eachproměnné . Příkaz Next s jednou nebo více řídicími proměnnými smyčky bude zleva doprava odpovídat For Each smyčkám, které mají stejnou řídicí proměnnou smyčky. Pokud proměnná odpovídá smyčce For Each , která není v tomto okamžiku nejvíce vnořenou smyčkou, dojde k chybě v době kompilace.
C Typ se říká, že se jedná o typ kolekce, pokud jeden z těchto typů:
Platí všechny následující skutečnosti:
-
Cobsahuje přístupnou instanci, sdílenou metodu nebo metodu rozšíření s podpisemGetEnumerator(), který vrací typE. -
Eobsahuje přístupnou instanci, sdílenou metodu nebo metodu rozšíření s podpisemMoveNext()a návratovým typemBoolean. -
Eobsahuje přístupnou instanci nebo sdílenou vlastnost s názvemCurrentgetter. Typ této vlastnosti je typ elementu typu kolekce.
-
Implementuje rozhraní
System.Collections.Generic.IEnumerable(Of T), v takovém případě typ prvku kolekce je považován zaT.Implementuje rozhraní
System.Collections.IEnumerable, v takovém případě typ prvku kolekce je považován zaObject.
Následuje příklad třídy, která se dá vyčíslit:
Public Class IntegerCollection
Private integers(10) As Integer
Public Class IntegerCollectionEnumerator
Private collection As IntegerCollection
Private index As Integer = -1
Friend Sub New(c As IntegerCollection)
collection = c
End Sub
Public Function MoveNext() As Boolean
index += 1
Return index <= 10
End Function
Public ReadOnly Property Current As Integer
Get
If index < 0 OrElse index > 10 Then
Throw New System.InvalidOperationException()
End If
Return collection.integers(index)
End Get
End Property
End Class
Public Sub New()
Dim i As Integer
For i = 0 To 10
integers(i) = I
Next i
End Sub
Public Function GetEnumerator() As IntegerCollectionEnumerator
Return New IntegerCollectionEnumerator(Me)
End Function
End Class
Před zahájením smyčky se vyhodnotí výraz enumerátoru. Pokud typ výrazu nevyhovuje vzoru návrhu, přetypuje se výraz na System.Collections.IEnumerable nebo System.Collections.Generic.IEnumerable(Of T). Pokud typ výrazu implementuje obecné rozhraní, je obecné rozhraní upřednostňované v době kompilace, ale ne generické rozhraní je upřednostňované za běhu. Pokud typ výrazu implementuje obecné rozhraní vícekrát, příkaz je považován za nejednoznačný a dojde k chybě kompilace.
Poznámka. Ne generické rozhraní je upřednostňované v pozdním vázaném případě, protože výběr obecného rozhraní by znamenal, že všechna volání metod rozhraní by zahrnovala parametry typu. Vzhledem k tomu, že není možné znát odpovídající argumenty typu za běhu, musí být všechna taková volání provedena pomocí pozdních volání. To by bylo pomalejší než volání jiného než obecné rozhraní, protože jiné než obecné rozhraní by mohlo být volána pomocí volání v době kompilace.
GetEnumerator se volá na výslednou hodnotu a návratová hodnota funkce je uložena v dočasném stavu. Na začátku každé iterace MoveNext se pak volá na dočasné. Pokud se vrátí False, smyčka se ukončí. V opačném případě se každá iterace smyčky provede takto:
- Pokud řídicí proměnná smyčky identifikovala novou místní proměnnou (nikoli existující proměnnou), vytvoří se nová kopie této místní proměnné. U aktuální iterace budou všechny odkazy v bloku smyčky odkazovat na tuto kopii.
- Vlastnost
Currentse načte, převede na typ řídicí proměnné smyčky (bez ohledu na to, zda je převod implicitní nebo explicitní) a přiřazen k řídicí proměnné smyčky. - Blok smyčky se spustí.
Poznámka. Mezi verzí 10.0 a 11.0 jazyka se mírně mění chování. Před verzí 11.0 se pro každou iteraci smyčky nevytvořila nová iterační proměnná. Tento rozdíl je pozorovatelný pouze v případě, že je proměnná iterace zachycena lambda nebo výrazEM LINQ, který se pak vyvolá po smyčce:
Dim lambdas As New List(Of Action)
For Each x In {1,2,3}
lambdas.Add(Sub() Console.WriteLine(x)
Next
lambdas(0).Invoke()
lambdas(1).Invoke()
lambdas(2).Invoke()
Až do jazyka Visual Basic 10.0 se vygeneroval upozornění při kompilaci a třikrát se vytisklo "3". Důvodem bylo, že všechny iterace smyčky sdílely pouze jednu proměnnou "x" a všechny tři výrazy lambda zachytily stejné "x" a v době, kdy se lambdas spustily, pak drželo číslo 3. Od verze Visual Basic 11.0 se vytiskne 1, 2, 3. Je to proto, že každá lambda zachycuje jinou proměnnou "x".
Poznámka. Aktuální prvek iterace je převeden na typ řídicí proměnné smyčky, i když převod je explicitní, protože neexistuje vhodné místo pro zavedení operátoru převodu v příkazu. To se stalo obzvláště problematické při práci s nyní zastaralým typem System.Collections.ArrayList, protože jeho typ prvku je Object. To by vyžadovalo přetypování ve velké řadě smyček, něco, co jsme cítili nebyli ideální. Je ironií, že generické typy umožnily vytvoření kolekce silného typu, System.Collections.Generic.List(Of T)která nám mohla změnit návrhový bod, ale kvůli kompatibilitě to teď nejde změnit.
Next Po dosažení příkazu se spuštění vrátí na začátek smyčky. Pokud je proměnná za klíčovým slovem Next zadaná, musí být stejná jako první proměnná za hodnotou For Each. Představte si například následující kód:
Module Test
Sub Main()
Dim i As Integer
Dim c As IntegerCollection = New IntegerCollection()
For Each i In c
Console.WriteLine(i)
Next i
End Sub
End Module
Je ekvivalentní následujícímu kódu:
Module Test
Sub Main()
Dim i As Integer
Dim c As IntegerCollection = New IntegerCollection()
Dim e As IntegerCollection.IntegerCollectionEnumerator
e = c.GetEnumerator()
While e.MoveNext()
i = e.Current
Console.WriteLine(i)
End While
End Sub
End Module
Pokud typ E enumerátoru implementuje System.IDisposable, enumerátor je uvolněn při ukončení smyčky voláním Dispose metody. Tím zajistíte, že budou uvolněny prostředky, které jsou uloženy enumerátorem. Pokud metoda obsahující For Each příkaz nepoužívá nestrukturované zpracování chyb, příkaz For Each se zabalí do Try příkazu s metodou Dispose volanou v Finally rámci vyčištění.
Poznámka. Typ System.Array je typ kolekce a vzhledem k tomu, že všechny typy pole jsou odvozeny od System.Array, je v příkazu povolen For Each libovolný výraz typu pole. U jednorozměrných polí příkaz vyčísluje For Each prvky pole ve vzestupném pořadí indexu, počínaje indexem 0 a končí indexem Length - 1. U multidimenzionálních polí se nejprve zvýší indexy pravé dimenze.
Například následující kód vytiskne 1 2 3 4:
Module Test
Sub Main()
Dim x(,) As Integer = { { 1, 2 }, { 3, 4 } }
Dim i As Integer
For Each i In x
Console.Write(i & " ")
Next i
End Sub
End Module
Větev do For Each bloku příkazu mimo blok není platná.
příkazy Exception-Handling
Visual Basic podporuje strukturované zpracování výjimek a nestrukturované zpracování výjimek. V metodě lze použít pouze jeden styl zpracování výjimek, ale Error příkaz lze použít ve strukturovaném zpracování výjimek. Pokud metoda používá oba styly zpracování výjimek, výsledky chyby v době kompilace.
ErrorHandlingStatement
: StructuredErrorStatement
| UnstructuredErrorStatement
;
Příkazy strukturovaného Exception-Handling
Strukturované zpracování výjimek je metoda zpracování chyb deklarováním explicitních bloků, ve kterých se budou zpracovávat určité výjimky. Strukturované zpracování výjimek se provádí prostřednictvím Try příkazu.
StructuredErrorStatement
: ThrowStatement
| TryStatement
;
TryStatement
: 'Try' StatementTerminator
Block?
CatchStatement*
FinallyStatement?
'End' 'Try' StatementTerminator
;
Například:
Module Test
Sub ThrowException()
Throw New Exception()
End Sub
Sub Main()
Try
ThrowException()
Catch e As Exception
Console.WriteLine("Caught exception!")
Finally
Console.WriteLine("Exiting try.")
End Try
End Sub
End Module
Příkaz Try se skládá ze tří druhů bloků: try blocks, catch blocks a finally blocks.
Blok try je blok příkazu, který obsahuje příkazy, které se mají spustit.
Blok catch je blok příkazu, který zpracovává výjimku.
Konečně blok je blok příkazu, který obsahuje příkazy, které se mají spustit při Try ukončení příkazu, bez ohledu na to, jestli došlo k výjimce a byla zpracována. Příkaz Try , který může obsahovat pouze jeden blok try a jeden blok finally, musí obsahovat alespoň jeden blok catch nebo nakonec blok. Není platné explicitně přenést spuštění do bloku try s výjimkou bloku catch ve stejném příkazu.
Konečné bloky
Finally Blok se vždy spustí, když provádění opustí jakoukoli část Try příkazu. K provedení Finally bloku není nutná žádná explicitní akce. Když spuštění opustí Try příkaz, systém automaticky spustí Finally blok a pak přenese spuštění do zamýšleného cíle. Blok Finally se spustí bez ohledu na to, jak provádění příkaz opustí Try : až na konec Try bloku, přes konec Catch bloku, příkazem Exit Try , příkazem GoTo nebo nezpracováním vyvolané výjimky.
Všimněte si, že Await výraz v asynchronní metodě a Yield příkaz v metodě iterátoru může způsobit pozastavení toku řízení v instanci asynchronní nebo iterátorové metody a obnovení v některé jiné instanci metody. Jedná se však pouze o pozastavení provádění a nezahrnuje ukončení příslušné asynchronní metody nebo instance metody iterátoru, a proto nezpůsobí Finally spuštění bloků.
Explicitní přenos spuštění do Finally bloku není platný. Je také neplatný pro přenos z Finally bloku s výjimkou výjimky.
FinallyStatement
: 'Finally' StatementTerminator
Block?
;
Zachytávání bloků
Pokud při zpracování Try bloku dojde k výjimce, je každý Catch příkaz zkoumán v textovém pořadí, aby se zjistilo, zda zpracovává výjimku.
CatchStatement
: 'Catch' ( Identifier ( 'As' NonArrayTypeName )? )?
( 'When' BooleanExpression )? StatementTerminator
Block?
;
Identifikátor zadaný v Catch klauzuli představuje výjimku, která byla vyvolána. Pokud identifikátor obsahuje As klauzuli, považuje se identifikátor za deklarovaný v Catch místním prostoru deklarace bloku. Jinak musí být identifikátor místní proměnnou (nikoli statickou proměnnou), která byla definována v bloku obsahujícím.
Klauzule Catch bez identifikátoru zachytí všechny výjimky odvozené z System.Exception.
Catch Klauzule s identifikátorem zachytí pouze výjimky, jejichž typy jsou stejné jako nebo odvozené od typu identifikátoru. Typ musí být System.Exceptionnebo typ odvozený z System.Exception. Je-li zachycena výjimka, která je odvozena z System.Exception, odkaz na objekt výjimky je uložen v objektu vrácené funkcí Microsoft.VisualBasic.Information.Err.
Catch Klauzule s When klauzulí zachytí pouze výjimky, když se výraz vyhodnotí jako True; typ výrazu musí být logický výraz podle logických výrazů oddílu.
When Klauzule se použije pouze po kontrole typu výjimky a výraz může odkazovat na identifikátor představující výjimku, jak ukazuje tento příklad:
Module Test
Sub Main()
Dim i As Integer = 5
Try
Throw New ArgumentException()
Catch e As OverflowException When i = 5
Console.WriteLine("First handler")
Catch e As ArgumentException When i = 4
Console.WriteLine("Second handler")
Catch When i = 5
Console.WriteLine("Third handler")
End Try
End Sub
End Module
Tento příklad vytiskne:
Third handler
Catch Pokud klauzule zpracovává výjimku, provádění se přenese do Catch bloku. Na konci Catch bloku se provádění přenese na první příkaz za příkazem Try . Příkaz Try nezpracuje žádné výjimky vyvolané v Catch bloku. Pokud žádná Catch klauzule nezpracuje výjimku, provádění se přenese do umístění určeného systémem.
Explicitní přenos spuštění do Catch bloku není platný.
Filtry v klauzulích When se obvykle vyhodnocují před vyvolání výjimky. Například následující kód vytiskne "Filter, Finally, Catch".
Sub Main()
Try
Foo()
Catch ex As Exception When F()
Console.WriteLine("Catch")
End Try
End Sub
Sub Foo()
Try
Throw New Exception
Finally
Console.WriteLine("Finally")
End Try
End Sub
Function F() As Boolean
Console.WriteLine("Filter")
Return True
End Function
Metody Async a Iterator však způsobují, že se všechny bloky uvnitř nich spustí před všemi filtry mimo ně. Pokud by například výše uvedený kód měl Async Sub Foo(), výstup by byl "Finally, Filter, Catch".
Throw – příkaz
Příkaz Throw vyvolá výjimku, která je reprezentována instancí typu odvozeného z System.Exception.
ThrowStatement
: 'Throw' Expression? StatementTerminator
;
Pokud výraz není klasifikovaný jako hodnota nebo není typem odvozeným z System.Exception, dojde k chybě v době kompilace. Pokud se výraz vyhodnotí jako hodnota null za běhu, System.NullReferenceException vyvolá se místo toho výjimka.
Příkaz Throw může vynechat výraz v bloku Try catch příkazu, pokud neexistuje žádný blok posledního zásady. V takovém případě příkaz znovu zvětšuje výjimku, která se právě zpracovává v rámci bloku catch. Například:
Sub Test(x As Integer)
Try
Throw New Exception()
Catch
If x = 0 Then
Throw ' OK, rethrows exception from above.
Else
Try
If x = 1 Then
Throw ' OK, rethrows exception from above.
End If
Finally
Throw ' Invalid, inside of a Finally.
End Try
End If
End Try
End Sub
Nestrukturované příkazy Exception-Handling
Nestrukturované zpracování výjimek je metoda zpracování chyb tím, že indikuje příkazy pro větvení v případě, že dojde k výjimce. Nestrukturované zpracování výjimek se implementuje pomocí tří příkazů: Error příkaz, On Error příkaz a Resume příkaz.
UnstructuredErrorStatement
: ErrorStatement
| OnErrorStatement
| ResumeStatement
;
Například:
Module Test
Sub ThrowException()
Error 5
End Sub
Sub Main()
On Error GoTo GotException
ThrowException()
Exit Sub
GotException:
Console.WriteLine("Caught exception!")
Resume Next
End Sub
End Module
Pokud metoda používá nestrukturované zpracování výjimek, vytvoří se jedna obslužná rutina strukturované výjimky pro celou metodu, která zachytí všechny výjimky. (Všimněte si, že v konstruktorech tato obslužná rutina nepřesahuje volání New volání na začátku konstruktoru.) Metoda pak sleduje poslední umístění obslužné rutiny výjimky a poslední výjimku, která byla vyvolána. Při vstupu do metody je umístění obslužné rutiny výjimky a výjimka obě nastaveny na Nothing. Při vyvolání výjimky v metodě, která používá nestrukturované zpracování výjimek, je odkaz na objekt výjimky uložen v objektu vrácený funkcí Microsoft.VisualBasic.Information.Err.
Nestrukturované příkazy zpracování chyb nejsou povoleny v iterátoru nebo asynchronních metodách.
Error – příkaz
Příkaz Error vyvolá System.Exception výjimku obsahující číslo výjimky jazyka Visual Basic 6. Výraz musí být klasifikován jako hodnota a jeho typ musí být implicitně konvertibilní na Integer.
ErrorStatement
: 'Error' Expression StatementTerminator
;
Příkaz On Error
Příkaz On Error upraví nejnovější stav zpracování výjimek.
OnErrorStatement
: 'On' 'Error' ErrorClause StatementTerminator
;
ErrorClause
: 'GoTo' '-' '1'
| 'GoTo' '0'
| GoToStatement
| 'Resume' 'Next'
;
Může se použít jedním ze čtyř způsobů:
On Error GoTo -1resetuje nejnovější výjimku naNothing.On Error GoTo 0resetuje nejnovější umístění obslužné rutiny výjimky naNothing.On Error GoTo LabelNamevytvoří popisek jako poslední umístění obslužné rutiny výjimky. Tento příkaz nelze použít v metodě, která obsahuje výraz lambda nebo dotaz.On Error Resume Nextvytvoří chování jako poslední umístění obslužné rutinyResume Nextvýjimky.
Resume – příkaz
Příkaz Resume vrátí spuštění příkazu, který způsobil poslední výjimku.
ResumeStatement
: 'Resume' ResumeClause? StatementTerminator
;
ResumeClause
: 'Next'
| LabelName
;
Next Pokud je zadán modifikátor, spuštění se vrátí do příkazu, který by byl proveden po příkazu, který způsobil poslední výjimku. Pokud je zadán název popisku, spuštění se vrátí k popisku.
Vzhledem k tomu, že příkaz SyncLock obsahuje implicitní blok pro zpracování chyb a Resume Next má zvláštní chování pro výjimky, ke kterým dochází v SyncLock příkazech. Resume
Resume vrátí spuštění na začátek SyncLock příkazu, zatímco Resume Next vrátí provádění na další příkaz následující příkaz SyncLock . Představte si například následující kód:
Class LockClass
End Class
Module Test
Sub Main()
Dim FirstTime As Boolean = True
Dim Lock As LockClass = New LockClass()
On Error GoTo Handler
SyncLock Lock
Console.WriteLine("Before exception")
Throw New Exception()
Console.WriteLine("After exception")
End SyncLock
Console.WriteLine("After SyncLock")
Exit Sub
Handler:
If FirstTime Then
FirstTime = False
Resume
Else
Resume Next
End If
End Sub
End Module
Vytiskne následující výsledek.
Before exception
Before exception
After SyncLock
Při prvním provedení příkazu SyncLockResume vrátí spuštění na začátek SyncLock příkazu. Druhý čas prostřednictvím SyncLock příkazu Resume Next vrátí spuštění na konec SyncLock příkazu.
Resume a Resume Next nejsou povoleny SyncLock v rámci příkazu.
Ve všech případech je při Resume spuštění příkazu poslední výjimka nastavena na Nothing. Pokud je Resume příkaz proveden bez nejnovější výjimky, příkaz vyvolá System.Exception výjimku obsahující číslo 20 chyby jazyka Visual Basic (Resume bez chyby).
Branch – příkazy
Příkazy branch upravují tok provádění v metodě. K dispozici je šest příkazů větve:
- Příkaz
GoTozpůsobí, že provádění se přenese na zadaný popisek v metodě. Není povoleno do bloku , , ,WithForSyncLockUsing, aniFor Eachdo bloku, ani do bloku smyčky, pokud je místní proměnná tohoto bloku zachycena ve výrazu lambda nebo LINQ.TryGoTo - Příkaz
Exitpřenese provádění na další příkaz za koncem bezprostředně obsahujícího blokový příkaz zadaného typu. Pokud je blokem metody, pak tok řízení ukončí metodu, jak je popsáno v části Řízení toku. Pokud příkazExitnení obsažen v rámci typu bloku zadaného v příkazu, dojde k chybě v době kompilace. - Příkaz
Continuepřenese provádění na konec bezprostředně obsahující příkaz blokové smyčky zadaného typu. Pokud příkazContinuenení obsažen v rámci typu bloku zadaného v příkazu, dojde k chybě v době kompilace. - Příkaz
Stopzpůsobí, že dojde k výjimce ladicího programu. - Příkaz
Endprogram ukončí. Finalizátory se spouští před vypnutím, ale nakonec se nespustí bloky všech aktuálně spouštěnýchTrypříkazů. Tento příkaz nelze použít v programech, které nejsou spustitelné (například knihovny DLL). - Příkaz
Returnbez výrazu je ekvivalentem příkazuExit SubneboExit Functionpříkazu. PříkazReturns výrazem je povolen pouze v běžné metodě, která je funkcí, nebo v asynchronní metodě, která je funkcí s návratovým typemTask(Of T)pro některéT. Výraz musí být klasifikován jako hodnota, která je implicitně konvertibilní na návratovou proměnnou funkce (v případě běžných metod) nebo na návratovou proměnnou úkolu (v případě asynchronních metod). Jeho chování je vyhodnotit jeho výraz, pak ho uložit do návratové proměnné a pak spustit implicitníExit Functionpříkaz.
BranchStatement
: GoToStatement
| ExitStatement
| ContinueStatement
| StopStatement
| EndStatement
| ReturnStatement
;
GoToStatement
: 'GoTo' LabelName StatementTerminator
;
ExitStatement
: 'Exit' ExitKind StatementTerminator
;
ExitKind
: 'Do' | 'For' | 'While' | 'Select' | 'Sub' | 'Function' | 'Property' | 'Try'
;
ContinueStatement
: 'Continue' ContinueKind StatementTerminator
;
ContinueKind
: 'Do' | 'For' | 'While'
;
StopStatement
: 'Stop' StatementTerminator
;
EndStatement
: 'End' StatementTerminator
;
ReturnStatement
: 'Return' Expression? StatementTerminator
;
příkazy Array-Handling
Dva příkazy zjednodušují práci s poli: ReDim příkazy a Erase příkazy.
ArrayHandlingStatement
: RedimStatement
| EraseStatement
;
Příkaz ReDim
Příkaz ReDim vytvoří instanci nových polí.
RedimStatement
: 'ReDim' 'Preserve'? RedimClauses StatementTerminator
;
RedimClauses
: RedimClause ( Comma RedimClause )*
;
RedimClause
: Expression ArraySizeInitializationModifier
;
Každá klauzule v příkazu musí být klasifikována jako proměnná nebo přístup k vlastnosti, jejíž typ je typ pole nebo Objecta za nimi následuje seznam maticových hranic. Počet hranic musí být konzistentní s typem proměnné; pro libovolný počet hranic je povolen Object. V době běhu se vytvoří instance pole pro každý výraz zleva doprava se zadanými hranicemi a pak je přiřazen k proměnné nebo vlastnosti. Pokud je Objecttyp proměnné , počet dimenzí je počet zadaných dimenzí a typ prvku pole je Object. Pokud je daný počet dimenzí nekompatibilní s proměnnou nebo vlastností za běhu, dojde k chybě kompilace. Například:
Module Test
Sub Main()
Dim o As Object
Dim b() As Byte
Dim i(,) As Integer
' The next two statements are equivalent.
ReDim o(10,30)
o = New Object(10, 30) {}
' The next two statements are equivalent.
ReDim b(10)
b = New Byte(10) {}
' Error: Incorrect number of dimensions.
ReDim i(10, 30, 40)
End Sub
End Module
Preserve Pokud je klíčové slovo zadáno, musí být výrazy také klasifikovatelné jako hodnota a nová velikost pro každou dimenzi s výjimkou pravé strany musí být stejná jako velikost existujícího pole. Hodnoty v existujícím poli se zkopírují do nového pole: pokud je nová matice menší, stávající hodnoty se zahodí; pokud je nové pole větší, extra prvky budou inicializovány na výchozí hodnotu typu prvku pole. Představte si například následující kód:
Module Test
Sub Main()
Dim x(5, 5) As Integer
x(3, 3) = 3
ReDim Preserve x(5, 6)
Console.WriteLine(x(3, 3) & ", " & x(3, 6))
End Sub
End Module
Vytiskne následující výsledek:
3, 0
Pokud je existující odkaz na pole hodnotou null za běhu, nezobrazí se žádná chyba. Pokud se velikost dimenze změní, System.ArrayTypeMismatchException vyvolá se jiná dimenze, než je dimenze úplně vpravo.
Poznámka.
Preserve není rezervované slovo.
Příkaz Erase
Příkaz Erase nastaví každou z proměnných pole nebo vlastností zadaných v příkazu na Nothing. Každý výraz v příkazu musí být klasifikován jako proměnná nebo vlastnost přístup, jehož typ je typ pole nebo Object. Například:
Module Test
Sub Main()
Dim x() As Integer = New Integer(5) {}
' The following two statements are equivalent.
Erase x
x = Nothing
End Sub
End Module
EraseStatement
: 'Erase' EraseExpressions StatementTerminator
;
EraseExpressions
: Expression ( Comma Expression )*
;
Using – příkaz
Instance typů jsou automaticky uvolněny uvolňováním paměti při spuštění kolekce a nejsou nalezeny žádné živé odkazy na instanci. Pokud typ obsahuje zvláště cenný a vzácný prostředek (například připojení k databázi nebo popisovače souborů), nemusí být žádoucí počkat na další uvolňování paměti, aby se vyčistil konkrétní instance typu, který se už nepoužívá. Pokud chcete poskytnout jednoduchý způsob uvolnění prostředků před kolekcí, může typ implementovat System.IDisposable rozhraní. Typ, který tak zpřístupní metodu, kterou lze volat k vynucení okamžitého Dispose uvolnění cenných prostředků, například:
Module Test
Sub Main()
Dim x As DBConnection = New DBConnection("...")
' Do some work
...
x.Dispose() ' Free the connection
End Sub
End Module
Tento Using příkaz automatizuje proces získání prostředku, spuštění sady příkazů a následného odstranění prostředku. Příkaz může mít dvě formy: v jedné je prostředek místní proměnnou deklarovanou jako součást příkazu a považována za regulární příkaz deklarace místní proměnné; v druhém je prostředek výsledkem výrazu.
UsingStatement
: 'Using' UsingResources StatementTerminator
Block?
'End' 'Using' StatementTerminator
;
UsingResources
: VariableDeclarators
| Expression
;
Pokud je prostředek příkaz deklarace místní proměnné, pak typ deklarace místní proměnné musí být typ, který lze implicitně převést na System.IDisposable. Deklarované místní proměnné jsou jen pro čtení, vymezeny na Using blok příkazu a musí obsahovat inicializátor. Pokud je prostředek výsledkem výrazu, musí být výraz klasifikován jako hodnota a musí být typu, který lze implicitně převést na System.IDisposable. Výraz se vyhodnocuje pouze jednou na začátku příkazu.
Blok Using je implicitně obsažen příkazem Try , jehož nakonec blok volá metodu IDisposable.Dispose prostředku. Tím se zajistí, že se prostředek odstraní i v případě, že dojde k vyvolání výjimky. V důsledku toho je neplatné vytvořit větev do Using bloku mimo blok a Using blok je považován za jediný příkaz pro účely Resume a Resume Next. Pokud je Nothingprostředek , nebude provedeno žádné volání Dispose . Příklad:
Using f As C = New C()
...
End Using
odpovídá:
Dim f As C = New C()
Try
...
Finally
If f IsNot Nothing Then
f.Dispose()
End If
End Try
Příkaz Using , který má příkaz deklarace místní proměnné, může získat více prostředků najednou, což je ekvivalentem vnořených Using příkazů. Například Using příkaz formuláře:
Using r1 As R = New R(), r2 As R = New R()
r1.F()
r2.F()
End Using
odpovídá:
Using r1 As R = New R()
Using r2 As R = New R()
r1.F()
r2.F()
End Using
End Using
Příkaz Await
Příkaz await má stejnou syntaxi jako výraz operátoru await (Section Await Operator), je povolen pouze v metodách, které také umožňují výrazy await a mají stejné chování jako výraz operátoru await.
Může se ale klasifikovat jako hodnota nebo void. Jakákoli hodnota vyplývající z vyhodnocení výrazu operátoru await se zahodí.
AwaitStatement
: AwaitOperatorExpression StatementTerminator
;
Příkaz Yield
Příkazy yield se vztahují k metodám iterátoru, které jsou popsány v části Iterator Methods.
YieldStatement
: 'Yield' Expression StatementTerminator
;
Yield je vyhrazené slovo, pokud bezprostředně uzavřená metoda nebo výraz lambda, ve kterém se zobrazí, má Iterator modifikátor, a pokud Yield se zobrazí za tímto Iterator modifikátorem; není rezervován jinde. Není také rezervován v direktivách preprocesoru. Příkaz yield je povolen pouze v těle metody nebo výrazu lambda, kde se jedná o rezervované slovo. V rámci bezprostředně uzavřené metody nebo lambda nemusí příkaz výnosu nastat uvnitř těla Catch nebo Finally bloku ani uvnitř těla SyncLock příkazu.
Příkaz yield přebírá jeden výraz, který musí být klasifikován jako hodnota a jehož typ je implicitně konvertibilní na typ aktuální proměnné iterátoru (Section Iterator Methods) jeho uzavřené metody iterátoru.
Tok řízení dosáhne příkazu pouze Yield při MoveNext vyvolání metody v objektu iterátoru. (Důvodem je to, že instance metody iterátoru spouští pouze své příkazy z důvodu MoveNext nebo Dispose metod, které jsou volány na objektu iterátoru; a Dispose metoda bude provádět pouze kód v Finally blocích, kde Yield není povoleno).
Yield Při spuštění příkazu se jeho výraz vyhodnotí a uloží v aktuální proměnné iterátoru instance metody iterátoru přidružené k danému objektu iterátoru. Hodnota True je vrácena do invoker of MoveNext, a řídicí bod této instance se zastaví až do další vyvolání MoveNext objektu iterátoru.
Visual Basic language spec