Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Anweisungen stellen ausführbaren Code dar.
Statement
: LabelDeclarationStatement
| LocalDeclarationStatement
| WithStatement
| SyncLockStatement
| EventStatement
| AssignmentStatement
| InvocationStatement
| ConditionalStatement
| LoopStatement
| ErrorHandlingStatement
| BranchStatement
| ArrayHandlingStatement
| UsingStatement
| AwaitStatement
| YieldStatement
;
Hinweis: Der Microsoft Visual Basic-Compiler lässt nur Anweisungen zu, die mit einem Schlüsselwort oder einem Bezeichner beginnen. So ist beispielsweise die Aufrufsanweisung "Call (Console).WriteLine" zulässig, aber die Aufrufsanweisung "(Console).WriteLine" ist nicht.
Kontrollfluss
Der Steuerungsfluss ist die Sequenz, in der Anweisungen und Ausdrücke ausgeführt werden. Die Reihenfolge der Ausführung hängt von der jeweiligen Anweisung oder dem jeweiligen Ausdruck ab.
Wenn Sie beispielsweise einen Additionsoperator (Section Addition Operator) auswerten, wird zuerst der linke Operand ausgewertet, dann der rechte Operand und dann der Operator selbst. Blöcke werden ausgeführt ( Abschnittsblöcke und Beschriftungen), indem sie zuerst ihre erste Unterausrichtung ausführen und dann nacheinander durch die Anweisungen des Blocks fortfahren.
Implizit in dieser Reihenfolge ist das Konzept eines Kontrollpunkts, bei dem es sich um den nächsten auszuführenden Vorgang handelt. Wenn eine Methode aufgerufen wird (oder "aufgerufen"), wird angenommen, dass sie eine Instanz der Methode erstellt. Eine Methodeninstanz besteht aus einer eigenen Kopie der Parameter und lokalen Variablen der Methode und ihrem eigenen Kontrollpunkt.
Reguläre Methoden
Nachfolgend sehen Sie ein Beispiel für eine reguläre Methode.
Function Test() As Integer
Console.WriteLine("hello")
Return 1
End Function
Dim x = Test() ' invokes the function, prints "hello", assigns 1 to x
Wenn eine reguläre Methode aufgerufen wird,
- Zuerst wird eine Instanz der Methode speziell für diesen Aufruf erstellt. Diese Instanz enthält eine Kopie aller Parameter und lokalen Variablen der Methode.
- Dann werden alle Parameter für die angegebenen Werte initialisiert, und alle lokalen Variablen werden auf die Standardwerte ihrer Typen initialisiert.
- Im Fall einer
Function, eine implizite lokale Variable wird auch als Funktionsrückgabevariable initialisiert, deren Name der Funktion name ist, deren Typ der Rückgabetyp der Funktion und deren Anfangswert der Standardwert des Typs ist. - Der Kontrollpunkt der Methodeninstanz wird dann an der ersten Anweisung des Methodentexts festgelegt, und der Methodentext beginnt sofort mit der Ausführung von dort ( Abschnittsblöcke und Bezeichnungen).
Wenn der Steuerungsfluss den Methodentext normal verlässt – durch Erreichen des End Function Endes oder End Sub durch eine explizite Return Oder Exit Anweisung – wird der Kontrollfluss an den Aufrufer der Methodeninstanz zurückgegeben. Wenn eine Funktionsrückgabevariable vorhanden ist, ist das Ergebnis des Aufrufs der Endwert dieser Variablen.
Wenn der Steuerfluss den Methodentext über eine unbehandelte Ausnahme verlässt, wird diese Ausnahme an den Aufrufer weitergegeben.
Nachdem der Steuerungsfluss beendet wurde, gibt es keine Liveverweise mehr auf die Methodeninstanz. Wenn die Methodeninstanz die einzigen Verweise auf die Kopie lokaler Variablen oder Parameter enthält, werden sie möglicherweise garbage collection.
Iteratormethoden
Iteratormethoden werden als bequeme Möglichkeit zum Generieren einer Sequenz verwendet, die von der For Each Anweisung verwendet werden kann. Iteratormethoden verwenden die Yield Anweisung (Section Yield Statement), um Elemente der Sequenz bereitzustellen. (Eine Iteratormethode ohne Yield Anweisungen erzeugt eine leere Sequenz). Hier ist ein Beispiel für eine Iteratormethode:
Iterator Function Test() As IEnumerable(Of Integer)
Console.WriteLine("hello")
Yield 1
Yield 2
End Function
Dim en = Test()
For Each x In en ' prints "hello" before the first x
Console.WriteLine(x) ' prints "1" and then "2"
Next
Wenn eine Iteratormethode aufgerufen wird, deren Rückgabetyp lautet IEnumerator(Of T),
- Zuerst wird eine Instanz der Iteratormethode speziell für diesen Aufruf erstellt. Diese Instanz enthält eine Kopie aller Parameter und lokalen Variablen der Methode.
- Dann werden alle Parameter für die angegebenen Werte initialisiert, und alle lokalen Variablen werden auf die Standardwerte ihrer Typen initialisiert.
- Eine implizite lokale Variable wird auch als iterator aktuelle Variable initialisiert, deren Typ und
Tdessen Anfangswert der Standardwert des Typs ist. - Der Kontrollpunkt der Methodeninstanz wird dann an der ersten Anweisung des Methodentexts festgelegt.
- Anschließend wird ein Iteratorobjekt erstellt, das dieser Methodeninstanz zugeordnet ist. Das Iteratorobjekt implementiert den deklarierten Rückgabetyp und weist wie unten beschrieben ein Verhalten auf.
- Der Steuerungsfluss wird dann sofort im Aufrufer fortgesetzt, und das Ergebnis des Aufrufs ist das Iteratorobjekt. Beachten Sie, dass diese Übertragung durchgeführt wird, ohne die Iteratormethodeninstanz zu beenden, und führt nicht dazu, dass schließlich Handler ausgeführt werden. Auf die Methodeninstanz wird weiterhin vom Iteratorobjekt verwiesen, und es wird keine Garbage Collection ausgeführt, solange ein Liveverweis auf das Iteratorobjekt vorhanden ist.
Wenn auf die Eigenschaft des Iteratorobjekts Current zugegriffen wird, wird die aktuelle Variable des Aufrufs zurückgegeben.
Wenn die Methode des Iteratorobjekts MoveNext aufgerufen wird, erstellt der Aufruf keine neue Methodeninstanz. Stattdessen wird die vorhandene Methodeninstanz verwendet (und ihr Kontrollpunkt und lokale Variablen und Parameter) – die Instanz, die beim ersten Aufrufen der Iteratormethode erstellt wurde. Der Steuerungsfluss setzt die Ausführung am Kontrollpunkt dieser Methodeninstanz fort und durchläuft den Textkörper der Iteratormethode normal.
Wenn die Methode des Iteratorobjekts Dispose aufgerufen wird, wird erneut die vorhandene Methodeninstanz verwendet. Der Steuerungsfluss wird am Kontrollpunkt dieser Methodeninstanz fortgesetzt, verhält sich dann aber sofort so, als ob eine Exit Function Anweisung der nächste Vorgang wäre.
Die obigen Beschreibungen des Verhaltens für den Aufruf oder MoveNextDispose für ein Iteratorobjekt gelten nur, wenn alle vorherigen Aufrufe des MoveNext iterator-Objekts Dispose bereits an ihre Aufrufer zurückgegeben wurden. Andernfalls ist das Verhalten nicht definiert.
Wenn der Steuerungsfluss den Iteratormethodentext normal verlässt - durch Erreichen des End Function zugehörigen Endes oder durch eine explizite Return oder Exit Anweisung -- muss dies im Kontext eines Aufrufs oder MoveNextDispose einer Funktion für ein Iterator-Objekt erfolgen, um die Iterator-Methodeninstanz fortzusetzen, und es wurde die Methodeninstanz verwendet, die erstellt wurde, als die Iteratormethode zum ersten Mal aufgerufen wurde. Der Kontrollpunkt dieser Instanz befindet sich in der End Function Anweisung, und der Steuerungsfluss wird im Aufrufer fortgesetzt. Wenn er von einem Aufruf MoveNext fortgesetzt wurde, wird der Wert False an den Aufrufer zurückgegeben.
Wenn der Steuerungsfluss den Text der Iteratormethode über eine unbehandelte Ausnahme verlässt, wird die Ausnahme an den Aufrufer weitergegeben, der wiederum ein Aufruf von MoveNext oder von .Dispose
Wie für die anderen möglichen Rückgabetypen einer Iteratorfunktion,
- Wenn eine Iteratormethode aufgerufen wird, deren Rückgabetyp für einige
Tbestimmt istIEnumerable(Of T), wird eine Instanz zuerst erstellt – spezifisch für diesen Aufruf der Iteratormethode - aller Parameter in der Methode, und sie werden mit den angegebenen Werten initialisiert. Das Ergebnis des Aufrufs ist ein Objekt, das den Rückgabetyp implementiert. Sollte die Methode dieses ObjektsGetEnumeratoraufgerufen werden, erstellt es eine Instanz , die für diesen AufrufGetEnumeratorspezifisch ist – aller Parameter und lokalen Variablen in der Methode. Er initialisiert die Parameter für die bereits gespeicherten Werte und wird wie für die oben beschriebene Iteratormethode fortgesetzt. - Wenn eine Iteratormethode aufgerufen wird, deren Rückgabetyp die nicht generische Schnittstelle
IEnumeratorist, ist das Verhalten genau wie fürIEnumerator(Of Object). - Wenn eine Iteratormethode aufgerufen wird, deren Rückgabetyp die nicht generische Schnittstelle
IEnumerableist, ist das Verhalten genau wie fürIEnumerable(Of Object).
Asynchrone Methoden
Asynchrone Methoden sind eine bequeme Möglichkeit, lange ausgeführte Aufgaben auszuführen, ohne z. B. die Benutzeroberfläche einer Anwendung zu blockieren. Async ist kurz für asynchron – das bedeutet, dass der Aufrufer der asynchronen Methode die Ausführung umgehend fortsetzen wird, aber der spätere Abschluss der Instanz der asynchronen Methode kann zu einem späteren Zeitpunkt in der Zukunft auftreten. Standardmäßig werden asynchrone Methoden mit dem Suffix "Async" benannt.
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"
Hinweis: Asynchrone Methoden werden nicht in einem Hintergrundthread ausgeführt. Stattdessen ermöglichen sie es einer Methode, sich selbst über den Await Operator anzusetzen, und planen, dass sie als Reaktion auf ein ereignis wieder aufgenommen werden.
Wenn eine asynchrone Methode aufgerufen wird
- Zuerst wird eine Instanz der asynchronen Methode speziell für diesen Aufruf erstellt. Diese Instanz enthält eine Kopie aller Parameter und lokalen Variablen der Methode.
- Dann werden alle Parameter für die angegebenen Werte initialisiert, und alle lokalen Variablen werden auf die Standardwerte ihrer Typen initialisiert.
- Bei einer asynchronen Methode mit Rückgabetyp
Task(Of T)für einigeTwird eine implizite lokale Variable auch als Task-Rückgabevariable initialisiert, deren Typ undTdessen Anfangswert der Standardwert istT. - Wenn es sich bei der asynchronen Methode um einen
FunctionRückgabetypTaskoderTask(Of T)für einigeThandelt, wird ein Objekt dieses Typs implizit erstellt, das dem aktuellen Aufruf zugeordnet ist. Dies wird als asynchrones Objekt bezeichnet und stellt die zukünftige Arbeit dar, die durch Ausführen der Instanz der asynchronen Methode ausgeführt wird. Wenn das Steuerelement im Aufrufer dieser asynchronen Methodeninstanz fortgesetzt wird, empfängt der Aufrufer dieses asynchrone Objekt als Ergebnis des Aufrufs. - Der Kontrollpunkt der Instanz wird dann an der ersten Anweisung des asynchronen Methodentexts festgelegt und beginnt sofort, den Methodentext von dort aus auszuführen ( Abschnittsblöcke und Bezeichnungen).
Reaktivierungsdelegat und aktueller Anrufer
Wie im Abschnitts-Await-Operator beschrieben, hat die Ausführung eines Await Ausdrucks die Möglichkeit, den Kontrollpunkt der Methodeninstanz anzuhalten, sodass der Kontrollfluss an einer anderen Stelle verschoben wird. Der Steuerungsfluss kann später durch Aufruf eines Reaktivierungsdelegats am Kontrollpunkt derselben Instanz fortgesetzt werden. Beachten Sie, dass dieses Anhalten ohne Beenden der asynchronen Methode erfolgt und nicht dazu führt, dass schließlich Handler ausgeführt werden. Auf die Methodeninstanz wird weiterhin sowohl vom Reaufnahmedelegat als Task auch vom Ergebnis Task(Of T) (sofern vorhanden) verwiesen, und es wird keine Garbage Collection durchgeführt, solange ein Liveverweis auf Stellvertretung oder Ergebnis vorhanden ist.
Es ist hilfreich, sich die Aussage Dim x = Await WorkAsync() ungefähr als syntaktische Kurzform für Folgendes vorzustellen:
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()
Im Folgenden wird der aktuelle Aufrufer der Methodeninstanz entweder als ursprünglicher Aufrufer oder als Aufrufer des Resummierungsdelegats definiert, je nachdem, welcher Zeitpunkt neuer ist.
Wenn der Steuerfluss den asynchronen Methodentext verlässt – durch Erreichen des End Sub Endes oder End Function durch eine explizite Return Oder Exit Anweisung oder durch eine unbehandelte Ausnahme – wird der Kontrollpunkt der Instanz auf das Ende der Methode festgelegt. Das Verhalten hängt dann vom Rückgabetyp der asynchronen Methode ab.
Im Falle eines
Async FunctionRückgabetypsTask:Wenn der Steuerungsfluss durch eine nicht behandelte Ausnahme beendet wird, wird der Status des asynchronen Objekts auf die Ausnahme festgelegt
TaskStatus.Faultedund seineException.InnerExceptionEigenschaft wird auf die Ausnahme festgelegt (außer: bestimmte implementierungsdefinierte Ausnahmen, zOperationCanceledException. B. ändern sie inTaskStatus.Canceled). Der Steuerungsfluss wird im aktuellen Aufrufer fortgesetzt.Andernfalls wird der Status des asynchronen Objekts auf
TaskStatus.Completed. Der Steuerungsfluss wird im aktuellen Aufrufer fortgesetzt.(Hinweis. Der gesamte Punkt von Task und was asynchrone Methoden interessant macht, ist, dass, wenn eine Aufgabe abgeschlossen wird, dann werden alle Methoden, die darauf warten, ihre Fortsetzungsdelegat ausgeführt, d. h. sie werden entsperrt.)
Im Falle eines Rückgabetyps
Task(Of T)für einigeAsync FunctionT: das Verhalten ist wie oben dargestellt, außer dass die Eigenschaft desResultasynchronen Objekts auch auf den endgültigen Wert der Task-Rückgabevariable festgelegt ist.Im Falle einer
Async Sub:- Wenn der Steuerungsfluss durch eine unbehandelte Ausnahme beendet wird, wird diese Ausnahme auf eine implementierungsspezifische Weise an die Umgebung weitergegeben. Der Steuerungsfluss wird im aktuellen Aufrufer fortgesetzt.
- Andernfalls wird der Steuerungsfluss einfach im aktuellen Aufrufer fortgesetzt.
Async Sub
Es gibt ein microsoftspezifisches Verhalten eines Async Sub.
Nothing Wenn SynchronizationContext.Current sich der Aufruf am Anfang befindet, werden alle unbehandelten Ausnahmen von einem Async-Sub in den Threadpool gepostet.
Wenn SynchronizationContext.Current sich der Aufruf nicht Nothing am Anfang des Aufrufs befindet, wird dieser OperationStarted() Kontext vor dem Start der Methode und OperationCompleted() nach dem Ende aufgerufen. Darüber hinaus werden alle unbehandelten Ausnahmen veröffentlicht, um im Synchronisierungskontext erneut zu ernennen.
Dies bedeutet, dass in Benutzeroberflächenanwendungen für einen Async Sub im UI-Thread aufgerufenen Benutzeroberflächenthread alle Ausnahmen, die sie nicht verarbeiten kann, den UI-Thread erneut bereitstellen.
Mutierbare Strukturen in asynchronen und Iteratormethoden
Mutierbare Strukturen werden im Allgemeinen als schlechte Praxis betrachtet, und sie werden nicht von asynchronen oder Iteratormethoden unterstützt. Insbesondere wird jeder Aufruf einer asynchronen oder iterator-Methode in einer Struktur implizit auf eine Kopie dieser Struktur ausgeführt, die zum Zeitpunkt des Aufrufs kopiert wird. So z. B.
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"
Blöcke und Beschriftungen
Eine Gruppe von ausführbaren Anweisungen wird als Anweisungsblock bezeichnet. Die Ausführung eines Anweisungsblocks beginnt mit der ersten Anweisung im Block. Sobald eine Anweisung ausgeführt wurde, wird die nächste Anweisung in lexikalischer Reihenfolge ausgeführt, es sei denn, eine Anweisung überträgt die Ausführung an anderer Stelle oder eine Ausnahme.
Innerhalb eines Anweisungsblocks ist die Division von Anweisungen in logischen Zeilen mit Ausnahme von Bezeichnungsdeklarationsanweisungen nicht signifikant. Eine Bezeichnung ist ein Bezeichner, der eine bestimmte Position innerhalb des Anweisungsblocks identifiziert, die als Ziel einer Verzweigungsanweisung verwendet werden kann, z GoTo. B. .
Block
: Statements*
;
LabelDeclarationStatement
: LabelName ':'
;
LabelName
: Identifier
| IntLiteral
;
Statements
: Statement? ( ':' Statement? )*
;
Bezeichnungsdeklarationsanweisungen müssen am Anfang einer logischen Zeile angezeigt werden, und Bezeichnungen können entweder ein Bezeichner oder ein ganzzahliges Literal sein. Da sowohl Bezeichnungsdeklarationsanweisungen als auch Aufrufanweisungen aus einem einzelnen Bezeichner bestehen können, wird ein einzelner Bezeichner am Anfang einer lokalen Zeile immer als Bezeichnungsdeklarationsanweisung betrachtet. Bezeichnungsdeklarationsanweisungen müssen immer durch einen Doppelpunkt gefolgt werden, auch wenn keine Anweisungen in derselben logischen Zeile folgen.
Bezeichnungen verfügen über einen eigenen Deklarationsbereich und beeinträchtigen keine anderen Bezeichner. Das folgende Beispiel ist gültig und verwendet die Namensvariable x sowohl als Parameter als auch als Bezeichnung.
Function F(x As Integer) As Integer
If x >= 0 Then
GoTo x
End If
x = -x
x:
Return x
End Function
Der Bereich einer Bezeichnung ist der Textkörper der Methode, die sie enthält.
Aus Gründen der Lesbarkeit werden Anweisungsproduktionen, die mehrere Unterstatements umfassen, in dieser Spezifikation als einzelne Produktion behandelt, auch wenn sich die Unterangaben jeweils einzeln in einer beschrifteten Zeile befinden können.
Lokale Variablen und Parameter
In den vorherigen Abschnitten wird erläutert, wie und wann Methodeninstanzen erstellt werden, und mit ihnen werden die Kopien der lokalen Variablen und Parameter einer Methode kopiert. Außerdem wird jedes Mal, wenn der Textkörper einer Schleife eingegeben wird, eine neue Kopie aus jeder lokalen Variablen erstellt, die in dieser Schleife wie in Section Loop-Anweisungen beschrieben deklariert wird, und die Methodeninstanz enthält jetzt diese Kopie der lokalen Variablen anstelle der vorherigen Kopie.
Alle Gebietsschemas werden auf den Standardwert ihres Typs initialisiert. Lokale Variablen und Parameter sind immer öffentlich zugänglich. Es ist ein Fehler, auf eine lokale Variable an einer textbezogenen Position zu verweisen, die der Deklaration vorausgeht, wie im folgenden Beispiel veranschaulicht wird:
Class A
Private i As Integer = 0
Sub F()
i = 1
Dim i As Integer ' Error, use precedes declaration.
i = 2
End Sub
Sub G()
Dim a As Integer = 1
Dim b As Integer = a ' This is valid.
End Sub
End Class
In der F obigen Methode bezieht sich die erste Zuordnung i ausdrücklich nicht auf das feld, das im äußeren Bereich deklariert ist. Stattdessen bezieht es sich auf die lokale Variable, und sie befindet sich im Fehler, da sie der Deklaration der Variablen textual vorausgeht. In der G Methode bezieht sich eine nachfolgende Variablendeklaration auf eine lokale Variable, die in einer früheren Variablendeklaration innerhalb derselben lokalen Variablendeklaration deklariert wurde.
Jeder Block in einer Methode erstellt einen Deklarationsbereich für lokale Variablen. Namen werden in diesen Deklarationsbereich über lokale Variablendeklarationen im Methodentext und über die Parameterliste der Methode eingeführt, die Namen in den Deklarationsbereich des äußersten Blocks einführt. Blöcke lassen das Schattieren von Namen durch Schachtelung nicht zu: Sobald ein Name in einem Block deklariert wurde, wird der Name möglicherweise nicht in geschachtelten Blöcken neu deklariert.
Daher sind die F Methoden und G Methoden im folgenden Beispiel fehlerhaft, da der Name i im äußeren Block deklariert und nicht im inneren Block neu deklariert werden kann. Die H Methoden sind I jedoch gültig, da die beiden iBlöcke in separaten, nicht geschachtelten Blöcken deklariert werden.
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
Wenn die Methode eine Funktion ist, wird implizit eine spezielle lokale Variable im Deklarationsraum des Methodentexts mit demselben Namen wie die Methode deklariert, die den Rückgabewert der Funktion darstellt. Die lokale Variable verfügt über spezielle Namensauflösungssemantik, wenn sie in Ausdrücken verwendet wird. Wenn die lokale Variable in einem Kontext verwendet wird, der einen ausdruck erwartet, der als Methodengruppe klassifiziert ist, z. B. einen Aufrufausdruck, wird der Name in die Funktion und nicht in die lokale Variable aufgelöst. Beispiel:
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
Die Verwendung von Klammern kann mehrdeutige Situationen verursachen (z F(1). B. eine Funktion, bei der F es sich um eine Funktion handelt, deren Rückgabetyp ein eindimensionales Array ist); in allen mehrdeutigen Situationen wird der Name in die Funktion anstelle der lokalen Variablen aufgelöst. Beispiel:
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
Wenn der Steuerfluss den Methodentext verlässt, wird der Wert der lokalen Variablen an den Aufrufausdruck übergeben. Wenn es sich bei der Methode um eine Unterroutine handelt, gibt es keine solche implizite lokale Variable, und das Steuerelement kehrt einfach zum Aufrufausdruck zurück.
Lokale Deklarationsanweisungen
Eine lokale Deklarationsanweisung deklariert eine neue lokale Variable, lokale Konstante oder statische Variable.
Lokale Variablen und lokale Konstanten entsprechen Instanzenvariablen und Konstanten für die Methode und werden auf die gleiche Weise deklariert.
Statische Variablen ähneln Shared Variablen und werden mithilfe des Static Modifizierers deklariert.
LocalDeclarationStatement
: LocalModifier VariableDeclarators StatementTerminator
;
LocalModifier
: 'Static' | 'Dim' | 'Const'
;
Statische Variablen sind Lokale, die ihren Wert über Aufrufe der Methode hinweg beibehalten. Statische Variablen, die in nicht freigegebenen Methoden deklariert sind, sind pro Instanz: Jede Instanz des Typs, der die Methode enthält, verfügt über eine eigene Kopie der statischen Variablen. Statische Variablen, die innerhalb von Shared Methoden deklariert sind, sind pro Typ vorhanden. Es gibt nur eine Kopie der statischen Variablen für alle Instanzen. Während lokale Variablen bei jedem Eintrag in der Methode auf den Standardwert ihres Typs initialisiert werden, werden statische Variablen nur für den Standardwert des Typs initialisiert, wenn der Typ oder die Typinstanz initialisiert wird. Statische Variablen werden möglicherweise nicht in Strukturen oder generischen Methoden deklariert.
Lokale Variablen, lokale Konstanten und statische Variablen weisen immer öffentliche Barrierefreiheit auf und geben möglicherweise keine Barrierefreiheitsmodifizierer an. Wenn für eine lokale Deklarationsanweisung kein Typ angegeben ist, bestimmen die folgenden Schritte den Typ der lokalen Deklaration:
Wenn die Deklaration ein Typzeichen aufweist, ist der Typ des Typzeichens der Typ der lokalen Deklaration.
Wenn es sich bei der lokalen Deklaration um eine lokale Konstante handelt oder die lokale Deklaration eine lokale Variable mit einem Initialisierungs- und lokalen Variablentyp ist, wird der Typ der lokalen Deklaration vom Typ des Initialisierers abgeleitet. Wenn sich der Initialisierer auf die lokale Deklaration bezieht, tritt ein Kompilierungszeitfehler auf. (Lokale Konstanten sind für Initialisierer erforderlich.)
Wenn keine strenge Semantik verwendet wird, wird der Typ der lokalen Deklarationsanweisung implizit
Objectverwendet.Andernfalls tritt ein Kompilierfehler auf.
Wenn kein Typ für eine lokale Deklarationsanweisung angegeben wird, die über eine Arraygröße oder einen Arraytypmodifizierer verfügt, handelt es sich bei dem Typ der lokalen Deklaration um ein Array mit dem angegebenen Rang, und die vorherigen Schritte werden verwendet, um den Elementtyp des Arrays zu bestimmen. Wenn lokale Variablentypausschlüsse verwendet werden, muss der Typ des Initialisierungsprogramms ein Arraytyp mit demselben Array-Shape (d. h. Arraytypmodifizierer) sein wie die lokale Deklarationsanweisung. Beachten Sie, dass der abgeleitete Elementtyp möglicherweise immer noch ein Arraytyp ist. Beispiel:
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
Wenn kein Typ für eine lokale Deklarationsanweisung angegeben wird, die einen Modifizierer mit nullablem Typ aufweist, ist der Typ der lokalen Deklaration die nullable Version des abgeleiteten Typs oder der abgeleitete Typ selbst, wenn er bereits nullablen Werttyp ist. Wenn der abgeleitete Typ kein Werttyp ist, der nullwertebar gemacht werden kann, tritt ein Kompilierungszeitfehler auf. Wenn sowohl ein modifizierbarer Typ als auch ein Arraygrößen- oder Arraytypmodifizierer in einer lokalen Deklarationsanweisung ohne Typ platziert werden, wird der Modifizierer vom Typ nullabler Typ als auf den Elementtyp des Arrays angewendet, und die vorherigen Schritte werden verwendet, um den Elementtyp zu bestimmen.
Variable Initializer für lokale Deklarationsanweisungen entsprechen zuordnungsanweisungen, die an der Textposition der Deklaration platziert werden. Wenn die Ausführung also über die lokale Deklarationsanweisung verzweigt, wird der Variableninitialisierer nicht ausgeführt. Wenn die lokale Deklarationsanweisung mehrmals ausgeführt wird, wird der Variableninitialisierer gleich oft ausgeführt. Statische Variablen führen ihren Initialisierer nur zum ersten Mal aus. Wenn beim Initialisieren einer statischen Variable eine Ausnahme auftritt, wird die statische Variable mit dem Standardwert des Typs der statischen Variablen initialisiert.
Das folgende Beispiel zeigt die Verwendung von Initialisierern:
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
Dieses Programm druckt:
Static variable x = 5
Static variable x = 6
Static variable x = 7
Local variable y = 8
Local variable y = 8
Local variable y = 8
Initialisierer für statische Lokale sind threadsicher und während der Initialisierung vor Ausnahmen geschützt. Wenn eine Ausnahme während eines statischen lokalen Initialisierungsprogramms auftritt, hat die statische lokale Datei ihren Standardwert und wird nicht initialisiert. Ein statischer lokaler Initialisierer
Module Test
Sub F()
Static x As Integer = 5
End Sub
End Module
entspricht
Imports System.Threading
Imports Microsoft.VisualBasic.CompilerServices
Module Test
Class InitFlag
Public State As Short
End Class
Private xInitFlag As InitFlag = New InitFlag()
Sub F()
Dim x As Integer
If xInitFlag.State <> 1 Then
Monitor.Enter(xInitFlag)
Try
If xInitFlag.State = 0 Then
xInitFlag.State = 2
x = 5
Else If xInitFlag.State = 2 Then
Throw New IncompleteInitialization()
End If
Finally
xInitFlag.State = 1
Monitor.Exit(xInitFlag)
End Try
End If
End Sub
End Module
Lokale Variablen, lokale Konstanten und statische Variablen sind auf den Anweisungsblock festgelegt, in dem sie deklariert werden. Statische Variablen sind besonders darauf spezialisiert, dass ihre Namen nur einmal während der gesamten Methode verwendet werden können. Beispielsweise ist es ungültig, zwei statische Variablendeklarationen mit demselben Namen anzugeben, auch wenn sie sich in verschiedenen Blöcken befinden.
Implizite lokale Deklarationen
Zusätzlich zu lokalen Deklarationsanweisungen können lokale Variablen implizit über die Verwendung deklariert werden. Ein einfacher Namensausdruck, der einen Namen verwendet, der nicht in eine andere Datei aufgelöst wird, deklariert eine lokale Variable anhand dieses Namens. Beispiel:
Option Explicit Off
Module Test
Sub Main()
x = 10
y = 20
Console.WriteLine(x + y)
End Sub
End Module
Implizite lokale Deklaration tritt nur in Ausdruckskontexten auf, die einen ausdruck akzeptieren können, der als Variable klassifiziert ist. Die Ausnahme dieser Regel besteht darin, dass eine lokale Variable möglicherweise nicht implizit deklariert wird, wenn sie das Ziel eines Funktionsaufrufausdrucks, indizierungsausdrucks oder eines Memberzugriffsausdrucks ist.
Implizite Lokale werden behandelt, als ob sie am Anfang der enthaltenden Methode deklariert werden. Daher sind sie immer auf den gesamten Methodentext festgelegt, auch wenn sie innerhalb eines Lambda-Ausdrucks deklariert sind. Beispiel: Der folgende Code:
Option Explicit Off
Module Test
Sub Main()
Dim x = Sub()
a = 10
End Sub
Dim y = Sub()
Console.WriteLine(a)
End Sub
x()
y()
End Sub
End Module
druckt den Wert 10. Implizite Lokale werden so eingegeben, als Object ob kein Typzeichen an den Variablennamen angefügt wurde. Andernfalls ist der Typ der Variablen der Typ des Typzeichens. Lokale Variablentypinference wird nicht für implizite Locals verwendet.
Wenn die explizite lokale Deklaration von der Kompilierungsumgebung oder von der Option ExplicitKompilierung angegeben wird, müssen alle lokalen Variablen explizit deklariert werden, und die implizite Variablendeklaration ist unzulässig.
With-Anweisung
Eine With Anweisung ermöglicht mehrere Verweise auf die Member eines Ausdrucks, ohne den Ausdruck mehrmals anzugeben.
WithStatement
: 'With' Expression StatementTerminator
Block?
'End' 'With' StatementTerminator
;
Der Ausdruck muss als Wert klassifiziert und einmal ausgewertet werden, wenn er in den Block eintritt. Innerhalb des Anweisungsblocks With wird ein Elementzugriffsausdruck oder ein Wörterbuchzugriffsausdruck, der mit einem Punkt oder einem Ausrufezeichen beginnt, ausgewertet, als ob ihm der With Ausdruck vorausging. Beispiel:
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
Es ist ungültig, in einen With Anweisungsblock außerhalb des Blocks zu verzweigen.
SyncLock-Anweisung
Eine SyncLock Anweisung ermöglicht die Synchronisierung von Anweisungen für einen Ausdruck, wodurch sichergestellt wird, dass mehrere Ausführungsthreads nicht gleichzeitig dieselben Anweisungen ausführen.
SyncLockStatement
: 'SyncLock' Expression StatementTerminator
Block?
'End' 'SyncLock' StatementTerminator
;
Der Ausdruck muss als Wert klassifiziert und einmal ausgewertet werden, wenn er in den Block eintritt. Beim Eingeben des SyncLock Blocks wird die Shared Methode System.Threading.Monitor.Enter für den angegebenen Ausdruck aufgerufen, der blockiert, bis der Ausführungsthread eine exklusive Sperre für das vom Ausdruck zurückgegebene Objekt aufweist. Der Typ des Ausdrucks in einer SyncLock Anweisung muss ein Verweistyp sein. Beispiel:
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
Im obigen Beispiel wird eine Synchronisierung für die bestimmte Instanz der Klasse Test ausgeführt, um sicherzustellen, dass für eine bestimmte Instanz nicht mehr als ein Ausführungsthread die Anzahlvariable addiert oder subtrahiert werden kann.
Der SyncLock Block ist implizit in einer Try Anweisung enthalten, deren Finally Block die Shared Methode System.Threading.Monitor.Exit für den Ausdruck aufruft. Dadurch wird sichergestellt, dass die Sperre auch dann freigegeben wird, wenn eine Ausnahme ausgelöst wird. Daher ist es ungültig, einen Block von außerhalb des Blocks zu SyncLock verzweigen, und ein SyncLock Block wird als einzelne Anweisung für die Zwecke Resume und .Resume Next Das obige Beispiel entspricht dem folgenden Code:
Class Test
Private count As Integer = 0
Public Function Add() As Integer
Try
System.Threading.Monitor.Enter(Me)
count += 1
Add = count
Finally
System.Threading.Monitor.Exit(Me)
End Try
End Function
Public Function Subtract() As Integer
Try
System.Threading.Monitor.Enter(Me)
count -= 1
Subtract = count
Finally
System.Threading.Monitor.Exit(Me)
End Try
End Function
End Class
Event-Anweisungen
Die RaiseEvent, AddHandlerund Anweisungen lösen Ereignisse aus RemoveHandler und behandeln Ereignisse dynamisch.
EventStatement
: RaiseEventStatement
| AddHandlerStatement
| RemoveHandlerStatement
;
RaiseEvent-Anweisung
Eine RaiseEvent Anweisung benachrichtigt Ereignishandler, dass ein bestimmtes Ereignis aufgetreten ist.
RaiseEventStatement
: 'RaiseEvent' IdentifierOrKeyword
( OpenParenthesis ArgumentList? CloseParenthesis )? StatementTerminator
;
Der einfache Namensausdruck in einer RaiseEvent Anweisung wird als Member-Nachschlagevorgang Meinterpretiert. Daher wird so interpretiert, RaiseEvent x als wäre es RaiseEvent Me.x. Das Ergebnis des Ausdrucks muss als Ereigniszugriff für ein in der Klasse selbst definiertes Ereignis klassifiziert werden. Ereignisse, die für Basistypen definiert sind, können in einer RaiseEvent Anweisung nicht verwendet werden.
Die RaiseEvent Anweisung wird als Aufruf der Invoke Methode des Stellvertretungs des Ereignisses unter Verwendung der angegebenen Parameter verarbeitet. Wenn der Wert der Stellvertretung lautet Nothing, wird keine Ausnahme ausgelöst. Wenn keine Argumente vorhanden sind, werden die Klammern möglicherweise weggelassen. Beispiel:
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
Die obige Klasse Raiser entspricht folgendem:
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- und RemoveHandler-Anweisungen
Obwohl die meisten Ereignishandler automatisch über WithEvents Variablen eingebunden werden, kann es erforderlich sein, Ereignishandler zur Laufzeit dynamisch hinzuzufügen und zu entfernen.
AddHandler und RemoveHandler Anweisungen tun dies.
AddHandlerStatement
: 'AddHandler' Expression Comma Expression StatementTerminator
;
RemoveHandlerStatement
: 'RemoveHandler' Expression Comma Expression StatementTerminator
;
Jede Anweisung akzeptiert zwei Argumente: Das erste Argument muss ein Ausdruck sein, der als Ereigniszugriff klassifiziert wird, und das zweite Argument muss ein Ausdruck sein, der als Wert klassifiziert wird. Der Typ des zweiten Arguments muss der Stellvertretungstyp sein, der dem Ereigniszugriff zugeordnet ist. Beispiel:
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
Bei einem Ereignis E, ruft die Anweisung die relevante add_E oder remove_E Methode für die Instanz auf, um den Delegaten als Handler für das Ereignis hinzuzufügen oder zu entfernen. Daher entspricht der obige Code folgendem:
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
Zuordnungsanweisungen
Eine Zuweisungsanweisung weist dem Wert eines Ausdrucks eine Variable zu. Es gibt mehrere Zuordnungstypen.
AssignmentStatement
: RegularAssignmentStatement
| CompoundAssignmentStatement
| MidAssignmentStatement
;
Reguläre Zuordnungsanweisungen
Eine einfache Zuweisungsanweisung speichert das Ergebnis eines Ausdrucks in einer Variablen.
RegularAssignmentStatement
: Expression Equals Expression StatementTerminator
;
Der Ausdruck auf der linken Seite des Zuordnungsoperators muss als Variable oder eigenschaftszugriff klassifiziert werden, während der Ausdruck auf der rechten Seite des Zuordnungsoperators als Wert klassifiziert werden muss. Der Typ des Ausdrucks muss implizit in den Typ der Variablen oder des Eigenschaftenzugriffs konvertierbar sein.
Wenn es sich bei der Variablen um ein Arrayelement eines Referenztyps handelt, wird eine Laufzeitüberprüfung durchgeführt, um sicherzustellen, dass der Ausdruck mit dem Arrayelementtyp kompatibel ist. Im folgenden Beispiel wird System.ArrayTypeMismatchException die letzte Zuordnung ausgelöst, da eine Instanz eines ArrayList Arrays nicht in einem Element eines String Arrays gespeichert werden kann.
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.
Wenn der Ausdruck auf der linken Seite des Zuordnungsoperators als Variable klassifiziert wird, speichert die Zuordnungsanweisung den Wert in der Variablen. Wenn der Ausdruck als Eigenschaftszugriff klassifiziert wird, wandelt die Zuordnungsanweisung den Eigenschaftenzugriff in einen Aufruf des Set Accessors der Eigenschaft um, wobei der Wert durch den Wertparameter ersetzt wird. Beispiel:
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
Wenn das Ziel der Variablen- oder Eigenschaftenzugriffsart als Werttyp, jedoch nicht als Variable klassifiziert wird, tritt ein Kompilierungszeitfehler auf. Beispiel:
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
Beachten Sie, dass die Semantik der Zuordnung vom Typ der Variablen oder Eigenschaft abhängt, der sie zugewiesen wird. Wenn die Variable, der sie zugewiesen wird, ein Werttyp ist, kopiert die Zuordnung den Wert des Ausdrucks in die Variable. Wenn die Variable, der sie zugewiesen wird, ein Verweistyp ist, kopiert die Zuordnung den Verweis, nicht den Wert selbst, in die Variable. Wenn der Typ der Variablen lautet Object, werden die Zuordnungssemantik bestimmt, ob der Typ des Werts ein Werttyp oder ein Bezugstyp zur Laufzeit ist.
Hinweis: Für systeminterne Typen wie Integer z. B. Referenz- Dateund Wertzuweisungssemantik sind identisch, da die Typen unveränderlich sind. Daher kann die Sprache die Referenzzuweisung für boxed systeminterne Typen als Optimierung verwenden. Aus Der Sicht eines Werts ist das Ergebnis identisch.
Da das Gleichheitszeichen (=) sowohl für die Zuordnung als auch für die Gleichheit verwendet wird, gibt es eine Mehrdeutigkeit zwischen einer einfachen Zuordnung und einer Aufrufanweisung in Situationen wie z x = y.ToString(). B. . In allen solchen Fällen hat die Zuordnungsanweisung Vorrang vor dem Gleichheitsoperator. Dies bedeutet, dass der Beispielausdruck nicht (x = y).ToString()als x = (y.ToString()) .
Zusammengesetzte Zuordnungsanweisungen
Eine zusammengesetzte Zuordnungsanweisung übernimmt die Form V op= E (dabei op handelt es sich um einen gültigen binären Operator).
CompoundAssignmentStatement
: Expression CompoundBinaryOperator LineTerminator? Expression StatementTerminator
;
CompoundBinaryOperator
: '^' '=' | '*' '=' | '/' '=' | '\\' '=' | '+' '=' | '-' '='
| '&' '=' | '<' '<' '=' | '>' '>' '='
;
Der Ausdruck auf der linken Seite des Zuordnungsoperators muss als Variable oder Eigenschaftszugriff klassifiziert werden, während der Ausdruck auf der rechten Seite des Zuordnungsoperators als Wert klassifiziert werden muss. Die zusammengesetzte Zuordnungsanweisung entspricht der Anweisung V = V op E mit dem Unterschied, dass die Variable auf der linken Seite des zusammengesetzten Zuordnungsoperators nur einmal ausgewertet wird. Im folgenden Beispiel wird dieser Unterschied veranschaulicht:
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
Der Ausdruck a(GetIndex()) wird zweimal für eine einfache Zuordnung ausgewertet, aber nur einmal für die zusammengesetzte Zuordnung, sodass der Code gedruckt wird:
Simple assignment
Getting index
Getting index
Compound assignment
Getting index
Mid Assignment-Anweisung
Eine Mid Zuordnungsanweisung weist einer anderen Zeichenfolge eine Zeichenfolge zu. Die linke Seite der Zuordnung weist die gleiche Syntax wie ein Aufruf der Funktion Microsoft.VisualBasic.Strings.Midauf.
MidAssignmentStatement
: 'Mid' '$'? OpenParenthesis Expression Comma Expression
( Comma Expression )? CloseParenthesis Equals Expression StatementTerminator
;
Das erste Argument ist das Ziel der Zuordnung und muss als Variable oder als Eigenschaftszugriff klassifiziert werden, deren Typ implizit in und von .String Der zweite Parameter ist die 1-basierte Startposition, die der Stelle entspricht, an der die Zuordnung in der Zielzeichenfolge beginnen soll, und muss als Wert klassifiziert werden, dessen Typ implizit konvertierbar Integersein muss. Der optionale dritte Parameter ist die Anzahl der Zeichen aus dem rechten Wert, der der Zielzeichenfolge zugewiesen werden soll, und muss als Wert klassifiziert werden, dessen Typ implizit zu konvertierbar Integerist. Die rechte Seite ist die Quellzeichenfolge und muss als Wert klassifiziert werden, dessen Typ implizit konvertierbar Stringist. Die rechte Seite wird an den Längenparameter abgeschnitten, sofern angegeben, und ersetzt die Zeichen in der linken Zeichenfolge, beginnend an der Startposition. Wenn die rechte Zeichenfolge weniger Zeichen als der dritte Parameter enthält, werden nur die Zeichen aus der rechten Seite kopiert.
Im folgenden Beispiel wird folgendes angezeigt 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
Hinweis:
Mid ist kein reserviertes Wort.
Aufrufanweisungen
Eine Aufruf-Anweisung ruft eine Methode auf, die dem optionalen Schlüsselwort Callvorausgeht. Die Aufruf-Anweisung wird auf die gleiche Weise wie der Funktionsaufrufausdruck verarbeitet, wobei einige Unterschiede unten angegeben sind. Der Aufrufausdruck muss als Wert oder void klassifiziert werden. Jeder Wert, der sich aus der Auswertung des Aufrufausdrucks ergibt, wird verworfen.
Wenn das Call Schlüsselwort nicht angegeben wird, muss der Aufrufausdruck mit einem Bezeichner oder Schlüsselwort oder mit . einem With Block beginnen. So ist beispielsweise "Call 1.ToString()" eine gültige Anweisung, aber "1.ToString()" nicht. (Beachten Sie, dass in einem Ausdruckskontext auch Aufrufausdrücke nicht mit einem Bezeichner beginnen müssen. Beispielsweise ist "Dim x = 1.ToString()" eine gültige Anweisung).
Es gibt einen weiteren Unterschied zwischen den Aufrufanweisungen und Aufrufausdrücken: Wenn eine Aufruf-Anweisung eine Argumentliste enthält, wird dies immer als Argumentliste des Aufrufs verwendet. Das folgende Beispiel veranschaulicht den Unterschied:
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
;
Conditional Statements (Bedingte Anweisungen)
Bedingte Anweisungen ermöglichen die bedingte Ausführung von Anweisungen basierend auf Ausdrücken, die zur Laufzeit ausgewertet werden.
ConditionalStatement
: IfStatement
| SelectStatement
;
Wenn... Dann... Else-Anweisungen
Eine If...Then...Else Anweisung ist die grundlegende bedingte Anweisung.
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'
;
Jeder Ausdruck in einer If...Then...Else Anweisung muss ein boolescher Ausdruck gemäß Section Boolean Expressions sein. (Hinweis: Dies erfordert keinen booleschen Typ). Wenn der Ausdruck in der If Anweisung true ist, werden die vom If Block eingeschlossenen Anweisungen ausgeführt. Wenn der Ausdruck falsch ist, werden alle ElseIf Ausdrücke ausgewertet. Wenn einer der ElseIf Ausdrücke als wahr ausgewertet wird, wird der entsprechende Block ausgeführt. Wenn kein Ausdruck auf "true" ausgewertet wird und ein Else Block vorhanden ist, wird der Else Block ausgeführt. Sobald der Ausführung eines Blocks abgeschlossen ist, wird die Ausführung an das Ende der If...Then...Else Anweisung übergeben.
Die Zeilenversion der If Anweisung weist einen einzelnen Satz von Anweisungen auf, die ausgeführt werden sollen, wenn der If Ausdruck vorhanden ist True , und einen optionalen Satz von Anweisungen, die ausgeführt werden sollen, wenn der Ausdruck ist False. Beispiel:
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
Die Zeilenversion der If-Anweisung bindet weniger eng als ":", und die Else Bindung wird an die lexikalisch nächste vorangehende, If die von der Syntax zulässig ist. Die folgenden beiden Versionen sind z. B. gleichwertig:
If True Then _
If True Then Console.WriteLine("a") Else Console.WriteLine("b") _
Else Console.WriteLine("c") : Console.WriteLine("d")
If True Then
If True Then
Console.WriteLine("a")
Else
Console.WriteLine("b")
End If
Console.WriteLine("c") : Console.WriteLine("d")
End If
Alle Anweisungen außer Bezeichnungsdeklarationsanweisungen sind innerhalb einer Zeilenanweisungen If zulässig, einschließlich Blockanweisungen. Sie können jedoch lineTerminators nicht als "StatementTerminators" verwenden, außer innerhalb von mehrzeiligen Lambda-Ausdrücken. Beispiel:
' 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-Anweisungen
Eine Select Case Anweisung führt Anweisungen basierend auf dem Wert eines Ausdrucks aus.
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?
;
Der Ausdruck muss als Wert klassifiziert werden. Wenn eine Select Case Anweisung ausgeführt wird, wird der Select Ausdruck zuerst ausgewertet, und die Case Anweisungen werden dann in der Reihenfolge der Textdeklaration ausgewertet. Die erste Case Anweisung, die ausgewertet wird, hat True ihren Block ausgeführt. Wenn keine Case Anweisung ausgewertet True wird und eine Case Else Anweisung vorhanden ist, wird dieser Block ausgeführt. Sobald ein Block die Ausführung abgeschlossen hat, wird die Ausführung an das Ende der Select Anweisung übergeben.
Die Ausführung eines Case Blocks darf nicht in den nächsten Schalterabschnitt fallen. Dadurch wird eine allgemeine Klasse von Fehlern verhindert, die in anderen Sprachen auftreten, wenn eine Case Beendigungsanweisung versehentlich weggelassen wird. Dieses Verhalten wird im folgenden Beispiel veranschaulicht:
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
Der Code druckt:
x = 10
Obwohl Case 10 und Case 20 - 10 wählen Sie für denselben Wert aus, wird ausgeführt, Case 10 da sie textmäßig vorangestellt ist Case 20 - 10 . Wenn die nächste Case Erreicht ist, wird die Ausführung nach der Select Anweisung fortgesetzt.
Eine Case Klausel kann zwei Formen annehmen. Ein Formular ist ein optionales Is Schlüsselwort, ein Vergleichsoperator und ein Ausdruck. Der Ausdruck wird in den Typ des Select Ausdrucks konvertiert. Wenn der Ausdruck nicht implizit in den Typ des Select Ausdrucks konvertiert wird, tritt ein Kompilierungszeitfehler auf. Wenn der Ausdruck E ist, lautet der Select Vergleichsoperator Op, und der Case Ausdruck ist E1, wird der Fall als E OP E1 ausgewertet. Der Operator muss für die Typen der beiden Ausdrücke gültig sein; andernfalls tritt ein Kompilierungszeitfehler auf.
Das andere Formular ist optional ein Ausdruck, gefolgt vom Schlüsselwort To und einem zweiten Ausdruck. Beide Ausdrücke werden in den Typ des Select Ausdrucks konvertiert. Wenn ein Ausdruck nicht implizit in den Typ des Select Ausdrucks konvertiert wird, tritt ein Kompilierungszeitfehler auf. Wenn der Select Ausdruck lautet, lautet E1Eder erste Case Ausdruck , und der zweite Case Ausdruck ist E2, wird dies Case entweder als E = E1 (wenn nicht E2 angegeben) oder (E >= E1) And (E <= E2)ausgewertet. Die Operatoren müssen für die Typen der beiden Ausdrücke gültig sein; andernfalls tritt ein Kompilierungszeitfehler auf.
Loop-Anweisungen
Schleifenanweisungen ermöglichen die wiederholte Ausführung der Anweisungen im Textkörper.
LoopStatement
: WhileStatement
| DoLoopStatement
| ForStatement
| ForEachStatement
;
Jedes Mal, wenn ein Schleifentext eingegeben wird, wird eine neue Kopie aus allen lokalen Variablen erstellt, die in diesem Textkörper deklariert sind, initialisiert für die vorherigen Werte der Variablen. Jeder Verweis auf eine Variable innerhalb des Schleifentexts verwendet die zuletzt vorgenommene Kopie. Dieser Code zeigt ein Beispiel:
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
Der Code erzeugt die Ausgabe:
31 32 33
Wenn der Schleifentext ausgeführt wird, wird verwendet, welche Kopie der Variablen aktuell ist. Die Anweisung Dim y = x bezieht sich beispielsweise auf die neueste Kopie y und die originale Kopie von x. Und wenn eine Lambda-Funktion erstellt wird, merkt sie sich, welche Kopie einer Variablen zum Zeitpunkt der Erstellung aktuell war. Daher verwendet jede Lambda-Funktion dieselbe freigegebene Kopie von x, aber eine andere Kopie von y. Am Ende des Programms befindet sich diese freigegebene Kopie x , auf die sie sich beziehen, jetzt am Endwert 3, wenn sie die Lambdas ausführen.
Beachten Sie, dass, wenn es keine Lambda- oder LINQ-Ausdrücke gibt, unmöglich zu wissen, dass eine neue Kopie bei der Schleifeneingabe erstellt wird. In der Tat werden Compileroptimierungen in diesem Fall keine Kopien erstellen. Beachten Sie auch, dass es unzulässig ist, GoTo eine Schleife mit Lambda- oder LINQ-Ausdrücken zu verwenden.
Während... End While and Do... Loop-Anweisungen
Eine While Oder Do Schleifen-Anweisung basiert auf einem booleschen Ausdruck.
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'
;
Eine While Loop-Anweisung wird so lange durchlaufen, wie der boolesche Ausdruck auf "true" ausgewertet wird. Eine Do Loop-Anweisung kann eine komplexere Bedingung enthalten. Ein Ausdruck kann nach dem Do Schlüsselwort oder nach dem Loop Schlüsselwort platziert werden, aber nicht nach beiden. Der boolesche Ausdruck wird gemäß Section Boolean Expressions ausgewertet. (Hinweis: Dies erfordert keinen booleschen Typ). Sie ist auch gültig, um überhaupt keinen Ausdruck anzugeben; in diesem Fall wird die Schleife nie beendet. Wenn der Ausdruck hinter Dodem Ausdruck platziert wird, wird er ausgewertet, bevor der Schleifenblock für jede Iteration ausgeführt wird. Wenn der Ausdruck hinter Loopdem Ausdruck platziert wird, wird er ausgewertet, nachdem der Schleifenblock für jede Iteration ausgeführt wurde. Wenn Sie den Ausdruck nach dem Ausdruck platzieren Loop , wird daher eine weitere Schleife als nach der Platzierung Dogeneriert. In folgendem Beispiel wird dieses Verhalten veranschaulicht:
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
Der Code erzeugt die Ausgabe:
Second Loop
Bei der ersten Schleife wird die Bedingung ausgewertet, bevor die Schleife ausgeführt wird. Bei der zweiten Schleife wird die Bedingung nach ausführung der Schleife ausgeführt. Dem bedingten Ausdruck muss entweder ein While Schlüsselwort oder ein Until Schlüsselwort vorangestellt werden. Der erste Bricht die Schleife auf, wenn die Bedingung als falsch ausgewertet wird, letztere, wenn die Bedingung als wahr ausgewertet wird.
Hinweis:
Until ist kein reserviertes Wort.
Für... Next-Anweisungen
Eine For...Next Anweisungsschleife basiert auf einer Gruppe von Grenzen. Eine For Anweisung gibt eine Schleifensteuerelementvariable, einen unteren gebundenen Ausdruck, einen ausdruck der oberen Grenze und einen optionalen Schrittwertausdruck an. Die Variable des Schleifensteuerelements wird entweder über einen Bezeichner angegeben, gefolgt von einer optionalen As Klausel oder einem Ausdruck.
ForStatement
: 'For' LoopControlVariable Equals Expression 'To' Expression
( 'Step' Expression )? StatementTerminator
Block?
( 'Next' NextExpressionList? StatementTerminator )?
;
LoopControlVariable
: Identifier ( IdentifierModifiers 'As' TypeName )?
| Expression
;
NextExpressionList
: Expression ( Comma Expression )*
;
Gemäß den folgenden Regeln bezieht sich die Variable des Schleifensteuerelements entweder auf eine neue lokale Variable, die für diese For...Next Anweisung spezifisch ist, oder auf eine bereits vorhandene Variable oder auf einen Ausdruck.
Wenn es sich bei der Schleifensteuerelementvariable um einen Bezeichner mit einer
AsKlausel handelt, definiert der Bezeichner eine neue lokale Variable des typs, der in derAsKlausel angegeben ist, die auf die gesamteForSchleife festgelegt ist.Wenn es sich bei der Variablen des Schleifensteuerelements um einen Bezeichner ohne Klausel
Ashandelt, wird der Bezeichner zuerst mithilfe der Regeln für die Auflösung einfacher Namen aufgelöst (siehe Abschnittsausdrücke mit einfachem Namen), mit der Ausnahme, dass sich dieses Vorkommen des Bezeichners nicht in und von sich selbst befindet, dazu führen würde, dass eine implizite lokale Variable erstellt wird (Abschnitt implizite lokale Deklarationen).Wenn diese Auflösung erfolgreich ist und das Ergebnis als Variable klassifiziert wird, ist die Variable des Schleifensteuerelements die bereits vorhandene Variable.
Wenn die Lösung fehlschlägt oder die Auflösung erfolgreich ist und das Ergebnis als Typ klassifiziert wird, dann:
- Wenn lokale Variablentypausschlüsse verwendet werden, definiert der Bezeichner eine neue lokale Variable, deren Typ von den gebundenen und Schrittausdrücken abgeleitet wird, die auf die gesamte
ForSchleife begrenzt ist; - wenn die lokale Variablentyp-Ableitung nicht verwendet wird, aber implizite lokale Deklaration ist, wird eine implizite lokale Variable erstellt, deren Bereich die gesamte Methode (Abschnitt implizite lokale Deklarationen) ist, und die Variable des Schleifensteuerelements bezieht sich auf diese bereits vorhandene Variable;
- Wenn weder lokale Variablentyp-Ableitungen noch implizite lokale Deklarationen verwendet werden, handelt es sich um einen Fehler.
- Wenn lokale Variablentypausschlüsse verwendet werden, definiert der Bezeichner eine neue lokale Variable, deren Typ von den gebundenen und Schrittausdrücken abgeleitet wird, die auf die gesamte
Wenn die Auflösung erfolgreich ist, die weder als Typ noch als Variable klassifiziert ist, handelt es sich um einen Fehler.
Wenn es sich bei der Variablen des Schleifensteuerelements um einen Ausdruck handelt, muss der Ausdruck als Variable klassifiziert werden.
Eine Loop-Steuerelementvariable kann nicht von einer anderen eingeschlossenen For...Next Anweisung verwendet werden. Der Typ der Schleifensteuerungsvariable einer For Anweisung bestimmt den Typ der Iteration und muss eine der folgenden Sein:
-
Byte,SByte,UShortIntegerUIntegerShort,ULong, ,Long, , ,SingleDecimalDouble - Ein Aufzählungstyp
Object- Ein Typ
Tmit den folgenden Operatoren, wobeiBes sich um einen Typ handelt, der in einem booleschen Ausdruck verwendet werden kann:
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
Die gebundenen und Schrittausdrücke müssen implizit in den Typ der Schleifensteuerelementvariablen konvertierbar sein und als Werte klassifiziert werden. Zur Kompilierungszeit wird der Typ der Schleifensteuerelementvariable abgeleitet, indem der breiteste Typ zwischen den Unteren, oberen Begrenzungs- und Schrittausdruckstypen ausgewählt wird. Wenn keine Erweiterung zwischen zwei typen vorhanden ist, tritt ein Kompilierungszeitfehler auf.
Wenn es sich bei der Laufzeit um den Typ der Schleifensteuerungsvariable handelt Object, wird der Typ der Iteration mit zwei Ausnahmen wie zur Kompilierungszeit abgeleitet. Wenn die gebundenen und Schrittausdrücke alle integralen Typen sind, aber keinen breiten Typ aufweisen, wird der breiteste Typ, der alle drei Typen umfasst, abgeleitet. Und zweitens, wenn der Typ der Schleifensteuerungsvariable abgeleitet Stringwird, Double wird stattdessen abgeleitet. Wenn zur Laufzeit kein Schleifensteuerungstyp bestimmt werden kann oder wenn eines der Ausdrücke nicht in den Schleifensteuerelementtyp konvertiert werden kann, tritt ein System.InvalidCastException Fehler auf. Sobald am Anfang der Schleife ein Schleifensteuerelementtyp ausgewählt wurde, wird derselbe Typ während der gesamten Iteration verwendet, unabhängig von Änderungen, die an dem Wert in der Variablen des Schleifensteuerelements vorgenommen wurden.
Eine For Anweisung muss durch eine übereinstimmende Next Anweisung geschlossen werden. Eine Next Anweisung ohne Variable entspricht der innersten geöffneten For Anweisung, während eine Next Anweisung mit einer oder mehreren Schleifensteuerungsvariablen von links nach rechts mit den Schleifen übereinstimmt, die For mit den einzelnen Variablen übereinstimmen. Wenn eine Variable mit einer For Schleife übereinstimmt, die an diesem Punkt nicht die geschachtelte Schleife ist, führt ein Kompilierungszeitfehler zu einem Fehler.
Am Anfang der Schleife werden die drei Ausdrücke in textbezogener Reihenfolge ausgewertet, und der untere gebundene Ausdruck wird der Variablen für Schleifensteuerelemente zugewiesen. Wenn der Schrittwert nicht angegeben wird, wird er implizit in das Literal 1konvertiert, das in den Typ der Loop-Steuerelementvariable konvertiert wird. Die drei Ausdrücke werden nur am Anfang der Schleife ausgewertet.
Am Anfang jeder Schleife wird die Steuerelementvariable verglichen, um festzustellen, ob sie größer als der Endpunkt ist, wenn der Schrittausdruck positiv oder kleiner als der Endpunkt ist, wenn der Schrittausdruck negativ ist. Andernfalls wird die For Schleife beendet. Andernfalls wird der Schleifenblock ausgeführt. Wenn die Variable des Schleifensteuerelements kein Grundtyp ist, wird der Vergleichsoperator bestimmt, ob der Ausdruck step >= step - step wahr oder falsch ist. Bei der Next Anweisung wird der Schrittwert der Steuerelementvariablen hinzugefügt, und die Ausführung wird an den Anfang der Schleife zurückgegeben.
Beachten Sie, dass für jede Iteration des Schleifenblocks keine neue Kopie der Schleifensteuerelementvariable erstellt wird. In dieser Hinsicht unterscheidet sich die For Anweisung von For Each (Abschnitt für Jede... Nächste Anweisungen).
Es ist ungültig, eine Schleife außerhalb der Schleife zu For verzweigen.
Für jeden... Next-Anweisungen
Eine For Each...Next Anweisung wird basierend auf den Elementen in einem Ausdruck in einer Schleife ausgeführt. Eine For Each Anweisung gibt eine Schleifensteuerelementvariable und einen Enumerationsausdruck an. Die Variable des Schleifensteuerelements wird entweder über einen Bezeichner angegeben, gefolgt von einer optionalen As Klausel oder einem Ausdruck.
ForEachStatement
: 'For' 'Each' LoopControlVariable 'In' LineTerminator? Expression StatementTerminator
Block?
( 'Next' NextExpressionList? StatementTerminator )?
;
Folgen der gleichen Regeln wie For...Next Anweisungen (Abschnitt für... Next Statements) bezieht sich die Variable des Schleifensteuerelements entweder auf eine neue lokale Variable, die für diese For Each spezifisch ist... Nächste Anweisung oder einer bereits vorhandenen Variable oder einem Ausdruck.
Der Enumerationsausdruck muss als Wert klassifiziert werden, und sein Typ muss ein Auflistungstyp oder Objectein Auflistungstyp sein. Wenn der Typ des Enumerationsausdrucks lautet Object, wird die gesamte Verarbeitung bis zur Laufzeit zurückgestellt. Andernfalls muss eine Konvertierung vom Elementtyp der Auflistung in den Typ der Schleifensteuerelementvariable vorhanden sein.
Die Variable des Schleifensteuerelements kann nicht von einer anderen eingeschlossenen For Each Anweisung verwendet werden. Eine For Each Anweisung muss durch eine übereinstimmende Next Anweisung geschlossen werden. Eine Next Anweisung ohne Schleifensteuerungsvariable entspricht der innersten geöffneten For Each. Eine Next Anweisung mit einer oder mehreren Schleifensteuerelementvariablen stimmt von links nach rechts mit den Schleifen überein, die For Each dieselbe Schleifensteuerelementvariable aufweisen. Wenn eine Variable mit einer For Each Schleife übereinstimmt, die nicht die verschachtelte Schleife an diesem Punkt ist, tritt ein Kompilierungsfehler auf.
Ein Typ C wird als Sammlungstyp bezeichnet, wenn einer von:
Alle folgenden Punkte sind wahr:
-
Centhält eine barrierefreie Instanz, eine freigegebene oder Erweiterungsmethode mit der SignaturGetEnumerator(), die einen TypEzurückgibt. -
Eenthält eine barrierefreie Instanz, eine freigegebene oder Erweiterungsmethode mit der SignaturMoveNext()und dem RückgabetypBoolean. -
Eenthält eine barrierefreie Instanz oder freigegebene Eigenschaft mit dem NamenCurrenteines Getter. Der Typ dieser Eigenschaft ist der Elementtyp des Auflistungstyps.
-
Sie implementiert die Schnittstelle
System.Collections.Generic.IEnumerable(Of T), in diesem Fall wird der Elementtyp der Auflistung als betrachtetT.Sie implementiert die Schnittstelle
System.Collections.IEnumerable, in diesem Fall wird der Elementtyp der Auflistung als betrachtetObject.
Im Folgenden finden Sie ein Beispiel für eine Klasse, die aufgezählt werden kann:
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
Bevor die Schleife beginnt, wird der Enumerationsausdruck ausgewertet. Wenn der Typ des Ausdrucks das Entwurfsmuster nicht erfüllt, wird der Ausdruck in System.Collections.IEnumerable oder System.Collections.Generic.IEnumerable(Of T). Wenn der Ausdruckstyp die generische Schnittstelle implementiert, wird die generische Schnittstelle zur Kompilierungszeit bevorzugt, aber die nicht generische Schnittstelle wird zur Laufzeit bevorzugt. Wenn der Ausdruckstyp die generische Schnittstelle mehrmals implementiert, wird die Anweisung als mehrdeutig betrachtet, und ein Kompilierungszeitfehler tritt auf.
Hinweis: Die nicht generische Schnittstelle wird im spät gebundenen Fall bevorzugt, da die Auswahl der generischen Schnittstelle bedeuten würde, dass alle Aufrufe der Schnittstellenmethoden Typparameter umfassen würden. Da es nicht möglich ist, die übereinstimmenden Typargumente zur Laufzeit zu kennen, müssten alle derartigen Aufrufe mit verspätet gebundenen Aufrufen erfolgen. Dies wäre langsamer als das Aufrufen der nicht generischen Schnittstelle, da die nicht generische Schnittstelle mithilfe von Kompilierungszeitaufrufen aufgerufen werden könnte.
GetEnumerator wird für den resultierenden Wert aufgerufen, und der Rückgabewert der Funktion wird in einer temporären Datei gespeichert. Anschließend wird am Anfang jeder Iteration MoveNext die temporären Aufrufe ausgeführt. Wenn sie zurückgegeben wird False, wird die Schleife beendet. Andernfalls wird jede Iteration der Schleife wie folgt ausgeführt:
- Wenn die Variable des Schleifensteuerelements eine neue lokale Variable (anstelle einer bereits vorhandenen Variable) identifiziert hat, wird eine neue Kopie dieser lokalen Variablen erstellt. Bei der aktuellen Iteration beziehen sich alle Verweise innerhalb des Schleifenblocks auf diese Kopie.
- Die
CurrentEigenschaft wird abgerufen, an den Typ der Schleifensteuerelementvariable (unabhängig davon, ob die Konvertierung implizit oder explizit ist) umgewandelt und der Variablen für Schleifensteuerelemente zugewiesen. - Der Schleifenblock wird ausgeführt.
Hinweis: Es gibt eine geringfügige Änderung des Verhaltens zwischen Version 10.0 und 11.0 der Sprache. Vor 11.0 wurde für jede Iteration der Schleife keine neue Iterationsvariable erstellt. Dieser Unterschied ist nur dann feststellbar, wenn die Iterationsvariable von einer Lambda- oder LINQ-Expression erfasst wird, die dann nach der Schleife aufgerufen wird:
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()
Bis zu Visual Basic 10.0 hat dies zur Kompilierungszeit eine Warnung erzeugt und dreimal "3" gedruckt. Das lag daran, dass nur eine einzelne Variable "x" von allen Iterationen der Schleife gemeinsam genutzt wurde, und alle drei Lambdas haben dasselbe "x" erfasst, und zum Zeitpunkt der Ausführung der Lambdas hielt sie dann die Zahl 3. Ab Visual Basic 11.0 wird "1, 2, 3" gedruckt. Der Grund dafür ist, dass jede Lambda-Funktion eine andere Variable "x" erfasst.
Hinweis: Das aktuelle Element der Iteration wird in den Typ der Schleifensteuerungsvariable konvertiert, auch wenn die Konvertierung explizit ist, da es keinen praktischen Ort gibt, um einen Konvertierungsoperator in der Anweisung einzuführen. Dies wurde beim Arbeiten mit dem nun veralteten Typ System.Collections.ArrayListbesonders problematisch, da der Elementtyp .Object Dies hätte Guss in einer großen Menge Schleifen benötigt, etwas, das wir fühlten, war nicht ideal. Ironischerweise ermöglichten Generika die Erstellung einer stark typierten Sammlung, System.Collections.Generic.List(Of T)die uns möglicherweise diesen Entwurfspunkt überdenken ließ, aber aus Gründen der Kompatibilität kann dies jetzt nicht geändert werden.
Wenn die Next Anweisung erreicht ist, wird die Ausführung an den Anfang der Schleife zurückgegeben. Wenn eine Variable nach dem Next Schlüsselwort angegeben wird, muss sie mit der ersten Variablen nach dem For EachSchlüsselwort identisch sein. Betrachten Sie z. B. den folgenden Code:
Module Test
Sub Main()
Dim i As Integer
Dim c As IntegerCollection = New IntegerCollection()
For Each i In c
Console.WriteLine(i)
Next i
End Sub
End Module
Sie entspricht dem folgenden Code:
Module Test
Sub Main()
Dim i As Integer
Dim c As IntegerCollection = New IntegerCollection()
Dim e As IntegerCollection.IntegerCollectionEnumerator
e = c.GetEnumerator()
While e.MoveNext()
i = e.Current
Console.WriteLine(i)
End While
End Sub
End Module
Wenn der Enumeratortyp E implementiert System.IDisposablewird, wird der Enumerator beim Beenden der Schleife durch Aufrufen der Dispose Methode verworfen. Dadurch wird sichergestellt, dass ressourcen, die vom Enumerator gehalten werden, freigegeben werden. Wenn die Methode, die die For Each Anweisung enthält, keine unstrukturierte Fehlerbehandlung verwendet, wird die For Each Anweisung in eine Try Anweisung eingeschlossen, in der die Dispose Methode aufgerufen Finally wird, um die Bereinigung sicherzustellen.
Hinweis: Der System.Array Typ ist ein Sammlungstyp, und da alle Arraytypen von System.Arraydiesem abgeleitet sind, ist jeder Arraytypausdruck in einer For Each Anweisung zulässig. Bei eindimensionalen Arrays listet die For Each Anweisung die Arrayelemente in zunehmender Indexreihenfolge auf, beginnend mit Index 0 und enden mit Indexlänge - 1. Bei mehrdimensionalen Arrays werden die Indizes der äußerst rechten Dimension zuerst erhöht.
Der folgende Code wird z. B. gedruckt 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
Es ist ungültig, in einen For Each Anweisungsblock außerhalb des Blocks zu verzweigen.
Exception-Handling-Anweisungen
Visual Basic unterstützt strukturierte Ausnahmebehandlung und unstrukturierte Ausnahmebehandlung. In einer Methode kann nur ein Ausnahmebehandlungsstil verwendet werden, die Anweisung kann jedoch Error in der strukturierten Ausnahmebehandlung verwendet werden. Wenn eine Methode beide Arten der Ausnahmebehandlung verwendet, führt ein Kompilierungszeitfehler zu ergebnissen.
ErrorHandlingStatement
: StructuredErrorStatement
| UnstructuredErrorStatement
;
Strukturierte Exception-Handling-Anweisungen
Die strukturierte Ausnahmebehandlung ist eine Methode zum Behandeln von Fehlern, indem explizite Blöcke deklariert werden, in denen bestimmte Ausnahmen behandelt werden. Die strukturierte Ausnahmebehandlung erfolgt über eine Try Anweisung.
StructuredErrorStatement
: ThrowStatement
| TryStatement
;
TryStatement
: 'Try' StatementTerminator
Block?
CatchStatement*
FinallyStatement?
'End' 'Try' StatementTerminator
;
Beispiel:
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
Eine Try Anweisung besteht aus drei Arten von Blöcken: Versuchen Sie Blöcke, Fangen von Blöcken und schließlich Blöcken. Ein Try-Block ist ein Anweisungsblock, der die auszuführenden Anweisungen enthält. Ein Catch-Block ist ein Anweisungsblock, der eine Ausnahme behandelt. Ein abschließender Block ist ein Anweisungsblock, der Anweisungen enthält, die ausgeführt werden sollen, wenn die Try Anweisung beendet wird, unabhängig davon, ob eine Ausnahme aufgetreten ist und behandelt wurde. Eine Try Anweisung, die nur einen Try-Block und einen endgültigen Block enthalten kann, muss mindestens einen Catch-Block oder schließlich block enthalten. Es ist ungültig, die Ausführung explizit in einen Try-Block zu übertragen, außer innerhalb eines Catch-Blocks in derselben Anweisung.
Finally-Blöcke
Ein Finally Block wird immer ausgeführt, wenn die Ausführung einen Teil der Try Anweisung verlässt. Es ist keine explizite Aktion erforderlich, um den Finally Block auszuführen. Wenn die Ausführung die Try Anweisung verlässt, führt das System den Finally Block automatisch aus und überträgt dann die Ausführung an das beabsichtigte Ziel. Der Finally Block wird unabhängig davon ausgeführt, wie die Ausführung die Try Anweisung verlässt: bis zum Ende des Try Blocks, bis zum Ende eines Catch Blocks, durch eine Anweisung, durch eine Exit TryGoTo Anweisung oder durch keine Behandlung einer ausgelösten Ausnahme.
Beachten Sie, dass der Await Ausdruck in einer asynchronen Methode und die Yield Anweisung in einer Iteratormethode dazu führen kann, dass der Steuerfluss in der asynchronen oder iterator-Methodeninstanz angehalten und in einer anderen Methodeninstanz fortgesetzt wird. Dies ist jedoch lediglich eine Aussetzung der Ausführung und beinhaltet nicht das Beenden der jeweiligen asynchronen Methode oder iterator-Methodeninstanz und führt daher nicht Finally dazu, dass Blöcke ausgeführt werden.
Es ist ungültig, die Ausführung explizit in einen Finally Block zu übertragen. Es ist auch ungültig, die Ausführung aus einem Finally Block außer über eine Ausnahme zu übertragen.
FinallyStatement
: 'Finally' StatementTerminator
Block?
;
catch-Blöcke
Wenn beim Verarbeiten des Try Blocks eine Ausnahme auftritt, wird jede Catch Anweisung in textualer Reihenfolge untersucht, um festzustellen, ob die Ausnahme behandelt wird.
CatchStatement
: 'Catch' ( Identifier ( 'As' NonArrayTypeName )? )?
( 'When' BooleanExpression )? StatementTerminator
Block?
;
Der in einer Catch Klausel angegebene Bezeichner stellt die Ausnahme dar, die ausgelöst wurde. Wenn der Bezeichner eine As Klausel enthält, wird der Bezeichner als im lokalen Deklarationsbereich des Catch Blocks deklariert. Andernfalls muss der Bezeichner eine lokale Variable (keine statische Variable) sein, die in einem enthaltenden Block definiert wurde.
Eine Catch Klausel ohne Bezeichner erfasst alle ausnahmen, die von System.Exception. Eine Catch Klausel mit einem Bezeichner erfasst nur Ausnahmen, deren Typen mit dem Typ des Bezeichners identisch oder abgeleitet sind. Der Typ muss System.Exceptionoder ein von System.Exception. Wenn eine Ausnahme abgefangen wird, von System.Exceptionder abgeleitet wird, wird ein Verweis auf das Ausnahmeobjekt im von der Funktion Microsoft.VisualBasic.Information.Errzurückgegebenen Objekt gespeichert.
A Catch clause with a When clause will only catch exceptions when the expression evaluates to True; the type of the expression must be a Boolean expression as per Section Boolean Expressions. Eine When Klausel wird erst angewendet, nachdem der Typ der Ausnahme überprüft wurde, und der Ausdruck kann auf den Bezeichner verweisen, der die Ausnahme darstellt, wie in diesem Beispiel veranschaulicht wird:
Module Test
Sub Main()
Dim i As Integer = 5
Try
Throw New ArgumentException()
Catch e As OverflowException When i = 5
Console.WriteLine("First handler")
Catch e As ArgumentException When i = 4
Console.WriteLine("Second handler")
Catch When i = 5
Console.WriteLine("Third handler")
End Try
End Sub
End Module
In diesem Beispiel wird gedruckt:
Third handler
Wenn eine Catch Klausel die Ausnahme behandelt, wird die Ausführung an den Catch Block übertragen. Am Ende des Catch Blocks überträgt die Ausführung an die erste Anweisung, die auf die Try Anweisung folgt. Die Try Anweisung behandelt keine Ausnahmen, die in einem Catch Block ausgelöst werden. Wenn keine Catch Klausel die Ausnahme behandelt, wird die Ausführung an einen vom System festgelegten Speicherort übertragen.
Es ist ungültig, die Ausführung explizit in einen Catch Block zu übertragen.
Die Filter in When-Klauseln werden normalerweise vor dem Auslösen der Ausnahme ausgewertet. Der folgende Code druckt z. B. "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
Async- und Iterator-Methoden führen jedoch dazu, dass alle Blöcke innerhalb dieser Blöcke vor allen Filtern außerhalb ausgeführt werden. Wenn beispielsweise der obige Code vorhanden war Async Sub Foo(), lautet die Ausgabe "Schließlich, Filter, Catch".
Throw-Anweisung
Die Throw Anweisung löst eine Ausnahme aus, die durch eine Instanz eines vom Typ abgeleiteten System.ExceptionTyps dargestellt wird.
ThrowStatement
: 'Throw' Expression? StatementTerminator
;
Wenn der Ausdruck nicht als Wert klassifiziert wird oder kein typ System.Exceptionabgeleiteter Typ ist, tritt ein Kompilierungszeitfehler auf. Wenn der Ausdruck zur Laufzeit als Nullwert ausgewertet wird, wird stattdessen eine System.NullReferenceException Ausnahme ausgelöst.
Eine Throw Anweisung kann den Ausdruck innerhalb eines Catch-Blocks einer Try Anweisung weglassen, solange kein endgültiger Block vorhanden ist. In diesem Fall beschreibt die Anweisung die Ausnahme, die derzeit im Catch-Block behandelt wird. Beispiel:
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
Unstrukturierte Exception-Handling-Anweisungen
Die unstrukturierte Ausnahmebehandlung ist eine Methode zum Behandeln von Fehlern, indem Anweisungen zum Verzweigen angibt werden, wenn eine Ausnahme auftritt. Die unstrukturierte Ausnahmebehandlung wird mithilfe von drei Anweisungen implementiert: der Anweisung, der ErrorOn Error Anweisung, der Anweisung und der Resume Anweisung.
UnstructuredErrorStatement
: ErrorStatement
| OnErrorStatement
| ResumeStatement
;
Beispiel:
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
Wenn eine Methode unstrukturierte Ausnahmebehandlung verwendet, wird ein einzelner strukturierter Ausnahmehandler für die gesamte Methode erstellt, die alle Ausnahmen abfangen soll. (Beachten Sie, dass dieser Handler in Konstruktoren nicht über den Aufruf des Aufrufs New am Anfang des Konstruktors erweitert wird.) Die Methode verfolgt dann den neuesten Ausnahmehandlerspeicherort und die neueste Ausnahme, die ausgelöst wurde. Bei der Eingabe in die Methode werden die Position des Ausnahmehandlers und die Ausnahme auf beide festgelegt Nothing. Wenn eine Ausnahme in einer Methode ausgelöst wird, die unstrukturierte Ausnahmebehandlung verwendet, wird ein Verweis auf das Ausnahmeobjekt im von der Funktion Microsoft.VisualBasic.Information.Errzurückgegebenen Objekt gespeichert.
Unstrukturierte Fehlerbehandlungsanweisungen sind in iterator- oder asynchronen Methoden nicht zulässig.
Error-Anweisung
Eine Error Anweisung löst eine System.Exception Ausnahme mit einer Visual Basic 6-Ausnahmenummer aus. Der Ausdruck muss als Wert klassifiziert werden, und sein Typ muss implizit in Integer.
ErrorStatement
: 'Error' Expression StatementTerminator
;
On Error-Anweisung
Eine On Error Anweisung ändert den neuesten Ausnahmebehandlungszustand.
OnErrorStatement
: 'On' 'Error' ErrorClause StatementTerminator
;
ErrorClause
: 'GoTo' '-' '1'
| 'GoTo' '0'
| GoToStatement
| 'Resume' 'Next'
;
Es kann auf eine von vier Arten verwendet werden:
On Error GoTo -1setzt die letzte Ausnahme aufNothing.On Error GoTo 0setzt den neuesten Ausnahmehandlerspeicherort aufNothing.On Error GoTo LabelNamelegt die Bezeichnung als aktuellen Ausnahmehandlerspeicherort fest. Diese Anweisung kann nicht in einer Methode verwendet werden, die einen Lambda- oder Abfrageausdruck enthält.On Error Resume Nextlegt dasResume NextVerhalten als den neuesten Speicherort des Ausnahmehandlers fest.
Resume-Anweisung
Eine Resume Anweisung gibt die Ausführung an die Anweisung zurück, die die letzte Ausnahme verursacht hat.
ResumeStatement
: 'Resume' ResumeClause? StatementTerminator
;
ResumeClause
: 'Next'
| LabelName
;
Wenn der Next Modifizierer angegeben ist, wird die Ausführung an die Anweisung zurückgegeben, die nach der Anweisung ausgeführt wurde, die die letzte Ausnahme verursacht hat. Wenn ein Bezeichnungsname angegeben ist, wird die Ausführung an die Bezeichnung zurückgegeben.
Da die SyncLock Anweisung einen impliziten strukturierten Fehlerbehandlungsblock enthält und Resume Next spezielle Verhaltensweisen für Ausnahmen aufweist, Resume die in SyncLock Anweisungen auftreten.
Resume gibt die Ausführung an den Anfang der SyncLock Anweisung zurück, während Resume Next die Ausführung an die nächste Anweisung nach der SyncLock Anweisung zurückgegeben wird. Betrachten Sie z. B. den folgenden Code:
Class LockClass
End Class
Module Test
Sub Main()
Dim FirstTime As Boolean = True
Dim Lock As LockClass = New LockClass()
On Error GoTo Handler
SyncLock Lock
Console.WriteLine("Before exception")
Throw New Exception()
Console.WriteLine("After exception")
End SyncLock
Console.WriteLine("After SyncLock")
Exit Sub
Handler:
If FirstTime Then
FirstTime = False
Resume
Else
Resume Next
End If
End Sub
End Module
Es druckt das folgende Ergebnis.
Before exception
Before exception
After SyncLock
Wenn Sie die SyncLock Anweisung zum ersten Mal durchlaufen, Resume wird die Ausführung an den Anfang der SyncLock Anweisung zurückgegeben. Das zweite Mal durch die SyncLock Anweisung gibt Resume Next die Ausführung bis zum Ende der SyncLock Anweisung zurück.
Resume und Resume Next sind innerhalb einer SyncLock Anweisung nicht zulässig.
In allen Fällen, wenn eine Resume Anweisung ausgeführt wird, wird die neueste Ausnahme auf Nothingfestgelegt. Wenn eine Resume Anweisung ohne letzte Ausnahme ausgeführt wird, löst die Anweisung eine System.Exception Ausnahme aus, die die Visual Basic-Fehlernummer 20 (Resume without error) enthält.
Branch-Anweisungen
Branch-Anweisungen ändern den Ausführungsfluss in einer Methode. Es gibt sechs Verzweigungsanweisungen:
- Eine
GoToAnweisung bewirkt, dass die Ausführung auf die angegebene Bezeichnung in der Methode übertragen wird. Es ist nicht zulässigGoTo,ForFor EachUsingWithSyncLockin einem Lambda- oder LINQ-Ausdruck eine lokale Variable dieses Blocks in einemTryLambda- oder LINQ-Ausdruck aufzunehmen. - Eine
ExitAnweisung überträgt die Ausführung an die nächste Anweisung nach dem Ende der sofort enthaltenden Block-Anweisung der angegebenen Art. Wenn der Block der Methodenblock ist, beendet der Ablauf die Methode, wie im Abschnittssteuerungsfluss beschrieben. Wenn dieExitAnweisung nicht in der Art des in der Anweisung angegebenen Blocks enthalten ist, tritt ein Kompilierungszeitfehler auf. - Eine
ContinueAnweisung überträgt die Ausführung an das Ende der sofort enthaltenden Blockschleifen-Anweisung der angegebenen Art. Wenn dieContinueAnweisung nicht in der Art des in der Anweisung angegebenen Blocks enthalten ist, tritt ein Kompilierungszeitfehler auf. - Eine
StopAnweisung bewirkt, dass eine Debuggerausnahme auftritt. - Eine
EndAnweisung beendet das Programm. Finalizer werden vor dem Herunterfahren ausgeführt, aber die abschließenden Blöcke aller derzeit ausgeführtenTryAnweisungen werden nicht ausgeführt. Diese Anweisung kann nicht in Programmen verwendet werden, die nicht ausführbar sind (z. B. DLLs). - Eine
ReturnAnweisung ohne Ausdruck entspricht einer oderExit FunctioneinerExit SubAnweisung. EineReturnAnweisung mit einem Ausdruck ist nur in einer regulären Methode zulässig, die eine Funktion ist, oder in einer asynchronen Methode, die eine Funktion mit RückgabetypTask(Of T)für einigeTist. Der Ausdruck muss als Wert klassifiziert werden, der implizit in die Funktionsrücklaufvariable (bei regulären Methoden) oder in die Task-Rückgabevariable (bei asynchronen Methoden) konvertierbar ist. Das Verhalten besteht darin, den Ausdruck auszuwerten und dann in der Rückgabevariablen zu speichern und dann eine impliziteExit FunctionAnweisung auszuführen.
BranchStatement
: GoToStatement
| ExitStatement
| ContinueStatement
| StopStatement
| EndStatement
| ReturnStatement
;
GoToStatement
: 'GoTo' LabelName StatementTerminator
;
ExitStatement
: 'Exit' ExitKind StatementTerminator
;
ExitKind
: 'Do' | 'For' | 'While' | 'Select' | 'Sub' | 'Function' | 'Property' | 'Try'
;
ContinueStatement
: 'Continue' ContinueKind StatementTerminator
;
ContinueKind
: 'Do' | 'For' | 'While'
;
StopStatement
: 'Stop' StatementTerminator
;
EndStatement
: 'End' StatementTerminator
;
ReturnStatement
: 'Return' Expression? StatementTerminator
;
Array-Handling-Anweisungen
Zwei Anweisungen vereinfachen das Arbeiten mit Arrays: ReDim Anweisungen und Erase Anweisungen.
ArrayHandlingStatement
: RedimStatement
| EraseStatement
;
ReDim-Anweisung
Eine ReDim Anweisung instanziiert neue Arrays.
RedimStatement
: 'ReDim' 'Preserve'? RedimClauses StatementTerminator
;
RedimClauses
: RedimClause ( Comma RedimClause )*
;
RedimClause
: Expression ArraySizeInitializationModifier
;
Jede Klausel in der Anweisung muss als Variable oder eigenschaftszugriff klassifiziert werden, deren Typ ein Arraytyp ist oder Object, und auf eine Liste von Arraygrenzen folgt. Die Anzahl der Grenzen muss mit dem Typ der Variablen übereinstimmen; eine beliebige Anzahl von Grenzen ist zulässig.Object Zur Laufzeit wird ein Array für jeden Ausdruck von links nach rechts mit den angegebenen Grenzen instanziiert und dann der Variablen oder Eigenschaft zugewiesen. Wenn der Variabletyp lautet Object, ist die Anzahl der Dimensionen die angegebene Anzahl von Dimensionen, und der Arrayelementtyp ist Object. Wenn die angegebene Anzahl von Dimensionen nicht mit der Variablen oder Eigenschaft zur Laufzeit kompatibel ist, tritt ein Kompilierungsfehler auf. Beispiel:
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
Wenn das Preserve Schlüsselwort angegeben ist, müssen die Ausdrücke auch als Wert klassifiziert werden, und die neue Größe für jede Dimension mit Ausnahme der am rechten Rand stehenden muss mit der Größe des vorhandenen Arrays identisch sein. Die Werte im vorhandenen Array werden in das neue Array kopiert: Wenn das neue Array kleiner ist, werden die vorhandenen Werte verworfen; Wenn das neue Array größer ist, werden die zusätzlichen Elemente auf den Standardwert des Elementtyps des Arrays initialisiert. Betrachten Sie z. B. den folgenden Code:
Module Test
Sub Main()
Dim x(5, 5) As Integer
x(3, 3) = 3
ReDim Preserve x(5, 6)
Console.WriteLine(x(3, 3) & ", " & x(3, 6))
End Sub
End Module
Es druckt das folgende Ergebnis:
3, 0
Wenn es sich bei dem vorhandenen Arrayverweis um einen Nullwert zur Laufzeit handelt, wird kein Fehler angegeben. Abgesehen von der äußerst rechten Dimension, wenn sich die Größe einer Dimension ändert, wird eine System.ArrayTypeMismatchException ausgelöst.
Hinweis:
Preserve ist kein reserviertes Wort.
Erase-Anweisung
Eine Erase Anweisung legt jede der Arrayvariablen oder Eigenschaften fest, die in der Anweisung angegeben sind, auf Nothing. Jeder Ausdruck in der Anweisung muss als Variable oder Eigenschaftszugriff klassifiziert werden, deren Typ ein Arraytyp oder Objectein Arraytyp ist. Beispiel:
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 statement (Using-Anweisung)
Instanzen von Typen werden automatisch vom Garbage Collector freigegeben, wenn eine Auflistung ausgeführt wird und keine Liveverweise auf die Instanz gefunden werden. Wenn ein Typ eine besonders wertvolle und knappe Ressource (z. B. Datenbankverbindungen oder Dateihandles) enthält, ist es möglicherweise nicht wünschenswert, bis die nächste Garbage Collection wartet, um eine bestimmte Instanz des Typs zu bereinigen, der nicht mehr verwendet wird. Um eine einfache Möglichkeit zum Freigeben von Ressourcen vor einer Sammlung bereitzustellen, kann ein Typ die System.IDisposable Schnittstelle implementieren. Ein Typ, der dies tut, macht eine Dispose Methode verfügbar, die aufgerufen werden kann, um zu erzwingen, dass wertvolle Ressourcen sofort freigegeben werden, z. B.:
Module Test
Sub Main()
Dim x As DBConnection = New DBConnection("...")
' Do some work
...
x.Dispose() ' Free the connection
End Sub
End Module
Die Using Anweisung automatisiert den Vorgang des Abrufens einer Ressource, das Ausführen einer Reihe von Anweisungen und das anschließende Löschen der Ressource. Die Anweisung kann zwei Formen annehmen: In einer ist die Ressource eine lokale Variable, die als Teil der Anweisung deklariert und als reguläre lokale Variablendeklarationsanweisung behandelt wird; in the other, the resource is the result of an expression.
UsingStatement
: 'Using' UsingResources StatementTerminator
Block?
'End' 'Using' StatementTerminator
;
UsingResources
: VariableDeclarators
| Expression
;
Wenn es sich bei der Ressource um eine lokale Variablendeklarationsanweisung handelt, muss der Typ der lokalen Variablendeklaration ein Typ sein, der implizit in konvertiert System.IDisposablewerden kann. Die deklarierten lokalen Variablen sind schreibgeschützt, auf den Using Anweisungsblock festgelegt und müssen einen Initialisierer enthalten. Wenn die Ressource das Ergebnis eines Ausdrucks ist, muss der Ausdruck als Wert klassifiziert werden und muss einen Typ aufweisen, der implizit in konvertiert System.IDisposablewerden kann. Der Ausdruck wird nur einmal ausgewertet, am Anfang der Anweisung.
Der Using Block ist implizit in einer Try Anweisung enthalten, deren block schließlich die Methode IDisposable.Dispose für die Ressource aufruft. Dadurch wird sichergestellt, dass die Ressource auch dann verworfen wird, wenn eine Ausnahme ausgelöst wird. Daher ist es ungültig, einen Block von außerhalb des Blocks zu Using verzweigen, und ein Using Block wird als einzelne Anweisung für die Zwecke Resume und .Resume Next Wenn die Ressource lautet Nothing, wird kein Aufruf Dispose ausgeführt. Das Beispiel:
Using f As C = New C()
...
End Using
entspricht:
Dim f As C = New C()
Try
...
Finally
If f IsNot Nothing Then
f.Dispose()
End If
End Try
Eine Using Anweisung mit einer lokalen Variablendeklarationsanweisung kann mehrere Ressourcen gleichzeitig abrufen, was geschachtelten Using Anweisungen entspricht. Beispiel: eine Using Anweisung des Formulars:
Using r1 As R = New R(), r2 As R = New R()
r1.F()
r2.F()
End Using
entspricht:
Using r1 As R = New R()
Using r2 As R = New R()
r1.F()
r2.F()
End Using
End Using
Await-Anweisung
Eine Await-Anweisung hat dieselbe Syntax wie ein Await-Operator-Ausdruck (Section Await Operator), ist nur in Methoden zulässig, die auch Await-Ausdrücke zulassen und dasselbe Verhalten wie ein Await-Operatorausdruck aufweisen.
Sie kann jedoch entweder als Wert oder nichtig klassifiziert werden. Alle Werte, die sich aus der Auswertung des Await-Operatorausdrucks ergeben, werden verworfen.
AwaitStatement
: AwaitOperatorExpression StatementTerminator
;
Yield-Anweisung
Yield-Anweisungen beziehen sich auf Iteratormethoden, die in Section Iterator-Methoden beschrieben werden.
YieldStatement
: 'Yield' Expression StatementTerminator
;
Yield ist ein reserviertes Wort, wenn die unmittelbar eingeschlossene Methode oder der Lambda-Ausdruck, in dem sie angezeigt wird, über einen Iterator Modifizierer verfügt und wenn dies nach diesem YieldIterator Modifizierer angezeigt wird; sie wird an anderer Stelle nicht reserviert. Sie ist auch in Präprozessordirektiven nicht reserviert. Die Rendite-Anweisung ist nur im Textkörper einer Methode oder eines Lambda-Ausdrucks zulässig, in dem es sich um ein reserviertes Wort handelt. Innerhalb der unmittelbar eingeschlossenen Methode oder Lambda kann die Ertragsanweisung nicht innerhalb des Textkörpers eines Catch Blocks oder Finally Blocks oder innerhalb des Textkörpers einer SyncLock Anweisung auftreten.
Die Ertragsanweisung akzeptiert einen einzelnen Ausdruck, der als Wert klassifiziert werden muss und dessen Typ implizit in den Typ der aktuellen Iteratorvariable (Section Iterator Methods) seiner eingeschlossenen Iteratormethode umsetzbar ist.
Der Steuerungsfluss erreicht nur eine Yield Anweisung, wenn die MoveNext Methode für ein Iteratorobjekt aufgerufen wird. (Dies liegt daran, dass eine Iteratormethodeninstanz ihre Anweisungen nur aufgrund der MoveNext oder Dispose der Methoden ausführt, die für ein Iteratorobjekt aufgerufen werden; und die Dispose Methode führt nur Code in Finally Blöcken aus, sofern Yield nicht zulässig).
Wenn eine Yield Anweisung ausgeführt wird, wird der Ausdruck ausgewertet und in der aktuellen Iteratorvariablen der iterator-Methodeninstanz gespeichert, die diesem Iteratorobjekt zugeordnet ist. Der Wert True wird an den Aufrufer von MoveNext, und der Kontrollpunkt dieser Instanz wird angehalten, bis der nächste Aufruf des MoveNext Iteratorobjekts fortschreitet.
Visual Basic language spec