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.
Ein Ausdruck ist eine Abfolge von Operatoren und Operanden, die eine Berechnung eines Werts angibt oder eine Variable oder Konstante angibt. In diesem Kapitel werden die Syntax, die Reihenfolge der Auswertung von Operanden und Operatoren und die Bedeutung von Ausdrücken definiert.
Expression
: SimpleExpression
| TypeExpression
| MemberAccessExpression
| DictionaryAccessExpression
| InvocationExpression
| IndexExpression
| NewExpression
| CastExpression
| OperatorExpression
| ConditionalExpression
| LambdaExpression
| QueryExpression
| XMLLiteralExpression
| XMLMemberAccessExpression
;
Ausdrucksklassifizierungen
Jeder Ausdruck wird als einer der folgenden Klassifiziert:
Ein Wert. Jeder Wert verfügt über einen zugeordneten Typ.
Eine Variable. Jede Variable hat einen zugeordneten Typ, nämlich den deklarierten Typ der Variablen.
Ein Namespace. Ein Ausdruck mit dieser Klassifizierung kann nur als linke Seite des Memberzugriffs angezeigt werden. In jedem anderen Kontext verursacht ein als Namespace klassifizierter Ausdruck einen Kompilierungszeitfehler.
Ein Typ. Ein Ausdruck mit dieser Klassifizierung kann nur als linke Seite des Memberzugriffs angezeigt werden. In jedem anderen Kontext verursacht ein Ausdruck, der als Typ klassifiziert wurde, einen Kompilierungsfehler.
Eine Methodengruppe, bei der es sich um eine Reihe von Methoden handelt, die mit demselben Namen überladen sind. Eine Methodengruppe verfügt möglicherweise über einen zugeordneten Zielausdruck und eine zugeordnete Typargumentliste.
Ein Methodenzeiger, der die Position einer Methode darstellt. Ein Methodenzeiger verfügt möglicherweise über einen zugeordneten Zielausdruck und eine zugeordnete Typargumentliste.
Eine Lambda-Methode, bei der es sich um eine anonyme Methode handelt.
Eine Eigenschaftengruppe, bei der es sich um eine Reihe von Eigenschaften handelt, die mit demselben Namen überladen sind. Eine Eigenschaftengruppe verfügt möglicherweise über einen zugeordneten Zielausdruck.
Eigenschaftszugriff Jeder Eigenschaftszugriff hat einen entsprechenden Typ, nämlich den Typ der Eigenschaft. Ein Eigenschaftenzugriff verfügt möglicherweise über einen zugeordneten Zielausdruck.
Ein spät gebundener Zugriff, der eine Methode oder einen Eigenschaftszugriff bis zur Laufzeit zurückgestellt darstellt. Ein spät gebundener Zugriff verfügt möglicherweise über einen zugeordneten Zielausdruck und eine zugeordnete Typargumentliste. Der Typ eines verspäteten Zugriffs ist immer
Object.Ereigniszugriff. Jeder Zugriff auf ein Ereignis hat einen zugehörigen Typ, nämlich den Typ des Ereignisses. Ein Ereigniszugriff verfügt möglicherweise über einen zugeordneten Zielausdruck. Ein Ereigniszugriff kann als erstes Argument des
RaiseEvent,AddHandlerundRemoveHandlerder Anweisungen angezeigt werden. In jedem anderen Kontext verursacht ein Ausdruck, der als Ereigniszugriff klassifiziert ist, einen Kompilierfehler.Ein Arrayliteral, das die Anfangswerte eines Arrays darstellt, dessen Typ noch nicht bestimmt wurde.
Leere. Dies tritt auf, wenn der Ausdruck ein Aufruf einer Unterroutine oder ein Await-Operatorausdruck ohne Ergebnis ist. Ein ausdruck, der als void klassifiziert ist, ist nur im Kontext einer Aufruf-Anweisung oder einer Await-Anweisung gültig.
Ein Standardwert. Nur das Literal
Nothingerzeugt diese Klassifizierung.
Das Endergebnis eines Ausdrucks ist in der Regel ein Wert oder eine Variable, wobei die anderen Kategorien von Ausdrücken als Zwischenwerte funktionieren, die nur in bestimmten Kontexten zulässig sind.
Beachten Sie, dass Ausdrücke, deren Typ ein Typparameter ist, in Anweisungen und Ausdrücken verwendet werden können, die den Typ eines Ausdrucks erfordern, bestimmte Merkmale aufweisen (z. B. Ein Bezugstyp, Werttyp, Ableitung von einem Typ usw.), wenn die für den Typparameter auferlegten Einschränkungen diese Merkmale erfüllen.
Expression Reclassification
Wenn ein Ausdruck normalerweise in einem Kontext verwendet wird, der eine andere Klassifizierung als der des Ausdrucks erfordert, tritt ein Kompilierungszeitfehler auf , z. B. beim Versuch, einem Literal einen Wert zuzuweisen. In vielen Fällen ist es jedoch möglich, die Klassifizierung eines Ausdrucks durch den Prozess der Neuklassifizierung zu ändern.
Wenn die Neuklassifizierung erfolgreich ist, wird die Neuklassifizierung als Verbreiterung oder Verengung beurteilt. Sofern nicht anders angegeben, werden alle Neuklassifizierungen in dieser Liste erweitert.
Die folgenden Arten von Ausdrücken können neu klassifiziert werden:
Eine Variable kann als Wert neu klassifiziert werden. Der in der Variablen gespeicherte Wert wird abgerufen.
Eine Methodengruppe kann als Wert neu klassifiziert werden. Der Methodengruppenausdruck wird als Aufrufausdruck mit dem zugeordneten Zielausdruck und der Typparameterliste interpretiert, und leere Klammern (d
f. a. wird alsf()f(Of Integer)und interpretiert alsf(Of Integer)()). Diese Neuklassifizierung kann dazu führen, dass der Ausdruck weiter als "void" klassifiziert wird.Ein Methodenzeiger kann als Wert neu klassifiziert werden. Diese Neuklassifizierung kann nur im Kontext einer Konvertierung auftreten, bei der der Zieltyp bekannt ist. Der Methodenzeigerausdruck wird als Argument für einen Stellvertretungsinstanzierungsausdruck des entsprechenden Typs mit der zugeordneten Typargumentliste interpretiert. Beispiel:
Delegate Sub D(i As Integer) Module Test Sub F(i As Integer) End Sub Sub Main() Dim del As D ' The next two lines are equivalent. del = AddressOf F del = New D(AddressOf F) End Sub End ModuleEine Lambda-Methode kann als Wert neu klassifiziert werden. Wenn die Neuklassifizierung im Kontext einer Konvertierung auftritt, bei der der Zieltyp bekannt ist, kann eine von zwei Neuklassifizierungen auftreten:
Wenn der Zieltyp ein Delegattyp ist, wird die Lambda-Methode als Argument für einen Delegatkonstruktionsausdruck des entsprechenden Typs interpretiert.
Wenn es sich bei dem Zieltyp
System.Linq.Expressions.Expression(Of T)Tum einen Delegattyp handelt, wird die Lambda-Methode so interpretiert, als ob sie im Delegatkonstruktionsausdruck verwendetTund dann in eine Ausdrucksstruktur konvertiert wurde.
Eine asynchrone oder iterator Lambda-Methode kann nur als Argument für einen Delegat-Konstruktionsausdruck interpretiert werden, wenn der Delegate keine ByRef-Parameter aufweist.
Wenn die Konvertierung von parametertypen des Delegaten in die entsprechenden Lambda-Parametertypen eine schmale Konvertierung ist, wird die Neuklassifizierung als schmal eingestuft; andernfalls wird sie verbreitert.
Hinweis: Die genaue Übersetzung zwischen Lambdamethoden und Ausdrucksstrukturen kann zwischen Versionen des Compilers nicht behoben werden und liegt außerhalb des Umfangs dieser Spezifikation. Für Microsoft Visual Basic 11.0 können alle Lambda-Ausdrücke in Ausdrucksstrukturen konvertiert werden, die den folgenden Einschränkungen unterliegen: (1) 1. Nur einzeilige Lambda-Ausdrücke ohne ByRef-Parameter können in Ausdrucksstrukturen konvertiert werden. Von den einzeiligen
SubLambdas können nur Aufrufanweisungen in Ausdrucksstrukturen konvertiert werden. (2) Anonyme Typausdrücke können nicht in Ausdrucksstrukturen konvertiert werden, wenn ein früherer Feldinitialisierer verwendet wird, um einen nachfolgenden Feldinitialisierer zu initialisieren, z. B.New With {.a=1, .b=.a}. (3) Objektinitialisierungsausdrücke können nicht in Ausdrucksstrukturen konvertiert werden, wenn ein Element des aktuellen Objekts, das initialisiert wird, in einem der Feldinitialisierer verwendet wird, z. B.New C1 With {.a=1, .b=.Method1()}. (4) Mehrdimensionale Arrayerstellungsausdrücke können nur in Ausdrucksstrukturen konvertiert werden, wenn sie den Elementtyp explizit deklarieren. (5) Spätbindungsausdrücke können nicht in Ausdrucksstrukturen konvertiert werden. (6) Wenn eine Variable oder ein Feld an einen Aufrufausdruck übergeben wird, aber nicht genau denselben Typ wie der ByRef-Parameter aufweist oder wenn eine Eigenschaft ByRef übergeben wird, handelt es sich bei der normalen VB-Semantik darum, dass eine Kopie des Arguments ByRef übergeben wird und der endgültige Wert dann wieder in die Variable oder das Feld oder die Eigenschaft kopiert wird. In Ausdrucksstrukturen tritt das Kopieren nicht auf. (7) Alle diese Einschränkungen gelten auch für geschachtelte Lambda-Ausdrücke.Wenn der Zieltyp nicht bekannt ist, wird die Lambda-Methode als Argument für einen Stellvertretungsinstanzierungsausdruck eines anonymen Delegatentyps mit derselben Signatur der Lambda-Methode interpretiert. Wenn strenge Semantik verwendet wird und der Typ eines der Parameter ausgelassen wird, tritt ein Kompilierungszeitfehler auf;
Objectandernfalls wird durch einen fehlenden Parametertyp ersetzt. Beispiel:Module Test Sub Main() ' Type of x will be equivalent to Func(Of Object, Object, Object) Dim x = Function(a, b) a + b ' Type of y will be equivalent to Action(Of Object, Object) Dim y = Sub(a, b) Console.WriteLine(a + b) End Sub End ModuleEine Eigenschaftengruppe kann als Eigenschaftszugriff neu klassifiziert werden. Der Eigenschaftengruppenausdruck wird als Indexausdruck mit leeren Klammern interpretiert (d
ff(). a.Ein Eigenschaftszugriff kann als Wert neu klassifiziert werden. Der Eigenschaftszugriffsausdruck wird als Aufrufausdruck des
GetAccessors der Eigenschaft interpretiert. Wenn die Eigenschaft keinen Getter hat, tritt ein Kompilierungszeitfehler auf.Ein spät gebundener Zugriff kann als spät gebundene Methode oder spät gebundener Eigenschaftszugriff neu klassifiziert werden. In einer Situation, in der ein spät gebundener Zugriff sowohl als Methodenzugriff als auch als Eigenschaftszugriff neu klassifiziert werden kann, wird eine Neuklassifizierung für einen Eigenschaftszugriff bevorzugt.
Ein spät gebundener Zugriff kann als Wert neu klassifiziert werden.
Ein Arrayliteral kann als Wert neu klassifiziert werden. Der Typ des Werts wird wie folgt bestimmt:
Wenn die Neuklassifizierung im Kontext einer Konvertierung auftritt, bei der der Zieltyp bekannt ist und der Zieltyp ein Arraytyp ist, wird das Arrayliteral als Wert vom Typ T() neu klassifiziert. Wenn der Zieltyp
System.Collections.Generic.IList(Of T)ist ,IReadOnlyList(Of T),ICollection(Of T), ,IReadOnlyCollection(Of T)oderIEnumerable(Of T), und das Arrayliteral eine Ebene der Schachtelung hat, wird das Arrayliteral als Wert des TypsT()neu klassifiziert.Andernfalls wird das Arrayliteral in einen Wert umklassifiziert, dessen Typ ein Array der Rangfolge gleich der Schachtelungsebene ist, wobei der Elementtyp durch den dominanten Typ der Elemente im Initialisierer bestimmt wird; wenn kein dominanter Typ bestimmt werden kann,
Objectwird verwendet. Beispiel:' x Is GetType(Double(,,)) Dim x = { { { 1, 2.0 }, { 3, 4 } }, { { 5, 6 }, { 7, 8 } } }.GetType() ' y Is GetType(Integer()) Dim y = { 1, 2, 3 }.GetType() ' z Is GetType(Object()) Dim z = { 1, "2" }.GetType() ' Error: Inconsistent nesting Dim a = { { 10 }, { 20, 30 } }.GetType()
Hinweis: Es gibt eine geringfügige Änderung des Verhaltens zwischen Version 9.0 und Version 10.0 der Sprache. Vor 10.0 wirkten sich Arrayelementinitialisierer nicht auf die Lokale Variablentypeinleitung aus, und jetzt haben sie dies getan. Hätte also
Dim a() = { 1, 2, 3 }als Typ vonaVersion 9.0 der Sprache undInteger()in Version 10.0 abgeleitetObject().Die Neuklassifizierung interpretiert dann das Arrayliteral als Arrayerstellungsausdruck neu. Also die Beispiele:
Dim x As Double = { 1, 2, 3, 4 } Dim y = { "a", "b" }entsprechen folgendem:
Dim x As Double = New Double() { 1, 2, 3, 4 } Dim y = New String() { "a", "b" }Die Neuklassifizierung wird als schmal eingestuft, wenn eine Konvertierung von einem Elementausdruck in den Arrayelementtyp eingeschränkt wird; andernfalls wird sie als Verbreiterung beurteilt.
Der Standardwert
Nothingkann als Wert neu klassifiziert werden. In einem Kontext, in dem der Zieltyp bekannt ist, ist das Ergebnis der Standardwert des Zieltyps. In einem Kontext, in dem der Zieltyp nicht bekannt ist, ist das Ergebnis ein Nullwert vom TypObject.
Ein Namespaceausdruck, Typausdruck, Ereigniszugriffsausdruck oder ungültiger Ausdruck kann nicht neu klassifiziert werden. Mehrere Umklassifizierungen können gleichzeitig durchgeführt werden. Beispiel:
Module Test
Sub F(i As Integer)
End Sub
ReadOnly Property P() As Integer
Get
End Get
End Sub
Sub Main()
F(P)
End Property
End Module
In diesem Fall wird der Eigenschaftengruppenausdruck P zuerst von einer Eigenschaftsgruppe in einen Eigenschaftszugriff klassifiziert und dann von einem Eigenschaftszugriff auf einen Wert neu klassifiziert. Die wenigsten Neuklassifizierungen werden durchgeführt, um eine gültige Klassifizierung im Kontext zu erreichen.
Konstante Ausdrücke
Ein konstanter Ausdruck ist ein Ausdruck, dessen Wert zur Kompilierungszeit vollständig ausgewertet werden kann.
ConstantExpression
: Expression
;
Der Typ eines Konstantenausdrucks kann , , , , Short, UInteger, Integer, ULong, , Long, , DateDoubleStringDecimalObjectBooleanSingleCharoder ein beliebiger Enumerationstyp sein.ByteUShortSByte Die folgenden Konstrukte sind in Konstantenausdrücken zulässig:
Literale (einschließlich
Nothing).Verweise auf Konstantentypmember oder Konstantenlokale.
Verweise auf Member von Enumerationstypen.
Unterausdrücke in Klammern.
Koersionsausdrücke, vorausgesetzt, der Zieltyp ist einer der oben aufgeführten Typen. Ersetzungen in und von dieser
StringRegel sind eine Ausnahme und sind nur für NULL-Werte zulässig, daStringKonvertierungen immer in der aktuellen Kultur der Ausführungsumgebung zur Laufzeit ausgeführt werden. Beachten Sie, dass konstanten Koersionsausdrücke nur systeminterne Konvertierungen verwenden können.Die
+Operatoren-undNotunäre Operatoren, sofern der Operand und das Ergebnis eines oben aufgeführten Typs sind.Die
+,-, , ,Mod*^,/<<&OrXor>>AndAlso\And,=OrElse,><><<=und=>binäre Operatoren, vorausgesetzt, jeder Operand und Ergebnis ist von einem oben aufgeführten Typ.Der bedingte Operator If, vorausgesetzt, jeder Operand und Das Ergebnis weist einen oben aufgeführten Typ auf.
Die folgenden Laufzeitfunktionen:
Microsoft.VisualBasic.Strings.ChrW;Microsoft.VisualBasic.Strings.Chrwenn der Konstantenwert zwischen 0 und 128 liegt;Microsoft.VisualBasic.Strings.AscWwenn die Konstantenzeichenfolge nicht leer ist;Microsoft.VisualBasic.Strings.Ascwenn die Konstantenzeichenfolge nicht leer ist.
Die folgenden Konstrukte sind in Konstantenausdrücken nicht zulässig:
- Implizite Bindung über einen
WithKontext.
Konstantenausdrücke eines integralen Typs (ULong, , Long, UInteger, IntegerUShort, Short, SByteoder Byte) können implizit in einen schmaleren integralen Typ konvertiert werden, und Konstantenausdrücke des Typs Double können implizit in konvertiert Singlewerden, vorausgesetzt, der Wert des Konstantenausdrucks befindet sich innerhalb des Bereichs des Zieltyps. Diese Schmalungskonvertierungen sind unabhängig davon zulässig, ob zulässige oder strenge Semantik verwendet wird.
Late-Bound Ausdrücke
Wenn das Ziel eines Elementzugriffsausdrucks oder Indexausdrucks vom Typ Objectist, kann die Verarbeitung des Ausdrucks bis zur Laufzeit zurückgestellt werden. Das Zurückstellen der Verarbeitung auf diese Weise wird als späte Bindung bezeichnet. Die späte Bindung ermöglicht Object die Verwendung von Variablen auf typlose Weise, wobei alle Auflösungen von Elementen auf dem tatsächlichen Laufzeittyp des Werts in der Variablen basieren. Wenn strenge Semantik durch die Kompilierungsumgebung oder durch Option Strictspäte Bindung angegeben werden, tritt ein Kompilierungszeitfehler auf. Nicht öffentliche Member werden ignoriert, wenn eine späte Bindung durchgeführt wird, einschließlich für die Zwecke der Überladungsauflösung. Beachten Sie, dass im Gegensatz zum früh gebundenen Fall das Aufrufen oder Zugreifen auf ein Shared Mitglied spät gebunden dazu führt, dass das Aufrufziel zur Laufzeit ausgewertet wird. Wenn der Ausdruck ein Aufrufausdruck für ein element ist, für das eine späte Bindung definiert System.Objectist, findet keine späte Bindung statt.
Im Allgemeinen werden spätgebundene Zugriffe zur Laufzeit aufgelöst, indem der Bezeichner für den tatsächlichen Laufzeittyp des Ausdrucks nachschlagen. Wenn die verspätete Membersuche zur Laufzeit fehlschlägt, wird eine System.MissingMemberException Ausnahme ausgelöst. Da die spät gebundene Elementsuche ausschließlich vom Laufzeittyp des zugeordneten Zielausdrucks erfolgt, ist der Laufzeittyp eines Objekts nie eine Schnittstelle. Daher ist es unmöglich, auf Schnittstellenmember in einem spät gebundenen Memberzugriffsausdruck zuzugreifen.
Die Argumente für einen spät gebundenen Memberzugriff werden in der Reihenfolge ausgewertet, in der sie im Memberzugriffsausdruck angezeigt werden: nicht die Reihenfolge, in der Parameter im spät gebundenen Element deklariert werden. Das folgende Beispiel veranschaulicht diesen Unterschied:
Class C
Public Sub f(ByVal x As Integer, ByVal y As Integer)
End Sub
End Class
Module Module1
Sub Main()
Console.Write("Early-bound: ")
Dim c As C = New C
c.f(y:=t("y"), x:=t("x"))
Console.Write(vbCrLf & "Late-bound: ")
Dim o As Object = New C
o.f(y:=t("y"), x:=t("x"))
End Sub
Function t(ByVal s As String) As Integer
Console.Write(s)
Return 0
End Function
End Module
Dieser Code zeigt Folgendes an:
Early-bound: xy
Late-bound: yx
Da die auflösung der spät gebundenen Überladung für den Laufzeittyp der Argumente erfolgt, kann ein Ausdruck abhängig davon, ob er zur Kompilierungszeit oder Laufzeit ausgewertet wird, unterschiedliche Ergebnisse erzeugen. Das folgende Beispiel veranschaulicht diesen Unterschied:
Class Base
End Class
Class Derived
Inherits Base
End Class
Module Test
Sub F(b As Base)
Console.WriteLine("F(Base)")
End Sub
Sub F(d As Derived)
Console.WriteLine("F(Derived)")
End Sub
Sub Main()
Dim b As Base = New Derived()
Dim o As Object = b
F(b)
F(o)
End Sub
End Module
Dieser Code zeigt Folgendes an:
F(Base)
F(Derived)
Einfache Ausdrücke
Einfache Ausdrücke sind Literale, klammerte Ausdrücke, Instanzausdrücke oder einfache Namensausdrücke.
SimpleExpression
: LiteralExpression
| ParenthesizedExpression
| InstanceExpression
| SimpleNameExpression
| AddressOfExpression
;
Literale Ausdrücke
Literale Ausdrücke werden auf den Wert ausgewertet, der durch das Literal dargestellt wird. Ein Literalausdruck wird als Wert klassifiziert, mit Ausnahme des Literals Nothing, der als Standardwert klassifiziert wird.
LiteralExpression
: Literal
;
Klammern von Ausdrücken
Ein Klammerausdruck besteht aus einem Ausdruck, der in Klammern eingeschlossen ist. Ein Klammerausdruck wird als Wert klassifiziert, und der eingeschlossene Ausdruck muss als Wert klassifiziert werden. Ein Klammerausdruck wertet den Wert des Ausdrucks innerhalb der Klammern aus.
ParenthesizedExpression
: OpenParenthesis Expression CloseParenthesis
;
Instanzausdrücke
Ein Instanzausdruck ist das Schlüsselwort Me. Sie darf nur im Textkörper einer nicht freigegebenen Methode, eines Konstruktors oder eines Eigenschaftsaccessors verwendet werden. Sie wird als Wert klassifiziert. Das Schlüsselwort Me stellt die Instanz des Typs dar, der die auszuführende Methode oder den Eigenschaftenaccessor enthält. Wenn ein Konstruktor explizit einen anderen Konstruktor (Section Constructors) aufruft, Me kann erst nach diesem Konstruktoraufruf verwendet werden, da die Instanz noch nicht erstellt wurde.
InstanceExpression
: 'Me'
;
Einfache Namensausdrücke
Ein einfacher Namensausdruck besteht aus einem einzelnen Bezeichner, gefolgt von einer optionalen Typargumentliste.
SimpleNameExpression
: Identifier ( OpenParenthesis 'Of' TypeArgumentList CloseParenthesis )?
;
Der Name wird durch die folgenden "Regeln für einfache Namensauflösung" aufgelöst und klassifiziert:
Beginnend mit dem unmittelbar eingeschlossenen Block und der Fortsetzung mit jedem eingeschlossenen äußeren Block (falls vorhanden), wenn der Bezeichner dem Namen einer lokalen Variablen, statischen Variablen, konstanten lokalen, Methodentypparameter oder Parameter entspricht, bezieht sich der Bezeichner auf die übereinstimmende Entität.
Wenn der Bezeichner mit einer lokalen Variablen, statischen Variablen oder konstanten lokalen Variablen übereinstimmt und eine Typargumentliste angegeben wurde, tritt ein Kompilierungszeitfehler auf. Wenn der Bezeichner mit einem Methodentypparameter übereinstimmt und eine Typargumentliste angegeben wurde, tritt keine Übereinstimmung auf, und die Auflösung wird fortgesetzt. Wenn der Bezeichner einer lokalen Variablen entspricht, entspricht die lokale Variable der impliziten Funktion oder
Getdes Accessors, die lokale Variable zurückgibt, und der Ausdruck ist Teil eines Aufrufausdrucks, eines Aufrufausdrucks oder einesAddressOfAusdrucks, und es tritt keine Übereinstimmung auf, und die Auflösung wird fortgesetzt.Der Ausdruck wird als Variable klassifiziert, wenn es sich um eine lokale Variable, statische Variable oder einen Parameter handelt. Der Ausdruck wird als Typ klassifiziert, wenn es sich um einen Methodentypparameter handelt. Der Ausdruck wird als Wert klassifiziert, wenn es sich um eine lokale Konstante handelt.
Für jeden geschachtelten Typ, der den Ausdruck enthält, beginnend am innersten und am äußersten Rand, wenn ein Nachschlagevorgang des Bezeichners im Typ eine Übereinstimmung mit einem barrierefreien Element erzeugt:
- Wenn es sich bei dem übereinstimmenden Typmemm um einen Typparameter handelt, wird das Ergebnis als Typ klassifiziert und ist der übereinstimmende Typparameter. Wenn eine Typargumentliste angegeben wurde, tritt keine Übereinstimmung auf, und die Auflösung wird fortgesetzt.
- Andernfalls entspricht das Ergebnis, wenn der Typ der sofort eingeschlossene Typ ist und die Suche ein nicht freigegebenes Typelement identifiziert, ist das Ergebnis mit dem Elementzugriff des Formulars
Me.E(Of A)identisch, wobeiEes sich um den Bezeichner undAdie Typargumentliste handelt, falls vorhanden. - Andernfalls entspricht das Ergebnis exakt dem Elementzugriff des Formulars
T.E(Of A), wobeiTes sich um den Typ handelt, der das übereinstimmende Element enthält,Eder Bezeichner ist undAggf. die Typargumentliste ist. In diesem Fall ist es ein Fehler für den Bezeichner, auf ein nicht freigegebenes Mitglied zu verweisen.
Führen Sie für jeden geschachtelten Namespace, beginnend vom innersten und zum äußersten Namespace, die folgenden Schritte aus:
- Wenn der Namespace einen barrierefreien Typ mit dem angegebenen Namen enthält und dieselbe Anzahl von Typparametern wie in der Typargumentliste angegeben wurde, bezieht sich der Bezeichner auf diesen Typ und wird als Typ klassifiziert.
- Andernfalls, wenn keine Typargumentliste angegeben wurde und der Namespace ein Namespacememm mit dem angegebenen Namen enthält, bezieht sich der Bezeichner auf diesen Namespace und wird als Namespace klassifiziert.
- Wenn der Namespace ein oder mehrere barrierefreie Standardmodule enthält und ein Membernamensuchvorgang des Bezeichners eine barrierefreie Übereinstimmung in genau einem Standardmodul erzeugt, entspricht das Ergebnis genau dem Memberzugriff des Formulars
M.E(Of A), wobeiMdas Standardmodul, das das übereinstimmende Element enthält,Eder Bezeichner ist undAdie Typargumentliste ist, wenn überhaupt. Wenn der Bezeichner mit Elementen des barrierefreien Typs in mehr als einem Standardmodul übereinstimmt, tritt ein Kompilierungszeitfehler auf.
Wenn die Quelldatei einen oder mehrere Importalias enthält und der Bezeichner mit dem Namen eines dieser Aliase übereinstimmt, bezieht sich der Bezeichner auf diesen Namespace oder Typ. Wenn eine Typargumentliste angegeben wird, tritt ein Kompilierungszeitfehler auf.
Wenn die Quelldatei, die den Namensverweis enthält, einen oder mehrere Importe hat:
- Wenn der Bezeichner in genau einem Import des Namens eines barrierefreien Typs mit der gleichen Anzahl von Typparametern übereinstimmt, wie in der Typargumentliste angegeben, falls vorhanden oder ein Typmemm, bezieht sich der Bezeichner auf diesen Typ oder element. Wenn der Bezeichner in mehr als einem Import des Namens eines barrierefreien Typs mit der gleichen Anzahl von Typparametern übereinstimmt, wie in der Typargumentliste angegeben wurde, falls vorhanden oder ein Member für barrierefreien Typ, tritt ein Kompilierungszeitfehler auf.
- Andernfalls, wenn keine Typargumentliste angegeben wurde und der Bezeichner in genau einem Import des Namens eines Namespaces mit barrierefreien Typen übereinstimmt, bezieht sich der Bezeichner auf diesen Namespace. Wenn keine Typargumentliste angegeben wurde und der Bezeichner in mehr als einem Import des Namens eines Namespaces mit barrierefreien Typen übereinstimmt, tritt ein Kompilierungszeitfehler auf.
- Wenn die Importe ein oder mehrere barrierefreie Standardmodule enthalten und ein Elementnamensuchvorgang des Bezeichners eine barrierefreie Übereinstimmung in genau einem Standardmodul erzeugt, entspricht das Ergebnis genau dem Memberzugriff des Formulars
M.E(Of A), wobeiMdas Standardmodul, das das übereinstimmende Element enthält,Eder Bezeichner ist undAdie Typargumentliste ist, wenn überhaupt. Wenn der Bezeichner mit Elementen des barrierefreien Typs in mehr als einem Standardmodul übereinstimmt, tritt ein Kompilierungszeitfehler auf.
Wenn die Kompilierungsumgebung einen oder mehrere Importalias definiert und der Bezeichner mit dem Namen eines dieser Aliase übereinstimmt, bezieht sich der Bezeichner auf diesen Namespace oder Typ. Wenn eine Typargumentliste angegeben wird, tritt ein Kompilierungszeitfehler auf.
Wenn die Kompilierungsumgebung einen oder mehrere Importe definiert:
- Wenn der Bezeichner in genau einem Import des Namens eines barrierefreien Typs mit der gleichen Anzahl von Typparametern übereinstimmt, wie in der Typargumentliste angegeben, falls vorhanden oder ein Typmemm, bezieht sich der Bezeichner auf diesen Typ oder element. Wenn der Bezeichner in mehr als einem Import des Namens eines barrierefreien Typs mit derselben Anzahl von Typparametern übereinstimmt, wie in der Typargumentliste angegeben, falls vorhanden oder ein Typmemm, tritt ein Kompilierungszeitfehler auf.
- Andernfalls, wenn keine Typargumentliste angegeben wurde und der Bezeichner in genau einem Import des Namens eines Namespaces mit barrierefreien Typen übereinstimmt, bezieht sich der Bezeichner auf diesen Namespace. Wenn keine Typargumentliste angegeben wurde und der Bezeichner in mehr als einem Import des Namens eines Namespaces mit barrierefreien Typen übereinstimmt, tritt ein Kompilierungszeitfehler auf.
- Wenn die Importe ein oder mehrere barrierefreie Standardmodule enthalten und ein Elementnamensuchvorgang des Bezeichners eine barrierefreie Übereinstimmung in genau einem Standardmodul erzeugt, entspricht das Ergebnis genau dem Memberzugriff des Formulars
M.E(Of A), wobeiMdas Standardmodul, das das übereinstimmende Element enthält,Eder Bezeichner ist undAdie Typargumentliste ist, wenn überhaupt. Wenn der Bezeichner mit Elementen des barrierefreien Typs in mehr als einem Standardmodul übereinstimmt, tritt ein Kompilierungszeitfehler auf.
Andernfalls ist der vom Bezeichner angegebene Name nicht definiert.
Ein einfacher Nameausdruck, der nicht definiert ist, ist ein Kompilierungszeitfehler.
Normalerweise kann ein Name nur einmal in einem bestimmten Namespace vorkommen. Da Namespaces jedoch über mehrere .NET-Assemblys deklariert werden können, ist es möglich, eine Situation zu haben, in der zwei Assemblys einen Typ mit demselben vollqualifizierten Namen definieren. In diesem Fall wird ein im aktuellen Satz von Quelldateien deklarierter Typ gegenüber einem Typ bevorzugt, der in einer externen .NET-Assembly deklariert ist. Andernfalls ist der Name mehrdeutig und es gibt keine Möglichkeit, den Namen zu disambiguieren.
AddressOf-Ausdrücke
Ein AddressOf Ausdruck wird verwendet, um einen Methodenzeiger zu erzeugen. Der Ausdruck besteht aus dem AddressOf Schlüsselwort und einem Ausdruck, der als Methodengruppe oder einen spät gebundenen Zugriff klassifiziert werden muss. Die Methodengruppe kann nicht auf Konstruktoren verweisen.
Das Ergebnis wird als Methodenzeiger mit demselben zugeordneten Zielausdruck und der Typargumentliste (falls vorhanden) wie die Methodengruppe klassifiziert.
AddressOfExpression
: 'AddressOf' Expression
;
Typausdrücke
Ein Typausdruck ist ein GetType Ausdruck, ein TypeOf...Is Ausdruck, ein Is Ausdruck oder ein GetXmlNamespace Ausdruck.
TypeExpression
: GetTypeExpression
| TypeOfIsExpression
| IsExpression
| GetXmlNamespaceExpression
;
GetType-Ausdrücke
Ein GetType Ausdruck besteht aus dem Schlüsselwort GetType und dem Namen eines Typs.
GetTypeExpression
: 'GetType' OpenParenthesis GetTypeTypeName CloseParenthesis
;
GetTypeTypeName
: TypeName
| QualifiedOpenTypeName
;
QualifiedOpenTypeName
: Identifier TypeArityList? (Period IdentifierOrKeyword TypeArityList?)*
| 'Global' Period IdentifierOrKeyword TypeArityList?
(Period IdentifierOrKeyword TypeArityList?)*
;
TypeArityList
: OpenParenthesis 'Of' CommaList? CloseParenthesis
;
CommaList
: Comma Comma*
;
Ein GetType Ausdruck wird als Wert klassifiziert, und sein Wert ist die Spiegelungsklasse (System.Type), die den GetTypeTypeName darstellt. Wenn der GetTypeTypeName ein Typparameter ist, gibt der Ausdruck das Objekt zurück, das System.Type dem Typargument entspricht, das zur Laufzeit für den Typparameter bereitgestellt wird.
Der GetTypeTypeName ist auf zwei Arten besonders:
Es ist zulässig
System.Void, die einzige Stelle in der Sprache zu sein, in der auf diesen Typnamen verwiesen werden kann.Es kann ein konstruierter generischer Typ sein, bei dem die Typargumente weggelassen werden. Dadurch kann der
GetTypeAusdruck dasSystem.TypeObjekt zurückgeben, das dem generischen Typ selbst entspricht.
Das folgende Beispiel veranschaulicht den GetType Ausdruck:
Module Test
Sub Main()
Dim t As Type() = { GetType(Integer), GetType(System.Int32), _
GetType(String), GetType(Double()) }
Dim i As Integer
For i = 0 To t.Length - 1
Console.WriteLine(t(i).Name)
Next i
End Sub
End Module
Die resultierende Ausgabe ist:
Int32
Int32
String
Double[]
TypeOf... Ist-Ausdrücke
Ein TypeOf...Is Ausdruck wird verwendet, um zu überprüfen, ob der Laufzeittyp eines Werts mit einem bestimmten Typ kompatibel ist. Der erste Operand muss als Wert klassifiziert werden, darf keine reklassifizierte Lambda-Methode sein und muss ein Verweistyp oder ein nicht eingeschränkter Typparametertyp sein. Der zweite Operand muss ein Typname sein. Das Ergebnis des Ausdrucks wird als Wert klassifiziert und ist ein Boolean Wert. Der Ausdruck wird ausgewertet, True wenn der Laufzeittyp des Operanden eine Identitäts-, Standard-, Verweis-, Array-, Werttyp- oder Typparameterkonvertierung in den Typ aufweist, False andernfalls. Wenn keine Konvertierung zwischen dem Typ des Ausdrucks und dem spezifischen Typ vorhanden ist, tritt ein Kompilierungsfehler auf.
TypeOfIsExpression
: 'TypeOf' Expression 'Is' LineTerminator? TypeName
;
Ist-Ausdrücke
Ein Is oder IsNot Ausdruck wird verwendet, um einen Vergleich der Referenzgleichheit zu erstellen.
IsExpression
: Expression 'Is' LineTerminator? Expression
| Expression 'IsNot' LineTerminator? Expression
;
Jeder Ausdruck muss als Wert klassifiziert werden, und der Typ jedes Ausdrucks muss ein Verweistyp, ein nicht eingeschränkter Typparametertyp oder ein Nullwerttyp sein. Wenn der Typ eines Ausdrucks ein nicht eingeschränkter Typparametertyp oder nullabler Werttyp ist, muss der andere Ausdruck jedoch das Literal Nothingsein.
Das Ergebnis wird als Wert klassifiziert und als .Boolean Ein Is Vorgang wird ausgewertet, True wenn beide Werte auf dieselbe Instanz oder beide Werte Nothingverweisen, oder False auf andere Weise. Ein IsNot Vorgang wird ausgewertet, False wenn beide Werte auf dieselbe Instanz oder beide Werte Nothingverweisen, oder True auf andere Weise.
GetXmlNamespace-Ausdrücke
Ein GetXmlNamespace Ausdruck besteht aus dem Schlüsselwort GetXmlNamespace und dem Namen eines XML-Namespace, der von der Quelldatei oder Kompilierungsumgebung deklariert wird.
GetXmlNamespaceExpression
: 'GetXmlNamespace' OpenParenthesis XMLNamespaceName? CloseParenthesis
;
Ein GetXmlNamespace Ausdruck wird als Wert klassifiziert, und sein Wert ist eine Instanz, die System.Xml.Linq.XNamespace den XMLNamespaceName darstellt. Wenn dieser Typ nicht verfügbar ist, tritt ein Kompilierungszeitfehler auf.
Beispiel:
Imports <xmlns:db="http://example.org/database">
Module Test
Sub Main()
Dim db = GetXmlNamespace(db)
' The following are equivalent
Dim customer1 = _
New System.Xml.Linq.XElement(db + "customer", "Bob")
Dim customer2 = <db:customer>Bob</>
End Sub
End Module
Alles zwischen den Klammern wird als Teil des Namespacenamens betrachtet, sodass XML-Regeln für Dinge wie Leerzeichen gelten. Beispiel:
Imports <xmlns:db-ns="http://example.org/database">
Module Test
Sub Main()
' Error, XML name expected
Dim db1 = GetXmlNamespace( db-ns )
' Error, ')' expected
Dim db2 = GetXmlNamespace(db _
)
' OK.
Dim db3 = GetXmlNamespace(db-ns)
End Sub
End Module
Der XML-Namespaceausdruck kann auch weggelassen werden. In diesem Fall gibt der Ausdruck das Objekt zurück, das den standard-XML-Namespace darstellt.
Memberzugriffsausdrücke
Ein Memberzugriffsausdruck wird für den Zugriff auf ein Element einer Entität verwendet.
MemberAccessExpression
: MemberAccessBase? Period IdentifierOrKeyword
( OpenParenthesis 'Of' TypeArgumentList CloseParenthesis )?
;
MemberAccessBase
: Expression
| NonArrayTypeName
| 'Global'
| 'MyClass'
| 'MyBase'
;
Ein Memberzugriff auf das Formular E.I(Of A), wobei E ein Ausdruck, ein Nicht-Array-Typname, das Schlüsselwort Globaloder nicht angegeben ist und I ein Bezeichner mit einer optionalen Typargumentliste Aist, wird ausgewertet und wie folgt klassifiziert:
Wenn
Enicht angegeben wird, wird der Ausdruck aus der unmittelbar enthaltendenWithAnweisung ersetztE, und der Memberzugriff wird ausgeführt. Wenn keine enthaltendeWithAnweisung vorhanden ist, tritt ein Kompilierungszeitfehler auf.Wenn
Esie als Namespace klassifiziert wird oderEdas SchlüsselwortGlobalist, erfolgt die Membersuche im Kontext des angegebenen Namespaces. IfIis the name of an accessible member of that namespace with the same number of type parameters as was in the type argument list, if any, the result is that member. Das Ergebnis wird je nach Element als Namespace oder Typ klassifiziert. Andernfalls tritt ein Kompilierungszeitfehler auf.Wenn
Ees sich um einen Typ oder einen Ausdruck handelt, der als Typ klassifiziert ist, erfolgt die Elementsuche im Kontext des angegebenen Typs. WennIes sich um den Namen eines barrierefreien Mitglieds handeltE, wird esE.Iausgewertet und wie folgt klassifiziert:- Wenn
Ies sich um das SchlüsselwortNewhandelt undEkeine Enumeration ist, tritt ein Kompilierungsfehler auf. - Wenn
Iein Typ mit der gleichen Anzahl von Typparametern identifiziert wird, wie in der Typargumentliste angegeben wurde, ist das Ergebnis dieser Art. - Wenn
Ieine oder mehrere Methoden identifiziert werden, ist das Ergebnis eine Methodengruppe mit der zugeordneten Typargumentliste und keinem zugeordneten Zielausdruck. - Wenn
Ieine oder mehrere Eigenschaften identifiziert und keine Typargumentliste angegeben wurde, ist das Ergebnis eine Eigenschaftsgruppe ohne zugeordneten Zielausdruck. - Wenn
Ieine freigegebene Variable identifiziert und keine Typargumentliste angegeben wurde, ist das Ergebnis entweder eine Variable oder ein Wert. Wenn die Variable schreibgeschützt ist und der Verweis außerhalb des freigegebenen Konstruktors des Typs auftritt, in dem die Variable deklariert wird, ist das Ergebnis der Wert der freigegebenen VariablenIinE. Andernfalls ist das Ergebnis die freigegebene VariableIinE. - Wenn
Iein freigegebenes Ereignis identifiziert und keine Typargumentliste angegeben wurde, ist das Ergebnis ein Ereigniszugriff ohne zugeordneten Zielausdruck. - Wenn
Ieine Konstante angegeben und keine Typargumentliste angegeben wurde, ist das Ergebnis der betreffenden Konstante der Wert. - Wenn
Iein Enumerationselement identifiziert und keine Typargumentliste angegeben wurde, ist das Ergebnis der Wert dieses Enumerationselements. - Andernfalls ist
E.Iein ungültiger Memberverweis, und ein Kompilierungszeitfehler tritt auf.
- Wenn
Wenn
Esie als Variable oder Wert klassifiziert wird, wirdTder Typ der Membersuche im Kontext vonT. WennIes sich um den Namen eines barrierefreien Mitglieds handeltT, wird esE.Iausgewertet und wie folgt klassifiziert:- Wenn
Ies sich um das SchlüsselwortNew,, istEMe,MyBaseoderMyClass, und keine Typargumente angegeben wurden, ist das Ergebnis eine Methodengruppe, die die Instanzenkonstruktoren des Typs mitEeinem zugeordneten Zielausdruck vonEund keine Typargumentliste darstellt. Andernfalls tritt ein Kompilierungszeitfehler auf. - Wenn
Ieine oder mehrere Methoden identifiziert werden, einschließlich Erweiterungsmethoden, fallsTnichtObject, ist das Ergebnis eine Methodengruppe mit der zugeordneten Typargumentliste und einem zugeordneten Zielausdruck vonE. - Wenn
Imindestens eine Eigenschaft identifiziert und keine Typargumente angegeben wurden, ist das Ergebnis eine Eigenschaftsgruppe mit einem zugeordneten Zielausdruck vonE. - Wenn
Ieine freigegebene Variable oder eine Instanzvariable identifiziert wird und keine Typargumente angegeben wurden, ist das Ergebnis entweder eine Variable oder ein Wert. Wenn die Variable schreibgeschützt ist und der Verweis außerhalb eines Konstruktors der Klasse auftritt, in der die Variable für die Art der Variablen (freigegeben oder Instanz) deklariert wird, ist das Ergebnis der Wert der VariablenIim Objekt, auf das verwiesen wirdE. WennTes sich um einen Verweistyp handelt, ist das Ergebnis die VariableIim Objekt, aufEdas verwiesen wird. Andernfalls ist das Ergebnis eine Variable, wennTes sich um einen Werttyp handelt und der AusdruckEals Variable klassifiziert wird. Andernfalls handelt es sich bei dem Ergebnis um einen Wert. - Wenn
Iein Ereignis identifiziert und keine Typargumente angegeben wurden, ist das Ergebnis ein Ereigniszugriff mit einem zugeordneten Zielausdruck vonE. - Wenn
Ieine Konstante angegeben und keine Typargumente angegeben wurden, ist das Ergebnis der betreffenden Konstante der Wert. - Wenn
Iein Enumerationselement identifiziert und keine Typargumente angegeben wurden, ist das Ergebnis der Wert dieses Enumerationselements. - Wenn
Tdies der Wert ist, handeltObjectes sich bei dem Ergebnis um eine spät gebundene Membersuche, die als spät gebundener Zugriff mit der zugeordneten Typargumentliste und einem zugeordneten Zielausdruck klassifiziertEist.
- Wenn
Andernfalls ist
E.Iein ungültiger Memberverweis, und ein Kompilierungszeitfehler tritt auf.
Ein Memberzugriff des Formulars MyClass.I(Of A) entspricht Me.I(Of A)dem, aber alle Mitglieder, auf die darauf zugegriffen wird, werden behandelt, als ob die Mitglieder nicht außer Kraft gesetzt werden können. Der Zugriff auf das Mitglied wird daher nicht von dem Laufzeittyp des Werts beeinflusst, auf den das Mitglied zugegriffen wird.
Ein Memberzugriff des Formulars MyBase.I(Of A) entspricht CType(Me, T).I(Of A)T dem direkten Basistyp des Typs, der den Elementzugriffsausdruck enthält. Alle Methodenaufrufe werden behandelt, als ob die aufgerufene Methode nicht überschrieben werden kann. Diese Form des Mitgliedszugriffs wird auch als Basiszugriff bezeichnet.
Im folgenden Beispiel wird veranschaulicht, wie und MyClass in MeMyBase beziehungsweise:
Class Base
Public Overridable Sub F()
Console.WriteLine("Base.F")
End Sub
End Class
Class Derived
Inherits Base
Public Overrides Sub F()
Console.WriteLine("Derived.F")
End Sub
Public Sub G()
MyClass.F()
End Sub
End Class
Class MoreDerived
Inherits Derived
Public Overrides Sub F()
Console.WriteLine("MoreDerived.F")
End Sub
Public Sub H()
MyBase.F()
End Sub
End Class
Module Test
Sub Main()
Dim x As MoreDerived = new MoreDerived()
x.F()
x.G()
x.H()
End Sub
End Module
Dieser Code druckt aus:
MoreDerived.F
Derived.F
Derived.F
Wenn ein Memberzugriffsausdruck mit dem Schlüsselwort beginnt, stellt das Schlüsselwort Globalden äußerst unbenannten Namespace dar, der in Situationen nützlich ist, in denen eine Deklaration einen umschließenden Namespace schattiert. Das Global Schlüsselwort ermöglicht in dieser Situation das Ausfangen von "escaping" in den äußersten Namespace. Beispiel:
Class System
End Class
Module Test
Sub Main()
' Error: Class System does not contain Console
System.Console.WriteLine("Hello, world!")
' Legal, binds to System in outermost namespace
Global.System.Console.WriteLine("Hello, world!")
End Sub
End Module
Im obigen Beispiel ist der erste Methodenaufruf ungültig, da der Bezeichner System an die Klasse Systemund nicht an den Namespace Systemgebunden ist. Die einzige Möglichkeit für den Zugriff auf den System Namespace besteht darin, das Escapezeichen für den äußersten Namespace zu verwenden Global .
Wenn der Zugriff auf das Mitglied freigegeben ist, ist jeder Ausdruck auf der linken Seite des Zeitraums überflüssig und wird erst ausgewertet, wenn der Memberzugriff spät gebunden ist. Betrachten Sie z. B. den folgenden Code:
Class C
Public Shared F As Integer = 10
End Class
Module Test
Public Function ReturnC() As C
Console.WriteLine("Returning a new instance of C.")
Return New C()
End Function
Public Sub Main()
Console.WriteLine("The value of F is: " & ReturnC().F)
End Sub
End Module
Sie wird gedruckt The value of F is: 10 , da die Funktion ReturnC nicht aufgerufen werden muss, um eine Instanz des C Zugriffs auf das freigegebene Element Fbereitzustellen.
Identische Typ- und Membernamen
Es ist nicht ungewöhnlich, Member mit demselben Namen wie ihren Typ zu benennen. In dieser Situation kann jedoch unannescher Name ausgeblendet werden:
Enum Color
Red
Green
Yellow
End Enum
Class Test
ReadOnly Property Color() As Color
Get
Return Color.Red
End Get
End Property
Shared Function DefaultColor() As Color
Return Color.Green ' Binds to the instance property!
End Function
End Class
Im vorherigen Beispiel wird der einfache Name ColorDefaultColor anstelle des Typs an die Instanzeigenschaft gebunden. Da auf ein Instanzmitglied in einem freigegebenen Mitglied nicht verwiesen werden kann, ist dies normalerweise ein Fehler.
Eine spezielle Regel ermöglicht jedoch den Zugriff auf den Typ in diesem Fall. Wenn der Basisausdruck eines Elementzugriffsausdrucks ein einfacher Name ist und an eine Konstante, ein Feld, eine Eigenschaft, eine lokale Variable oder einen Parameter gebunden ist, deren Typ denselben Namen hat, kann der Basisausdruck entweder auf das Element oder den Typ verweisen. Dies kann niemals zu Mehrdeutigkeit führen, da die Mitglieder, auf die von beiden zugegriffen werden kann, identisch sind.
Standardinstanzen
In manchen Situationen verfügen Klassen, die von einer gemeinsamen Basisklasse abgeleitet wurden, normalerweise oder immer nur über eine einzelne Instanz. Die meisten Fenster, die auf einer Benutzeroberfläche angezeigt werden, haben beispielsweise immer nur eine Instanz, die auf dem Bildschirm angezeigt wird. Um die Arbeit mit diesen Klassentypen zu vereinfachen, kann Visual Basic automatisch Standardinstanzen der Klassen generieren, die eine einzelne, einfach referenzierte Instanz für jede Klasse bereitstellen.
Standardinstanzen werden immer für eine Familie von Typen und nicht für einen bestimmten Typ erstellt. Anstatt also eine Standardinstanz für eine Klasse "Form1" zu erstellen, die von "Form" abgeleitet wird, werden Standardinstanzen für alle von Form abgeleiteten Klassen erstellt. Dies bedeutet, dass jede einzelne Klasse, die von der Basisklasse abgeleitet wird, nicht speziell gekennzeichnet werden muss, damit sie über eine Standardinstanz verfügt.
Die Standardinstanz einer Klasse wird durch eine compilergenerierte Eigenschaft dargestellt, die die Standardinstanz dieser Klasse zurückgibt. Die Eigenschaft, die als Mitglied einer Klasse generiert wurde, die die Gruppenklasse genannt wird, die das Zuweisen und Zerstören von Standardinstanzen für alle Klassen verwaltet, die von der jeweiligen Basisklasse abgeleitet wurden. Beispielsweise können alle Standardinstanzeigenschaften von Klassen, die von Form der Klasse abgeleitet werden, in der MyForms Klasse erfasst werden. Wenn eine Instanz der Gruppenklasse vom Ausdruck My.Formszurückgegeben wird, greift der folgende Code auf die Standardinstanzen abgeleiteter Klassen Form1 zu und Form2:
Class Form1
Inherits Form
Public x As Integer
End Class
Class Form2
Inherits Form
Public y As Integer
End Class
Module Main
Sub Main()
My.Forms.Form1.x = 10
Console.WriteLine(My.Forms.Form2.y)
End Sub
End Module
Standardinstanzen werden erst erstellt, wenn der erste Verweis darauf erfolgt; Das Abrufen der Eigenschaft, die die Standardinstanz darstellt, bewirkt, dass die Standardinstanz erstellt wird, wenn sie noch nicht erstellt wurde oder auf Nothing. Um tests für das Vorhandensein einer Standardinstanz zuzulassen, wird die Standardinstanz nicht erstellt, wenn eine Standardinstanz das Ziel eines Is oder IsNot eines Operators ist. Daher ist es möglich, zu testen, ob eine Standardinstanz oder ein anderer Verweis ist Nothing , ohne dass die Standardinstanz erstellt wird.
Standardinstanzen sollen das Verweisen auf die Standardinstanz von außerhalb der Klasse erleichtern, die über die Standardinstanz verfügt. Die Verwendung einer Standardinstanz aus einer Klasse, die sie definiert, kann Verwirrung verursachen, auf welche Instanz verwiesen wird, d. h. die Standardinstanz oder die aktuelle Instanz. Der folgende Code ändert beispielsweise nur den Wert x in der Standardinstanz, obwohl er von einer anderen Instanz aufgerufen wird. Daher würde der Code den Wert 5 anstelle von 10:
Class Form1
Inherits Form
Public x As Integer = 5
Public Sub ChangeX()
Form1.x = 10
End Sub
End Class
Module Main
Sub Main()
Dim f As Form1 = New Form1()
f.ChangeX()
Console.WriteLine(f.x)
End Sub
End Module
Um diese Art von Verwirrung zu verhindern, ist es ungültig, auf eine Standardinstanz innerhalb einer Instanzmethode des Standardinstanztyps zu verweisen.
Standardinstanzen und Typnamen
Auf eine Standardinstanz kann auch direkt über den Namen des Typs zugegriffen werden. In diesem Fall wird in einem Ausdruckskontext, in dem der Typname nicht zulässig ist, der ausdruck E, wobei E der vollqualifizierte Name der Klasse mit einer Standardinstanz geändert E'wird, wobei E' ein Ausdruck steht, der die Standardinstanzeigenschaft abruft. Wenn z. B. Standardinstanzen für klassen, die vom Form Zugriff auf die Standardinstanz über den Typnamen abgeleitet wurden, entspricht der folgende Code dem Code im vorherigen Beispiel:
Module Main
Sub Main()
Form1.x = 10
Console.WriteLine(Form2.y)
End Sub
End Module
Dies bedeutet auch, dass eine Standardinstanz, auf die über den Namen des Typs zugegriffen werden kann, auch über den Typnamen zugewiesen werden kann. Mit dem folgenden Code wird z. B. die Standardinstanz von Form1 :Nothing
Module Main
Sub Main()
Form1 = Nothing
End Sub
End Module
Beachten Sie, dass die Bedeutung einer E.I Klasse I und ein freigegebenes Mitglied nicht geändert wirdE. Ein solcher Ausdruck greift weiterhin direkt von der Klasseninstanz auf das freigegebene Element zu und verweist nicht auf die Standardinstanz.
Gruppenklassen
Das Microsoft.VisualBasic.MyGroupCollectionAttribute Attribut gibt die Gruppenklasse für eine Familie von Standardinstanzen an. Das Attribut hat vier Parameter:
Der Parameter
TypeToCollectgibt die Basisklasse für die Gruppe an. Alle instanziierbaren Klassen ohne offene Typparameter, die von einem Typ mit diesem Namen abgeleitet werden (unabhängig von Typparametern), weisen automatisch eine Standardinstanz auf.Der Parameter
CreateInstanceMethodNamegibt die Methode an, die in der Gruppenklasse aufgerufen werden soll, um eine neue Instanz in einer Standardinstanzeigenschaft zu erstellen.Der Parameter
DisposeInstanceMethodNamegibt die Methode an, die in der Gruppenklasse aufgerufen werden soll, um eine Standardinstanzeigenschaft zu löschen, wenn der WertNothingder Standardinstanzeigenschaft zugewiesen wird.Der Parameter
DefaultInstanceAliasgibt den AusdruckE'an, der durch den Klassennamen ersetzt werden soll, wenn auf die Standardinstanzen direkt über ihren Typnamen zugegriffen werden kann. Wenn dieser Parameter oder eine leere Zeichenfolge istNothing, können Standardinstanzen für diesen Gruppentyp nicht direkt über den Namen des Typs zugegriffen werden. (Hinweis. In allen aktuellen Implementierungen der Visual Basic-Sprache wird derDefaultInstanceAliasParameter ignoriert, mit Ausnahme des vom Compiler bereitgestellten Codes.)
Mehrere Typen können in derselben Gruppe gesammelt werden, indem die Namen der Typen und Methoden in den ersten drei Parametern mithilfe von Kommas getrennt werden. Es muss die gleiche Anzahl von Elementen in jedem Parameter vorhanden sein, und die Listenelemente werden in der Reihenfolge übereinstimmen. Die folgende Attributdeklaration sammelt z. B. Typen, die von C1oder C2C3 zu einer einzelnen Gruppe abgeleitet werden:
<Microsoft.VisualBasic.MyGroupCollection("C1, C2, C3", _
"CreateC1, CreateC2, CreateC3", _
"DisposeC1, DisposeC2, DisposeC3", "My.Cs")>
Public NotInheritable Class MyCs
...
End Class
Die Signatur der Create-Methode muss dem Formular Shared Function <Name>(Of T As {New, <Type>})(Instance Of T) As Tzugeordnet sein. Die Dispose-Methode muss das Formular Shared Sub <Name>(Of T As <Type>)(ByRef Instance Of T)sein. Daher kann die Gruppenklasse für das Beispiel im vorherigen Abschnitt wie folgt deklariert werden:
<Microsoft.VisualBasic.MyGroupCollection("Form", "Create", _
"Dispose", "My.Forms")> _
Public NotInheritable Class MyForms
Private Shared Function Create(Of T As {New, Form}) _
(Instance As T) As T
If Instance Is Nothing Then
Return New T()
Else
Return Instance
End If
End Function
Private Shared Sub Dispose(Of T As Form)(ByRef Instance As T)
Instance.Close()
Instance = Nothing
End Sub
End Class
Wenn eine Quelldatei eine abgeleitete Klasse Form1deklariert hat, entspricht die generierte Gruppenklasse folgendem:
<Microsoft.VisualBasic.MyGroupCollection("Form", "Create", _
"Dispose", "My.Forms")> _
Public NotInheritable Class MyForms
Private Shared Function Create(Of T As {New, Form}) _
(Instance As T) As T
If Instance Is Nothing Then
Return New T()
Else
Return Instance
End If
End Function
Private Shared Sub Dispose(Of T As Form)(ByRef Instance As T)
Instance.Close()
Instance = Nothing
End Sub
Private m_Form1 As Form1
Public Property Form1() As Form1
Get
Return Create(m_Form1)
End Get
Set (Value As Form1)
If Value IsNot Nothing AndAlso Value IsNot m_Form1 Then
Throw New ArgumentException( _
"Property can only be set to Nothing.")
End If
Dispose(m_Form1)
End Set
End Property
End Class
Extension-Methodenauflistung
Erweiterungsmethoden für den Memberzugriffsausdruck E.I werden gesammelt, indem alle Erweiterungsmethoden mit dem Namen I erfasst werden, der im aktuellen Kontext verfügbar ist:
- Zunächst wird jeder geschachtelte Typ, der den Ausdruck enthält, überprüft, beginnend am innersten und am äußersten Rand.
- Anschließend wird jeder geschachtelte Namespace überprüft, beginnend vom innersten und zum äußersten Namespace.
- Anschließend werden die Importe in der Quelldatei überprüft.
- Anschließend werden die von der Kompilierungsumgebung definierten Importe überprüft.
Eine Erweiterungsmethode wird nur gesammelt, wenn eine erweiterungseigene Konvertierung vom Zielausdruckstyp auf den Typ des ersten Parameters der Erweiterungsmethode vorhanden ist. Und im Gegensatz zur Bindung regulärer einfacher Namensausdrücke sammelt die Suche alle Erweiterungsmethoden; die Auflistung wird nicht beendet, wenn eine Erweiterungsmethode gefunden wird. Beispiel:
Imports System.Runtime.CompilerServices
Class C1
End Class
Namespace N1
Module N1C1Extensions
<Extension> _
Sub M1(c As C1, x As Integer)
End Sub
End Module
End Namespace
Namespace N1.N2
Module N2C1Extensions
<Extension> _
Sub M1(c As C1, y As Double)
End Sub
End Module
End Namespace
Namespace N1.N2.N3
Module Test
Sub Main()
Dim x As New C1()
' Calls N1C1Extensions.M1
x.M1(10)
End Sub
End Module
End Namespace
In diesem Beispiel werden beide als Erweiterungsmethoden betrachtet, obwohl N2C1Extensions.M1 sie vorher N1C1Extensions.M1gefunden werden. Sobald alle Erweiterungsmethoden gesammelt wurden, werden sie dann verhärtet. Das Ziel des Aufrufs der Erweiterungsmethode übernimmt Das Ziel des Erweiterungsmethodeaufrufs und wendet es auf den Aufruf der Erweiterungsmethode an, wodurch eine neue Methodensignatur mit dem ersten entfernten Parameter entsteht (da er angegeben wurde). Beispiel:
Imports System.Runtime.CompilerServices
Module Ext1
<Extension> _
Sub M(x As Integer, y As Integer)
End Sub
End Module
Module Ext2
<Extension> _
Sub M(x As Integer, y As Double)
End Sub
End Module
Module Main
Sub Test()
Dim v As Integer = 10
' The curried method signatures considered are:
' Ext1.M(y As Integer)
' Ext2.M(y As Double)
v.M(10)
End Sub
End Module
Im obigen Beispiel ist das geschweifte Ergebnis der Anwendung vExt1.M auf die Methodensignatur Sub M(y As Integer).
Neben dem Entfernen des ersten Parameters der Erweiterungsmethode werden auch alle Methodentypparameter entfernt, die Teil des Typs des ersten Parameters sind. Beim Anwenden einer Erweiterungsmethode mit dem Methodentypparameter wird die Typableitung auf den ersten Parameter angewendet, und das Ergebnis wird für alle abgeleiteten Typparameter festgelegt. Wenn typinference fehlschlägt, wird die Methode ignoriert. Beispiel:
Imports System.Runtime.CompilerServices
Module Ext1
<Extension> _
Sub M(Of T, U)(x As T, y As U)
End Sub
End Module
Module Ext2
<Extension> _
Sub M(Of T)(x As T, y As T)
End Sub
End Module
Module Main
Sub Test()
Dim v As Integer = 10
' The curried method signatures considered are:
' Ext1.M(Of U)(y As U)
' Ext2.M(y As Integer)
v.M(10)
End Sub
End Module
Im obigen Beispiel ist das geschiebte Ergebnis der Anwendung v auf Ext1.M die Methodensignatur Sub M(Of U)(y As U), da der Typparameter T als Ergebnis der Formatierung abgeleitet und jetzt behoben ist. Da der Typparameter U nicht als Teil der Ctrling abgeleitet wurde, bleibt er ein offener Parameter. Da der Typparameter T aufgrund der Anwendung vExt2.Mauf den Parameter abgeleitet wird, wird der Parametertyp y wie folgt festgelegt.Integer Er wird nicht als anderer Typ abgeleitet. Bei der Signatur werden auch alle Einschränkungen angewendet, mit Ausnahme von New Einschränkungen. Wenn die Einschränkungen nicht erfüllt sind oder von einem Typ abhängig sind, der nicht als Teil von Ctrling abgeleitet wurde, wird die Erweiterungsmethode ignoriert. Beispiel:
Imports System.Runtime.CompilerServices
Module Ext1
<Extension> _
Sub M1(Of T As Structure)(x As T, y As Integer)
End Sub
<Extension> _
Sub M2(Of T As U, U)(x As T, y As U)
End Sub
End Module
Module Main
Sub Test()
Dim s As String = "abc"
' Error: String does not satisfy the Structure constraint
s.M1(10)
' Error: T depends on U, which cannot be inferred
s.M2(10)
End Sub
End Module
Hinweis: Einer der Hauptgründe für die Ausführung von Erweiterungsmethoden besteht darin, dass Abfrageausdrücke den Typ der Iteration ableiten können, bevor die Argumente auf eine Abfragemustermethode ausgewertet werden. Da die meisten Abfragemustermethoden Lambda-Ausdrücke verwenden, die eine Typausleitung selbst erfordern, vereinfacht dies den Prozess der Auswertung eines Abfrageausdrucks erheblich.
Im Gegensatz zur normalen Schnittstellenvererbung sind Erweiterungsmethoden, die zwei Schnittstellen erweitern, die nicht miteinander in Beziehung stehen, verfügbar, solange sie nicht über dieselbe curriede Signatur verfügen:
Imports System.Runtime.CompilerServices
Interface I1
End Interface
Interface I2
End Interface
Class C1
Implements I1, I2
End Class
Module I1Ext
<Extension> _
Sub M1(i As I1, x As Integer)
End Sub
<Extension> _
Sub M2(i As I1, x As Integer)
End Sub
End Module
Module I2Ext
<Extension> _
Sub M1(i As I2, x As Integer)
End Sub
<Extension> _
Sub M2(I As I2, x As Double)
End Sub
End Module
Module Main
Sub Test()
Dim c As New C1()
' Error: M is ambiguous between I1Ext.M1 and I2Ext.M1.
c.M1(10)
' Calls I1Ext.M2
c.M2(10)
End Sub
End Module
Schließlich ist es wichtig zu beachten, dass Erweiterungsmethoden bei verspäteter Bindung nicht berücksichtigt werden:
Module Test
Sub Main()
Dim o As Object = ...
' Ignores extension methods
o.M1()
End Sub
End Module
Zugriffsausdrücke für Wörterbuchmitglieder
Ein Wörterbuchelementzugriffsausdruck wird verwendet, um ein Element einer Auflistung nachzuschlagen. Ein Wörterbuchmemembezugriff hat die Form von E!I, wobei E es sich um einen Ausdruck handelt, der als Wert klassifiziert wird und I ein Bezeichner ist.
DictionaryAccessExpression
: Expression? '!' IdentifierOrKeyword
;
Der Typ des Ausdrucks muss über eine Standardeigenschaft verfügen, die von einem einzelnen String Parameter indiziert wird. Der Zugriffsausdruck E!I des Wörterbuchelements wird in den Ausdruck E.D("I")transformiert. D Dabei handelt es sich um die Standardeigenschaft von E. Beispiel:
Class Keys
Public ReadOnly Default Property Item(s As String) As Integer
Get
Return 10
End Get
End Property
End Class
Module Test
Sub Main()
Dim x As Keys = new Keys()
Dim y As Integer
' The two statements are equivalent.
y = x!abc
y = x("abc")
End Sub
End Module
Wenn ein Ausrufezeichen ohne Ausdruck angegeben wird, wird der Ausdruck aus der unmittelbar enthaltenden With Anweisung angenommen. Wenn keine enthaltende With Anweisung vorhanden ist, tritt ein Kompilierungszeitfehler auf.
Aufrufausdrücke
Ein Aufrufausdruck besteht aus einem Aufrufziel und einer optionalen Argumentliste.
InvocationExpression
: Expression ( OpenParenthesis ArgumentList? CloseParenthesis )?
;
ArgumentList
: PositionalArgumentList
| PositionalArgumentList Comma NamedArgumentList
| NamedArgumentList
;
PositionalArgumentList
: Expression? ( Comma Expression? )*
;
NamedArgumentList
: IdentifierOrKeyword ColonEquals Expression
( Comma IdentifierOrKeyword ColonEquals Expression )*
;
Der Zielausdruck muss als Methodengruppe oder als Wert klassifiziert werden, dessen Typ ein Delegattyp ist. Wenn der Zielausdruck ein Wert ist, dessen Typ ein Delegattyp ist, wird das Ziel des Aufrufausdrucks zur Methodengruppe für das Invoke Mitglied des Delegatentyps, und der Zielausdruck wird zum zugeordneten Zielausdruck der Methodengruppe.
Eine Argumentliste weist zwei Abschnitte auf: Positionsargumente und benannte Argumente.
Positionsargumente sind Ausdrücke und müssen vor benannten Argumenten stehen.
Benannte Argumente beginnen mit einem Bezeichner, der Schlüsselwörtern entsprechen kann, gefolgt von := und einem Ausdruck.
Wenn die Methodengruppe nur eine barrierefreie Methode enthält, einschließlich Instanz- und Erweiterungsmethoden, und diese Methode keine Argumente akzeptiert und eine Funktion ist, wird die Methodengruppe als Aufrufausdruck mit einer leeren Argumentliste interpretiert, und das Ergebnis wird als Ziel eines Aufrufausdrucks mit der angegebenen Argumentliste(n) verwendet. Beispiel:
Class C1
Function M1() As Integer()
Return New Integer() { 1, 2, 3 }
End Sub
End Class
Module Test
Sub Main()
Dim c As New C1()
' Prints 3
Console.WriteLine(c.M1(2))
End Sub
End Module
Andernfalls wird die Überladungsauflösung auf die Methoden angewendet, um die am besten geeignete Methode für die angegebene Argumentliste(n) auszuwählen. Wenn die am besten geeignete Methode eine Funktion ist, wird das Ergebnis des Aufrufausdrucks als Wert klassifiziert, der als Rückgabetyp der Funktion eingegeben wird. Wenn es sich bei der am besten zutreffenden Methode um eine Unterroutine handelt, wird das Ergebnis als "void" klassifiziert. Wenn es sich bei der am besten zutreffenden Methode um eine partielle Methode ohne Text handelt, wird der Aufrufausdruck ignoriert, und das Ergebnis wird als "void" klassifiziert.
Bei einem früh gebundenen Aufrufausdruck werden die Argumente in der Reihenfolge ausgewertet, in der die entsprechenden Parameter in der Zielmethode deklariert werden. Bei einem spät gebundenen Elementzugriffsausdruck werden sie in der Reihenfolge ausgewertet, in der sie im Elementzugriffsausdruck angezeigt werden: siehe Abschnitt Late-Bound Ausdrücke.
Auflösung überladener Methoden:
For Overload Resolution, Specificity of members/types given an argument list, Genericity, Applicability to Argument List, Passing Arguments, and Pick Arguments for Optional Parameters, Conditional Methods, and Type Argument Inference: see Section Overload Resolution.
Indexausdrücke
Ein Indexausdruck führt zu einem Arrayelement oder klassifiziert eine Eigenschaftsgruppe in einen Eigenschaftszugriff um. Ein Indexausdruck besteht aus einem Ausdruck in der Reihenfolge, einem Ausdruck, einer öffnenden Klammer, einer Indexargumentliste und einer schließenden Klammer.
IndexExpression
: Expression OpenParenthesis ArgumentList? CloseParenthesis
;
Das Ziel des Indexausdrucks muss entweder als Eigenschaftsgruppe oder als Wert klassifiziert werden. Ein Indexausdruck wird wie folgt verarbeitet:
Wenn der Zielausdruck als Wert klassifiziert wird und sein Typ kein Arraytyp ist,
ObjectoderSystem.Arraymuss der Typ über eine Standardeigenschaft verfügen. Der Index wird für eine Eigenschaftengruppe ausgeführt, die alle Standardeigenschaften des Typs darstellt. Obwohl es nicht gültig ist, eine parameterlose Standardeigenschaft in Visual Basic zu deklarieren, können andere Sprachen eine solche Eigenschaft deklarieren. Folglich ist das Indizieren einer Eigenschaft ohne Argumente zulässig.Wenn der Ausdruck zu einem Wert eines Arraytyps führt, muss die Anzahl der Argumente in der Argumentliste mit dem Rang des Arraytyps identisch sein und darf keine benannten Argumente enthalten. Wenn eine der Indizes zur Laufzeit ungültig ist, wird eine
System.IndexOutOfRangeExceptionAusnahme ausgelöst. Jeder Ausdruck muss implizit in TypIntegerkonvertierbar sein. Das Ergebnis des Indexausdrucks ist die Variable am angegebenen Index und wird als Variable klassifiziert.Wenn der Ausdruck als Eigenschaftsgruppe klassifiziert wird, wird die Überladungsauflösung verwendet, um zu bestimmen, ob eine der Eigenschaften auf die Indexargumentliste anwendbar ist. Wenn die Eigenschaftengruppe nur eine Eigenschaft enthält, die über einen
GetAccessor verfügt und dieser Accessor keine Argumente akzeptiert, wird die Eigenschaftengruppe als Indexausdruck mit einer leeren Argumentliste interpretiert. Das Ergebnis wird als Ziel des aktuellen Indexausdrucks verwendet. Wenn keine Eigenschaften vorhanden sind, tritt ein Kompilierungszeitfehler auf. Andernfalls führt der Ausdruck zu einem Eigenschaftszugriff mit dem zugeordneten Zielausdruck (sofern vorhanden) der Eigenschaftengruppe.Wenn der Ausdruck als spät gebundene Eigenschaftsgruppe oder als Wert klassifiziert wird, deren Typ oder Wert ist
ObjectoderSystem.Array, wird die Verarbeitung des Indexausdrucks bis zur Laufzeit zurückgestellt, und die Indizierung ist spät gebunden. Der Ausdruck führt zu einem spät gebundenen Eigenschaftszugriff, der alsObject. Der zugeordnete Zielausdruck ist entweder der Zielausdruck, wenn es sich um einen Wert oder den zugeordneten Zielausdruck der Eigenschaftsgruppe handelt. Zur Laufzeit wird der Ausdruck wie folgt verarbeitet:Wenn der Ausdruck als spät gebundene Eigenschaftsgruppe klassifiziert wird, kann der Ausdruck zu einer Methodengruppe, einer Eigenschaftsgruppe oder einem Wert führen (wenn es sich bei dem Element um eine Instanz oder eine freigegebene Variable handelt). Wenn es sich bei dem Ergebnis um eine Methodengruppe oder Eine Eigenschaftengruppe handelt, wird die Überladungsauflösung auf die Gruppe angewendet, um die richtige Methode für die Argumentliste zu ermitteln. Wenn die Überladungsauflösung fehlschlägt, wird eine
System.Reflection.AmbiguousMatchExceptionAusnahme ausgelöst. Anschließend wird das Ergebnis entweder als Eigenschaftszugriff oder als Aufruf verarbeitet, und das Ergebnis wird zurückgegeben. Wenn der Aufruf einer Unterroutine ist, lautetNothingdas Ergebnis .Wenn der Laufzeittyp des Zielausdrucks ein Arraytyp ist oder
System.Arraydas Ergebnis des Indexausdrucks der Wert der Variablen am angegebenen Index ist.Andernfalls muss der Laufzeittyp des Ausdrucks über eine Standardeigenschaft verfügen, und der Index wird für die Eigenschaftengruppe ausgeführt, die alle Standardeigenschaften des Typs darstellt. Wenn der Typ keine Standardeigenschaft aufweist, wird eine
System.MissingMemberExceptionAusnahme ausgelöst.
Neue Ausdrücke
Der New-Operator wird verwendet, um neue Instanzen von Typen zu erstellen. Es gibt vier Arten von New Ausdrücken:
Objekterstellungsausdrücke werden verwendet, um neue Instanzen von Klassentypen und Werttypen zu erstellen.
Arrayerstellungsausdrücke werden verwendet, um neue Instanzen von Arraytypen zu erstellen.
Stellvertretungs-Erstellungsausdrücke (die keine unterschiedliche Syntax von Objekterstellungsausdrücken aufweisen) werden verwendet, um neue Instanzen von Delegatentypen zu erstellen.
Anonyme Objekterstellungsausdrücke werden verwendet, um neue Instanzen anonymer Klassentypen zu erstellen.
NewExpression
: ObjectCreationExpression
| ArrayExpression
| AnonymousObjectCreationExpression
;
Ein New Ausdruck wird als Wert klassifiziert, und das Ergebnis ist die neue Instanz des Typs.
Object-Creation Ausdrücke
Ein Objekterstellungsausdruck wird verwendet, um eine neue Instanz eines Klassentyps oder eines Strukturtyps zu erstellen.
ObjectCreationExpression
: 'New' NonArrayTypeName ( OpenParenthesis ArgumentList? CloseParenthesis )?
ObjectCreationExpressionInitializer?
;
ObjectCreationExpressionInitializer
: ObjectMemberInitializer
| ObjectCollectionInitializer
;
ObjectMemberInitializer
: 'With' OpenCurlyBrace FieldInitializerList CloseCurlyBrace
;
FieldInitializerList
: FieldInitializer ( Comma FieldInitializer )*
;
FieldInitializer
: 'Key'? ('.' IdentifierOrKeyword Equals )? Expression
;
ObjectCollectionInitializer
: 'From' CollectionInitializer
;
CollectionInitializer
: OpenCurlyBrace CollectionElementList? CloseCurlyBrace
;
CollectionElementList
: CollectionElement ( Comma CollectionElement )*
;
CollectionElement
: Expression
| CollectionInitializer
;
Der Typ eines Objekterstellungsausdrucks muss ein Klassentyp, ein Strukturtyp oder ein Typparameter mit einer New Einschränkung sein und darf keine Klasse sein MustInherit . Angesichts eines Objekterstellungsausdrucks des Formulars New T(A), wobei T es sich um einen Klassentyp oder Strukturtyp handelt und A eine optionale Argumentliste ist, bestimmt die Überladungsauflösung den richtigen Konstruktor des T Aufrufs. Ein Typparameter mit einer New Einschränkung wird als einzelner parameterloser Konstruktor betrachtet. Wenn kein Konstruktor aufgerufen werden kann, tritt ein Kompilierungszeitfehler auf; andernfalls führt der Ausdruck zur Erstellung einer neuen Instanz der Verwendung des T ausgewählten Konstruktors. Wenn keine Argumente vorhanden sind, werden die Klammern möglicherweise weggelassen.
Wo eine Instanz zugewiesen wird, hängt davon ab, ob es sich bei der Instanz um einen Klassentyp oder einen Werttyp handelt.
New Instanzen von Klassentypen werden auf dem System heap erstellt, während neue Instanzen von Werttypen direkt im Stapel erstellt werden.
Ein Objekterstellungsausdruck kann optional eine Liste von Memberinitialisierern nach den Konstruktorargumenten angeben. Diese Memberinitialisierer werden dem Schlüsselwort Withvorangestellt, und die Initialisierungsliste wird so interpretiert, als ob sie sich im Kontext einer With Anweisung befand. Beispiel:
Class Customer
Dim Name As String
Dim Address As String
End Class
Der Code:
Module Test
Sub Main()
Dim x As New Customer() With { .Name = "Bob Smith", _
.Address = "123 Main St." }
End Sub
End Module
Entspricht ungefähr folgendem:
Module Test
Sub Main()
Dim x, _t1 As Customer
_t1 = New Customer()
With _t1
.Name = "Bob Smith"
.Address = "123 Main St."
End With
x = _t1
End Sub
End Module
Jeder Initialisierer muss einen Namen angeben, der zugewiesen werden soll, und der Name muss eine NichtinstanzvariableReadOnly oder Eigenschaft des zu erstellenden Typs sein. Der Memberzugriff wird nicht verspätet gebunden, wenn der erstellte Typ ist Object. Initialisierer verwenden möglicherweise nicht das Key Schlüsselwort. Jedes Element in einem Typ kann nur einmal initialisiert werden. Die Initialisierungsausdrücke können sich jedoch gegenseitig beziehen. Beispiel:
Module Test
Sub Main()
Dim x As New Customer() With { .Name = "Bob Smith", _
.Address = .Name & " St." }
End Sub
End Module
Die Initialisierer werden von links nach rechts zugewiesen. Wenn sich ein Initialisierer also auf ein Element bezieht, das noch nicht initialisiert wurde, wird der Wert der Instanzvariable angezeigt, nachdem der Konstruktor ausgeführt wurde:
Module Test
Sub Main()
' The value of Address will be " St." since Name has not been
' assigned yet.
Dim x As New Customer() With { .Address = .Name & " St." }
End Sub
End Module
Initialisierer können geschachtelt werden:
Class Customer
Dim Name As String
Dim Address As Address
Dim Age As Integer
End Class
Class Address
Dim Street As String
Dim City As String
Dim State As String
Dim ZIP As String
End Class
Module Test
Sub Main()
Dim c As New Customer() With { _
.Name = "John Smith", _
.Address = New Address() With { _
.Street = "23 Main St.", _
.City = "Peoria", _
.State = "IL", _
.ZIP = "13934" }, _
.Age = 34 }
End Sub
End Module
Wenn der typ, der erstellt wird, ein Sammlungstyp ist und eine Instanzmethode namens Add (einschließlich Erweiterungsmethoden und freigegebener Methoden) aufweist, kann der Ausdruck für die Objekterstellung einen Sammlungsinitialisierer angeben, der durch das Schlüsselwort Frompräfixiert wird. Ein Objekterstellungsausdruck kann nicht sowohl einen Memberinitialisierer als auch einen Auflistungsinitialisierer angeben. Jedes Element in der Sammlungsinitialisierer wird als Argument an einen Aufruf der Add Funktion übergeben. Beispiel:
Dim list = New List(Of Integer)() From { 1, 2, 3, 4 }
entspricht:
Dim list = New List(Of Integer)()
list.Add(1)
list.Add(2)
list.Add(3)
Wenn es sich bei einem Element um einen Sammlungsinitialisierer selbst handelt, wird jedes Element des Subsammlungsinitialisierers als einzelnes Argument an die Add Funktion übergeben. Beispiel:
Dim dict = Dictionary(Of Integer, String) From { { 1, "One" },{ 2, "Two" } }
entspricht:
Dim dict = New Dictionary(Of Integer, String)
dict.Add(1, "One")
dict.Add(2, "Two")
Diese Erweiterung wird immer getan und wird immer nur eine Ebene tief gemacht; Danach werden Subinitialisierer als Arrayliterale betrachtet. Beispiel:
' Error: List(Of T) does not have an Add method that takes two parameters.
Dim list = New List(Of Integer())() From { { 1, 2 }, { 3, 4 } }
' OK, this initializes the dictionary with (Integer, Integer()) pairs.
Dim dict = New Dictionary(Of Integer, Integer())() From _
{ { 1, { 2, 3 } }, { 3, { 4, 5 } } }
Arrayausdrücke
Ein Arrayausdruck wird verwendet, um eine neue Instanz eines Arraytyps zu erstellen. Es gibt zwei Arten von Arrayausdrücken: Arrayerstellungsausdrücke und Arrayliterale.
Ausdrücke zur Array-Erstellung
Wenn ein Modifizierer für die Arraygröße angegeben wird, wird der resultierende Arraytyp durch Löschen der einzelnen Argumente aus der Initialisierungsargumentliste der Arraygröße abgeleitet. Der Wert jedes Arguments bestimmt die obere Grenze der entsprechenden Dimension in der neu zugeordneten Arrayinstanz. Wenn der Ausdruck über einen nicht leeren Auflistungsinitialisierer verfügt, muss jedes Argument in der Argumentliste eine Konstante sein, und die durch die Ausdrucksliste angegebenen Rang- und Bemaßungslängen müssen mit denen des Auflistungsinitialisierers übereinstimmen.
Dim a() As Integer = New Integer(2) {}
Dim b() As Integer = New Integer(2) { 1, 2, 3 }
Dim c(,) As Integer = New Integer(1, 2) { { 1, 2, 3 } , { 4, 5, 6 } }
' Error, length/initializer mismatch.
Dim d() As Integer = New Integer(2) { 0, 1, 2, 3 }
Wenn kein Modifizierer für die Initialisierung der Arraygröße angegeben wird, muss der Typname ein Arraytyp sein, und der Sammlungsinitialisierer muss leer sein oder dieselbe Anzahl von Schachtelungsebenen aufweisen wie der Rang des angegebenen Arraytyps. Alle Elemente in der innersten Schachtelungsebene müssen implizit in den Elementtyp des Arrays konvertierbar sein und als Wert klassifiziert werden. Die Anzahl der Elemente in jedem geschachtelten Sammlungsinitialisierer muss immer mit der Größe der anderen Auflistungen auf derselben Ebene übereinstimmen. Die einzelnen Dimensionslängen werden von der Anzahl der Elemente in jedem der entsprechenden Schachtelungsebenen des Sammlungsinitialisierers abgeleitet. Wenn der Sammlungsinitialisierer leer ist, ist die Länge jeder Dimension null.
Dim e() As Integer = New Integer() { 1, 2, 3 }
Dim f(,) As Integer = New Integer(,) { { 1, 2, 3 } , { 4, 5, 6 } }
' Error: Inconsistent numbers of elements!
Dim g(,) As Integer = New Integer(,) { { 1, 2 }, { 4, 5, 6 } }
' Error: Inconsistent levels of nesting!
Dim h(,) As Integer = New Integer(,) { 1, 2, { 3, 4 } }
Die äußerste Schachtelungsebene eines Auflistungsinitialisierers entspricht der äußersten Dimension eines Arrays, und die innerste Schachtelungsebene entspricht der äußersten Dimension ganz rechts. Das Beispiel:
Dim array As Integer(,) = _
{ { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, { 8, 9 } }
Entspricht folgendem:
Dim array(4, 1) As Integer
array(0, 0) = 0: array(0, 1) = 1
array(1, 0) = 2: array(1, 1) = 3
array(2, 0) = 4: array(2, 1) = 5
array(3, 0) = 6: array(3, 1) = 7
array(4, 0) = 8: array(4, 1) = 9
Wenn der Sammlungsinitialisierer leer ist (d. h. eine, die geschweifte Klammern, aber keine Initialisierungsliste enthält) und die Grenzen der Dimensionen des arrays, das initialisiert wird, bekannt sind, stellt der leere Sammlungsinitialisierer eine Arrayinstanz der angegebenen Größe dar, in der alle Elemente auf den Standardwert des Elementtyps initialisiert wurden. Wenn die Grenzen der Dimensionen des arrays, das initialisiert wird, nicht bekannt sind, stellt der leere Auflistungsinitialisierer eine Arrayinstanz dar, in der alle Dimensionen Die Größe Null sind.
Die Rang- und Länge einer Arrayinstanz sind für die gesamte Lebensdauer der Instanz konstant. Mit anderen Worten, es ist nicht möglich, den Rang einer vorhandenen Arrayinstanz zu ändern, noch ist es möglich, die Größe seiner Dimensionen zu ändern.
Arrayliterale
Ein Arrayliteral gibt ein Array an, dessen Elementtyp, Rang und Grenzen aus einer Kombination aus dem Ausdruckskontext und einem Sammlungsinitialisierer abgeleitet werden. Dies wird in Abschnittsausdruck-Neuklassifizierung erläutert.
ArrayExpression
: ArrayCreationExpression
| ArrayLiteralExpression
;
ArrayCreationExpression
: 'New' NonArrayTypeName ArrayNameModifier CollectionInitializer
;
ArrayLiteralExpression
: CollectionInitializer
;
Beispiel:
' array of integers
Dim a = {1, 2, 3}
' array of shorts
Dim b = {1S, 2S, 3S}
' array of shorts whose type is taken from the context
Dim c As Short() = {1, 2, 3}
' array of type Integer(,)
Dim d = {{1, 0}, {0, 1}}
' jagged array of rank ()()
Dim e = {({1, 0}), ({0, 1})}
' error: inconsistent rank
Dim f = {{1}, {2, 3}}
' error: inconsistent rank
Dim g = {1, {2}}
Das Format und die Anforderungen für den Sammlungsinitialisierer in einem Arrayliteral entsprechen genau dem für den Sammlungsinitialisierer in einem Arrayerstellungsausdruck.
Hinweis: Ein Arrayliteral erstellt das Array nicht in und von sich selbst; Stattdessen handelt es sich um die Neuklassifizierung des Ausdrucks in einen Wert, der dazu führt, dass das Array erstellt wird. Die Konvertierung CType(new Integer() {1,2,3}, Short()) ist beispielsweise nicht möglich, da keine Konvertierung von Integer() zu Short(); der Ausdruck CType({1,2,3},Short()) ist jedoch möglich, da das Arrayliteral zuerst in den Ausdruck für die Arrayerstellung New Short() {1,2,3}neu klassifiziert wird.
Delegate-Creation Ausdrücke
Ein Stellvertretungsausdruck wird verwendet, um eine neue Instanz eines Delegatentyps zu erstellen. Das Argument eines Delegaterstellungsausdrucks muss ein Ausdruck sein, der als Methodenzeiger oder Lambda-Methode klassifiziert ist.
Wenn es sich bei dem Argument um einen Methodenzeiger handelt, muss eine der Methoden, auf die vom Methodenzeiger verwiesen wird, auf die Signatur des Delegatentyps angewendet werden. Eine Methode M gilt für einen Delegatentyp D , wenn:
Mist nichtPartialoder hat einen Körper.Beide
MundDsind Funktionen oderDeine Unterroutine.MundDdie gleiche Anzahl von Parametern aufweisen.Die Parametertypen jeder
Mweisen eine Konvertierung vom Typ des entsprechenden ParametertypsDund deren Modifizierer (d. h.ByRef,ByVal) ab.Der Rückgabetyp von
M, falls vorhanden, hat eine Konvertierung in den Rückgabetyp vonD.
Wenn der Methodenzeiger auf einen spät gebundenen Zugriff verweist, wird davon ausgegangen, dass der spät gebundene Zugriff auf eine Funktion mit der gleichen Anzahl von Parametern wie der Delegattyp verwendet wird.
Wenn keine strenge Semantik verwendet wird und nur eine Methode vom Methodenzeiger referenziert wird, aber nicht anwendbar ist, da sie keine Parameter enthält und der Delegattyp ausgeführt wird, wird die Methode als zutreffend betrachtet, und die Parameter oder der Rückgabewert werden einfach ignoriert. Beispiel:
Delegate Sub F(x As Integer)
Module Test
Sub M()
End Sub
Sub Main()
' Valid
Dim x As F = AddressOf M
End Sub
End Module
Hinweis: Diese Entspannung ist nur zulässig, wenn strenge Semantik aufgrund von Erweiterungsmethoden nicht verwendet wird. Da Erweiterungsmethoden nur berücksichtigt werden, wenn eine reguläre Methode nicht anwendbar war, ist es möglich, dass eine Instanzmethode ohne Parameter eine Erweiterungsmethode mit Parametern für den Zweck der Stellvertretungskonstruktion ausblenden kann.
Wenn mehr als eine Methode, auf die vom Methodenzeiger verwiesen wird, auf den Delegattyp angewendet wird, wird die Überladungsauflösung verwendet, um zwischen den Kandidatenmethoden auszuwählen. Die Typen der Parameter für den Delegaten werden als Typen der Argumente für die Überladungsauflösung verwendet. Wenn kein Methodenkandidat am besten geeignet ist, tritt ein Kompilierungszeitfehler auf. Im folgenden Beispiel wird die lokale Variable mit einem Delegaten initialisiert, der auf die zweite Square Methode verweist, da diese Methode für die Signatur und den Rückgabetyp von DoubleFunc.
Delegate Function DoubleFunc(x As Double) As Double
Module Test
Function Square(x As Single) As Single
Return x * x
End Function
Function Square(x As Double) As Double
Return x * x
End Function
Sub Main()
Dim a As New DoubleFunc(AddressOf Square)
End Sub
End Module
Wäre die zweite Square Methode nicht vorhanden gewesen, wäre die erste Square Methode gewählt worden. Wenn strenge Semantik durch die Kompilierungsumgebung oder durch Option Strictdie Kompilierung angegeben wird, tritt ein Kompilierungszeitfehler auf, wenn die spezifischste Methode, auf die der Methodenzeiger verweist, schmaler als die Stellvertretungssignatur ist. Eine Methode M gilt als schmaler als ein Delegattyp D , wenn:
Ein Parametertyp hat
Meine Erweiterungskonvertierung auf den entsprechenden Parametertyp vonD.Oder der Rückgabetyp , falls vorhanden, von
Meiner schmalen Konvertierung in den Rückgabetyp vonD.
Wenn Typargumente dem Methodenzeiger zugeordnet sind, werden nur Methoden mit derselben Anzahl von Typargumenten berücksichtigt. Wenn dem Methodenzeiger keine Typargumente zugeordnet sind, wird die Typableitung beim Abgleichen von Signaturen mit einer generischen Methode verwendet. Im Gegensatz zu anderen normalen Typrückschlüssen wird der Rückgabetyp des Delegaten beim Ableiten von Typargumenten verwendet, aber Rückgabetypen werden bei der Bestimmung der am wenigsten generischen Überladung noch nicht berücksichtigt. Das folgende Beispiel zeigt beide Methoden zum Bereitstellen eines Typarguments für einen Delegaterstellungsausdruck:
Delegate Function D(s As String, i As Integer) As Integer
Delegate Function E() As Integer
Module Test
Public Function F(Of T)(s As String, t1 As T) As T
End Function
Public Function G(Of T)() As T
End Function
Sub Main()
Dim d1 As D = AddressOf f(Of Integer) ' OK, type arg explicit
Dim d2 As D = AddressOf f ' OK, type arg inferred
Dim e1 As E = AddressOf g(Of Integer) ' OK, type arg explicit
Dim e2 As E = AddressOf g ' OK, infer from return
End Sub
End Module
Im obigen Beispiel wurde ein nicht generischer Delegattyp mithilfe einer generischen Methode instanziiert. Es ist auch möglich, eine Instanz eines konstruierten Delegattyps mithilfe einer generischen Methode zu erstellen. Beispiel:
Delegate Function Predicate(Of U)(u1 As U, u2 As U) As Boolean
Module Test
Function Compare(Of T)(t1 As List(of T), t2 As List(of T)) As Boolean
...
End Function
Sub Main()
Dim p As Predicate(Of List(Of Integer))
p = AddressOf Compare(Of Integer)
End Sub
End Module
Wenn das Argument für den Delegaterstellungsausdruck eine Lambda-Methode ist, muss die Lambda-Methode auf die Signatur des Delegatentyps angewendet werden. Eine Lambda-Methode L gilt für einen Delegatentyp D , wenn:
Wenn
LParameter vorhanden sind,Dweist die gleiche Anzahl von Parametern auf. (WennLkeine Parameter vorhanden sind, werden die ParameterDignoriert.)Die Parametertypen jeder
Lhaben eine Konvertierung in den Typ des entsprechenden Parametertyps undDderen Modifizierer (d. h.ByRef,ByVal) Übereinstimmung.Wenn
Des sich um eine Funktion handelt, hat der RückgabetypLeine Konvertierung in den Rückgabetyp vonD. (WennDes sich um eine Unterroutine handelt, wird der RückgabewertLignoriert.)
Wenn der Parametertyp eines Parameters ausgelassen L wird, wird der Typ des entsprechenden Parameters D abgeleitet. Wenn der Parameter L array- oder nullable Namensmodifizierer enthält, ergibt sich ein Kompilierungszeitfehler. Sobald alle Parametertypen L verfügbar sind, wird der Typ des Ausdrucks in der Lambda-Methode abgeleitet. Beispiel:
Delegate Function F(x As Integer, y As Long) As Long
Module Test
Sub Main()
' b inferred to Integer, c and return type inferred to Long
Dim a As F = Function(b, c) b + c
' e and return type inferred to Integer, f inferred to Long
Dim d As F = Function(e, f) e + CInt(f)
End Sub
End Module
In einigen Fällen, in denen die Stellvertretungssignatur nicht exakt mit der Lambda-Methode oder Methodensignatur übereinstimmt, unterstützt .NET Framework die Stellvertretungserstellung möglicherweise nicht nativ. In diesem Fall wird ein Lambda-Methodenausdruck verwendet, um mit den beiden Methoden übereinzugleichen. Beispiel:
Delegate Function IntFunc(x As Integer) As Integer
Module Test
Function SquareString(x As String) As String
Return CInt(x) * CInt(x)
End Function
Sub Main()
' The following two lines are equivalent
Dim a As New IntFunc(AddressOf SquareString)
Dim b As New IntFunc( _
Function(x As Integer) CInt(SquareString(CStr(x))))
End Sub
End Module
Das Ergebnis eines Delegaterstellungsausdrucks ist eine Delegateninstanz, die sich auf die entsprechende Methode mit dem zugeordneten Zielausdruck (falls vorhanden) aus dem Methodenzeigerausdruck bezieht. Wenn der Zielausdruck als Werttyp eingegeben wird, wird der Werttyp in den Systemhap kopiert, da ein Delegat nur auf eine Methode eines Objekts im Heap verweisen kann. Die Methode und das Objekt, auf die sich ein Delegate bezieht, bleiben für die gesamte Lebensdauer des Delegaten konstant. Mit anderen Worten, es ist nicht möglich, das Ziel oder Objekt eines Delegaten zu ändern, nachdem er erstellt wurde.
Anonyme Object-Creation Ausdrücke
Ein Objekterstellungsausdruck mit Memberinitialisierern kann auch den Typnamen vollständig weglassen.
AnonymousObjectCreationExpression
: 'New' ObjectMemberInitializer
;
In diesem Fall wird ein anonymer Typ basierend auf den Typen und Namen der Member erstellt, die als Teil des Ausdrucks initialisiert wurden. Beispiel:
Module Test
Sub Main()
Dim Customer = New With { .Name = "John Smith", .Age = 34 }
Console.WriteLine(Customer.Name)
End Sub
End Module
Der typ, der von einem anonymen Objekterstellungsausdruck erstellt wurde, ist eine Klasse ohne Namen, erbt direkt von Object, und verfügt über einen Satz von Eigenschaften mit demselben Namen wie die Elemente, die in der Memberinitialisiererliste zugewiesen sind. Der Typ jeder Eigenschaft wird mit den gleichen Regeln wie die Ableitung des lokalen Variablentyps abgeleitet. Generierte anonyme Typen überschreiben ToStringauch die Zeichenfolgendarstellung aller Elemente und deren Werte. (Das genaue Format dieser Zeichenfolge liegt außerhalb des Umfangs dieser Spezifikation).
Standardmäßig sind die vom anonymen Typ generierten Eigenschaften schreibgeschützt. Es ist möglich, eine anonyme Typeigenschaft mithilfe des Key Modifizierers als schreibgeschützt zu markieren. Der Key Modifizierer gibt an, dass das Feld verwendet werden kann, um den Wert eindeutig zu identifizieren, den der anonyme Typ darstellt. Zusätzlich zur schreibgeschützten Eigenschaft bewirkt dies auch, dass der anonyme Typ außer Equals Kraft setzt und GetHashCode die Schnittstelle System.IEquatable(Of T) implementiert (das Ausfüllen des anonymen Typs für T). Die Member sind wie folgt definiert:
Function Equals(obj As Object) As Boolean und Function Equals(val As T) As Boolean werden implementiert, indem überprüft wird, dass die beiden Instanzen denselben Typ aufweisen und dann jedes Key Element mit demselben Object.EqualsTyp vergleichen. Wenn alle Key Member gleich sind, wird Equals zurückgegeben True, andernfalls Equals wird zurückgegeben False.
Function GetHashCode() As Integer wird so implementiert, dass bei Equals zwei Instanzen des anonymen Typs GetHashCode der Gleiche Wert zurückgegeben wird. Der Hash beginnt mit einem Ausgangswert und multipliziert dann für jedes Key Element den Hash mit 31 und fügt den Key Hashwert (bereitgestellt von GetHashCode) hinzu, wenn das Element kein Verweistyp oder nullabler Werttyp mit dem Wert des NothingElements ist.
Der in der Anweisung erstellte Typ:
Dim zipState = New With { Key .ZipCode = 98112, .State = "WA" }
erstellt eine Klasse, die ungefähr wie folgt aussieht (obwohl die genaue Implementierung variieren kann):
Friend NotInheritable Class $Anonymous1
Implements IEquatable(Of $Anonymous1)
Private ReadOnly _zipCode As Integer
Private _state As String
Public Sub New(zipCode As Integer, state As String)
_zipCode = zipcode
_state = state
End Sub
Public ReadOnly Property ZipCode As Integer
Get
Return _zipCode
End Get
End Property
Public Property State As String
Get
Return _state
End Get
Set (value As Integer)
_state = value
End Set
End Property
Public Overrides Function Equals(obj As Object) As Boolean
Dim val As $Anonymous1 = TryCast(obj, $Anonymous1)
Return Equals(val)
End Function
Public Overloads Function Equals(val As $Anonymous1) As Boolean _
Implements IEquatable(Of $Anonymous1).Equals
If val Is Nothing Then
Return False
End If
If Not Object.Equals(_zipCode, val._zipCode) Then
Return False
End If
Return True
End Function
Public Overrides Function GetHashCode() As Integer
Dim hash As Integer = 0
hash = hash Xor _zipCode.GetHashCode()
Return hash
End Function
Public Overrides Function ToString() As String
Return "{ Key .ZipCode = " & _zipCode & ", .State = " & _state & " }"
End Function
End Class
Um die Situation zu vereinfachen, in der ein anonymer Typ aus den Feldern eines anderen Typs erstellt wird, können Feldnamen direkt aus Ausdrücken in den folgenden Fällen abgeleitet werden:
Ein einfacher Nameausdruck
xleitet den Namenxab.Ein Elementzugriffsausdruck
x.yleitet den Namenyab.Ein Wörterbuch-Nachschlageausdruck
x!yleitet den Namenyab.Ein Aufruf oder Indexausdruck ohne Argumente
x()leitet den Namenxab.Ein XML-Memberzugriffsausdruck
x.<y>,x...<y>x.@yleitet den Namenyab.Ein XML-Memberzugriffsausdruck, der das Ziel eines Memberzugriffsausdrucks
x.<y>.zist, leitet den Namenzab.Ein XML-Memberzugriffsausdruck, der das Ziel eines Aufrufs oder Indexausdrucks ohne Argumente
x.<y>.z()ist, leitet den Namenzab.Ein XML-Memberzugriffsausdruck, der das Ziel eines Aufrufs oder Indexausdrucks
x.<y>(0)ist, leitet den Namenyab.
Der Initialisierer wird als Zuordnung des Ausdrucks zum abgeleiteten Namen interpretiert. Die folgenden Initialisierer sind z. B. gleichwertig:
Class Address
Public Street As String
Public City As String
Public State As String
Public ZIP As String
End Class
Class C1
Sub Test(a As Address)
Dim cityState1 = New With { .City = a.City, .State = a.State }
Dim cityState2 = New With { a.City, a.State }
End Sub
End Class
Wenn ein Membername abgeleitet wird, der mit einem vorhandenen Element des Typs in Konflikt steht, z GetHashCode. B. , tritt ein Kompilierungszeitfehler auf. Im Gegensatz zu regulären Memberinitialisierern dürfen anonyme Objekterstellungsausdrücke keine Memberinitialisierer über Zirkelbezüge verfügen oder auf ein Element verweisen, bevor es initialisiert wurde. Beispiel:
Module Test
Sub Main()
' Error: Circular references
Dim x = New With { .a = .b, .b = .a }
' Error: Referring to .b before it has been assigned to
Dim y = New With { .a = .b, .b = 10 }
' Error: Referring to .a before it has been assigned to
Dim z = New With { .a = .a }
End Sub
End Module
Wenn zwei anonyme Klassenerstellungsausdrücke innerhalb derselben Methode auftreten und dieselbe resultierende Form liefern – wenn die Eigenschaftsreihenfolge, Eigenschaftsnamen und Eigenschaftentypen alle übereinstimmen – verweisen beide auf dieselbe anonyme Klasse. Der Methodenbereich einer Instanz oder einer freigegebenen Membervariable mit einem Initialisierer ist der Konstruktor, in dem die Variable initialisiert wird.
Hinweis: Es ist möglich, dass sich ein Compiler dafür entscheiden kann, anonyme Typen weiter zu vereinheitlichen, z. B. auf Assemblyebene, aber dies kann zu diesem Zeitpunkt nicht verwendet werden.
Umwandlungsausdrücke
Ein Umwandlungsausdruck wandelt einen Ausdruck in einen bestimmten Typ um. Bestimmte Umwandlungsstichwörter wandeln Ausdrücke in die Grundtypen um. Drei allgemeine Umwandlungsstichwörter CTypeTryCast und DirectCast, wandeln einen Ausdruck in einen Typ um.
CastExpression
: 'DirectCast' OpenParenthesis Expression Comma TypeName CloseParenthesis
| 'TryCast' OpenParenthesis Expression Comma TypeName CloseParenthesis
| 'CType' OpenParenthesis Expression Comma TypeName CloseParenthesis
| CastTarget OpenParenthesis Expression CloseParenthesis
;
CastTarget
: 'CBool' | 'CByte' | 'CChar' | 'CDate' | 'CDec' | 'CDbl' | 'CInt'
| 'CLng' | 'CObj' | 'CSByte' | 'CShort' | 'CSng' | 'CStr' | 'CUInt'
| 'CULng' | 'CUShort'
;
DirectCast und TryCast haben spezielle Verhaltensweisen. Aus diesem Gründen unterstützen sie nur systemeigene Konvertierungen. Darüber hinaus kann der Zieltyp in einem TryCast Ausdruck kein Werttyp sein. Benutzerdefinierte Konvertierungsoperatoren werden nicht berücksichtigt, wenn DirectCast oder TryCast verwendet wird. (Hinweis. Der Konvertierungssatz, der DirectCast und TryCast die Unterstützung eingeschränkt sind, da sie "native CLR"-Konvertierungen implementieren. Der Zweck DirectCast besteht darin, die Funktionalität der "unbox"-Anweisung bereitzustellen, während der Zweck TryCast darin besteht, die Funktionalität der "isinst"-Anweisung bereitzustellen. Da sie CLR-Anweisungen zuordnen, würde die Unterstützung von Konvertierungen, die nicht direkt von der CLR unterstützt werden, den beabsichtigten Zweck besiegen.)
DirectCastkonvertiert Ausdrücke, die anders CTypeals eingegeben Object werden. Beim Konvertieren eines Ausdrucks vom TypObject, dessen Laufzeittyp ein Grundtyp ist, wird eine System.InvalidCastException Ausnahme ausgelöst, DirectCast wenn der angegebene Typ nicht mit dem Laufzeittyp des Ausdrucks übereinstimmt oder wenn System.NullReferenceException der Ausdruck ausgewertet wirdNothing. (Hinweis. Wie oben erwähnt, wird die CLR-Anweisung "unbox" direkt zugeordnet, DirectCast wenn der Typ des Ausdrucks ist Object. Wandelt sich dagegen CType in einen Aufruf eines Laufzeithilfsers um, um die Konvertierung durchzuführen, sodass Konvertierungen zwischen Grundtypen unterstützt werden können. Wenn ein Object Ausdruck in einen primitiven Werttyp konvertiert wird und der Typ der tatsächlichen Instanz mit dem Zieltyp übereinstimmt, DirectCast ist dies wesentlich schneller als CType.)
TryCast konvertiert Ausdrücke, löst jedoch keine Ausnahme aus, wenn der Ausdruck nicht in den Zieltyp konvertiert werden kann. Führt Nothing stattdessen dazu, TryCast dass der Ausdruck zur Laufzeit nicht konvertiert werden kann. (Hinweis. Wie oben erwähnt, TryCast wird die CLR-Anweisung "isinst" direkt zugeordnet. Durch die Kombination der Typüberprüfung und der Konvertierung in einen einzelnen Vorgang TryCast kann dies günstiger sein als ein TypeOf ... Is und dann eine CType.)
Beispiel:
Interface ITest
Sub Test()
End Interface
Module Test
Sub Convert(o As Object)
Dim i As ITest = TryCast(o, ITest)
If i IsNot Nothing Then
i.Test()
End If
End Sub
End Module
Wenn keine Konvertierung vom Typ des Ausdrucks in den angegebenen Typ vorhanden ist, tritt ein Kompilierungszeitfehler auf. Andernfalls wird der Ausdruck als Wert klassifiziert, und das Ergebnis ist der von der Konvertierung erzeugte Wert.
Operatorausdrücke
Es gibt zwei Arten von Operatoren.
Unäre Operatoren verwenden einen Operanden und verwenden die Präfixnotation (z. B -x. ).
Binäre Operatoren verwenden zwei Operanden und verwenden die Infixnotation (z. B x + y. ). Mit Ausnahme der relationalen Operatoren, die immer dazu führen Boolean, führt ein für einen bestimmten Typ definierter Operator zu diesem Typ. Die Operanden eines Operators müssen immer als Wert klassifiziert werden. das Ergebnis eines Operatorausdrucks wird als Wert klassifiziert.
OperatorExpression
: ArithmeticOperatorExpression
| RelationalOperatorExpression
| LikeOperatorExpression
| ConcatenationOperatorExpression
| ShortCircuitLogicalOperatorExpression
| LogicalOperatorExpression
| ShiftOperatorExpression
| AwaitOperatorExpression
;
Priorität und Assoziativität von Operatoren
Wenn ein Ausdruck mehrere binäre Operatoren enthält, steuert die Rangfolge der Operatoren die Reihenfolge, in der die einzelnen binären Operatoren ausgewertet werden. Der Ausdruck x + y * z wird z. B. als x + (y * z) ausgewertet, da der * Operator eine höhere Priorität hat als der + Operator. In der folgenden Tabelle sind die binären Operatoren in absteigender Reihenfolge der Rangfolge aufgeführt:
| Kategorie | Betriebspersonal |
|---|---|
| Primär | Alle Nicht-Operator-Ausdrücke |
| Erwarten | Await |
| Potenzierung | ^ |
| Unäre Negation |
+, - |
| Multiplikativ |
*, / |
| Ganzzahldivision | \ |
| Modul | Mod |
| Additiv |
+, - |
| Verkettung | & |
| Shift |
<<, >> |
| Beziehungsorientiert |
=, <>, , ><, <=, >=, Like, , IsIsNot |
| Logische NOT | Not |
| Logisches AND |
And, AndAlso |
| Logisches OR |
Or, OrElse |
| Logisches XOR | Xor |
Wenn ein Ausdruck zwei Operatoren mit derselben Rangfolge enthält, steuert die Zuordnung der Operatoren die Reihenfolge, in der die Vorgänge ausgeführt werden. Alle binären Operatoren sind linksassoziativ, d. h. Vorgänge werden von links nach rechts ausgeführt. Rangfolge und Zuordnung können mithilfe von Klammerausdrücken gesteuert werden.
Objektopernden
Zusätzlich zu den regulären Typen, die von jedem Operator unterstützt werden, unterstützen alle Operatoren Operanden vom Typ Object. Operatoren, die auf Object Operanden angewendet werden, werden ähnlich wie Methodenaufrufe für Object Werte behandelt: Ein spät gebundener Methodenaufruf kann ausgewählt werden, in diesem Fall bestimmt der Laufzeittyp der Operanden anstelle des Kompilierungszeittyps die Gültigkeit und den Typ des Vorgangs. Wenn strenge Semantik durch die Kompilierungsumgebung oder durch Option Strictdie Kompilierungsumgebung angegeben werden, verursachen alle Operatoren mit Operanden vom Typ Object einen Kompilierungszeitfehler, mit Ausnahme der TypeOf...IsIs Operatoren und IsNot Operatoren.
Wenn die Operatorauflösung bestimmt, dass ein Vorgang verspätet ausgeführt werden soll, ist das Ergebnis des Vorgangs das Ergebnis des Vorgangs, wenn der Operator auf die Operandentypen angewendet wird, wenn die Laufzeittypen der Operanden Typen sind, die vom Operator unterstützt werden. Der Wert Nothing wird als Standardwert des Typs des anderen Operanden in einem Binären Operatorausdruck behandelt. In einem unären Operatorausdruck oder wenn sich beide Operanden in einem binären Operatorausdruck befinden Nothing , ist Integer der Typ des Vorgangs oder der einzige Ergebnistyp des Operators, wenn der Operator nicht zu einem Ergebnis führt Integer. Das Ergebnis des Vorgangs wird dann immer wieder in Object. Wenn die Operandentypen keinen gültigen Operator haben, wird eine System.InvalidCastException Ausnahme ausgelöst. Konvertierungen zur Laufzeit erfolgen ohne Rücksicht darauf, ob sie implizit oder explizit sind.
Wenn das Ergebnis eines numerischen Binären Vorgangs eine Überlaufausnahme erzeugt (unabhängig davon, ob die Überprüfung des Ganzzahlüberlaufs aktiviert oder deaktiviert ist), wird der Ergebnistyp nach Möglichkeit zum nächsten breiteren numerischen Typ heraufgestuft. Betrachten Sie z. B. den folgenden Code:
Module Test
Sub Main()
Dim o As Object = CObj(CByte(2)) * CObj(CByte(255))
Console.WriteLine(o.GetType().ToString() & " = " & o)
End Sub
End Module
Es druckt das folgende Ergebnis:
System.Int16 = 512
Wenn kein breiterer numerischer Typ verfügbar ist, um die Zahl aufzunehmen, wird eine System.OverflowException Ausnahme ausgelöst.
Operatorauflösung
Angesichts eines Operatortyps und einer Reihe von Operanden bestimmt die Operatorauflösung, welcher Operator für die Operanden verwendet werden soll. Beim Auflösen von Operatoren werden benutzerdefinierte Operatoren zuerst mit den folgenden Schritten berücksichtigt:
Zunächst werden alle Kandidatenoperatoren gesammelt. Die Kandidatenoperatoren sind alle benutzerdefinierten Operatoren des jeweiligen Operatortyps im Quelltyp und alle benutzerdefinierten Operatoren des jeweiligen Typs im Zieltyp. Wenn der Quelltyp und der Zieltyp verknüpft sind, werden allgemeine Operatoren nur einmal berücksichtigt.
Anschließend wird die Überladungsauflösung auf die Operatoren und Operanden angewendet, um den spezifischsten Operator auszuwählen. Bei binären Operatoren kann dies zu einem verspäteten Aufruf führen.
Beim Sammeln der Kandidatenoperatoren für einen Typ T?werden stattdessen die Operatoren des Typs T verwendet.
TAlle benutzerdefinierten Operatoren, die nur nicht nullable Werttypen umfassen, werden ebenfalls aufgehoben. Ein angehobener Operator verwendet die NULL-Version beliebiger Werttypen, mit Ausnahme der Rückgabetypen und IsTrueIsFalse (die müssen sein Boolean). Angehobene Operatoren werden ausgewertet, indem die Operanden in ihre nicht nullable Version konvertiert werden, dann den benutzerdefinierten Operator auswerten und dann den Ergebnistyp in seine nullable Version konvertieren. Wenn der Etheropernd lautet, ist Nothingdas Ergebnis des Ausdrucks ein Wert des Typs, der Nothing als NULL-Version des Ergebnistyps eingegeben wird. Beispiel:
Structure T
...
End Structure
Structure S
Public Shared Operator +(ByVal op1 As S, ByVal op2 As T) As T
...
End Operator
End Structure
Module Test
Sub Main()
Dim x As S?
Dim y, z As T?
' Valid, as S + T = T is lifted to S? + T? = T?
z = x + y
End Sub
End Module
Wenn der Operator ein binärer Operator ist und einer der Operanden ein Verweistyp ist, wird der Operator ebenfalls aufgehoben, aber jede Bindung an den Operator erzeugt einen Fehler. Beispiel:
Structure S1
Public F1 As Integer
Public Shared Operator +(left As S1, right As String) As S1
...
End Operator
End Structure
Module Test
Sub Main()
Dim a? As S1
Dim s As String
' Error: '+' is not defined for S1? and String
a = a + s
End Sub
End Module
Hinweis: Diese Regel ist vorhanden, da berücksichtigt wurde, ob in einer zukünftigen Version Null-verteilte Verweistypen hinzugefügt werden sollen. In diesem Fall würde sich das Verhalten bei binären Operatoren zwischen den beiden Typen ändern.
Wie bei Konvertierungen werden benutzerdefinierte Operatoren immer gegenüber aufgehobenen Operatoren bevorzugt.
Beim Auflösen überladener Operatoren gibt es möglicherweise Unterschiede zwischen Klassen, die in Visual Basic und in anderen Sprachen definiert sind:
In anderen Sprachen
Not, ,AndundOrkann sowohl als logische Operatoren als auch bitweise Operatoren überladen werden. Beim Import aus einer externen Assembly wird beide Formulare als gültige Überladung für diese Operatoren akzeptiert. Für einen Typ, der logische und bitweise Operatoren definiert, wird jedoch nur die bitweise Implementierung berücksichtigt.In anderen Sprachen
>>können<<sowohl als signierte Operatoren als auch nicht signierte Operatoren überladen werden. Beim Import aus einer externen Assembly wird beide Formulare als gültige Überladung akzeptiert. Für einen Typ, der signierte und nicht signierte Operatoren definiert, wird jedoch nur die signierte Implementierung berücksichtigt.Wenn kein benutzerdefinierter Operator für die Operanden spezifisch ist, werden systeminterne Operatoren berücksichtigt. Wenn kein systeminterner Operator für die Operanden definiert ist und beide Operanden den Typ "Object" haben, wird der Operator verspätet aufgelöst. andernfalls ergibt sich ein Kompilierungszeitfehler.
Wenn in früheren Versionen von Visual Basic genau ein Operand vom Typ "Object" vorhanden war und keine anwendbaren benutzerdefinierten Operatoren und keine anwendbaren systeminternen Operatoren vorhanden sind, war es ein Fehler. Ab Visual Basic 11 ist sie jetzt spät gebunden. Beispiel:
Module Module1
Sub Main()
Dim p As Object = Nothing
Dim U As New Uri("http://www.microsoft.com")
Dim j = U * p ' is now resolved late-bound
End Sub
End Module
Ein Typ T mit einem systeminternen Operator definiert auch denselben Operator für T?. Das Ergebnis des Operators ist T? identisch mit Tder Ausnahme, dass bei beiden Operanden Nothingdas Ergebnis des Operators ( Nothing d. h. der Nullwert wird weitergegeben) sein. Für die Auflösung des Typs eines Vorgangs werden die ? Operanden, die sie enthalten, der Typ des Vorgangs bestimmt und dem ? Typ des Vorgangs hinzugefügt, wenn eines der Operanden nullable Werttypen waren. Beispiel:
Dim v1? As Integer = 10
Dim v2 As Long = 20
' Type of operation will be Long?
Console.WriteLine(v1 + v2)
Jeder Operator listet die systeminternen Typen auf, für die er definiert ist, und den Typ des Vorgangs, der bei den Operandentypen ausgeführt wird. Das Ergebnis des Typs eines systeminternen Vorgangs folgt den folgenden allgemeinen Regeln:
Wenn alle Operanden denselben Typ aufweisen und der Operator für den Typ definiert ist, tritt keine Konvertierung auf, und der Operator für diesen Typ wird verwendet.
Jeder Operand, dessen Typ für den Operator nicht definiert ist, wird mithilfe der folgenden Schritte konvertiert, und der Operator wird anhand der neuen Typen aufgelöst:
Der Operand wird in den nächsten breiten Typ konvertiert, der sowohl für den Operator als auch für den Operanden definiert ist und in den er implizit konvertierbar ist.
Wenn kein solcher Typ vorhanden ist, wird der Operand in den nächsten schmalsten Typ konvertiert, der sowohl für den Operator als auch für den Operanden definiert ist und in den er implizit konvertierbar ist.
Wenn kein solcher Typ vorhanden ist oder die Konvertierung nicht ausgeführt werden kann, tritt ein Kompilierungszeitfehler auf.
Andernfalls werden die Operanden in den breiteren Der Operandentyp konvertiert, und der Operator für diesen Typ wird verwendet. Wenn der schmalere Operandtyp nicht implizit in den breiteren Operatortyp konvertiert werden kann, tritt ein Kompilierungszeitfehler auf.
Trotz dieser allgemeinen Regeln gibt es jedoch eine Reihe von Sonderfällen, die in den Ergebnistabellen des Operators genannt werden.
Hinweis: Aus Formatierungsgründen abkürzt der Operator Tabellen die vordefinierten Namen auf die ersten beiden Zeichen. "By" ist Bytealso , "UI" ist UInteger, "St" ist String, usw. "Err" bedeutet, dass für die angegebenen Operandentypen kein Vorgang definiert ist.
Arithmetische Operatoren
Die *Arithmetikoperatoren , /\, ^, Mod, +und - Operatoren sind.
ArithmeticOperatorExpression
: UnaryPlusExpression
| UnaryMinusExpression
| AdditionOperatorExpression
| SubtractionOperatorExpression
| MultiplicationOperatorExpression
| DivisionOperatorExpression
| ModuloOperatorExpression
| ExponentOperatorExpression
;
Gleitkommaarithmetische Operationen können mit höherer Genauigkeit als der Ergebnistyp des Vorgangs ausgeführt werden. Beispielsweise unterstützen einige Hardwarearchitekturen einen Gleitkommatyp "erweitert" oder "long double" mit größerer Reichweite und Genauigkeit als dem Double Typ und führen implizit alle Gleitkommavorgänge mit diesem Typ mit höherer Genauigkeit aus. Hardwarearchitekturen können vorgenommen werden, um Gleitkommavorgänge mit geringerer Genauigkeit nur bei übermäßigen Leistungseinbußen durchzuführen; Anstatt eine Implementierung zu erfordern, um sowohl Leistung als auch Genauigkeit zu verwirken, ermöglicht Visual Basic die Verwendung des Typs mit höherer Genauigkeit für alle Gleitkommavorgänge. Abgesehen davon, dass präzisere Ergebnisse erzielt werden, hat dies nur selten messbare Auswirkungen. In Ausdrücken des Formulars x * y / z, bei denen die Multiplikation ein Ergebnis erzeugt, das sich außerhalb des Double Bereichs befindet, aber die nachfolgende Division das temporäre Ergebnis wieder in den Double Bereich zurückgibt, kann die Tatsache, dass der Ausdruck in einem Format mit höherem Bereich ausgewertet wird, zu einem endlichen Ergebnis führen, das anstelle von Unendlichkeit erzeugt wird.
Unary Plus-Operator
UnaryPlusExpression
: '+' Expression
;
Der unäre Plusoperator wird für die ByteTypen , , ShortUShortSByte, UIntegerLongULongSingleIntegerund DoubleDecimal Typen definiert.
Vorgangstyp:
| Bo | SB | Von | Pst | USA | In | UI | Lo | UL | De | Si | Empfohlene Vorgehensweise | Da | Ch | St | Ob |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Pst | SB | Bis | Pst | USA | In | Benutzeroberfläche | Lo | UL | De | Si | Tun | Fehler | Fehler | Tun | Ob |
Unary Minus-Operator
UnaryMinusExpression
: '-' Expression
;
Der unäre Minusoperator wird für die folgenden Typen definiert:
SByte, Short, Integerund Long. Das Ergebnis wird berechnet, indem der Operand von Null subtrahiert wird. Wenn die Überprüfung des Ganzzahlüberlaufs aktiviert ist und der Wert des Operanden das maximum negative SByte, Short, , , Integeroder Long, eine System.OverflowException Ausnahme ausgelöst wird. Andernfalls ist der Wert des Operanden das maximum negative SByteShortInteger, oder Long, das Ergebnis ist derselbe Wert, und der Überlauf wird nicht gemeldet.
Single und Double. Das Ergebnis ist der Wert des Operanden mit dem invertierten Zeichen, einschließlich der Werte 0 und Infinity. Wenn der Operand NaN ist, ist das Ergebnis auch NaN.
Decimal. Das Ergebnis wird berechnet, indem der Operand von Null subtrahiert wird.
Vorgangstyp:
| Bo | SB | Von | Pst | USA | In | UI | Lo | UL | De | Si | Empfohlene Vorgehensweise | Da | Ch | St | Ob |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Pst | SB | Pst | Pst | In | In | Lo | Lo | De | De | Si | Tun | Fehler | Fehler | Tun | Ob |
Additionsoperator
Der Additionsoperator berechnet die Summe der beiden Operanden.
AdditionOperatorExpression
: Expression '+' LineTerminator? Expression
;
Der Additionsoperator wird für die folgenden Typen definiert:
Byte,SByte,UShort,Short,UInteger,Integer,ULongundLong. Wenn die Überprüfung des Ganzzahlüberlaufs aktiviert ist und sich die Summe außerhalb des Bereichs des Ergebnistyps befindet, wird eineSystem.OverflowExceptionAusnahme ausgelöst. Andernfalls werden Überläufe nicht gemeldet, und alle signifikanten Hochreihenfolgen des Ergebnisses werden verworfen.SingleundDouble. Die Summe wird gemäß den Regeln von IEEE 754 arithmetisch berechnet.Decimal. Wenn der resultierende Wert zu groß ist, um im Dezimalformat darzustellen, wird eineSystem.OverflowExceptionAusnahme ausgelöst. Wenn der Ergebniswert zu klein ist, um es im Dezimalformat darzustellen, lautet das Ergebnis 0.String. Die beidenStringOperanden werden miteinander verkettet.Date. DerSystem.DateTimeTyp definiert überladene Additionsoperatoren. DaSystem.DateTimesie dem systeminternenDateTyp entspricht, sind diese Operatoren auch für denDateTyp verfügbar.
Vorgangstyp:
| Bo | SB | Von | Pst | USA | In | UI | Lo | UL | De | Si | Empfohlene Vorgehensweise | Da | Ch | St | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Bo | Pst | SB | Pst | Pst | In | In | Lo | Lo | De | De | Si | Tun | Fehler | Fehler | Tun | Ob |
| SB | SB | Pst | Pst | In | In | Lo | Lo | De | De | Si | Tun | Fehler | Fehler | Tun | Ob | |
| Von | Bis | Pst | USA | In | Benutzeroberfläche | Lo | UL | De | Si | Tun | Fehler | Fehler | Tun | Ob | ||
| Pst | Pst | In | In | Lo | Lo | De | De | Si | Tun | Fehler | Fehler | Tun | Ob | |||
| USA | USA | In | Benutzeroberfläche | Lo | UL | De | Si | Tun | Fehler | Fehler | Tun | Ob | ||||
| In | In | Lo | Lo | De | De | Si | Tun | Fehler | Fehler | Tun | Ob | |||||
| UI | Benutzeroberfläche | Lo | UL | De | Si | Tun | Fehler | Fehler | Tun | Ob | ||||||
| Lo | Lo | De | De | Si | Tun | Fehler | Fehler | Tun | Ob | |||||||
| UL | UL | De | Si | Tun | Fehler | Fehler | Tun | Ob | ||||||||
| De | De | Si | Tun | Fehler | Fehler | Tun | Ob | |||||||||
| Si | Si | Tun | Fehler | Fehler | Tun | Ob | ||||||||||
| Empfohlene Vorgehensweise | Tun | Fehler | Fehler | Tun | Ob | |||||||||||
| Da | St | Fehler | St | Ob | ||||||||||||
| Ch | St | St | Ob | |||||||||||||
| St | St | Ob | ||||||||||||||
| Ob | Ob |
Subtraktionsoperator
Der Subtraktionsoperator subtrahiert den zweiten Operanden vom ersten Operanden.
SubtractionOperatorExpression
: Expression '-' LineTerminator? Expression
;
Der Subtraktionsoperator ist für die folgenden Typen definiert:
Byte,SByte,UShort,Short,UInteger,Integer,ULongundLong. Wenn die Überprüfung des Ganzzahlüberlaufs aktiviert ist und sich der Unterschied außerhalb des Bereichs des Ergebnistyps befindet, wird eineSystem.OverflowExceptionAusnahme ausgelöst. Andernfalls werden Überläufe nicht gemeldet, und alle signifikanten Hochreihenfolgen des Ergebnisses werden verworfen.SingleundDouble. Der Unterschied wird nach den Regeln von IEEE 754 arithmetisch berechnet.Decimal. Wenn der resultierende Wert zu groß ist, um im Dezimalformat darzustellen, wird eineSystem.OverflowExceptionAusnahme ausgelöst. Wenn der Ergebniswert zu klein ist, um es im Dezimalformat darzustellen, lautet das Ergebnis 0.Date. DerSystem.DateTimeTyp definiert überladene Subtraktionsoperatoren. DaSystem.DateTimesie dem systeminternenDateTyp entspricht, sind diese Operatoren auch für denDateTyp verfügbar.
Vorgangstyp:
| Bo | SB | Von | Pst | USA | In | UI | Lo | UL | De | Si | Empfohlene Vorgehensweise | Da | Ch | St | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Bo | Pst | SB | Pst | Pst | In | In | Lo | Lo | De | De | Si | Tun | Fehler | Fehler | Tun | Ob |
| SB | SB | Pst | Pst | In | In | Lo | Lo | De | De | Si | Tun | Fehler | Fehler | Tun | Ob | |
| Von | Bis | Pst | USA | In | Benutzeroberfläche | Lo | UL | De | Si | Tun | Fehler | Fehler | Tun | Ob | ||
| Pst | Pst | In | In | Lo | Lo | De | De | Si | Tun | Fehler | Fehler | Tun | Ob | |||
| USA | USA | In | Benutzeroberfläche | Lo | UL | De | Si | Tun | Fehler | Fehler | Tun | Ob | ||||
| In | In | Lo | Lo | De | De | Si | Tun | Fehler | Fehler | Tun | Ob | |||||
| UI | Benutzeroberfläche | Lo | UL | De | Si | Tun | Fehler | Fehler | Tun | Ob | ||||||
| Lo | Lo | De | De | Si | Tun | Fehler | Fehler | Tun | Ob | |||||||
| UL | UL | De | Si | Tun | Fehler | Fehler | Tun | Ob | ||||||||
| De | De | Si | Tun | Fehler | Fehler | Tun | Ob | |||||||||
| Si | Si | Tun | Fehler | Fehler | Tun | Ob | ||||||||||
| Empfohlene Vorgehensweise | Tun | Fehler | Fehler | Tun | Ob | |||||||||||
| Da | Fehler | Fehler | Fehler | Fehler | ||||||||||||
| Ch | Fehler | Fehler | Fehler | |||||||||||||
| St | Tun | Ob | ||||||||||||||
| Ob | Ob |
Multiplikationsoperator
Der Multiplikationsoperator berechnet das Produkt von zwei Operanden.
MultiplicationOperatorExpression
: Expression '*' LineTerminator? Expression
;
Der Multiplikationsoperator wird für die folgenden Typen definiert:
Byte,SByte,UShort,Short,UInteger,Integer,ULongundLong. Wenn die Überprüfung des Ganzzahlüberlaufs aktiviert ist und sich das Produkt außerhalb des Bereichs des Ergebnistyps befindet, wird eineSystem.OverflowExceptionAusnahme ausgelöst. Andernfalls werden Überläufe nicht gemeldet, und alle signifikanten Hochreihenfolgen des Ergebnisses werden verworfen.SingleundDouble. Das Produkt wird gemäß den Regeln von IEEE 754 arithmetisch berechnet.Decimal. Wenn der resultierende Wert zu groß ist, um im Dezimalformat darzustellen, wird eineSystem.OverflowExceptionAusnahme ausgelöst. Wenn der Ergebniswert zu klein ist, um es im Dezimalformat darzustellen, lautet das Ergebnis 0.
Vorgangstyp:
| Bo | SB | Von | Pst | USA | In | UI | Lo | UL | De | Si | Empfohlene Vorgehensweise | Da | Ch | St | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Bo | Pst | SB | Pst | Pst | In | In | Lo | Lo | De | De | Si | Tun | Fehler | Fehler | Tun | Ob |
| SB | SB | Pst | Pst | In | In | Lo | Lo | De | De | Si | Tun | Fehler | Fehler | Tun | Ob | |
| Von | Bis | Pst | USA | In | Benutzeroberfläche | Lo | UL | De | Si | Tun | Fehler | Fehler | Tun | Ob | ||
| Pst | Pst | In | In | Lo | Lo | De | De | Si | Tun | Fehler | Fehler | Tun | Ob | |||
| USA | USA | In | Benutzeroberfläche | Lo | UL | De | Si | Tun | Fehler | Fehler | Tun | Ob | ||||
| In | In | Lo | Lo | De | De | Si | Tun | Fehler | Fehler | Tun | Ob | |||||
| UI | Benutzeroberfläche | Lo | UL | De | Si | Tun | Fehler | Fehler | Tun | Ob | ||||||
| Lo | Lo | De | De | Si | Tun | Fehler | Fehler | Tun | Ob | |||||||
| UL | UL | De | Si | Tun | Fehler | Fehler | Tun | Ob | ||||||||
| De | De | Si | Tun | Fehler | Fehler | Tun | Ob | |||||||||
| Si | Si | Tun | Fehler | Fehler | Tun | Ob | ||||||||||
| Empfohlene Vorgehensweise | Tun | Fehler | Fehler | Tun | Ob | |||||||||||
| Da | Fehler | Fehler | Fehler | Fehler | ||||||||||||
| Ch | Fehler | Fehler | Fehler | |||||||||||||
| St | Tun | Ob | ||||||||||||||
| Ob | Ob |
Abteilungsoperatoren
Divisionsoperatoren berechnen den Quotient von zwei Operanden. Es gibt zwei Divisionsoperatoren: den regulären Divisionsoperator (Gleitkomma) und den ganzzahligen Divisionsoperator.
DivisionOperatorExpression
: FPDivisionOperatorExpression
| IntegerDivisionOperatorExpression
;
FPDivisionOperatorExpression
: Expression '/' LineTerminator? Expression
;
IntegerDivisionOperatorExpression
: Expression '\\' LineTerminator? Expression
;
Der reguläre Divisionsoperator wird für die folgenden Typen definiert:
SingleundDouble. Der Quotient wird gemäß den Regeln von IEEE 754 arithmetisch berechnet.Decimal. Wenn der Wert des rechten Operanden null ist, wird eineSystem.DivideByZeroExceptionAusnahme ausgelöst. Wenn der resultierende Wert zu groß ist, um im Dezimalformat darzustellen, wird eineSystem.OverflowExceptionAusnahme ausgelöst. Wenn der Ergebniswert zu klein ist, um es im Dezimalformat darzustellen, ist das Ergebnis null. Die Skala des Ergebnisses vor jeder Rundung ist die nächstgelegene Skalierung der bevorzugten Skala, die ein Ergebnis gleich dem exakten Ergebnis behält. Die bevorzugte Skala ist der Maßstab des ersten Operanden weniger als der Maßstab des zweiten Operanden.
Gemäß normalen Regeln zur Operatorauflösung würde die normale Division rein zwischen Operanden von Typen wie Byte, Short, Integer, und Long dazu führen, dass beide Operanden in Typ Decimalkonvertiert werden. Bei der Operatorauflösung für den Divisionsoperator gilt DecimalDouble dies jedoch als schmaler als Decimal. Diese Konvention wird befolgt, da Double die Division effizienter ist als Decimal die Division.
Vorgangstyp:
| Bo | SB | Von | Pst | USA | In | UI | Lo | UL | De | Si | Empfohlene Vorgehensweise | Da | Ch | St | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Bo | Tun | Tun | Tun | Tun | Tun | Tun | Tun | Tun | Tun | De | Si | Tun | Fehler | Fehler | Tun | Ob |
| SB | Tun | Tun | Tun | Tun | Tun | Tun | Tun | Tun | De | Si | Tun | Fehler | Fehler | Tun | Ob | |
| Von | Tun | Tun | Tun | Tun | Tun | Tun | Tun | De | Si | Tun | Fehler | Fehler | Tun | Ob | ||
| Pst | Tun | Tun | Tun | Tun | Tun | Tun | De | Si | Tun | Fehler | Fehler | Tun | Ob | |||
| USA | Tun | Tun | Tun | Tun | Tun | De | Si | Tun | Fehler | Fehler | Tun | Ob | ||||
| In | Tun | Tun | Tun | Tun | De | Si | Tun | Fehler | Fehler | Tun | Ob | |||||
| UI | Tun | Tun | Tun | De | Si | Tun | Fehler | Fehler | Tun | Ob | ||||||
| Lo | Tun | Tun | De | Si | Tun | Fehler | Fehler | Tun | Ob | |||||||
| UL | Tun | De | Si | Tun | Fehler | Fehler | Tun | Ob | ||||||||
| De | De | Si | Tun | Fehler | Fehler | Tun | Ob | |||||||||
| Si | Si | Tun | Fehler | Fehler | Tun | Ob | ||||||||||
| Empfohlene Vorgehensweise | Tun | Fehler | Fehler | Tun | Ob | |||||||||||
| Da | Fehler | Fehler | Fehler | Fehler | ||||||||||||
| Ch | Fehler | Fehler | Fehler | |||||||||||||
| St | Tun | Ob | ||||||||||||||
| Ob | Ob |
Der ganzzahlige Divisionsoperator wird für Byte, , , UShort, Short, UInteger, Integer, , und LongULong. SByte Wenn der Wert des rechten Operanden null ist, wird eine System.DivideByZeroException Ausnahme ausgelöst. Die Division rundet das Ergebnis in Richtung Null ab, und der absolute Wert des Ergebnisses ist die größtmögliche ganze Zahl, die kleiner als der absolute Wert des Quotienten der beiden Operanden ist. Das Ergebnis ist null oder positiv, wenn die beiden Operanden dasselbe Vorzeichen haben und null oder negativ, wenn die beiden Operanden gegensätzige Zeichen aufweisen. Wenn der linke Operand der maximale negative SByte, , , Integer, oder Long, und der rechte Operand ist -1, tritt ein Überlauf auf. Wenn die Überlaufüberprüfung für ganze Zahlen aktiviert ist, wird eine System.OverflowException Ausnahme Shortausgelöst. Andernfalls wird der Überlauf nicht gemeldet, und das Ergebnis ist stattdessen der Wert des linken Operanden.
Hinweis: Da die beiden Operanden für nicht signierte Typen immer null oder positiv sein werden, ist das Ergebnis immer null oder positiv. Da das Ergebnis des Ausdrucks immer kleiner oder gleich dem größten der beiden Operanden ist, ist es nicht möglich, dass ein Überlauf erfolgt. Eine solche Ganzzahlüberlaufüberprüfung wird nicht für die Ganzzahlteilung mit zwei nicht signierten ganzzahligen Zahlen durchgeführt. Das Ergebnis ist der Typ des linken Operanden.
Vorgangstyp:
| Bo | SB | Von | Pst | USA | In | UI | Lo | UL | De | Si | Empfohlene Vorgehensweise | Da | Ch | St | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Bo | Pst | SB | Pst | Pst | In | In | Lo | Lo | Lo | Lo | Lo | Lo | Fehler | Fehler | Lo | Ob |
| SB | SB | Pst | Pst | In | In | Lo | Lo | Lo | Lo | Lo | Lo | Fehler | Fehler | Lo | Ob | |
| Von | Bis | Pst | USA | In | Benutzeroberfläche | Lo | UL | Lo | Lo | Lo | Fehler | Fehler | Lo | Ob | ||
| Pst | Pst | In | In | Lo | Lo | Lo | Lo | Lo | Lo | Fehler | Fehler | Lo | Ob | |||
| USA | USA | In | Benutzeroberfläche | Lo | UL | Lo | Lo | Lo | Fehler | Fehler | Lo | Ob | ||||
| In | In | Lo | Lo | Lo | Lo | Lo | Lo | Fehler | Fehler | Lo | Ob | |||||
| UI | Benutzeroberfläche | Lo | UL | Lo | Lo | Lo | Fehler | Fehler | Lo | Ob | ||||||
| Lo | Lo | Lo | Lo | Lo | Lo | Fehler | Fehler | Lo | Ob | |||||||
| UL | UL | Lo | Lo | Lo | Fehler | Fehler | Lo | Ob | ||||||||
| De | Lo | Lo | Lo | Fehler | Fehler | Lo | Ob | |||||||||
| Si | Lo | Lo | Fehler | Fehler | Lo | Ob | ||||||||||
| Empfohlene Vorgehensweise | Lo | Fehler | Fehler | Lo | Ob | |||||||||||
| Da | Fehler | Fehler | Fehler | Fehler | ||||||||||||
| Ch | Fehler | Fehler | Fehler | |||||||||||||
| St | Lo | Ob | ||||||||||||||
| Ob | Ob |
Mod-Operator
Der Mod Operator (Modulo) berechnet den Rest der Division zwischen zwei Operanden.
ModuloOperatorExpression
: Expression 'Mod' LineTerminator? Expression
;
Der Mod Operator wird für die folgenden Typen definiert:
Byte,SByte,UShort,Short,UInteger, ,IntegerundLongULong. Das Ergebnis vonx Mod yist der durchx - (x \ y) * yerzeugte Wert. Wennynull ist, wird eineSystem.DivideByZeroExceptionAusnahme ausgelöst. Der Modulooperator verursacht nie einen Überlauf.SingleundDouble. Der Rest wird gemäß den Regeln von IEEE 754 arithmetisch berechnet.Decimal. Wenn der Wert des rechten Operanden null ist, wird eineSystem.DivideByZeroExceptionAusnahme ausgelöst. Wenn der resultierende Wert zu groß ist, um im Dezimalformat darzustellen, wird eineSystem.OverflowExceptionAusnahme ausgelöst. Wenn der Ergebniswert zu klein ist, um es im Dezimalformat darzustellen, ist das Ergebnis null.
Vorgangstyp:
| Bo | SB | Von | Pst | USA | In | UI | Lo | UL | De | Si | Empfohlene Vorgehensweise | Da | Ch | St | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Bo | Pst | SB | Pst | Pst | In | In | Lo | Lo | De | De | Si | Tun | Fehler | Fehler | Tun | Ob |
| SB | SB | Pst | Pst | In | In | Lo | Lo | De | De | Si | Tun | Fehler | Fehler | Tun | Ob | |
| Von | Bis | Pst | USA | In | Benutzeroberfläche | Lo | UL | De | Si | Tun | Fehler | Fehler | Tun | Ob | ||
| Pst | Pst | In | In | Lo | Lo | De | De | Si | Tun | Fehler | Fehler | Tun | Ob | |||
| USA | USA | In | Benutzeroberfläche | Lo | UL | De | Si | Tun | Fehler | Fehler | Tun | Ob | ||||
| In | In | Lo | Lo | De | De | Si | Tun | Fehler | Fehler | Tun | Ob | |||||
| UI | Benutzeroberfläche | Lo | UL | De | Si | Tun | Fehler | Fehler | Tun | Ob | ||||||
| Lo | Lo | De | De | Si | Tun | Fehler | Fehler | Tun | Ob | |||||||
| UL | UL | De | Si | Tun | Fehler | Fehler | Tun | Ob | ||||||||
| De | De | Si | Tun | Fehler | Fehler | Tun | Ob | |||||||||
| Si | Si | Tun | Fehler | Fehler | Tun | Ob | ||||||||||
| Empfohlene Vorgehensweise | Tun | Fehler | Fehler | Tun | Ob | |||||||||||
| Da | Fehler | Fehler | Fehler | Fehler | ||||||||||||
| Ch | Fehler | Fehler | Fehler | |||||||||||||
| St | Tun | Ob | ||||||||||||||
| Ob | Ob |
Exponentiation-Operator
Der Exponentiationsoperator berechnet den ersten Operanden, der mit der Potenz des zweiten Operanden ausgelöst wurde.
ExponentOperatorExpression
: Expression '^' LineTerminator? Expression
;
Der Exponentiationsoperator ist für den Typ Doubledefiniert. Der Wert wird gemäß den Regeln von IEEE 754 arithmetisch berechnet.
Vorgangstyp:
| Bo | SB | Von | Pst | USA | In | UI | Lo | UL | De | Si | Empfohlene Vorgehensweise | Da | Ch | St | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Bo | Tun | Tun | Tun | Tun | Tun | Tun | Tun | Tun | Tun | Tun | Tun | Tun | Fehler | Fehler | Tun | Ob |
| SB | Tun | Tun | Tun | Tun | Tun | Tun | Tun | Tun | Tun | Tun | Tun | Fehler | Fehler | Tun | Ob | |
| Von | Tun | Tun | Tun | Tun | Tun | Tun | Tun | Tun | Tun | Tun | Fehler | Fehler | Tun | Ob | ||
| Pst | Tun | Tun | Tun | Tun | Tun | Tun | Tun | Tun | Tun | Fehler | Fehler | Tun | Ob | |||
| USA | Tun | Tun | Tun | Tun | Tun | Tun | Tun | Tun | Fehler | Fehler | Tun | Ob | ||||
| In | Tun | Tun | Tun | Tun | Tun | Tun | Tun | Fehler | Fehler | Tun | Ob | |||||
| UI | Tun | Tun | Tun | Tun | Tun | Tun | Fehler | Fehler | Tun | Ob | ||||||
| Lo | Tun | Tun | Tun | Tun | Tun | Fehler | Fehler | Tun | Ob | |||||||
| UL | Tun | Tun | Tun | Tun | Fehler | Fehler | Tun | Ob | ||||||||
| De | Tun | Tun | Tun | Fehler | Fehler | Tun | Ob | |||||||||
| Si | Tun | Tun | Fehler | Fehler | Tun | Ob | ||||||||||
| Empfohlene Vorgehensweise | Tun | Fehler | Fehler | Tun | Ob | |||||||||||
| Da | Fehler | Fehler | Fehler | Fehler | ||||||||||||
| Ch | Fehler | Fehler | Fehler | |||||||||||||
| St | Tun | Ob | ||||||||||||||
| Ob | Ob |
Relationale Operatoren
Die relationalen Operatoren vergleichen Werte miteinander. Die Vergleichsoperatoren sind =, , <>, <, >, <=und >=.
RelationalOperatorExpression
: Expression '=' LineTerminator? Expression
| Expression '<' '>' LineTerminator? Expression
| Expression '<' LineTerminator? Expression
| Expression '>' LineTerminator? Expression
| Expression '<' '=' LineTerminator? Expression
| Expression '>' '=' LineTerminator? Expression
;
Alle relationalen Operatoren führen zu einem Boolean Wert.
Die relationalen Operatoren haben die folgende allgemeine Bedeutung:
Der
=Operator testet, ob die beiden Operanden gleich sind.Der
<>Operator testet, ob die beiden Operanden nicht gleich sind.Der
<Operator testet, ob der erste Operand kleiner als der zweite Operand ist.Der
>Operator testet, ob der erste Operand größer als der zweite Operand ist.Der
<=Operator testet, ob der erste Operand kleiner oder gleich dem zweiten Operanden ist.Der
>=Operator testet, ob der erste Operand größer oder gleich dem zweiten Operanden ist.
Die relationalen Operatoren werden für die folgenden Typen definiert:
Boolean. Die Operatoren vergleichen die Wahrheitswerte der beiden Operanden.Truewird als kleinerFalseals betrachtet, der mit ihren numerischen Werten übereinstimmt.Byte,SByte,UShort,Short,UInteger,Integer,ULongundLong. Die Operatoren vergleichen die numerischen Werte der beiden integralen Operanden.SingleundDouble. Die Operatoren vergleichen die Operanden gemäß den Regeln des IEEE 754-Standards.Decimal. Die Operatoren vergleichen die numerischen Werte der beiden Dezimalopernden.Date. Die Operatoren geben das Ergebnis des Vergleichens der beiden Datums-/Uhrzeitwerte zurück.Char. Die Operatoren geben das Ergebnis des Vergleichs der beiden Unicode-Werte zurück.String. Die Operatoren geben das Ergebnis des Vergleichens der beiden Werte mithilfe eines binären Vergleichs oder eines Textvergleichs zurück. Der verwendete Vergleich wird durch die Kompilierungsumgebung und dieOption CompareAnweisung bestimmt. Ein binärer Vergleich bestimmt, ob der numerische Unicode-Wert jedes Zeichens in jeder Zeichenfolge identisch ist. Ein Textvergleich führt einen Unicode-Textvergleich basierend auf der aktuellen Kultur aus, die in .NET Framework verwendet wird. Bei einem Zeichenfolgenvergleich entspricht ein NULL-Wert dem Zeichenfolgenliteral"".
Vorgangstyp:
| Bo | SB | Von | Pst | USA | In | UI | Lo | UL | De | Si | Empfohlene Vorgehensweise | Da | Ch | St | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Bo | Bo | SB | Pst | Pst | In | In | Lo | Lo | De | De | Si | Tun | Fehler | Fehler | Bo | Ob |
| SB | SB | Pst | Pst | In | In | Lo | Lo | De | De | Si | Tun | Fehler | Fehler | Tun | Ob | |
| Von | Bis | Pst | USA | In | Benutzeroberfläche | Lo | UL | De | Si | Tun | Fehler | Fehler | Tun | Ob | ||
| Pst | Pst | In | In | Lo | Lo | De | De | Si | Tun | Fehler | Fehler | Tun | Ob | |||
| USA | USA | In | Benutzeroberfläche | Lo | UL | De | Si | Tun | Fehler | Fehler | Tun | Ob | ||||
| In | In | Lo | Lo | De | De | Si | Tun | Fehler | Fehler | Tun | Ob | |||||
| UI | Benutzeroberfläche | Lo | UL | De | Si | Tun | Fehler | Fehler | Tun | Ob | ||||||
| Lo | Lo | De | De | Si | Tun | Fehler | Fehler | Tun | Ob | |||||||
| UL | UL | De | Si | Tun | Fehler | Fehler | Tun | Ob | ||||||||
| De | De | Si | Tun | Fehler | Fehler | Tun | Ob | |||||||||
| Si | Si | Tun | Fehler | Fehler | Tun | Ob | ||||||||||
| Empfohlene Vorgehensweise | Tun | Fehler | Fehler | Tun | Ob | |||||||||||
| Da | Da | Fehler | Da | Ob | ||||||||||||
| Ch | Ch | St | Ob | |||||||||||||
| St | St | Ob | ||||||||||||||
| Ob | Ob |
Like-Operator
Der Like Operator bestimmt, ob eine Zeichenfolge einem bestimmten Muster entspricht.
LikeOperatorExpression
: Expression 'Like' LineTerminator? Expression
;
Der Like Operator wird für den String Typ definiert. Der erste Operand ist die übereinstimmende Zeichenfolge, und der zweite Operand ist das Muster, mit dem abgeglichen werden soll. Das Muster besteht aus Unicode-Zeichen. Die folgenden Zeichenfolgen haben besondere Bedeutungen:
Das Zeichen
?entspricht einem beliebigen einzelnen Zeichen.Das Zeichen
*entspricht null oder mehr Zeichen.Das Zeichen
#entspricht einer beliebigen einzelnen Ziffer (0-9).Eine Liste von Zeichen, die in eckigen Klammern (
[ab...]) eingeschlossen sind, entspricht einem beliebigen einzelnen Zeichen in der Liste.Eine Liste von Zeichen, die in eckigen Klammern eingeschlossen sind und einem Ausrufezeichen (
[!ab...]) vorangestellt sind, entspricht einem einzelnen Zeichen, das nicht in der Zeichenliste enthalten ist.Zwei Zeichen in einer durch einen Bindestrich getrennten Zeichenliste (
-) geben einen Unicode-Zeichenbereich an, der mit dem ersten Zeichen beginnt und mit dem zweiten Zeichen endet. Wenn das zweite Zeichen nicht später in der Sortierreihenfolge als das erste Zeichen liegt, tritt eine Laufzeit ausnahme auf. Ein Bindestrich, der am Anfang oder Ende einer Zeichenliste angezeigt wird, gibt sich selbst an.
Damit die Sonderzeichen in der linken Klammer ([), fragezeichen (?), Nummernzeichen (#) und Sternchen (*) übereinstimmen, müssen sie in eckige Klammern eingeschlossen werden. Die rechte Klammer (]) kann nicht innerhalb einer Gruppe verwendet werden, um sich selbst abzugleichen, aber sie kann außerhalb einer Gruppe als einzelnes Zeichen verwendet werden. Die Zeichenfolge [] wird als Zeichenfolgenliteral ""betrachtet.
Beachten Sie, dass Zeichenvergleiche und Die Sortierung für Zeichenlisten von dem verwendeten Vergleichstyp abhängig sind. Wenn binäre Vergleiche verwendet werden, basieren Zeichenvergleiche und -sortierungen auf den numerischen Unicode-Werten. Wenn Textvergleiche verwendet werden, basieren Zeichenvergleiche und -sortierungen auf dem aktuellen Gebietsschema, das im .NET Framework verwendet wird.
In einigen Sprachen stellen Sonderzeichen im Alphabet zwei separate Zeichen dar und umgekehrt. Beispielsweise verwenden mehrere Sprachen das Zeichen æ , um die Zeichen a darzustellen, und e wenn sie zusammen angezeigt werden, während die Zeichen ^ und O können verwendet werden, um das Zeichen Ôdarzustellen. Bei verwendung von Textvergleichen erkennt der Like Operator solche kulturellen Äquivalenz. In diesem Fall entspricht ein Vorkommen des einzelnen Sonderzeichens in einem Muster oder einer Zeichenfolge der entsprechenden zweistelligen Sequenz in der anderen Zeichenfolge. Ebenso entspricht ein einzelnes Sonderzeichen im Muster, das in Klammern (selbst, in einer Liste oder in einem Bereich) eingeschlossen ist, der entsprechenden zweistelligen Sequenz in der Zeichenfolge und umgekehrt.
In einem Like Ausdruck, in dem beide Operanden oder Nothing ein Operand eine systeminterne Konvertierung String aufweisen und der andere Operand Nothingist, Nothing wird behandelt, als wäre es das leere Zeichenfolgenliteral "".
Vorgangstyp:
| Bo | SB | Von | Pst | USA | In | UI | Lo | UL | De | Si | Empfohlene Vorgehensweise | Da | Ch | St | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Bo | St | St | St | St | St | St | St | St | St | St | St | St | St | St | St | Ob |
| SB | St | St | St | St | St | St | St | St | St | St | St | St | St | St | Ob | |
| Von | St | St | St | St | St | St | St | St | St | St | St | St | St | Ob | ||
| Pst | St | St | St | St | St | St | St | St | St | St | St | St | Ob | |||
| USA | St | St | St | St | St | St | St | St | St | St | St | Ob | ||||
| In | St | St | St | St | St | St | St | St | St | St | Ob | |||||
| UI | St | St | St | St | St | St | St | St | St | Ob | ||||||
| Lo | St | St | St | St | St | St | St | St | Ob | |||||||
| UL | St | St | St | St | St | St | St | Ob | ||||||||
| De | St | St | St | St | St | St | Ob | |||||||||
| Si | St | St | St | St | St | Ob | ||||||||||
| Empfohlene Vorgehensweise | St | St | St | St | Ob | |||||||||||
| Da | St | St | St | Ob | ||||||||||||
| Ch | St | St | Ob | |||||||||||||
| St | St | Ob | ||||||||||||||
| Ob | Ob |
Verkettungsoperator
ConcatenationOperatorExpression
: Expression '&' LineTerminator? Expression
;
Der Verkettungsoperator wird für alle systeminternen Typen definiert, einschließlich der nullablen Versionen der systeminternen Werttypen. Sie wird auch für die Verkettung zwischen den oben genannten Typen und System.DBNull, die als Nothing Zeichenfolge behandelt werden, definiert. Der Verkettungsoperator konvertiert alle Operanden Stringin ; im Ausdruck werden alle Konvertierungen String als Verbreiterung betrachtet, unabhängig davon, ob strenge Semantik verwendet werden. Ein System.DBNull Wert wird in den Literaltyp Nothing konvertiert als String. Ein Nullwerttyp, dessen Wert ebenfalls in den Literaltyp Nothing konvertiert wirdNothing, Stringanstatt einen Laufzeitfehler auszuwerfen.
Ein Verkettungsvorgang führt zu einer Zeichenfolge, die die Verkettung der beiden Operanden in der Reihenfolge von links nach rechts darstellt. Der Wert Nothing wird behandelt, als ob es sich um das leere Zeichenfolgenliteral ""handelt.
Vorgangstyp:
| Bo | SB | Von | Pst | USA | In | UI | Lo | UL | De | Si | Empfohlene Vorgehensweise | Da | Ch | St | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Bo | St | St | St | St | St | St | St | St | St | St | St | St | St | St | St | Ob |
| SB | St | St | St | St | St | St | St | St | St | St | St | St | St | St | Ob | |
| Von | St | St | St | St | St | St | St | St | St | St | St | St | St | Ob | ||
| Pst | St | St | St | St | St | St | St | St | St | St | St | St | Ob | |||
| USA | St | St | St | St | St | St | St | St | St | St | St | Ob | ||||
| In | St | St | St | St | St | St | St | St | St | St | Ob | |||||
| UI | St | St | St | St | St | St | St | St | St | Ob | ||||||
| Lo | St | St | St | St | St | St | St | St | Ob | |||||||
| UL | St | St | St | St | St | St | St | Ob | ||||||||
| De | St | St | St | St | St | St | Ob | |||||||||
| Si | St | St | St | St | St | Ob | ||||||||||
| Empfohlene Vorgehensweise | St | St | St | St | Ob | |||||||||||
| Da | St | St | St | Ob | ||||||||||||
| Ch | St | St | Ob | |||||||||||||
| St | St | Ob | ||||||||||||||
| Ob | Ob |
Logische Operatoren
Die Andlogischen OrNotXor Operatoren werden als logische Operatoren bezeichnet.
LogicalOperatorExpression
: 'Not' Expression
| Expression 'And' LineTerminator? Expression
| Expression 'Or' LineTerminator? Expression
| Expression 'Xor' LineTerminator? Expression
;
Die logischen Operatoren werden wie folgt ausgewertet:
Für den
BooleanTyp:Für die beiden Operanden wird ein logischer
AndVorgang ausgeführt.Für den Operanden wird ein logischer
NotVorgang ausgeführt.Für die beiden Operanden wird ein logischer
OrVorgang ausgeführt.Für die beiden Operanden wird eine logische Exklusivoperation
Orausgeführt.
Für
BytealleSByteIntegerLongUShortShortUIntegerULongaufgezählten Typen wird der angegebene Vorgang für jedes Bit der binären Darstellung der beiden Operanden ausgeführt:And: Das Ergebnisbit ist 1, wenn beide Bits 1 sind; andernfalls ist das Ergebnisbit 0.Not: Das Ergebnisbit ist 1, wenn das Bit 0 ist; andernfalls ist das Ergebnisbit 1.Or: Das Ergebnisbit ist 1, wenn eines der Bits 1 ist; andernfalls ist das Ergebnisbit 0.Xor: Das Ergebnisbit ist 1, wenn eines der Bits 1, aber nicht beide Bits ist; andernfalls ist das Ergebnisbit 0 (d. a. 1Xor0 = 1, 1Xor1 = 0).
Wenn die logischen Operatoren
AndOrfür den TypBoolean?aufgehoben werden, werden sie erweitert, um die dreiwertige boolesche Logik wie diese einzuschließen:Andwertet auf "true" aus, wenn beide Operanden wahr sind; false, wenn einer der Operanden falsch ist;Nothingsonst.Orwertet auf "true" aus, wenn ein Operand wahr ist; false ist beide Operanden falsch;Nothingsonst.
Beispiel:
Module Test
Sub Main()
Dim x?, y? As Boolean
x = Nothing
y = True
If x Or y Then
' Will execute
End If
End Sub
End Module
Hinweis: Im Idealfall werden die logischen Operatoren AndOr mit drei Werten für jeden Typ aufgehoben, der in einem booleschen Ausdruck verwendet werden kann (d. h. einen Typ, der implementiert IsTrue und IsFalse), auf die gleiche Weise wie AndAlso bei jedem Typ, der in einem booleschen Ausdruck verwendet werden kann, und OrElse Kurzschluss über alle Typen hinweg, die in einem booleschen Ausdruck verwendet werden können. Leider werden dreiwertige Hebevorgänge nur angewendet Boolean?, sodass benutzerdefinierte Typen, die dreiwertige Logik wünschen, manuell durch Definieren And und Or Operatoren für ihre nullable Version ausgeführt werden müssen.
Bei diesen Operationen sind keine Überläufe möglich. Die Aufzählungstypoperatoren führen den bitweisen Vorgang für den zugrunde liegenden Typ des Aufzählungstyps aus, der Rückgabewert ist jedoch der Aufzählungstyp.
Kein Vorgangstyp:
| Bo | SB | Von | Pst | USA | In | UI | Lo | UL | De | Si | Empfohlene Vorgehensweise | Da | Ch | St | Ob |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Bo | SB | Bis | Pst | USA | In | Benutzeroberfläche | Lo | UL | Lo | Lo | Lo | Fehler | Fehler | Lo | Ob |
Und, Oder, Xor-Vorgangstyp:
| Bo | SB | Von | Pst | USA | In | UI | Lo | UL | De | Si | Empfohlene Vorgehensweise | Da | Ch | St | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Bo | Bo | SB | Pst | Pst | In | In | Lo | Lo | Lo | Lo | Lo | Lo | Fehler | Fehler | Bo | Ob |
| SB | SB | Pst | Pst | In | In | Lo | Lo | Lo | Lo | Lo | Lo | Fehler | Fehler | Lo | Ob | |
| Von | Bis | Pst | USA | In | Benutzeroberfläche | Lo | UL | Lo | Lo | Lo | Fehler | Fehler | Lo | Ob | ||
| Pst | Pst | In | In | Lo | Lo | Lo | Lo | Lo | Lo | Fehler | Fehler | Lo | Ob | |||
| USA | USA | In | Benutzeroberfläche | Lo | UL | Lo | Lo | Lo | Fehler | Fehler | Lo | Ob | ||||
| In | In | Lo | Lo | Lo | Lo | Lo | Lo | Fehler | Fehler | Lo | Ob | |||||
| UI | Benutzeroberfläche | Lo | UL | Lo | Lo | Lo | Fehler | Fehler | Lo | Ob | ||||||
| Lo | Lo | Lo | Lo | Lo | Lo | Fehler | Fehler | Lo | Ob | |||||||
| UL | UL | Lo | Lo | Lo | Fehler | Fehler | Lo | Ob | ||||||||
| De | Lo | Lo | Lo | Fehler | Fehler | Lo | Ob | |||||||||
| Si | Lo | Lo | Fehler | Fehler | Lo | Ob | ||||||||||
| Empfohlene Vorgehensweise | Lo | Fehler | Fehler | Lo | Ob | |||||||||||
| Da | Fehler | Fehler | Fehler | Fehler | ||||||||||||
| Ch | Fehler | Fehler | Fehler | |||||||||||||
| St | Lo | Ob | ||||||||||||||
| Ob | Ob |
Kurzschluss logische Operatoren
OrElse Bei den AndAlso Operatoren handelt es sich um die Kurzschlussversionen der And logischen Or Operatoren.
ShortCircuitLogicalOperatorExpression
: Expression 'AndAlso' LineTerminator? Expression
| Expression 'OrElse' LineTerminator? Expression
;
Aufgrund des Kurzschlussverhaltens wird der zweite Operand zur Laufzeit nicht ausgewertet, wenn das Operatorergebnis nach der Auswertung des ersten Operanden bekannt ist.
Die logischen Kurzschlussoperatoren werden wie folgt ausgewertet:
Wenn der erste Operand in einem
AndAlsoVorgang "FalseTrue" aus demIsFalseOperator ausgewertet oder zurückgegeben wird, gibt der Ausdruck den ersten Operanden zurück. Andernfalls wird der zweite Operand ausgewertet, und für die beiden Ergebnisse wird ein logischerAndVorgang ausgeführt.Wenn der erste Operand in einem
OrElseVorgang "TrueTrue" aus demIsTrueOperator ausgewertet oder zurückgegeben wird, gibt der Ausdruck den ersten Operanden zurück. Andernfalls wird der zweite Operand ausgewertet, und für die beiden Ergebnisse wird ein logischerOrVorgang ausgeführt.
Die AndAlso Operatoren und OrElse Operatoren werden für den Typ Booleanoder für einen beliebigen Typ T definiert, der die folgenden Operatoren überlastet:
Public Shared Operator IsTrue(op As T) As Boolean
Public Shared Operator IsFalse(op As T) As Boolean
sowie überladen den entsprechenden And oder Or Operator:
Public Shared Operator And(op1 As T, op2 As T) As T
Public Shared Operator Or(op1 As T, op2 As T) As T
Bei der Auswertung der AndAlsoOrElse Operatoren wird der erste Operand nur einmal ausgewertet, und der zweite Operand wird entweder nicht ausgewertet oder genau einmal ausgewertet. Betrachten Sie z. B. den folgenden Code:
Module Test
Function TrueValue() As Boolean
Console.Write(" True")
Return True
End Function
Function FalseValue() As Boolean
Console.Write(" False")
Return False
End Function
Sub Main()
Console.Write("And:")
If FalseValue() And TrueValue() Then
End If
Console.WriteLine()
Console.Write("Or:")
If TrueValue() Or FalseValue() Then
End If
Console.WriteLine()
Console.Write("AndAlso:")
If FalseValue() AndAlso TrueValue() Then
End If
Console.WriteLine()
Console.Write("OrElse:")
If TrueValue() OrElse FalseValue() Then
End If
Console.WriteLine()
End Sub
End Module
Es druckt das folgende Ergebnis:
And: False True
Or: True False
AndAlso: False
OrElse: True
In der angehobenen Form der AndAlso Und OrElse Operatoren, wenn der erste Operand null Boolean?war, wird der zweite Operand ausgewertet, aber das Ergebnis ist immer null Boolean?.
Vorgangstyp:
| Bo | SB | Von | Pst | USA | In | UI | Lo | UL | De | Si | Empfohlene Vorgehensweise | Da | Ch | St | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Fehler | Fehler | Bo | Ob |
| SB | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Fehler | Fehler | Bo | Ob | |
| Von | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Fehler | Fehler | Bo | Ob | ||
| Pst | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Fehler | Fehler | Bo | Ob | |||
| USA | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Fehler | Fehler | Bo | Ob | ||||
| In | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Fehler | Fehler | Bo | Ob | |||||
| UI | Bo | Bo | Bo | Bo | Bo | Bo | Fehler | Fehler | Bo | Ob | ||||||
| Lo | Bo | Bo | Bo | Bo | Bo | Fehler | Fehler | Bo | Ob | |||||||
| UL | Bo | Bo | Bo | Bo | Fehler | Fehler | Bo | Ob | ||||||||
| De | Bo | Bo | Bo | Fehler | Fehler | Bo | Ob | |||||||||
| Si | Bo | Bo | Fehler | Fehler | Bo | Ob | ||||||||||
| Empfohlene Vorgehensweise | Bo | Fehler | Fehler | Bo | Ob | |||||||||||
| Da | Fehler | Fehler | Fehler | Fehler | ||||||||||||
| Ch | Fehler | Fehler | Fehler | |||||||||||||
| St | Bo | Ob | ||||||||||||||
| Ob | Ob |
Schiebeoperatoren
Die binären Operatoren << und >> führen Bitverschiebungsvorgänge aus.
ShiftOperatorExpression
: Expression '<' '<' LineTerminator? Expression
| Expression '>' '>' LineTerminator? Expression
;
Die Operatoren werden für die ByteTypen , , SByte, UShort, ShortUInteger, usw LongIntegerULong. definiert. Im Gegensatz zu den anderen binären Operatoren wird der Ergebnistyp eines Schichtvorgangs bestimmt, als wäre der Operator ein unärer Operator mit nur dem linken Operanden. Der Typ des richtigen Operanden muss implizit konvertierbar Integer sein und wird nicht verwendet, um den Ergebnistyp des Vorgangs zu bestimmen.
Der << Operator bewirkt, dass die Bits im ersten Operanden nach links verschoben werden, um die Anzahl der durch den Schichtbetrag angegebenen Stellen zu verschieben. Die Hochreihenfolgebits außerhalb des Bereichs des Ergebnistyps werden verworfen, und die leerstehenden Bitpositionen in niedriger Reihenfolge sind nullgefüllt.
Der >> Operator bewirkt, dass die Bits im ersten Operanden nach rechts verschoben werden, die Anzahl der durch den Schichtbetrag angegebenen Stellen. Die Bits mit niedriger Reihenfolge werden verworfen, und die positionen mit hoher Reihenfolge werden auf Null festgelegt, wenn der linke Operand positiv oder negativ ist. Wenn der linke Operand vom Typ Byte, , UShort, UIntegeroder ULong die leeren Hochreihenfolgen bits nullgefüllt sind.
Die Schichtoperatoren verschieben die Bits der zugrunde liegenden Darstellung des ersten Operanden um die Menge des zweiten Operanden. Wenn der Wert des zweiten Operanden größer als die Anzahl der Bits im ersten Operanden oder negativ ist, wird der Schichtbetrag wie RightOperand And SizeMask folgt SizeMask berechnet:
| LeftOperand-Typ | SizeMask |
|---|---|
Byte, SByte |
7 (&H7) |
UShort, Short |
15 (&HF) |
UInteger, Integer |
31 (&H1F) |
ULong, Long |
63 (&H3F) |
Wenn der Schichtbetrag null ist, ist das Ergebnis des Vorgangs mit dem Wert des ersten Operanden identisch. Bei diesen Operationen sind keine Überläufe möglich.
Vorgangstyp:
| Bo | SB | Von | Pst | USA | In | UI | Lo | UL | De | Si | Empfohlene Vorgehensweise | Da | Ch | St | Ob |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Pst | SB | Bis | Pst | USA | In | Benutzeroberfläche | Lo | UL | Lo | Lo | Lo | Fehler | Fehler | Lo | Ob |
Boolesche Ausdrücke
Ein boolescher Ausdruck ist ein Ausdruck, der getestet werden kann, um festzustellen, ob er wahr ist oder falsch ist.
BooleanExpression
: Expression
;
Ein Typ T kann in einem booleschen Ausdruck verwendet werden, wenn, in der Reihenfolge der Voreinstellung:
TistBooleanoderBoolean?That eine Erweiterungskonvertierung inBooleanThat eine Erweiterungskonvertierung inBoolean?Tdefiniert zwei PseudooperatorenIsTrueundIsFalse.That eine Schmalungskonvertierung, inBoolean?die keine Konvertierung vonBooleanzuBoolean?.That eine Schmalungskonvertierung inBoolean.
Hinweis: Es ist interessant zu beachten, dass ein Option Strict Ausdruck, der eine schmale Konvertierung Boolean hat, ohne kompilierungszeitfehler akzeptiert wird, aber die Sprache bevorzugt weiterhin einen IsTrue Operator, falls vorhanden. Dies liegt daran, dass Option Strict nur das, was von der Sprache akzeptiert wird und nicht akzeptiert wird, und niemals die tatsächliche Bedeutung eines Ausdrucks ändert.
IsTrue Daher muss immer eine schmale Konvertierung bevorzugt werden, unabhängig von Option Strict.
Die folgende Klasse definiert z. B. keine Erweiterungskonvertierung in Boolean. Die Verwendung in der If Anweisung führt dazu, dass der IsTrue Operator aufgerufen wird.
Class MyBool
Public Shared Widening Operator CType(b As Boolean) As MyBool
...
End Operator
Public Shared Narrowing Operator CType(b As MyBool) As Boolean
...
End Operator
Public Shared Operator IsTrue(b As MyBool) As Boolean
...
End Operator
Public Shared Operator IsFalse(b As MyBool) As Boolean
...
End Operator
End Class
Module Test
Sub Main()
Dim b As New MyBool
If b Then Console.WriteLine("True")
End Sub
End Module
Wenn ein boolescher Ausdruck als oder konvertiert in oder Boolean?konvertiert Boolean wird, ist er wahr, wenn der Wert andernfalls "false" istTrue.
Andernfalls ruft ein boolescher Ausdruck den IsTrue Operator auf und gibt zurück True , wenn der Operator zurückgegeben wurde True; andernfalls ist er "false" (aber nie ruft den IsFalse Operator auf).
Im folgenden Beispiel Integer weist eine Schmalungskonvertierung auf Boolean, sodass eine Null Integer? eine Schmalungskonvertierung in beide Boolean? (ergibt einen Nullwert Boolean) und ( Boolean was eine Ausnahme auslöst) hat. Die Schmalungskonvertierung in Boolean? wird bevorzugt, sodass der Wert von "i" als boolescher Ausdruck daher Falselautet.
Dim i As Integer? = Nothing
If i Then Console.WriteLine()
Lambda-Ausdrücke
Ein Lambda-Ausdruck definiert eine anonyme Methode, die als Lambda-Methode bezeichnet wird. Lambda-Methoden erleichtern das Übergeben von "inline"-Methoden an andere Methoden, die Stellvertretungstypen verwenden.
LambdaExpression
: SingleLineLambda
| MultiLineLambda
;
SingleLineLambda
: LambdaModifier* 'Function' ( OpenParenthesis ParameterList? CloseParenthesis )? Expression
| 'Sub' ( OpenParenthesis ParameterList? CloseParenthesis )? Statement
;
MultiLineLambda
: MultiLineFunctionLambda
| MultiLineSubLambda
;
MultiLineFunctionLambda
: LambdaModifier? 'Function' ( OpenParenthesis ParameterList? CloseParenthesis )? ( 'As' TypeName )? LineTerminator
Block
'End' 'Function'
;
MultiLineSubLambda
: LambdaModifier? 'Sub' ( OpenParenthesis ParameterList? CloseParenthesis )? LineTerminator
Block
'End' 'Sub'
;
LambdaModifier
: 'Async' | 'Iterator'
;
Das Beispiel:
Module Test
Delegate Function IntFunc(x As Integer) As Integer
Sub Apply(a() As Integer, func As IntFunc)
For index As Integer = 0 To a.Length - 1
a(index) = func(a(index))
Next index
End Sub
Sub Main()
Dim a() As Integer = { 1, 2, 3, 4 }
Apply(a, Function(x As Integer) x * 2)
For Each value In a
Console.Write(value & " ")
Next value
End Sub
End Module
wird ausgegeben:
2 4 6 8
Ein Lambda-Ausdruck beginnt mit den optionalen Async Modifizierern oder Iterator, gefolgt vom Schlüsselwort Function oder Sub einer Parameterliste. Parameter in einem Lambda-Ausdruck können nicht deklariert oder nicht über OptionalParamArray Attribute verfügen. Im Gegensatz zu regulären Methoden wird das Weglassen eines Parametertyps für eine Lambda-Methode nicht automatisch ableiten Object. Wenn stattdessen eine Lambda-Methode neu klassifiziert wird, werden die ausgelassenen Parametertypen und ByRef Modifizierer vom Zieltyp abgeleitet. Im vorherigen Beispiel könnte der Lambda-Ausdruck als Function(x) x * 2geschrieben worden sein, und er hätte den Typ abgeleitet x , zu dem Integer die Lambda-Methode verwendet wurde, um eine Instanz des IntFunc Delegatentyps zu erstellen. Im Gegensatz zur lokalen Variablenausleitung tritt ein Kompilierungsfehler auf, wenn ein Lambda-Methodenparameter einen Typ ausgelassen, aber einen Array- oder Null-Variablenmodifizierer aufweist.
Ein regulärer Lambda-Ausdruck ist ein Ausdruck mit weder Async modifizierern noch Iterator Modifizierern.
Ein Iterator-Lambda-Ausdruck ist ein Ausdruck mit dem Iterator Modifizierer und ohne Async Modifizierer. Es muss eine Funktion sein. Wenn sie in einen Wert neu klassifiziert wird, kann sie nur auf einen Wert des Delegatentyps, dessen Rückgabetyp IEnumeratorlautet , oder IEnumerablefür einige T, die IEnumerator(Of T)IEnumerable(Of T) keine ByRef-Parameter aufweisen, neu klassifiziert werden.
Ein asynchroner Lambda-Ausdruck ist ein Ausdruck mit dem Async Modifizierer und ohne Iterator Modifizierer. Eine asynchrone Unter-Lambda-Funktion kann nur in einen Wert des Unterdelegattyps ohne ByRef-Parameter klassifiziert werden. Eine asynchrone Funktions-Lambda-Funktion kann nur in einen Wert des Funktionsdelegattyps klassifiziert werden, deren Rückgabetyp Task oder Task(Of T) für einige ist und für die Tkeine ByRef-Parameter vorhanden sind.
Lambda-Ausdrücke können entweder einzeilig oder mehrzeilig sein. Einzeilige Function Lambda-Ausdrücke enthalten einen einzelnen Ausdruck, der den von der Lambda-Methode zurückgegebenen Wert darstellt. Einzeilige Sub Lambda-Ausdrücke enthalten eine einzelne Anweisung ohne schließende Anweisung StatementTerminator. Beispiel:
Module Test
Sub Do(a() As Integer, action As Action(Of Integer))
For index As Integer = 0 To a.Length - 1
action(a(index))
Next index
End Sub
Sub Main()
Dim a() As Integer = { 1, 2, 3, 4 }
Do(a, Sub(x As Integer) Console.WriteLine(x))
End Sub
End Module
Einzeilige Lambda-Konstrukte binden weniger eng als alle anderen Ausdrücke und Anweisungen. Daher entspricht "Function() x + 5" beispielsweise "Function() (x+5)" anstelle von "(Function() x) + 5". Um Mehrdeutigkeit zu vermeiden, enthält ein einzeiligen Sub Lambda-Ausdruck möglicherweise keine Dim-Anweisung oder eine Bezeichnungsdeklarationsanweisung. Es sei denn, sie ist in Klammern eingeschlossen, kann ein einzeiliges Sub Lambda-Ausdruck nicht unmittelbar gefolgt von einem Doppelpunkt ":", einem Memberzugriffsoperator ".", einem Wörterbuchmitgliedszugriffsoperator "!" oder einer geöffneten Klammer "(". Es darf keine Block-Anweisung (With, , SyncLock, If...EndIf, While, For, Do, Using) oder auch OnError keine Resume.
Hinweis: Im Lambda-Ausdruck Function(i) x=iwird der Textkörper als Ausdruck interpretiert (der testet, ob x und i gleich sind). Im Lambda-Ausdruck Sub(i) x=iwird der Textkörper jedoch als Anweisung interpretiert (die zugewiesen wird ix).
Ein mehrzeiliges Lambda-Ausdruck enthält einen Anweisungsblock und muss mit einer entsprechenden End Anweisung enden (d. h. End Function ).End Sub Wie bei regulären Methoden müssen sich die Anweisungen oder Sub Anweisungen End einer mehrzeiligen Lambda-Methode Function in ihren eigenen Zeilen befinden. Beispiel:
' Error: Function statement must be on its own line!
Dim x = Sub(x As Integer) : Console.WriteLine(x) : End Sub
' OK
Dim y = Sub(x As Integer)
Console.WriteLine(x)
End Sub
Mehrzeilige Function Lambda-Ausdrücke können einen Rückgabetyp deklarieren, aber keine Attribute darauf platzieren. Wenn ein mehrzeiliges Function Lambda-Ausdruck keinen Rückgabetyp deklariert, aber der Rückgabetyp aus dem Kontext abgeleitet werden kann, in dem der Lambda-Ausdruck verwendet wird, wird dieser Rückgabetyp verwendet. Andernfalls wird der Rückgabetyp der Funktion wie folgt berechnet:
In einem regulären Lambda-Ausdruck ist der Rückgabetyp der dominante Typ der Ausdrücke in allen
ReturnAnweisungen im Anweisungsblock.In einem asynchronen Lambda-Ausdruck ist
Task(Of T)Tder Rückgabetyp der dominante Typ der Ausdrücke in allenReturnAnweisungen im Anweisungsblock.In einem Iterator-Lambda-Ausdruck ist
IEnumerable(Of T)Tder Rückgabetyp der dominante Typ der Ausdrücke in allenYieldAnweisungen im Anweisungsblock.
Beispiel:
Function f(min As Integer, max As Integer) As IEnumerable(Of Integer)
If min > max Then Throw New ArgumentException()
Dim x = Iterator Function()
For i = min To max
Yield i
Next
End Function
' infers x to be a delegate with return type IEnumerable(Of Integer)
Return x()
End Function
Wenn in allen Fällen keine Return (bzw Yield.) Anweisungen vorhanden sind oder es keinen dominanten Typ gibt, und strenge Semantik verwendet wird, tritt ein Kompilierungszeitfehler auf; andernfalls ist der dominante Typ implizit Object.
Beachten Sie, dass der Rückgabetyp aus allen Return Anweisungen berechnet wird, auch wenn sie nicht erreichbar sind. Beispiel:
' Return type is Double
Dim x = Function()
Return 10
Return 10.50
End Function
Es gibt keine implizite Rückgabevariable, da für die Variable kein Name vorhanden ist.
Die Anweisungsblöcke innerhalb von mehrzeiligen Lambda-Ausdrücken haben die folgenden Einschränkungen:
On ErrorundResumeAnweisungen sind nicht zulässig, obwohlTryAnweisungen zulässig sind.Statische Lokale können nicht in mehrzeiligen Lambda-Ausdrücken deklariert werden.
Es ist nicht möglich, innerhalb oder außerhalb des Anweisungsblocks eines mehrzeiligen Lambda-Ausdrucks zu verzweigen, obwohl die normalen Verzweigungsregeln darin gelten. Beispiel:
Label1: Dim x = Sub() ' Error: Cannot branch out GoTo Label1 ' OK: Wholly within the lamba. GoTo Label2: Label2: End Sub ' Error: Cannot branch in GoTo Label2
Ein Lambda-Ausdruck entspricht ungefähr einer anonymen Methode, die für den enthaltenden Typ deklariert ist. Das erste Beispiel entspricht ungefähr folgendem Beispiel:
Module Test
Delegate Function IntFunc(x As Integer) As Integer
Sub Apply(a() As Integer, func As IntFunc)
For index As Integer = 0 To a.Length - 1
a(index) = func(a(index))
Next index
End Sub
Function $Lambda1(x As Integer) As Integer
Return x * 2
End Function
Sub Main()
Dim a() As Integer = { 1, 2, 3, 4 }
Apply(a, AddressOf $Lambda1)
For Each value In a
Console.Write(value & " ")
Next value
End Sub
End Module
Schließungen
Lambda-Ausdrücke haben Zugriff auf alle Variablen im Bereich, einschließlich lokaler Variablen oder Parameter, die in der enthaltenden Methode und Lambda-Ausdrücke definiert sind. Wenn ein Lambda-Ausdruck auf eine lokale Variable oder einen lokalen Parameter verweist, erfasst der Lambda-Ausdruck die Variable, auf die verwiesen wird, in eine Schließung. Bei einer Schließung handelt es sich um ein Objekt, das sich auf dem Heap anstelle des Stapels befindet, und wenn eine Variable erfasst wird, werden alle Verweise auf die Variable an den Abschluss umgeleitet. Dadurch können Lambda-Ausdrücke weiterhin auf lokale Variablen und Parameter verweisen, auch nachdem die enthaltende Methode abgeschlossen ist. Beispiel:
Module Test
Delegate Function D() As Integer
Function M() As D
Dim x As Integer = 10
Return Function() x
End Function
Sub Main()
Dim y As D = M()
' Prints 10
Console.WriteLine(y())
End Sub
End Module
ist ungefähr gleichbedeutend mit:
Module Test
Delegate Function D() As Integer
Class $Closure1
Public x As Integer
Function $Lambda1() As Integer
Return x
End Function
End Class
Function M() As D
Dim c As New $Closure1()
c.x = 10
Return AddressOf c.$Lambda1
End Function
Sub Main()
Dim y As D = M()
' Prints 10
Console.WriteLine(y())
End Sub
End Module
Bei einem Schließen wird jedes Mal eine neue Kopie einer lokalen Variablen erfasst, wenn sie den Block eingibt, in dem die lokale Variable deklariert wird, die neue Kopie jedoch mit dem Wert der vorherigen Kopie initialisiert wird. Beispiel:
Module Test
Delegate Function D() As Integer
Function M() As D()
Dim a(9) As D
For i As Integer = 0 To 9
Dim x
a(i) = Function() x
x += 1
Next i
Return a
End Function
Sub Main()
Dim y() As D = M()
For i As Integer = 0 To 9
Console.Write(y(i)() & " ")
Next i
End Sub
End Module
druckt
1 2 3 4 5 6 7 8 9 10
Statt
9 9 9 9 9 9 9 9 9 9
Da Schließungen beim Eingeben eines Blocks initialisiert werden müssen, ist es nicht erlaubt GoTo , in einen Block mit einer Schließung von außerhalb dieses Blocks zu Resume gelangen, obwohl sie mit einem Schließen in einen Block aufgenommen werden darf. Beispiel:
Module Test
Sub Main()
Dim a = 10
If a = 10 Then
L1:
Dim x = Function() a
' Valid, source is within block
GoTo L2
L2:
End If
' ERROR: target is inside block with closure
GoTo L1
End Sub
End Module
Da sie nicht in einem Schließen erfasst werden können, kann Folgendes nicht innerhalb eines Lambda-Ausdrucks angezeigt werden:
Referenzparameter.
Instanzausdrücke (
Me,MyClass,MyBase), wenn der Typ derMeKlasse keine Klasse ist.
Die Member eines anonymen Typerstellungsausdrucks, wenn der Lambda-Ausdruck Teil des Ausdrucks ist. Beispiel:
' Error: Lambda cannot refer to anonymous type field
Dim x = New With { .a = 12, .b = Function() .a }
ReadOnly Instanzvariablen in Instanzkonstruktoren oder ReadOnly freigegebenen Variablen in gemeinsam genutzten Konstruktoren, bei denen die Variablen in einem Nicht-Wert-Kontext verwendet werden. Beispiel:
Class C1
ReadOnly F1 As Integer
Sub New()
' Valid, doesn't modify F1
Dim x = Function() F1
' Error, tries to modify F1
Dim f = Function() ModifyValue(F1)
End Sub
Sub ModifyValue(ByRef x As Integer)
End Sub
End Class
Abfrageausdrücke
Ein Abfrageausdruck ist ein Ausdruck, der eine Reihe von Abfrageoperatoren auf die Elemente einer abfragefähigen Auflistung anwendet. Der folgende Ausdruck verwendet beispielsweise eine Auflistung von Customer Objekten und gibt die Namen aller Kunden im Bundesstaat Washington zurück:
Dim names = _
From cust In Customers _
Where cust.State = "WA" _
Select cust.Name
Ein Abfrageausdruck muss mit einem From oder einem Aggregate Operator beginnen und kann mit einem beliebigen Abfrageoperator enden. Das Ergebnis eines Abfrageausdrucks wird als Wert klassifiziert. Der Ergebnistyp des Ausdrucks hängt vom Ergebnistyp des letzten Abfrageoperators im Ausdruck ab.
QueryExpression
: FromOrAggregateQueryOperator QueryOperator*
;
FromOrAggregateQueryOperator
: FromQueryOperator
| AggregateQueryOperator
;
QueryOperator
: FromQueryOperator
| AggregateQueryOperator
| SelectQueryOperator
| DistinctQueryOperator
| WhereQueryOperator
| OrderByQueryOperator
| PartitionQueryOperator
| LetQueryOperator
| GroupByQueryOperator
| JoinOrGroupJoinQueryOperator
;
JoinOrGroupJoinQueryOperator
: JoinQueryOperator
| GroupJoinQueryOperator
;
Bereichsvariablen
Einige Abfrageoperatoren führen eine spezielle Art von Variable ein, die als Bereichsvariable bezeichnet wird. Bereichsvariablen sind keine echten Variablen; Stattdessen stellen sie die einzelnen Werte während der Auswertung der Abfrage über die Eingabeauflistungen dar.
CollectionRangeVariableDeclarationList
: CollectionRangeVariableDeclaration ( Comma CollectionRangeVariableDeclaration )*
;
CollectionRangeVariableDeclaration
: Identifier ( 'As' TypeName )? 'In' LineTerminator? Expression
;
ExpressionRangeVariableDeclarationList
: ExpressionRangeVariableDeclaration ( Comma ExpressionRangeVariableDeclaration )*
;
ExpressionRangeVariableDeclaration
: Identifier ( 'As' TypeName )? Equals Expression
;
Bereichsvariablen werden vom einführenden Abfrageoperator bis zum Ende eines Abfrageausdrucks oder auf einen Abfrageoperator festgelegt, z Select . B. deren Ausblenden. Beispiel: in der folgenden Abfrage
Dim waCusts = _
From cust As Customer In Customers _
Where cust.State = "WA"
Der From Abfrageoperator führt eine Bereichsvariable cust ein, die so eingegeben wird, dass Customer jeder Kunde in der Customers Auflistung darstellt. Der folgende Where Abfrageoperator verweist dann auf die Bereichsvariable cust im Filterausdruck, um zu bestimmen, ob ein einzelner Kunde aus der resultierenden Auflistung heraus gefiltert werden soll.
Es gibt zwei Arten von Bereichsvariablen: Sammlungsbereichsvariablen und Ausdrucksbereichsvariablen. Sammlungsbereichsvariablen nehmen ihre Werte aus den Elementen der abgefragten Auflistungen ab. Der Auflistungsausdruck in einer Variablendeklaration des Sammlungsbereichs muss als Wert klassifiziert werden, dessen Typ abfragbar ist. Wenn der Typ einer Sammlungsbereichsvariable weggelassen wird, wird sie abgeleitet, als Elementtyp des Auflistungsausdrucks oder Object wenn der Auflistungsausdruck keinen Elementtyp aufweist (d. h. nur eine Cast Methode definiert). Wenn der Auflistungsausdruck nicht abgefragt werden kann (d. h. der Elementtyp der Auflistung kann nicht abgeleitet werden), führt ein Kompilierungszeitfehler zu einem Fehler.
Eine Ausdrucksbereichsvariable ist eine Bereichsvariable, deren Wert von einem Ausdruck anstelle einer Auflistung berechnet wird. Im folgenden Beispiel führt der Select Abfrageoperator eine Ausdrucksbereichsvariable ein, die aus zwei Feldern berechnet wird cityState :
Dim cityStates = _
From cust As Customer In Customers _
Select cityState = cust.City & "," & cust.State _
Where cityState.Length() < 10
Eine Ausdrucksbereichsvariable ist nicht erforderlich, um auf eine andere Bereichsvariable zu verweisen, obwohl eine solche Variable einen zweifelhaften Wert aufweisen kann. Der ausdruck, der einer Ausdrucksbereichsvariablen zugewiesen ist, muss als Wert klassifiziert werden und muss bei Angabe implizit in den Typ der Bereichsvariablen konvertierbar sein.
Nur in einem Let-Operator kann eine Ausdrucksbereichsvariable ihren Typ angeben. In anderen Operatoren oder wenn der Typ nicht angegeben ist, wird die Ableitung des lokalen Variablentyps verwendet, um den Typ der Bereichsvariable zu bestimmen.
Eine Bereichsvariable muss den Regeln für das Deklarieren lokaler Variablen in Bezug auf die Schattenung entsprechen. Daher kann eine Bereichsvariable den Namen einer lokalen Variablen oder eines Parameters in der eingeschlossenen Methode oder einer anderen Bereichsvariable nicht ausblenden (es sei denn, der Abfrageoperator blendet alle aktuellen Bereichsvariablen im Bereich aus).
Abfragbare Typen
Abfrageausdrücke werden implementiert, indem der Ausdruck in Aufrufe bekannter Methoden für einen Auflistungstyp übersetzt wird. Diese gut definierten Methoden definieren den Elementtyp der abfragefähigen Auflistung sowie die Ergebnistypen von Abfrageoperatoren, die in der Auflistung ausgeführt werden. Jeder Abfrageoperator gibt die Methode oder Methoden an, in die der Abfrageoperator im Allgemeinen übersetzt wird, obwohl die spezifische Übersetzung von der Implementierung abhängig ist. Die Methoden werden in der Spezifikation mit einem allgemeinen Format angegeben, das wie folgt aussieht:
Function Select(selector As Func(Of T, R)) As CR
Für die Methoden gilt Folgendes:
Die Methode muss eine Instanz oder ein Erweiterungselement des Sammlungstyps sein und auf sie zugreifen können.
Die Methode kann generisch sein, vorausgesetzt, dass alle Typargumente ableiten können.
Die Methode kann überladen werden, in diesem Fall wird die Überladungsauflösung verwendet, um die genau zu verwendende Methode zu bestimmen.
Ein anderer Delegattyp kann anstelle des Stellvertretungstyps
Funcverwendet werden, vorausgesetzt, er hat dieselbe Signatur, einschließlich Rückgabetyp, wie der übereinstimmendeFuncTyp.Der Typ
System.Linq.Expressions.Expression(Of D)kann anstelle des DelegattypsFuncverwendet werden, vorausgesetzt, esDhandelt sich um einen Delegattyp mit derselben Signatur, einschließlich rückgabetyp, wie der übereinstimmendeFuncTyp.Der Typ
Tstellt den Elementtyp der Eingabeauflistung dar. Alle von einem Auflistungstyp definierten Methoden müssen denselben Eingabeelementtyp aufweisen, damit der Sammlungstyp abfragbar ist.Der Typ
Sstellt den Elementtyp der zweiten Eingabeauflistung im Fall von Abfrageoperatoren dar, die Verknüpfungen ausführen.Der Typ
Kstellt einen Schlüsseltyp im Fall von Abfrageoperatoren dar, die über eine Reihe von Bereichsvariablen verfügen, die als Schlüssel fungieren.Der Typ
Nstellt einen Typ dar, der als numerischer Typ verwendet wird (obwohl er immer noch ein benutzerdefinierter Typ und kein systeminterner numerischer Typ sein kann).Der Typ
Bstellt einen Typ dar, der in einem booleschen Ausdruck verwendet werden kann.Der Typ
Rstellt den Elementtyp der Ergebnisauflistung dar, wenn der Abfrageoperator eine Ergebnisauflistung erzeugt.Rhängt von der Anzahl der Bereichsvariablen im Bereich zum Abschluss des Abfrageoperators ab. Wenn sich eine einzelne Bereichsvariable im Bereich befindet, handelt es sich umRden Typ dieser Bereichsvariable. Im BeispielDim custNames = From c In Customers Select c.NameDas Ergebnis der Abfrage ist ein Sammlungstyp mit einem Elementtyp von
String. Wenn sich mehrere Bereichsvariablen im Bereich befinden, handelt es sich umReinen anonymen Typ, der alle Bereichsvariablen im Bereich alsKeyFelder enthält. Im Beispiel:Dim custNames = From c In Customers, o In c.Orders Select Name = c.Name, ProductName = o.ProductNameDas Ergebnis der Abfrage ist ein Sammlungstyp mit einem Elementtyp eines anonymen Typs mit einer schreibgeschützten Eigenschaft namens
NameTypStringund einer schreibgeschützten Eigenschaft mit dem NamenProductNamedes TypsString.Innerhalb eines Abfrageausdrucks sind anonyme Typen, die generiert werden, um Bereichsvariablen zu enthalten, transparent, was bedeutet, dass Bereichsvariablen immer ohne Qualifikation verfügbar sind. Im vorherigen Beispiel konnten beispielsweise die Bereichsvariablen
cohneoQualifikation imSelectAbfrageoperator aufgerufen werden, obwohl der Elementtyp der Eingabeauflistung ein anonymer Typ war.Der Typ
CXstellt einen Auflistungstyp dar, nicht unbedingt der Eingabeauflistungstyp, dessen Elementtyp ein TypXist.
Ein abfragebarer Sammlungstyp muss eine der folgenden Bedingungen erfüllen, in der Reihenfolge der Voreinstellung:
Sie muss eine konforme
SelectMethode definieren.Sie muss über eine der folgenden Methoden verfügen:
Function AsEnumerable() As CT Function AsQueryable() As CTdie aufgerufen werden kann, um eine abfragebare Auflistung abzurufen. Wenn beide Methoden bereitgestellt werden,
AsQueryablewird bevorzugt gegenüberAsEnumerable.Sie muss über eine Methode verfügen.
Function Cast(Of T)() As CTdie mit dem Typ der Bereichsvariablen aufgerufen werden kann, um eine abfragefähige Auflistung zu erzeugen.
Da die Ermittlung des Elementtyps einer Auflistung unabhängig von einem tatsächlichen Methodenaufruf erfolgt, kann die Anwendbarkeit bestimmter Methoden nicht bestimmt werden. Wenn Sie also den Elementtyp einer Auflistung bestimmen, wenn Instanzenmethoden mit bekannten Methoden übereinstimmen, werden alle Erweiterungsmethoden, die mit bekannten Methoden übereinstimmen, ignoriert.
Die Abfrageoperatorenübersetzung erfolgt in der Reihenfolge, in der die Abfrageoperatoren im Ausdruck auftreten. Es ist nicht erforderlich, dass ein Auflistungsobjekt alle methoden implementiert, die von allen Abfrageoperatoren benötigt werden, obwohl jedes Auflistungsobjekt mindestens den Select Abfrageoperator unterstützen muss. Wenn keine erforderliche Methode vorhanden ist, tritt ein Kompilierungszeitfehler auf. Bei der Bindung bekannter Methodennamen werden Nicht-Methoden für die Mehrfachvererbung in Schnittstellen- und Erweiterungsmethodenbindung ignoriert, obwohl Schattensemantik weiterhin angewendet wird. Beispiel:
Class Q1
Public Function [Select](selector As Func(Of Integer, Integer)) As Q1
End Function
End Class
Class Q2
Inherits Q1
Public [Select] As Integer
End Class
Module Test
Sub Main()
Dim qs As New Q2()
' Error: Q2.Select still hides Q1.Select
Dim zs = From q In qs Select q
End Sub
End Module
Standardabfrageindexer
Jeder abfragefähige Auflistungstyp, dessen Elementtyp und T nicht bereits über eine Standardeigenschaft verfügt, wird als Standardeigenschaft des folgenden allgemeinen Formulars betrachtet:
Public ReadOnly Default Property Item(index As Integer) As T
Get
Return Me.ElementAtOrDefault(index)
End Get
End Property
Auf die Standardeigenschaft kann nur mithilfe der Standardeigenschaftenzugriffssyntax verwiesen werden. auf die Standardeigenschaft kann nicht durch den Namen verwiesen werden. Beispiel:
Dim customers As IEnumerable(Of Customer) = ...
Dim customerThree = customers(2)
' Error, no such property
Dim customerFour = customers.Item(4)
Wenn der Sammlungstyp nicht über ein ElementAtOrDefault Element verfügt, tritt ein Kompilierungszeitfehler auf.
Vom Abfrageoperator
Der From Abfrageoperator führt eine Sammlungsbereichsvariable ein, die die einzelnen Elemente einer Auflistung darstellt, die abgefragt werden sollen.
FromQueryOperator
: LineTerminator? 'From' LineTerminator? CollectionRangeVariableDeclarationList
;
Beispiel: Der Abfrageausdruck:
From c As Customer In Customers ...
kann als gleichwertig betrachtet werden mit
For Each c As Customer In Customers
...
Next c
Wenn ein From Abfrageoperator mehrere Sammlungsbereichsvariablen deklariert oder nicht der erste From Abfrageoperator im Abfrageausdruck ist, wird jede neue Sammlungsbereichsvariable mit der vorhandenen Gruppe von Bereichsvariablen verknüpft . Das Ergebnis ist, dass die Abfrage über das Produktübergreifende aller Elemente in den verknüpften Auflistungen ausgewertet wird. Der Ausdruck:
From c In Customers _
From e In Employees _
...
kann man sich als gleichwertig mit folgendem Äquivalent überlegen:
For Each c In Customers
For Each e In Employees
...
Next e
Next c
und entspricht genau den:
From c In Customers, e In Employees ...
Die in früheren Abfrageoperatoren eingeführten Bereichsvariablen können in einem späteren From Abfrageoperator verwendet werden. Im folgenden Abfrageausdruck verweist der zweite From Abfrageoperator beispielsweise auf den Wert der ersten Bereichsvariable:
From c As Customer In Customers _
From o As Order In c.Orders _
Select c.Name, o
Mehrere Bereichsvariablen in einem From Abfrageoperator oder mehreren From Abfrageoperatoren werden nur unterstützt, wenn der Auflistungstyp eine oder beide der folgenden Methoden enthält:
Function SelectMany(selector As Func(Of T, CR)) As CR
Function SelectMany(selector As Func(Of T, CS), _
resultsSelector As Func(Of T, S, R)) As CR
Der Code
Dim xs() As Integer = ...
Dim ys() As Integer = ...
Dim zs = From x In xs, y In ys ...
wird in der Regel übersetzt in
Dim xs() As Integer = ...
Dim ys() As Integer = ...
Dim zs = _
xs.SelectMany( _
Function(x As Integer) ys, _
Function(x As Integer, y As Integer) New With {x, y})...
Hinweis:
From ist kein reserviertes Wort.
Verknüpfungsabfrageoperator
Der Join Abfrageoperator verknüpft vorhandene Bereichsvariablen mit einer neuen Sammlungsbereichsvariable und erzeugt eine einzelne Auflistung, deren Elemente basierend auf einem Gleichheitsausdruck miteinander verknüpft wurden.
JoinQueryOperator
: LineTerminator? 'Join' LineTerminator? CollectionRangeVariableDeclaration
JoinOrGroupJoinQueryOperator? LineTerminator? 'On' LineTerminator? JoinConditionList
;
JoinConditionList
: JoinCondition ( 'And' LineTerminator? JoinCondition )*
;
JoinCondition
: Expression 'Equals' LineTerminator? Expression
;
Beispiel:
Dim customersAndOrders = _
From cust In Customers _
Join ord In Orders On cust.ID Equals ord.CustomerID
Der Gleichheitsausdruck ist eingeschränkter als ein regulärer Gleichheitsausdruck:
Beide Ausdrücke müssen als Wert klassifiziert werden.
Beide Ausdrücke müssen auf mindestens eine Bereichsvariable verweisen.
Auf die im Verknüpfungsabfrageoperator deklarierte Bereichsvariable muss von einem der Ausdrücke verwiesen werden, und dieser Ausdruck darf nicht auf andere Bereichsvariablen verweisen.
Wenn die Typen der beiden Ausdrücke nicht den gleichen Typ aufweisen, dann
Wenn der Gleichheitsoperator für die beiden Typen definiert ist, sind beide Ausdrücke implizit konvertierbar und nicht
Object, und konvertieren Sie dann beide Ausdrücke in diesen Typ.Andernfalls, wenn ein dominanter Typ vorhanden ist, in den beide Ausdrücke implizit konvertiert werden können, konvertieren Sie beide Ausdrücke in diesen Typ.
Andernfalls tritt ein Kompilierungszeitfehler auf.
Die Ausdrücke werden mithilfe von Hashwerten (d. h. durch Aufrufen GetHashCode()) verglichen und nicht mithilfe von Gleichheitsoperatoren zur Effizienz. Ein Join Abfrageoperator kann mehrere Verknüpfungen oder Gleichheitsbedingungen im selben Operator ausführen. Ein Join Abfrageoperator wird nur unterstützt, wenn der Sammlungstyp eine Methode enthält:
Function Join(inner As CS, _
outerSelector As Func(Of T, K), _
innerSelector As Func(Of S, K), _
resultSelector As Func(Of T, S, R)) As CR
Der Code
Dim xs() As Integer = ...
Dim ys() As Integer = ...
Dim zs = From x In xs _
Join y In ys On x Equals y _
...
wird in der Regel übersetzt in
Dim xs() As Integer = ...
Dim ys() As Integer = ...
Dim zs = _
xs.Join( _
ys, _
Function(x As Integer) x, _
Function(y As Integer) y, _
Function(x As Integer, y As Integer) New With {x, y})...
Hinweis.Join, On und Equals sind keine reservierten Wörter.
Abfrageoperator zulassen
Der Let Abfrageoperator führt eine Ausdrucksbereichsvariable ein. Dies ermöglicht die Berechnung eines Zwischenwerts, sobald dieser mehrmals in späteren Abfrageoperatoren verwendet wird.
LetQueryOperator
: LineTerminator? 'Let' LineTerminator? ExpressionRangeVariableDeclarationList
;
Beispiel:
Dim taxedPrices = _
From o In Orders _
Let tax = o.Price * 0.088 _
Where tax > 3.50 _
Select o.Price, tax, total = o.Price + tax
kann man sich als gleichwertig mit folgendem Äquivalent überlegen:
For Each o In Orders
Dim tax = o.Price * 0.088
...
Next o
Ein Let Abfrageoperator wird nur unterstützt, wenn der Sammlungstyp eine Methode enthält:
Function Select(selector As Func(Of T, R)) As CR
Der Code
Dim xs() As Integer = ...
Dim zs = From x In xs _
Let y = x * 10 _
...
wird in der Regel übersetzt in
Dim xs() As Integer = ...
Dim zs = _
xs.Select(Function(x As Integer) New With {x, .y = x * 10})...
Abfrageoperator auswählen
Der Select Abfrageoperator ähnelt dem Let Abfrageoperator, in dem Ausdrucksbereichsvariablen eingeführt werden. Ein Select Abfrageoperator blendet jedoch die derzeit verfügbaren Bereichsvariablen aus, anstatt sie hinzuzufügen. Außerdem wird der Typ einer ausdrucksbereichsvariablen, die von einem Select Abfrageoperator eingeführt wurde, immer mithilfe von Regeln für den lokalen Variablentyp abgeleitet. Ein expliziter Typ kann nicht angegeben werden, und wenn kein Typ abgeleitet werden kann, tritt ein Kompilierungszeitfehler auf.
SelectQueryOperator
: LineTerminator? 'Select' LineTerminator? ExpressionRangeVariableDeclarationList
;
Beispiel:
Dim smiths = _
From cust In Customers _
Select name = cust.name _
Where name.EndsWith("Smith")
der Where Abfrageoperator hat nur Zugriff auf die name Bereichsvariable, die vom Select Operator eingeführt wurde. Wenn der Where Operator versucht hätte, auf diese zu verweisen cust, wäre ein Kompilierungszeitfehler aufgetreten.
Anstatt explizit die Namen der Bereichsvariablen anzugeben, kann ein Select Abfrageoperator die Namen der Bereichsvariablen mit den gleichen Regeln wie Die Erstellungsausdrücke des anonymen Typs ableiten. Beispiel:
Dim custAndOrderNames = _
From cust In Customers, ord In cust.Orders _
Select cust.name, ord.ProductName _
Where name.EndsWith("Smith")
Wenn der Name der Bereichsvariable nicht angegeben wird und kein Name abgeleitet werden kann, tritt ein Kompilierungszeitfehler auf. Wenn der Select Abfrageoperator nur einen einzelnen Ausdruck enthält, tritt kein Fehler auf, wenn ein Name für diese Bereichsvariable nicht abgeleitet werden kann, die Bereichsvariable jedoch namelos ist. Beispiel:
Dim custAndOrderNames = _
From cust In Customers, ord In cust.Orders _
Select cust.Name & " bought " & ord.ProductName _
Take 10
Wenn in einem Select Abfrageoperator zwischen dem Zuweisen eines Namens zu einer Bereichsvariablen und einem Gleichheitsausdruck eine Mehrdeutigkeit besteht, wird die Namenszuweisung bevorzugt. Beispiel:
Dim badCustNames = _
From c In Customers _
Let name = "John Smith" _
Select name = c.Name ' Creates a range variable named "name"
Dim goodCustNames = _
From c In Customers _
Let name = "John Smith" _
Select match = (name = c.Name)
Jeder Ausdruck im Select Abfrageoperator muss als Wert klassifiziert werden. Ein Select Abfrageoperator wird nur unterstützt, wenn der Sammlungstyp eine Methode enthält:
Function Select(selector As Func(Of T, R)) As CR
Der Code
Dim xs() As Integer = ...
Dim zs = From x In xs _
Select x, y = x * 10 _
...
wird in der Regel übersetzt in
Dim xs() As Integer = ...
Dim zs = _
xs.Select(Function(x As Integer) New With {x, .y = x * 10})...
Distinct Query Operator
Der Distinct Abfrageoperator schränkt die Werte in einer Auflistung nur auf diejenigen mit unterschiedlichen Werten ein, wie durch den Vergleich des Elementtyps für die Gleichheit bestimmt.
DistinctQueryOperator
: LineTerminator? 'Distinct' LineTerminator?
;
Beispielsweise die Abfrage:
Dim distinctCustomerPrice = _
From cust In Customers, ord In cust.Orders _
Select cust.Name, ord.Price _
Distinct
gibt nur eine Zeile für jede unterschiedliche Kopplung von Kundennamen und Bestellpreis zurück, auch wenn der Kunde mehrere Bestellungen mit demselben Preis hat. Ein Distinct Abfrageoperator wird nur unterstützt, wenn der Sammlungstyp eine Methode enthält:
Function Distinct() As CT
Der Code
Dim xs() As Integer = ...
Dim zs = From x In xs _
Distinct _
...
wird in der Regel übersetzt in
Dim xs() As Integer = ...
Dim zs = xs.Distinct()...
Hinweis:
Distinct ist kein reserviertes Wort.
Where Query Operator
Der Where Abfrageoperator schränkt die Werte in einer Auflistung auf diejenigen ein, die eine bestimmte Bedingung erfüllen.
WhereQueryOperator
: LineTerminator? 'Where' LineTerminator? BooleanExpression
;
Ein Where Abfrageoperator verwendet einen booleschen Ausdruck, der für jeden Satz von Bereichsvariablenwerten ausgewertet wird. Wenn der Wert des Ausdrucks "true" ist, werden die Werte in der Ausgabeauflistung angezeigt, andernfalls werden die Werte übersprungen. Beispiel: Der Abfrageausdruck:
From cust In Customers, ord In Orders _
Where cust.ID = ord.CustomerID _
...
kann als gleichwertig mit der geschachtelten Schleife betrachtet werden
For Each cust In Customers
For Each ord In Orders
If cust.ID = ord.CustomerID Then
...
End If
Next ord
Next cust
Ein Where Abfrageoperator wird nur unterstützt, wenn der Sammlungstyp eine Methode enthält:
Function Where(predicate As Func(Of T, B)) As CT
Der Code
Dim xs() As Integer = ...
Dim zs = From x In xs _
Where x < 10 _
...
wird in der Regel übersetzt in
Dim xs() As Integer = ...
Dim zs = _
xs.Where(Function(x As Integer) x < 10)...
Hinweis:
Where ist kein reserviertes Wort.
Partitionsabfrageoperatoren
PartitionQueryOperator
: LineTerminator? 'Take' LineTerminator? Expression
| LineTerminator? 'Take' 'While' LineTerminator? BooleanExpression
| LineTerminator? 'Skip' LineTerminator? Expression
| LineTerminator? 'Skip' 'While' LineTerminator? BooleanExpression
;
Der Take Abfrageoperator führt zu den ersten n Elementen einer Auflistung. Bei Verwendung mit dem While Modifizierer führt der Take Operator zu den ersten n Elementen einer Auflistung, die einen booleschen Ausdruck erfüllen. Der Skip Operator überspringt die ersten n Elemente einer Auflistung und gibt dann den Rest der Auflistung zurück. Bei Verwendung in Verbindung mit dem While Modifizierer überspringt der Skip Operator die ersten n Elemente einer Auflistung, die einem booleschen Ausdruck entsprechen, und gibt dann den Rest der Auflistung zurück. Die Ausdrücke in einem Take Oder Skip Abfrageoperator müssen als Wert klassifiziert werden.
Ein Take Abfrageoperator wird nur unterstützt, wenn der Sammlungstyp eine Methode enthält:
Function Take(count As N) As CT
Ein Skip Abfrageoperator wird nur unterstützt, wenn der Sammlungstyp eine Methode enthält:
Function Skip(count As N) As CT
Ein Take While Abfrageoperator wird nur unterstützt, wenn der Sammlungstyp eine Methode enthält:
Function TakeWhile(predicate As Func(Of T, B)) As CT
Ein Skip While Abfrageoperator wird nur unterstützt, wenn der Sammlungstyp eine Methode enthält:
Function SkipWhile(predicate As Func(Of T, B)) As CT
Der Code
Dim xs() As Integer = ...
Dim zs = From x In xs _
Skip 10 _
Take 5 _
Skip While x < 10 _
Take While x > 5 _
...
wird in der Regel übersetzt in
Dim xs() As Integer = ...
Dim zs = _
xs.Skip(10). _
Take(5). _
SkipWhile(Function(x) x < 10). _
TakeWhile(Function(x) x > 5)...
Hinweis:
Take und Skip sind keine reservierten Wörter.
Order By Query Operator
Der Order By Abfrageoperator sortiert die Werte, die in den Bereichsvariablen angezeigt werden.
OrderByQueryOperator
: LineTerminator? 'Order' 'By' LineTerminator? OrderExpressionList
;
OrderExpressionList
: OrderExpression ( Comma OrderExpression )*
;
OrderExpression
: Expression Ordering?
;
Ordering
: 'Ascending' | 'Descending'
;
Ein Order By Abfrageoperator verwendet Ausdrücke, die die Schlüsselwerte angeben, mit denen die Iterationsvariablen sortiert werden sollen. Die folgende Abfrage gibt z. B. Produkte nach Preis sortiert zurück:
Dim productsByPrice = _
From p In Products _
Order By p.Price _
Select p.Name
Eine Sortierung kann als Ascending, in diesem Fall kleinere Werte vor größeren Werten oder Descending, in diesem Fall größere Werte vor kleineren Werten, markiert werden. Die Standardeinstellung für eine Sortierung, wenn keine angegeben Ascendingist. Beispielsweise gibt die folgende Abfrage Produkte nach Preis sortiert mit dem teuersten Produkt zuerst zurück:
Dim productsByPriceDesc = _
From p In Products _
Order By p.Price Descending _
Select p.Name
Der Order By Abfrageoperator kann mehrere Ausdrücke für die Sortierung angeben, in diesem Fall wird die Auflistung auf geschachtelte Weise sortiert. Die folgende Abfrage ordnet Kunden z. B. nach Bundesland, dann nach Ort in jedem Bundesland und dann nach POSTLEITZAHL innerhalb jeder Stadt an:
Dim customersByLocation = _
From c In Customers _
Order By c.State, c.City, c.ZIP _
Select c.Name, c.State, c.City, c.ZIP
Die Ausdrücke in einem Order By Abfrageoperator müssen als Wert klassifiziert werden. Ein Order By Abfrageoperator wird nur unterstützt, wenn der Auflistungstyp eine oder beide der folgenden Methoden enthält:
Function OrderBy(keySelector As Func(Of T, K)) As CT
Function OrderByDescending(keySelector As Func(Of T, K)) As CT
Der Rückgabetyp CT muss eine sortierte Auflistung sein. Eine sortierte Auflistung ist ein Auflistungstyp, der eine oder beide Methoden enthält:
Function ThenBy(keySelector As Func(Of T, K)) As CT
Function ThenByDescending(keySelector As Func(Of T, K)) As CT
Der Code
Dim xs() As Integer = ...
Dim zs = From x In xs _
Order By x Ascending, x Mod 2 Descending _
...
wird in der Regel übersetzt in
Dim xs() As Integer = ...
Dim zs = _
xs.OrderBy(Function(x) x).ThenByDescending(Function(x) x Mod 2)...
Hinweis: Da Abfrageoperatoren einfach Methoden, die einen bestimmten Abfragevorgang implementieren, eine Syntax zuordnen, wird die Order-Erhaltung nicht von der Sprache bestimmt und durch die Implementierung des Operators selbst bestimmt. Dies ist sehr ähnlich wie benutzerdefinierte Operatoren, da die Implementierung, um den Additionsoperator für einen benutzerdefinierten numerischen Typ zu überladen, möglicherweise nichts wie eine Ergänzung ausführt. Um die Vorhersagbarkeit zu erhalten, wird natürlich nicht empfohlen, etwas zu implementieren, das nicht den Erwartungen der Benutzer entspricht.
Hinweis:
Order und By sind keine reservierten Wörter.
Nach Abfrageoperator gruppieren
Der Group By Abfrageoperator gruppiert die Bereichsvariablen im Bereich basierend auf einem oder mehreren Ausdrücken und erzeugt dann neue Bereichsvariablen basierend auf diesen Gruppierungen.
GroupByQueryOperator
: LineTerminator? 'Group' ( LineTerminator? ExpressionRangeVariableDeclarationList )?
LineTerminator? 'By' LineTerminator? ExpressionRangeVariableDeclarationList
LineTerminator? 'Into' LineTerminator? ExpressionRangeVariableDeclarationList
;
Die folgende Abfrage gruppiert beispielsweise alle Kunden nach State, und berechnet dann die Anzahl und das durchschnittliche Alter jeder Gruppe:
Dim averageAges = _
From cust In Customers _
Group By cust.State _
Into Count(), Average(cust.Age)
Der Group By Abfrageoperator verfügt über drei Klauseln: die optionale Group Klausel, die By Klausel und die Into Klausel. Die Group Klausel hat dieselbe Syntax und Wirkung wie ein Select Abfrageoperator, mit der Ausnahme, dass sie nur die bereichsvariablen in der Into Klausel und nicht die By Klausel betrifft. Beispiel:
Dim averageAges = _
From cust In Customers _
Group cust.Age By cust.State _
Into Count(), Average(Age)
Die By Klausel deklariert Ausdrucksbereichsvariablen, die als Schlüsselwerte im Gruppierungsvorgang verwendet werden. Die Into Klausel ermöglicht die Deklaration von Ausdrucksbereichsvariablen, die Aggregationen über die einzelnen Gruppen berechnen, die durch die By Klausel gebildet werden. Innerhalb der Into Klausel kann der Ausdrucksbereichsvariable nur einem Ausdruck zugewiesen werden, bei dem es sich um einen Methodenaufruf einer Aggregatfunktion handelt. Eine Aggregatfunktion ist eine Funktion für den Sammlungstyp der Gruppe (die möglicherweise nicht unbedingt derselbe Sammlungstyp der ursprünglichen Auflistung ist), die wie eine der folgenden Methoden aussieht:
Function _name_() As _type_
Function _name_(selector As Func(Of T, R)) As R
Wenn eine Aggregatfunktion ein Stellvertretungsargument verwendet, kann der Aufrufausdruck einen Argumentausdruck aufweisen, der als Wert klassifiziert werden muss. Der Argumentausdruck kann die Bereichsvariablen verwenden, die sich im Bereich befinden. innerhalb des Aufrufs einer Aggregatfunktion stellen diese Bereichsvariablen die Werte in der Gruppe dar, die gebildet wird, nicht alle Werte in der Auflistung. Im ursprünglichen Beispiel in diesem Abschnitt berechnet die Average Funktion beispielsweise den Durchschnitt der Altersstufen der Kunden pro Bundesland und nicht für alle Kunden zusammen.
Alle Auflistungstypen werden als Aggregatfunktion Group definiert, die keine Parameter akzeptiert und einfach die Gruppe zurückgibt. Andere Standardaggregatfunktionen, die ein Sammlungstyp bereitstellen kann, sind:
Count und LongCount, die die Anzahl der Elemente in der Gruppe oder die Anzahl der Elemente in der Gruppe zurückgeben, die einen booleschen Ausdruck erfüllen.
Count und LongCount werden nur unterstützt, wenn der Sammlungstyp eine der Methoden enthält:
Function Count() As N
Function Count(selector As Func(Of T, B)) As N
Function LongCount() As N
Function LongCount(selector As Func(Of T, B)) As N
Sum, die die Summe eines Ausdrucks über alle Elemente in der Gruppe zurückgibt.
Sum wird nur unterstützt, wenn der Auflistungstyp eine der Methoden enthält:
Function Sum() As N
Function Sum(selector As Func(Of T, N)) As N
Min der den Minimalwert eines Ausdrucks für alle Elemente in der Gruppe zurückgibt.
Min wird nur unterstützt, wenn der Auflistungstyp eine der Methoden enthält:
Function Min() As N
Function Min(selector As Func(Of T, N)) As N
Max, der den Maximalwert eines Ausdrucks für alle Elemente in der Gruppe zurückgibt.
Max wird nur unterstützt, wenn der Auflistungstyp eine der Methoden enthält:
Function Max() As N
Function Max(selector As Func(Of T, N)) As N
Average, der den Mittelwert eines Ausdrucks über alle Elemente in der Gruppe zurückgibt.
Average wird nur unterstützt, wenn der Auflistungstyp eine der Methoden enthält:
Function Average() As N
Function Average(selector As Func(Of T, N)) As N
Any, der bestimmt, ob eine Gruppe Mitglieder enthält oder ob ein boolescher Ausdruck für ein beliebiges Element in der Gruppe wahr ist.
Any gibt einen Wert zurück, der in einem booleschen Ausdruck verwendet werden kann und nur unterstützt wird, wenn der Auflistungstyp eine der Methoden enthält:
Function Any() As B
Function Any(predicate As Func(Of T, B)) As B
All, der bestimmt, ob ein boolescher Ausdruck für alle Elemente in der Gruppe wahr ist.
All gibt einen Wert zurück, der in einem booleschen Ausdruck verwendet werden kann und nur unterstützt wird, wenn der Auflistungstyp eine Methode enthält:
Function All(predicate As Func(Of T, B)) As B
Nach einem Group By Abfrageoperator sind die Bereichsvariablen, die zuvor im Bereich liegen, ausgeblendet, und die bereichsvariablen, die von den By Und-Klauseln Into eingeführt wurden, sind verfügbar. Ein Group By Abfrageoperator wird nur unterstützt, wenn der Sammlungstyp die Methode enthält:
Function GroupBy(keySelector As Func(Of T, K), _
resultSelector As Func(Of K, CT, R)) As CR
Bereichsvariablendeklarationen in der Group Klausel werden nur unterstützt, wenn der Sammlungstyp die Methode enthält:
Function GroupBy(keySelector As Func(Of T, K), _
elementSelector As Func(Of T, S), _
resultSelector As Func(Of K, CS, R)) As CR
Der Code
Dim xs() As Integer = ...
Dim zs = From x In xs _
Group y = x * 10, z = x / 10 By evenOdd = x Mod 2 _
Into Sum(y), Average(z) _
...
wird in der Regel übersetzt in
Dim xs() As Integer = ...
Dim zs = _
xs.GroupBy( _
Function(x As Integer) x Mod 2, _
Function(x As Integer) New With {.y = x * 10, .z = x / 10}, _
Function(evenOdd, group) New With { _
evenOdd, _
.Sum = group.Sum(Function(e) e.y), _
.Average = group.Average(Function(e) e.z)})...
Hinweis.Group, By, und Into sind keine reservierten Wörter.
Aggregatabfrageoperator
Der Aggregate Abfrageoperator führt eine ähnliche Funktion wie der Group By Operator aus, es sei denn, er ermöglicht das Aggregieren über Bereits gebildete Gruppen. Da die Gruppe bereits gebildet wurde, blendet die Into Klausel eines Aggregate Abfrageoperators die Bereichsvariablen nicht aus (auf diese Weise Aggregate ist es eher wie ein Let, und Group By ist eher wie ein Select).
AggregateQueryOperator
: LineTerminator? 'Aggregate' LineTerminator? CollectionRangeVariableDeclaration QueryOperator*
LineTerminator? 'Into' LineTerminator? ExpressionRangeVariableDeclarationList
;
Die folgende Abfrage aggregiert beispielsweise die Summe aller Bestellungen, die von Kunden in Washington getätigt wurden:
Dim orderTotals = _
From cust In Customers _
Where cust.State = "WA" _
Aggregate order In cust.Orders _
Into Sum(order.Total)
Das Ergebnis dieser Abfrage ist eine Auflistung, deren Elementtyp ein anonymer Typ mit einer Eigenschaft mit dem Namen cust "typed" Customer und einer Eigenschaft mit dem Namen Sum "typed" Integerist.
Im Gegensatz dazu Group Bykönnen zusätzliche Abfrageoperatoren zwischen den Aggregate Und-Klauseln Into platziert werden. Zwischen einer Aggregate Klausel und dem Ende der Into Klausel können alle Bereichsvariablen im Bereich verwendet werden, einschließlich der von der Aggregate Klausel deklarierten Variablen. Die folgende Abfrage aggregiert beispielsweise die Summe aller Bestellungen, die von Kunden in Washington vor 2006 getätigt wurden:
Dim orderTotals = _
From cust In Customers _
Where cust.State = "WA" _
Aggregate order In cust.Orders _
Where order.Date <= #01/01/2006# _
Into Sum = Sum(order.Total)
Der Aggregate Operator kann auch verwendet werden, um einen Abfrageausdruck zu starten. In diesem Fall ist das Ergebnis des Abfrageausdrucks der einzelne Wert, der von der Into Klausel berechnet wird. Die folgende Abfrage berechnet beispielsweise die Summe aller Bestellsummen vor dem 1. Januar 2006:
Dim ordersTotal = _
Aggregate order In Orders _
Where order.Date <= #01/01/2006# _
Into Sum(order.Total)
Das Ergebnis der Abfrage ist ein einzelner Integer Wert. Ein Aggregate Abfrageoperator ist immer verfügbar (obwohl die Aggregatfunktion auch verfügbar sein muss, damit der Ausdruck gültig ist). Der Code
Dim xs() As Integer = ...
Dim zs = _
Aggregate x In xs _
Where x < 5 _
Into Sum()
wird in der Regel übersetzt in
Dim xs() As Integer = ...
Dim zs = _
xs.Where(Function(x) x < 5).Sum()
Hinweis:
Aggregate und Into sind keine reservierten Wörter.
Gruppenbeitrittsabfrageoperator
Der Group Join Abfrageoperator kombiniert die Funktionen der Join Operatoren und Group By Abfrageoperatoren in einem einzigen Operator.
Group Join verknüpft zwei Auflistungen basierend auf übereinstimmenden Schlüsseln, die aus den Elementen extrahiert wurden, und gruppiert alle Elemente auf der rechten Seite der Verknüpfung, die einem bestimmten Element auf der linken Seite der Verknüpfung entsprechen. Daher erzeugt der Operator eine Reihe hierarchischer Ergebnisse.
GroupJoinQueryOperator
: LineTerminator? 'Group' 'Join' LineTerminator? CollectionRangeVariableDeclaration
JoinOrGroupJoinQueryOperator? LineTerminator? 'On' LineTerminator? JoinConditionList
LineTerminator? 'Into' LineTerminator? ExpressionRangeVariableDeclarationList
;
Die folgende Abfrage erzeugt z. B. Elemente, die den Namen eines einzelnen Kunden, eine Gruppe aller Bestellungen und den Gesamtbetrag aller dieser Bestellungen enthalten:
Dim custsWithOrders = _
From cust In Customers _
Group Join order In Orders On cust.ID Equals order.CustomerID _
Into Orders = Group, OrdersTotal = Sum(order.Total) _
Select cust.Name, Orders, OrdersTotal
Das Ergebnis der Abfrage ist eine Auflistung, deren Elementtyp ein anonymer Typ mit drei Eigenschaften ist: Name, typiert als String, Orders als Auflistung eingegeben, deren Elementtyp ist Orderund OrdersTotalals eingegeben als Integer. Ein Group Join Abfrageoperator wird nur unterstützt, wenn der Sammlungstyp die Methode enthält:
Function GroupJoin(inner As CS, _
outerSelector As Func(Of T, K), _
innerSelector As Func(Of S, K), _
resultSelector As Func(Of T, CS, R)) As CR
Der Code
Dim xs() As Integer = ...
Dim ys() As Integer = ...
Dim zs = From x In xs _
Group Join y in ys On x Equals y _
Into g = Group _
...
wird in der Regel übersetzt in
Dim xs() As Integer = ...
Dim ys() As Integer = ...
Dim zs = _
xs.GroupJoin( _
ys, _
Function(x As Integer) x, _
Function(y As Integer) y, _
Function(x, group) New With {x, .g = group})...
Hinweis.Group, Join, und Into sind keine reservierten Wörter.
Bedingte Ausdrücke
Ein bedingter If Ausdruck testet einen Ausdruck und gibt einen Wert zurück.
ConditionalExpression
: 'If' OpenParenthesis BooleanExpression Comma Expression Comma Expression CloseParenthesis
| 'If' OpenParenthesis Expression Comma Expression CloseParenthesis
;
Im Gegensatz zur IIF Laufzeitfunktion wertet ein bedingter Ausdruck die Operanden jedoch nur bei Bedarf aus. Daher löst der Ausdruck If(c Is Nothing, c.Name, "Unknown") beispielsweise keine Ausnahme aus, wenn der Wert lautet cNothing. Der bedingte Ausdruck hat zwei Formen: eine, die zwei Operanden akzeptiert, und eine, die drei Operanden akzeptiert.
Wenn drei Operanden bereitgestellt werden, müssen alle drei Ausdrücke als Werte klassifiziert werden, und der erste Operand muss ein boolescher Ausdruck sein. Wenn das Ergebnis des Ausdrucks "true" ist, ist der zweite Ausdruck das Ergebnis des Operators, andernfalls ist der dritte Ausdruck das Ergebnis des Operators. Der Ergebnistyp des Ausdrucks ist der dominante Typ zwischen den Typen des zweiten und dritten Ausdrucks. Wenn kein dominanter Typ vorhanden ist, tritt ein Kompilierungszeitfehler auf.
Wenn zwei Operanden bereitgestellt werden, müssen beide Operanden als Werte klassifiziert werden, und der erste Operand muss entweder ein Bezugstyp oder ein Nullwerttyp sein. Der Ausdruck If(x, y) wird dann als ob der Ausdruck If(x IsNot Nothing, x, y)mit zwei Ausnahmen ausgewertet. Zuerst wird der erste Ausdruck nur einmal ausgewertet, und zweitens, wenn der Typ des zweiten Operanden ein nicht nullabler Werttyp ist und der typ des ersten Operanden lautet, wird der ? Wert aus dem Typ des ersten Operanden entfernt, wenn der dominante Typ für den Ergebnistyp des Ausdrucks bestimmt wird. Beispiel:
Module Test
Sub Main()
Dim x?, y As Integer
Dim a?, b As Long
a = If(x, a) ' Result type: Long?
y = If(x, 0) ' Result type: Integer
End Sub
End Module
In beiden Formen des Ausdrucks wird Nothingbei einem Operanden der Typ nicht verwendet, um den dominanten Typ zu bestimmen. Im Fall des Ausdrucks If(<expression>, Nothing, Nothing)gilt der dominante Typ als Object.
XML-Literalausdrücke
Ein XML-Literalausdruck stellt einen XML-Wert (eXtensible Markup Language) 1.0 dar.
XMLLiteralExpression
: XMLDocument
| XMLElement
| XMLProcessingInstruction
| XMLComment
| XMLCDATASection
;
Das Ergebnis eines XML-Literalausdrucks ist ein Wert, der als einer der Typen aus dem System.Xml.Linq Namespace eingegeben wird. Wenn die Typen in diesem Namespace nicht verfügbar sind, verursacht ein XML-Literalausdruck einen Kompilierungszeitfehler. Die Werte werden über Konstruktoraufrufe generiert, die aus dem XML-Literalausdruck übersetzt werden. Der Code:
Dim book As System.Xml.Linq.XElement = _
<book title="My book"></book>
entspricht ungefähr dem Code:
Dim book As System.Xml.Linq.XElement = _
New System.Xml.Linq.XElement( _
"book", _
New System.Xml.Linq.XAttribute("title", "My book"))
Ein XML-Literalausdruck kann die Form eines XML-Dokuments, eines XML-Elements, einer XML-Verarbeitungsanweisung, eines XML-Kommentars oder eines CDATA-Abschnitts annehmen.
Hinweis: Diese Spezifikation enthält nur genügend XML-Beschreibung, um das Verhalten der Visual Basic-Sprache zu beschreiben. Weitere Informationen zu XML finden Sie unter http://www.w3.org/TR/REC-xml/.
Lexikalische Regeln
XMLCharacter
: '<Unicode tab character (0x0009)>'
| '<Unicode linefeed character (0x000A)>'
| '<Unicode carriage return character (0x000D)>'
| '<Unicode characters 0x0020 - 0xD7FF>'
| '<Unicode characters 0xE000 - 0xFFFD>'
| '<Unicode characters 0x10000 - 0x10FFFF>'
;
XMLString
: XMLCharacter+
;
XMLWhitespace
: XMLWhitespaceCharacter+
;
XMLWhitespaceCharacter
: '<Unicode carriage return character (0x000D)>'
| '<Unicode linefeed character (0x000A)>'
| '<Unicode space character (0x0020)>'
| '<Unicode tab character (0x0009)>'
;
XMLNameCharacter
: XMLLetter
| XMLDigit
| '.'
| '-'
| '_'
| ':'
| XMLCombiningCharacter
| XMLExtender
;
XMLNameStartCharacter
: XMLLetter
| '_'
| ':'
;
XMLName
: XMLNameStartCharacter XMLNameCharacter*
;
XMLLetter
: '<Unicode character as defined in the Letter production of the XML 1.0 specification>'
;
XMLDigit
: '<Unicode character as defined in the Digit production of the XML 1.0 specification>'
;
XMLCombiningCharacter
: '<Unicode character as defined in the CombiningChar production of the XML 1.0 specification>'
;
XMLExtender
: '<Unicode character as defined in the Extender production of the XML 1.0 specification>'
;
XML-Literalausdrücke werden mithilfe der lexikalischen Regeln von XML anstelle der lexikalischen Regeln regulärer Visual Basic-Code interpretiert. Die beiden Regelsätze unterscheiden sich in der Regel auf die folgenden Arten:
Leerraum ist in XML von Bedeutung. Daher gibt die Grammatik für XML-Literalausdrücke explizit an, wo Leerzeichen zulässig sind. Leerzeichen bleiben nicht erhalten, außer wenn sie im Kontext von Zeichendaten innerhalb eines Elements auftreten. Beispiel:
' The following element preserves no whitespace Dim e1 = _ <customer> <name>Bob</> </> ' The following element preserves all of the whitespace Dim e2 = _ <customer> Bob </>Die XML-End-of-Line-Leerzeichen werden gemäß der XML-Spezifikation normalisiert.
BEI XML wird die Groß-/Kleinschreibung beachtet. Schlüsselwörter müssen genau mit der Groß-/Kleinschreibung übereinstimmen, andernfalls tritt ein Kompilierungsfehler auf.
Zeilenterminatoren werden als Leerzeichen in XML betrachtet. Daher werden in XML-Literalausdrücken keine Zeilenfortsetzungszeichen benötigt.
XML akzeptiert keine Zeichen mit voller Breite. Wenn Zeichen mit voller Breite verwendet werden, tritt ein Kompilierungszeitfehler auf.
Eingebettete Ausdrücke
XML-Literalausdrücke können eingebettete Ausdrücke enthalten. Ein eingebetteter Ausdruck ist ein Visual Basic-Ausdruck, der ausgewertet und zum Ausfüllen eines oder mehrerer Werte an der Position des eingebetteten Ausdrucks verwendet wird.
XMLEmbeddedExpression
: '<' '%' '=' LineTerminator? Expression LineTerminator? '%' '>'
;
Der folgende Code platziert beispielsweise die Zeichenfolge John Smith als Wert des XML-Elements:
Dim name as String = "John Smith"
Dim element As System.Xml.Linq.XElement = <customer><%= name %></customer>
Ausdrücke können in eine Reihe von Kontexten eingebettet werden. Der folgende Code erzeugt z. B. ein Element mit dem Namen customer:
Dim name As String = "customer"
Dim element As System.Xml.Linq.XElement = <<%= name %>>John Smith</>
Jeder Kontext, in dem ein eingebetteter Ausdruck verwendet werden kann, gibt die Typen an, die akzeptiert werden. Wenn im Kontext des Ausdrucksteils eines eingebetteten Ausdrucks die normalen lexikalischen Regeln für Visual Basic-Code weiterhin gelten, müssen beispielsweise Zeilenfortsetzungen verwendet werden:
' Visual Basic expression uses line continuation, XML does not
Dim element As System.Xml.Linq.XElement = _
<<%= name & _
name %>>John
Smith</>
XML-Dokumente
XMLDocument
: XMLDocumentPrologue XMLMisc* XMLDocumentBody XMLMisc*
;
XMLDocumentPrologue
: '<' '?' 'xml' XMLVersion XMLEncoding? XMLStandalone? XMLWhitespace? '?' '>'
;
XMLVersion
: XMLWhitespace 'version' XMLWhitespace? '=' XMLWhitespace? XMLVersionNumberValue
;
XMLVersionNumberValue
: SingleQuoteCharacter '1' '.' '0' SingleQuoteCharacter
| DoubleQuoteCharacter '1' '.' '0' DoubleQuoteCharacter
;
XMLEncoding
: XMLWhitespace 'encoding' XMLWhitespace? '=' XMLWhitespace? XMLEncodingNameValue
;
XMLEncodingNameValue
: SingleQuoteCharacter XMLEncodingName SingleQuoteCharacter
| DoubleQuoteCharacter XMLEncodingName DoubleQuoteCharacter
;
XMLEncodingName
: XMLLatinAlphaCharacter XMLEncodingNameCharacter*
;
XMLEncodingNameCharacter
: XMLUnderscoreCharacter
| XMLLatinAlphaCharacter
| XMLNumericCharacter
| XMLPeriodCharacter
| XMLDashCharacter
;
XMLLatinAlphaCharacter
: '<Unicode Latin alphabetic character (0x0041-0x005a, 0x0061-0x007a)>'
;
XMLNumericCharacter
: '<Unicode digit character (0x0030-0x0039)>'
;
XMLHexNumericCharacter
: XMLNumericCharacter
| '<Unicode Latin hex alphabetic character (0x0041-0x0046, 0x0061-0x0066)>'
;
XMLPeriodCharacter
: '<Unicode period character (0x002e)>'
;
XMLUnderscoreCharacter
: '<Unicode underscore character (0x005f)>'
;
XMLDashCharacter
: '<Unicode dash character (0x002d)>'
;
XMLStandalone
: XMLWhitespace 'standalone' XMLWhitespace? '=' XMLWhitespace? XMLYesNoValue
;
XMLYesNoValue
: SingleQuoteCharacter XMLYesNo SingleQuoteCharacter
| DoubleQuoteCharacter XMLYesNo DoubleQuoteCharacter
;
XMLYesNo
: 'yes'
| 'no'
;
XMLMisc
: XMLComment
| XMLProcessingInstruction
| XMLWhitespace
;
XMLDocumentBody
: XMLElement
| XMLEmbeddedExpression
;
Ein XML-Dokument führt zu einem Wert, der als System.Xml.Linq.XDocument. Im Gegensatz zur XML 1.0-Spezifikation sind XML-Dokumente in XML-Literalausdrücken erforderlich, um den PROlog des XML-Dokuments anzugeben. XML-Literalausdrücke ohne den XML-Dokumentprolog werden als ihre einzelne Entität interpretiert. Beispiel:
Dim doc As System.Xml.Linq.XDocument = _
<?xml version="1.0"?>
<?instruction?>
<customer>Bob</>
Dim pi As System.Xml.Linq.XProcessingInstruction = _
<?instruction?>
Ein XML-Dokument kann einen eingebetteten Ausdruck enthalten, dessen Typ ein beliebiger Typ sein kann; zur Laufzeit muss das Objekt jedoch die Anforderungen des XDocument Konstruktors erfüllen, oder ein Laufzeitfehler tritt auf.
Im Gegensatz zu regulärem XML unterstützen XML-Dokumentausdrücke DTDs (Dokumenttypdeklarationen) nicht. Darüber hinaus wird das Codierungsattribut, falls angegeben, ignoriert, da die Codierung des Xml-Literalausdrucks immer mit der Codierung der Quelldatei selbst identisch ist.
Hinweis: Obwohl das Codierungsattribut ignoriert wird, ist es weiterhin gültiges Attribut, um die Möglichkeit zu erhalten, gültige XML 1.0-Dokumente in Quellcode einzuschließen.
XML-Elemente
XMLElement
: XMLEmptyElement
| XMLElementStart XMLContent XMLElementEnd
;
XMLEmptyElement
: '<' XMLQualifiedNameOrExpression XMLAttribute* XMLWhitespace? '/' '>'
;
XMLElementStart
: '<' XMLQualifiedNameOrExpression XMLAttribute* XMLWhitespace? '>'
;
XMLElementEnd
: '<' '/' '>'
| '<' '/' XMLQualifiedName XMLWhitespace? '>'
;
XMLContent
: XMLCharacterData? ( XMLNestedContent XMLCharacterData? )+
;
XMLCharacterData
: '<Any XMLCharacterDataString that does not contain the string "]]>">'
;
XMLCharacterDataString
: '<Any Unicode character except < or &>'+
;
XMLNestedContent
: XMLElement
| XMLReference
| XMLCDATASection
| XMLProcessingInstruction
| XMLComment
| XMLEmbeddedExpression
;
XMLAttribute
: XMLWhitespace XMLAttributeName XMLWhitespace? '=' XMLWhitespace? XMLAttributeValue
| XMLWhitespace XMLEmbeddedExpression
;
XMLAttributeName
: XMLQualifiedNameOrExpression
| XMLNamespaceAttributeName
;
XMLAttributeValue
: DoubleQuoteCharacter XMLAttributeDoubleQuoteValueCharacter* DoubleQuoteCharacter
| SingleQuoteCharacter XMLAttributeSingleQuoteValueCharacter* SingleQuoteCharacter
| XMLEmbeddedExpression
;
XMLAttributeDoubleQuoteValueCharacter
: '<Any XMLCharacter except <, &, or DoubleQuoteCharacter>'
| XMLReference
;
XMLAttributeSingleQuoteValueCharacter
: '<Any XMLCharacter except <, &, or SingleQuoteCharacter>'
| XMLReference
;
XMLReference
: XMLEntityReference
| XMLCharacterReference
;
XMLEntityReference
: '&' XMLEntityName ';'
;
XMLEntityName
: 'lt' | 'gt' | 'amp' | 'apos' | 'quot'
;
XMLCharacterReference
: '&' '#' XMLNumericCharacter+ ';'
| '&' '#' 'x' XMLHexNumericCharacter+ ';'
;
Ein XML-Element führt zu einem Wert, der als System.Xml.Linq.XElement. Im Gegensatz zu regulärem XML können XML-Elemente den Namen im schließenden Tag weglassen, und das aktuelle am häufigsten geschachtelte Element wird geschlossen. Beispiel:
Dim name = <name>Bob</>
Attributdeklarationen in einem XML-Element führen zu Werten, die als System.Xml.Linq.XAttribute. Attributwerte werden gemäß der XML-Spezifikation normalisiert. Wenn der Wert eines Attributs nicht Nothing erstellt wird, muss der Attributwertausdruck daher nicht überprüft Nothingwerden. Beispiel:
Dim expr = Nothing
' Throws null argument exception
Dim direct = New System.Xml.Linq.XElement( _
"Name", _
New System.Xml.Linq.XAttribute("Length", expr))
' Doesn't throw exception, the result is <Name/>
Dim literal = <Name Length=<%= expr %>/>
XML-Elemente und Attribute können geschachtelte Ausdrücke an folgenden Stellen enthalten:
Der Name des Elements, in diesem Fall muss der eingebettete Ausdruck ein Wert eines Typs sein, der System.Xml.Linq.XNameimplizit in . Beispiel:
Dim name = <<%= "name" %>>Bob</>
Der Name eines Attributs des Elements, in diesem Fall muss der eingebettete Ausdruck ein Wert eines Typs sein, der System.Xml.Linq.XNameimplizit in . Beispiel:
Dim name = <name <%= "length" %>="3">Bob</>
Der Wert eines Attributs des Elements, in diesem Fall kann der eingebettete Ausdruck ein Wert eines beliebigen Typs sein. Beispiel:
Dim name = <name length=<%= 3 %>>Bob</>
Ein Attribut des Elements, in diesem Fall kann der eingebettete Ausdruck ein Wert eines beliebigen Typs sein. Beispiel:
Dim name = <name <%= new XAttribute("length", 3) %>>Bob</>
Der Inhalt des Elements, in diesem Fall kann der eingebettete Ausdruck ein Wert eines beliebigen Typs sein. Beispiel:
Dim name = <name><%= "Bob" %></>
Wenn der Typ des eingebetteten Ausdrucks lautet Object(), wird das Array als Paramarray an den XElement Konstruktor übergeben.
XML-Namespaces
XML-Elemente können XML-Namespacedeklarationen enthalten, wie in der XML-Namespaces 1.0-Spezifikation definiert.
XMLNamespaceAttributeName
: XMLPrefixedNamespaceAttributeName
| XMLDefaultNamespaceAttributeName
;
XMLPrefixedNamespaceAttributeName
: 'xmlns' ':' XMLNamespaceName
;
XMLDefaultNamespaceAttributeName
: 'xmlns'
;
XMLNamespaceName
: XMLNamespaceNameStartCharacter XMLNamespaceNameCharacter*
;
XMLNamespaceNameStartCharacter
: '<Any XMLNameCharacter except :>'
;
XMLNamespaceNameCharacter
: XMLLetter
| '_'
;
XMLQualifiedNameOrExpression
: XMLQualifiedName
| XMLEmbeddedExpression
;
XMLQualifiedName
: XMLPrefixedName
| XMLUnprefixedName
;
XMLPrefixedName
: XMLNamespaceName ':' XMLNamespaceName
;
XMLUnprefixedName
: XMLNamespaceName
;
Die Einschränkungen beim Definieren der Namespaces und xmlns werden erzwungen und führen zu Kompilierungsfehlernxml. XML-Namespacedeklarationen können keinen eingebetteten Ausdruck für ihren Wert aufweisen; Der angegebene Wert muss ein nicht leeres Zeichenfolgenliteral sein. Beispiel:
' Declares a valid namespace
Dim customer = <db:customer xmlns:db="http://example.org/database">Bob</>
' Error: xmlns cannot be re-defined
Dim bad1 = <elem xmlns:xmlns="http://example.org/namespace"/>
' Error: cannot have an embedded expression
Dim bad2 = <elem xmlns:db=<%= "http://example.org/database" %>>Bob</>
Hinweis: Diese Spezifikation enthält nur ausreichend eine Beschreibung des XML-Namespaces, um das Verhalten der Visual Basic-Sprache zu beschreiben. Weitere Informationen zu XML-Namespaces finden Sie unter http://www.w3.org/TR/REC-xml-names/.
XML-Element- und Attributnamen können mithilfe von Namespacenamen qualifiziert werden. Namespaces sind wie in regulärem XML gebunden, mit der Ausnahme, dass alle auf Dateiebene deklarierten Namespaceimporte in einem Kontext deklariert werden, der die Deklaration einschließt, die selbst von allen Namespaceimporten eingeschlossen wird, die von der Kompilierungsumgebung deklariert werden. Wenn kein Namespacename gefunden werden kann, tritt ein Kompilierungszeitfehler auf. Beispiel:
Imports System.Xml.Linq
Imports <xmlns:db="http://example.org/database">
Module Test
Sub Main()
' Binds to the imported namespace above.
Dim c1 = <db:customer>Bob</>
' Binds to the namespace declaration in the element
Dim c2 = _
<db:customer xmlns:db="http://example.org/database-other">Mary</>
' Binds to the inner namespace declaration
Dim c3 = _
<database xmlns:db="http://example.org/database-one">
<db:customer xmlns:db="http://example.org/database-two">Joe</>
</>
' Error: namespace db2 cannot be found
Dim c4 = _
<db2:customer>Jim</>
End Sub
End Module
XML-Namespaces, die in einem Element deklariert sind, gelten nicht für XML-Literale in eingebetteten Ausdrücken. Beispiel:
' Error: Namespace prefix 'db' is not declared
Dim customer = _
<db:customer xmlns:db="http://example.org/database">
<%= <db:customer>Bob</> %>
</>
Hinweis: Dies liegt daran, dass der eingebettete Ausdruck alles sein kann, einschließlich eines Funktionsaufrufs. Wenn der Funktionsaufruf einen XML-Literalausdruck enthielt, ist nicht klar, ob Programmierer erwarten würden, dass der XML-Namespace angewendet oder ignoriert wird.
XML-Verarbeitungsanweisungen
Eine XML-Verarbeitungsanweisung führt zu einem Wert, der als System.Xml.Linq.XProcessingInstruction. XML-Verarbeitungsanweisungen können keine eingebetteten Ausdrücke enthalten, da sie eine gültige Syntax innerhalb der Verarbeitungsanweisung sind.
XMLProcessingInstruction
: '<' '?' XMLProcessingTarget ( XMLWhitespace XMLProcessingValue? )? '?' '>'
;
XMLProcessingTarget
: '<Any XMLName except a casing permutation of the string "xml">'
;
XMLProcessingValue
: '<Any XMLString that does not contain a question-mark followed by ">">'
;
XML-Kommentare
Ein XML-Kommentar führt zu einem Wert, der als System.Xml.Linq.XComment. XML-Kommentare können keine eingebetteten Ausdrücke enthalten, da sie eine gültige Syntax innerhalb des Kommentars sind.
XMLComment
: '<' '!' '-' '-' XMLCommentCharacter* '-' '-' '>'
;
XMLCommentCharacter
: '<Any XMLCharacter except dash (0x002D)>'
| '-' '<Any XMLCharacter except dash (0x002D)>'
;
CDATA-Abschnitte
Ein CDATA-Abschnitt führt zu einem Wert, der als System.Xml.Linq.XCData. CDATA-Abschnitte können keine eingebetteten Ausdrücke enthalten, da sie gültige Syntax im CDATA-Abschnitt sind.
XMLCDATASection
: '<' '!' ( 'CDATA' '[' XMLCDATASectionString? ']' )? '>'
;
XMLCDATASectionString
: '<Any XMLString that does not contain the string "]]>">'
;
XML-Memberzugriffsausdrücke
Ein XML-Memberzugriffsausdruck greift auf die Member eines XML-Werts zu.
XMLMemberAccessExpression
: Expression '.' LineTerminator? '<' XMLQualifiedName '>'
| Expression '.' LineTerminator? '@' LineTerminator? '<' XMLQualifiedName '>'
| Expression '.' LineTerminator? '@' LineTerminator? IdentifierOrKeyword
| Expression '.' '.' '.' LineTerminator? '<' XMLQualifiedName '>'
;
Es gibt drei Typen von XML-Memberzugriffsausdrücken:
Elementzugriff, bei dem ein XML-Name einem einzelnen Punkt folgt. Beispiel:
Dim customer = _ <customer> <name>Bob</> </> Dim customerName = customer.<name>.ValueElementzugriff ist der Funktion zugeordnet:
Function Elements(name As System.Xml.Linq.XName) As _ System.Collections.Generic.IEnumerable(Of _ System.Xml.Linq.XNode)Das obige Beispiel entspricht also folgendem:
Dim customerName = customer.Elements("name").ValueAttributzugriff, bei dem ein Visual Basic-Bezeichner auf einen Punkt und ein At-Zeichen folgt, oder ein XML-Name folgt einem Punkt und einem At-Zeichen. Beispiel:
Dim customer = <customer age="30"/> Dim customerAge = customer.@ageAttributzugriff ist der Funktion zugeordnet:
Function AttributeValue(name As System.Xml.Linq.XName) as StringDas obige Beispiel entspricht also folgendem:
Dim customerAge = customer.AttributeValue("age")Hinweis: Die
AttributeValueErweiterungsmethode (sowie die zugehörige ErweiterungseigenschaftValue) ist derzeit in keiner Assembly definiert. Wenn die Erweiterungsmber benötigt werden, werden sie automatisch in der erstellten Assembly definiert.Absteigender Zugriff, in dem eine XML-Namen drei Punkte folgt. Beispiel:
Dim company = _ <company> <customers> <customer>Bob</> <customer>Mary</> <customer>Joe</> </> </> Dim customers = company...<customer>Absteigende Zugriff auf Zuordnungen zu der Funktion:
Function Descendents(name As System.Xml.Linq.XName) As _ System.Collections.Generic.IEnumerable(Of _ System.Xml.Linq.XElement)Das obige Beispiel entspricht also folgendem:
Dim customers = company.Descendants("customer")
Der Basisausdruck eines XML-Memberzugriffsausdrucks muss ein Wert sein und muss vom Typ sein:
Wenn ein Element oder ein absteigender Zugriff oder
System.Xml.Linq.XContainerein abgeleiteter Typ oderSystem.Collections.Generic.IEnumerable(Of T)ein abgeleiteter Typ verwendet wird, wobeiTes sich um einen abgeleiteten Typ oder einen abgeleiteten Typ handeltSystem.Xml.Linq.XContainer.Wenn ein Attributzugriff oder
System.Xml.Linq.XElementein abgeleiteter Typ oderSystem.Collections.Generic.IEnumerable(Of T)ein abgeleiteter Typ vorhanden ist oder ein abgeleiteter TypTistSystem.Xml.Linq.XElementoder ein abgeleiteter Typ.
Namen in XML-Memberzugriffsausdrücken können nicht leer sein. Sie können namespacefähig sein, wobei alle durch Importe definierten Namespaces verwendet werden. Beispiel:
Imports <xmlns:db="http://example.org/database">
Module Test
Sub Main()
Dim customer = _
<db:customer>
<db:name>Bob</>
</>
Dim name = customer.<db:name>
End Sub
End Module
Leerzeichen sind nach den Punkten in einem XML-Elementzugriffsausdruck oder zwischen den winkeln Klammern und dem Namen nicht zulässig. Beispiel:
Dim customer = _
<customer age="30">
<name>Bob</>
</>
' All the following are error cases
Dim age = customer.@ age
Dim name = customer.< name >
Dim names = customer...< name >
Wenn die Typen im System.Xml.Linq Namespace nicht verfügbar sind, führt ein XML-Memberzugriffsausdruck zu einem Kompilierungszeitfehler.
Await-Operator
Der Await-Operator bezieht sich auf asynchrone Methoden, die in "Section Async"-Methoden beschrieben werden.
AwaitOperatorExpression
: 'Await' Expression
;
Await ist ein reserviertes Wort, wenn die unmittelbar eingeschlossene Methode oder der Lambda-Ausdruck, in dem sie angezeigt wird, über einen Async Modifizierer verfügt und wenn dies nach diesem AwaitAsync Modifizierer angezeigt wird; sie wird an anderer Stelle nicht reserviert. Sie ist auch in Präprozessordirektiven nicht reserviert. Der Await-Operator ist nur im Textkörper einer Methode oder lambda-Ausdrücke zulässig, bei denen es sich um ein reserviertes Wort handelt. Innerhalb der unmittelbar eingeschlossenen Methode oder Lambda kann ein Await-Ausdruck weder innerhalb des Textkörpers eines Catch Blocks noch Finally innerhalb des Textkörpers einer SyncLock Anweisung oder innerhalb eines Abfrageausdrucks auftreten.
Der Await-Operator akzeptiert einen einzelnen Ausdruck, der als Wert klassifiziert werden muss und deren Typ ein erwarteter Typ sein muss, oder Object. Wenn der typ ist Object , wird die gesamte Verarbeitung bis zur Laufzeit zurückgestellt. Es wird davon abgewartet, dass ein Typ C erwartet wird, wenn alle folgenden Werte zutreffen:
Centhält eine barrierefreie Instanz oder Erweiterungsmethode,GetAwaiterdie keine Argumente enthält und die einen TypEzurückgibt;Eenthält eine lesbare Instanz oder Erweiterungseigenschaft namensIsCompleted, die keine Argumente akzeptiert und vom Typ Boolean verfügt.Eenthält eine barrierefreie Instanz oder Erweiterungsmethode,GetResultdie keine Argumente akzeptiert;Eimplementiert entwederSystem.Runtime.CompilerServices.INotifyCompletionoderICriticalNotifyCompletion.
Wenn GetResult es sich um einen Sub, dann wird der Await-Ausdruck als "void" klassifiziert. Andernfalls wird der Await-Ausdruck als Wert klassifiziert, und sein Typ ist der Rückgabetyp der GetResult Methode.
Hier ist ein Beispiel für eine Klasse, auf die gewartet werden kann:
Class MyTask(Of T)
Function GetAwaiter() As MyTaskAwaiter(Of T)
Return New MyTaskAwaiter With {.m_Task = Me}
End Function
...
End Class
Structure MyTaskAwaiter(Of T)
Implements INotifyCompletion
Friend m_Task As MyTask(Of T)
ReadOnly Property IsCompleted As Boolean
Get
Return m_Task.IsCompleted
End Get
End Property
Sub OnCompleted(r As Action) Implements INotifyCompletion.OnCompleted
' r is the "resumptionDelegate"
Dim sc = SynchronizationContext.Current
If sc Is Nothing Then
m_Task.ContinueWith(Sub() r())
Else
m_Task.ContinueWith(Sub() sc.Post(Sub() r(), Nothing))
End If
End Sub
Function GetResult() As T
If m_Task.IsCanceled Then Throw New TaskCanceledException(m_Task)
If m_Task.IsFaulted Then Throw m_Task.Exception.InnerException
Return m_Task.Result
End Function
End Structure
Hinweis: Bibliotheksautoren werden empfohlen, dem Muster zu folgen, für das sie den Fortsetzungsdelegat auf dieselbe SynchronizationContext Weise aufrufen wie sie OnCompleted selbst aufgerufen wurde. Außerdem sollte der Reaufnahmedelegat nicht synchron innerhalb der OnCompleted Methode ausgeführt werden, da dies zu Stapelüberlauf führen kann: Stattdessen sollte der Delegat für die nachfolgende Ausführung in die Warteschlange gestellt werden.
Wenn der Steuerungsfluss einen Await Operator erreicht, sieht das Verhalten wie folgt aus.
Die
GetAwaiterMethode des Await-Operanden wird aufgerufen. Das Ergebnis dieses Aufrufs wird als Awaiter bezeichnet.Die Eigenschaft des
IsCompletedAwaiters wird abgerufen. Wenn das Ergebnis "true" ist, gehen Sie wie folgt vor:- Die
GetResultMethode des Awaiters wird aufgerufen. WennGetResultes sich um eine Funktion handelt, ist der Wert des Await-Ausdrucks der Rückgabewert dieser Funktion.
- Die
Wenn die IsCompleted-Eigenschaft nicht wahr ist, dann gilt Folgendes:
Entweder
ICriticalNotifyCompletion.UnsafeOnCompletedwird für den Awaiter aufgerufen (wenn der Awaiter-TypEimplementiertICriticalNotifyCompletionwird) oderINotifyCompletion.OnCompleted(andernfalls). In beiden Fällen wird ein Reaufnahmedelegat übergeben, der der aktuellen Instanz der asynchronen Methode zugeordnet ist.Der Kontrollpunkt der aktuellen asynchronen Methodeninstanz wird angehalten, und der Steuerungsfluss wird im aktuellen Aufrufer fortgesetzt (definiert in Section Async-Methoden).
Wenn später die Wiederaufnahmedelegat aufgerufen wird,
- die Wiederaufnahmedelegat stellt zunächst wieder her, was sie zum Zeitpunkt
OnCompleteddes AufrufsSystem.Threading.Thread.CurrentThread.ExecutionContextwar, - anschließend wird der Steuerungsfluss am Kontrollpunkt der asynchronen Methodeninstanz fortgesetzt (siehe Section Async-Methoden),
- wo sie die
GetResultMethode des Awaiters aufruft, wie in 2.1 oben beschrieben.
- die Wiederaufnahmedelegat stellt zunächst wieder her, was sie zum Zeitpunkt
Wenn der Await-Operand den Typ "Object" hat, wird dieses Verhalten bis zur Laufzeit zurückgestellt:
- Schritt 1 wird durch Aufrufen von GetAwaiter() ohne Argumente erreicht; sie kann daher zur Laufzeit an Instanzenmethoden gebunden werden, die optionale Parameter verwenden.
- Schritt 2 erfolgt durch Abrufen der IsCompleted()-Eigenschaft ohne Argumente und durch Versuch einer systeminternen Konvertierung in Boolean.
- Schritt 3.a wird durch versuchen
TryCast(awaiter, ICriticalNotifyCompletion), und wenn dies fehlschlägt, dannDirectCast(awaiter, INotifyCompletion).
Der in 3.a übergebene Wiederaufnahmedelegat kann nur einmal aufgerufen werden. Wenn sie mehrmals aufgerufen wird, wird das Verhalten nicht definiert.
Visual Basic language spec