Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Wyrażenie to sekwencja operatorów i operandów, która określa obliczenie wartości lub wyznacza zmienną lub stałą. W tym rozdziale zdefiniowano składnię, kolejność obliczania operandów i operatorów oraz znaczenie wyrażeń.
Expression
: SimpleExpression
| TypeExpression
| MemberAccessExpression
| DictionaryAccessExpression
| InvocationExpression
| IndexExpression
| NewExpression
| CastExpression
| OperatorExpression
| ConditionalExpression
| LambdaExpression
| QueryExpression
| XMLLiteralExpression
| XMLMemberAccessExpression
;
Klasyfikacje wyrażeń
Każde wyrażenie jest klasyfikowane jako jedno z następujących:
Wartość. Każda wartość ma skojarzony typ.
Zmienna. Każda zmienna ma skojarzony typ, czyli zadeklarowany typ zmiennej.
Przestrzeń nazw. Wyrażenie z tą klasyfikacją może być wyświetlane tylko po lewej stronie dostępu do elementu członkowskiego. W jakimkolwiek innym kontekście wyrażenie zaklasyfikowane jako przestrzeń nazw powoduje błąd czasu kompilacji.
Typ Wyrażenie z tą klasyfikacją może być wyświetlane tylko po lewej stronie dostępu do elementu członkowskiego. W każdym innym kontekście wyrażenie sklasyfikowane jako typ powoduje błąd czasu kompilacji.
Grupa metod, która jest zestawem metod przeciążonych o tej samej nazwie. Grupa metod może mieć skojarzone wyrażenie docelowe i skojarzona lista argumentów typu.
Wskaźnik metody, który reprezentuje lokalizację metody. Wskaźnik metody może mieć skojarzone wyrażenie docelowe i skojarzona lista argumentów typu.
Metoda lambda, która jest metodą anonimową.
Grupa właściwości, która jest zestawem właściwości przeciążonych o tej samej nazwie. Grupa właściwości może mieć skojarzone wyrażenie docelowe.
Dostęp do właściwości. Każdy dostęp do właściwości ma skojarzony typ, czyli typ właściwości. Dostęp do właściwości może mieć skojarzone wyrażenie docelowe.
Opóźniony dostęp, który reprezentuje metodę lub dostęp do właściwości odroczony do czasu wykonywania. Dostęp opóźniony może mieć skojarzone wyrażenie docelowe i skojarzoną listę argumentów typu. Typ dostępu z opóźnieniem to zawsze
Object.Dostęp do wydarzenia. Każdy dostęp do zdarzenia jest powiązany z określonym typem, czyli typem tego zdarzenia. Dostęp do zdarzenia może mieć skojarzone wyrażenie docelowe. Dostęp do zdarzeń może być wyświetlany jako pierwszy argument instrukcji
RaiseEvent,AddHandleriRemoveHandler. W każdym innym kontekście wyrażenie sklasyfikowane jako dostęp do zdarzenia powoduje błąd czasu kompilacji.Literał tablicy, który reprezentuje początkowe wartości tablicy, której typ nie został jeszcze określony.
Nieważny. Dzieje się tak, gdy wyrażenie jest wywołaniem podroutine lub wyrażenie operatora await bez wyniku. Wyrażenie sklasyfikowane jako void jest prawidłowe tylko w kontekście instrukcji wywołania lub instrukcji await.
Wartość domyślna. Tylko literał
Nothingtworzy tę klasyfikację.
Końcowy wynik wyrażenia jest zwykle wartością lub zmienną, z innymi kategoriami wyrażeń działających jako wartości pośrednie, które są dozwolone tylko w niektórych kontekstach.
Należy pamiętać, że wyrażenia, których typ jest parametrem typu, mogą być używane w instrukcjach i wyrażeniach, które wymagają, aby typ wyrażenia miał pewne cechy (takie jak typ odwołania, typ wartości, wyprowadzany z określonego typu itp.), jeśli ograniczenia nałożone na parametr typu spełniają te cechy.
Reklasyfikacja wyrażeń
Zwykle, gdy wyrażenie jest używane w kontekście, który wymaga klasyfikacji innej niż wyrażenie, występuje błąd czasu kompilacji — na przykład próba przypisania wartości do literału. Jednak w wielu przypadkach można zmienić klasyfikację wyrażenia przez proces ponownej klasyfikacji.
Jeśli zmiana klasyfikacji powiedzie się, klasyfikacja jest oceniana jako rozszerzająca lub zawężająca. O ile nie określono inaczej, wszystkie ponowne klasyfikacje na tej liście są rozszerzane.
Następujące typy wyrażeń można ponownie sklasyfikować:
Zmienną można sklasyfikować jako wartość. Pobierana jest wartość przechowywana w zmiennej.
Grupę metod można sklasyfikować jako wartość. Wyrażenie grupy metod jest interpretowane jako wyrażenie wywołania ze skojarzonym wyrażeniem docelowym i listą parametrów typu oraz pustymi nawiasami (czyli
fjest interpretowane jakof()if(Of Integer)interpretowane jakof(Of Integer)()). Ta zmiana klasyfikacji może spowodować dalsze ponowne sklasyfikowanie wyrażenia jako void.Wskaźnik metody można sklasyfikować jako wartość. Ta ponowna klasyfikacja może wystąpić tylko w kontekście konwersji, w której jest znany typ docelowy. Wyrażenie wskaźnika metody jest interpretowane jako argument do delegowanego wyrażenia wystąpienia odpowiedniego typu z listą argumentów skojarzonego typu. Przykład:
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 ModuleMetodę lambda można sklasyfikować jako wartość. Jeśli zmiana klasyfikacji występuje w kontekście konwersji, w której jest znany typ docelowy, może wystąpić jedna z dwóch ponownych klasyfikacji:
Jeśli typ docelowy jest typem delegata, metoda lambda jest interpretowana jako argument wyrażenia delegalnego budowy odpowiedniego typu.
Jeśli typ docelowy to
System.Linq.Expressions.Expression(Of T), iTjest typem delegata, metoda lambda jest interpretowana tak, jakby była używana w wyrażeniuTdelegalnym dla, a następnie przekonwertowana na drzewo wyrażeń.
Metoda lambda asynchronizatora lub iteratora może być interpretowana tylko jako argument wyrażenia konstrukcji delegata, jeśli delegat nie ma parametrów ByRef.
Jeśli konwersja z któregokolwiek z typów parametrów delegata do odpowiednich typów parametrów lambda jest konwersją zawężającą, klasyfikacja jest oceniana jako zawężająca; w przeciwnym razie rozszerza się.
Uwaga. Dokładne tłumaczenie między metodami lambda i drzewami wyrażeń może nie zostać naprawione między wersjami kompilatora i wykracza poza zakres tej specyfikacji. W przypadku programu Microsoft Visual Basic 11.0 wszystkie wyrażenia lambda mogą być konwertowane na drzewa wyrażeń podlegające następującym ograniczeniom: (1) 1. Tylko wyrażenia lambda jednowierszowe bez parametrów ByRef mogą być konwertowane na drzewa wyrażeń. Z jednowierszowych
Sublambd, tylko instrukcje wywołania mogą być konwertowane na drzewa wyrażeń. (2) Nie można przekonwertować wyrażeń typu anonimowego na drzewa wyrażeń, jeśli wcześniejszy inicjator pola jest używany do inicjowania kolejnego inicjatora pól, np.New With {.a=1, .b=.a}. (3) Nie można przekonwertować wyrażeń inicjatora obiektów na drzewa wyrażeń, jeśli element członkowski bieżącego obiektu inicjowanego jest używany w jednym z inicjatorów pól, np.New C1 With {.a=1, .b=.Method1()}. (4) Wyrażenia tworzenia tablicy wielowymiarowej można przekonwertować tylko na drzewa wyrażeń, jeśli jawnie zadeklarują typ elementu. (5) Nie można przekonwertować wyrażeń późnych powiązań na drzewa wyrażeń. (6) Gdy zmienna lub pole jest przekazywane przez element ByRef do wyrażenia wywołania, ale nie ma dokładnie tego samego typu co parametr ByRef lub gdy właściwość jest przekazywana przez element ByRef, normalne semantyka VB oznacza, że kopia argumentu jest przekazywana przez Element ByRef, a jego ostateczna wartość jest następnie kopiowana z powrotem do zmiennej lub pola lub właściwości. W drzewach wyrażeń kopiowanie kopii nie jest wykonywane. (7) Wszystkie te ograniczenia dotyczą również zagnieżdżonych wyrażeń lambda.Jeśli typ docelowy nie jest znany, metoda lambda jest interpretowana jako argument wyrażenia wystąpienia wystąpienia anonimowego typu delegata z tym samym podpisem metody lambda. Jeśli są używane ścisłe semantyka, a typ dowolnego z parametrów zostanie pominięty, wystąpi błąd czasu kompilacji;
Objectw przeciwnym razie jest zastępowany dla dowolnego brakującego typu parametru. Przykład: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 ModuleGrupę właściwości można sklasyfikować jako dostęp do właściwości. Wyrażenie grupy właściwości jest interpretowane jako wyrażenie indeksu z pustymi nawiasami (czyli
fjest interpretowane jakof()).Dostęp do właściwości można sklasyfikować jako wartość. Wyrażenie dostępu do właściwości jest interpretowane jako wyrażenie
Getwywołania metody dostępu do właściwości. Jeśli właściwość nie ma metody getter, wystąpi błąd czasu kompilacji.Dostęp z opóźnieniem można sklasyfikować jako metodę z późną granicą lub dostęp do właściwości z późnym opóźnieniem. W sytuacji, gdy można ponownie sklasyfikować dostęp związany z opóźnieniem zarówno jako dostęp do metody, jak i jako dostęp do właściwości, preferowane jest ponowne sklasyfikowanie dostępu do właściwości.
Dostęp z opóźnieniem można sklasyfikować jako wartość.
Literał tablicy można sklasyfikować jako wartość. Typ wartości jest określany w następujący sposób:
Jeśli zmiana klasyfikacji występuje w kontekście konwersji, w której typ docelowy jest znany, a typ docelowy jest typem tablicy, literał tablicy zostanie ponownie sklasyfikowany jako wartość typu T(). Jeśli typ docelowy to
System.Collections.Generic.IList(Of T), ,ICollection(Of T)IReadOnlyList(Of T),IReadOnlyCollection(Of T)lubIEnumerable(Of T), a literał tablicy ma jeden poziom zagnieżdżania, literał tablicy zostanie ponownie sklasyfikowany jako wartość typuT().W przeciwnym razie literał tablicy jest ponownie klasyfikowany do wartości, której typ jest tablicą rangi równej poziomowi zagnieżdżania, z typem elementu określonym przez dominujący typ elementów w inicjatorze; jeśli nie można określić typu dominującego,
Objectjest używany. Przykład:' 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()
Uwaga. Istnieje niewielka zmiana zachowania między wersją 9.0 a wersją 10.0 języka. Przed 10.0 inicjatory elementów tablicy nie miały wpływu na wnioskowanie typu zmiennej lokalnej, a teraz to robią. Dlatego
Dim a() = { 1, 2, 3 }wywnioskowałbyObject()atyp języka w wersji 9.0 iInteger()w wersji 10.0.Następnie ponowna klasyfikacja interpretuje literał tablicy jako wyrażenie tworzenia tablicy. Przykłady:
Dim x As Double = { 1, 2, 3, 4 } Dim y = { "a", "b" }są równoważne:
Dim x As Double = New Double() { 1, 2, 3, 4 } Dim y = New String() { "a", "b" }Klasyfikacja jest oceniana jako zawężanie, jeśli jakakolwiek konwersja z wyrażenia elementu na typ elementu tablicy jest zawężana; w przeciwnym razie jest oceniana jako rozszerzająca.
Wartość
Nothingdomyślna może zostać ponownie sklasyfikowana jako wartość. W kontekście, w którym jest znany typ docelowy, wynik jest wartością domyślną typu docelowego. W kontekście, w którym typ docelowy nie jest znany, wynik jest wartością null typuObject.
Nie można ponownie sklasyfikować wyrażenia przestrzeni nazw, wyrażenia typu, wyrażenia dostępu do zdarzeń lub wyrażenia void. W tym samym czasie można przeprowadzić wiele reklasyfikacji. Przykład:
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
W takim przypadku wyrażenie P grupy właściwości jest najpierw ponownie sklasyfikowane z grupy właściwości do dostępu do właściwości, a następnie ponownie sklasyfikowane z dostępu do właściwości do wartości. Najmniejsza liczba ponownych klasyfikacji jest wykonywana w celu osiągnięcia prawidłowej klasyfikacji w kontekście.
Wyrażenia stałe
Wyrażenie stałe to wyrażenie, którego wartość może być w pełni obliczana w czasie kompilacji.
ConstantExpression
: Expression
;
Typ wyrażenia stałego może mieć Bytetyp , , SByteBooleanDoubleSingleDecimalCharDateStringUShortUIntegerObjectShortIntegerULongLonglub dowolny typ wyliczenia. Następujące konstrukcje są dozwolone w wyrażeniach stałych:
Literały (w tym
Nothing).Odwołania do stałych elementów członkowskich typu lub stałych ustawień lokalnych.
Odwołania do elementów typów wyliczenia.
Nawiasy podwyrażeń.
Wyrażenia przymusu, pod warunkiem, że typ docelowy jest jednym z typów wymienionych powyżej. Odwołania do i z
Stringsą wyjątkiem od tej reguły i są dozwolone tylko dla wartości null, ponieważStringkonwersje są zawsze wykonywane w bieżącej kulturze środowiska wykonawczego w czasie wykonywania. Należy pamiętać, że wyrażenia stałe przymusu mogą używać tylko konwersji wewnętrznych.Operatory
+,-iNotjednoargumentowe, pod warunkiem, że operand i wynik jest typu wymienionego powyżej.Operatory
+, ,^<-*ModAnd&Or>>Xor<<AndAlsoOrElse\/><>=<=i=>binarne , pod warunkiem, że każdy operand i wynik jest typem wymienionym powyżej.Operator warunkowy Jeśli, pod warunkiem, każdy operand i wynik jest typu wymienionego powyżej.
Następujące funkcje czasu wykonywania:
Microsoft.VisualBasic.Strings.ChrW;Microsoft.VisualBasic.Strings.Chrjeśli stała wartość wynosi od 0 do 128;Microsoft.VisualBasic.Strings.AscWjeśli ciąg stały nie jest pusty;Microsoft.VisualBasic.Strings.Ascjeśli ciąg stały nie jest pusty.
Następujące konstrukcje nie są dozwolone w wyrażeniach stałych:
- Powiązanie niejawne za pośrednictwem
Withkontekstu.
Wyrażenia stałe typu całkowitego (ULong, Long, UShortSByteShortUIntegerIntegerlub Byte) mogą być niejawnie konwertowane na węższy typ całkowity, a wyrażenia stałe typu Double mogą być niejawnie konwertowane na Single, pod warunkiem, że wartość wyrażenia stałego mieści się w zakresie typu docelowego. Te konwersje zawężające są dozwolone niezależnie od tego, czy są używane permisywne, czy ścisłe semantyka.
wyrażenia Late-Bound
Gdy elementem docelowym wyrażenia dostępu do składowej lub wyrażenia indeksu jest typ Object, przetwarzanie wyrażenia może zostać odroczone do czasu wykonywania. Odroczenie przetwarzania w ten sposób jest nazywane późnym powiązaniem. Opóźnione powiązanie umożliwia Object używanie zmiennych w sposób bez typu , gdzie wszystkie rozpoznawanie elementów członkowskich jest oparte na rzeczywistym typie czasu wykonywania wartości w zmiennej. Jeśli ściśle semantyka jest określona przez środowisko kompilacji lub przez Option Strict, opóźnione powiązanie powoduje błąd czasu kompilacji. Elementy członkowskie inne niż publiczne są ignorowane podczas wykonywania opóźnionego powiązania, w tym do celów rozwiązywania przeciążeń. Należy pamiętać, że w przeciwieństwie do przypadku wczesnej granicy wywoływanie lub uzyskiwanie dostępu do elementu członkowskiego z opóźnieniem Shared spowoduje ocenę celu wywołania w czasie wykonywania. Jeśli wyrażenie jest wyrażeniem wywołania dla elementu członkowskiego zdefiniowanego w elemencie System.Object, nie zostanie wykonane późne powiązanie.
Ogólnie rzecz biorąc, dostępy z opóźnieniem są rozwiązywane w czasie wykonywania przez wyszukanie identyfikatora rzeczywistego typu czasu wykonywania wyrażenia. Jeśli wyszukiwanie elementów członkowskich z opóźnieniem kończy się niepowodzeniem w czasie wykonywania, zgłaszany System.MissingMemberException jest wyjątek. Ponieważ wyszukiwanie elementów członkowskich z opóźnieniem odbywa się wyłącznie poza typem czasu wykonywania skojarzonego wyrażenia docelowego, typ czasu wykonywania obiektu nigdy nie jest interfejsem. W związku z tym nie można uzyskać dostępu do elementów członkowskich interfejsu w wyrażeniu dostępu do elementów członkowskich z późnym opóźnieniem.
Argumenty dostępu do elementu członkowskiego z późnym opóźnieniem są oceniane w kolejności, w jakiej są wyświetlane w wyrażeniu dostępu do składowych: a nie w kolejności, w jakiej parametry są deklarowane w późnym powiązaniu elementu członkowskiego. Poniższy przykład ilustruje tę różnicę:
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
Ten kod wyświetla:
Early-bound: xy
Late-bound: yx
Ponieważ rozpoznawanie przeciążenia opóźnionego odbywa się na typie czasu wykonywania argumentów, istnieje możliwość, że wyrażenie może wygenerować różne wyniki na podstawie tego, czy jest obliczany w czasie kompilacji, czy w czasie wykonywania. Poniższy przykład ilustruje tę różnicę:
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
Ten kod wyświetla:
F(Base)
F(Derived)
Wyrażenia proste
Wyrażenia proste to literały, wyrażenia nawiasów, wyrażenia wystąpień lub proste wyrażenia nazw.
SimpleExpression
: LiteralExpression
| ParenthesizedExpression
| InstanceExpression
| SimpleNameExpression
| AddressOfExpression
;
Wyrażenia literału
Wyrażenia literału obliczają wartość reprezentowaną przez literał. Wyrażenie literału jest klasyfikowane jako wartość, z wyjątkiem literału Nothing, który jest klasyfikowany jako wartość domyślna.
LiteralExpression
: Literal
;
Wyrażenia nawiasów
Wyrażenie nawiasowe składa się z wyrażenia ujętego w nawiasy. Wyrażenie nawiasu jest klasyfikowane jako wartość, a ujęte wyrażenie musi być klasyfikowane jako wartość. Wyrażenie nawiasu oblicza wartość wyrażenia w nawiasach.
ParenthesizedExpression
: OpenParenthesis Expression CloseParenthesis
;
Wyrażenia wystąpień
Wyrażenie wystąpienia to słowo kluczowe Me. Można go używać tylko w treści metody, konstruktora lub metody dostępu właściwości innej niż współdzielona. Jest klasyfikowana jako wartość. Słowo kluczowe Me reprezentuje wystąpienie typu zawierającego wykonywaną metodę lub metodę dostępu właściwości. Jeśli konstruktor jawnie wywołuje inny konstruktor ( konstruktory sekcji), Me nie można go użyć do momentu wywołania tego konstruktora, ponieważ wystąpienie nie zostało jeszcze skonstruowane.
InstanceExpression
: 'Me'
;
Wyrażenia nazw prostych
Proste wyrażenie nazwy składa się z pojedynczego identyfikatora, po którym następuje opcjonalna lista argumentów typu.
SimpleNameExpression
: Identifier ( OpenParenthesis 'Of' TypeArgumentList CloseParenthesis )?
;
Nazwa jest rozpoznawana i klasyfikowana przez następujące "proste reguły rozpoznawania nazw":
Począwszy od natychmiast otaczającego bloku i kontynuując każdy otaczający blok zewnętrzny (jeśli istnieje), jeśli identyfikator jest zgodny z nazwą zmiennej lokalnej, zmiennej statycznej, stałej lokalnej, parametru typu metody lub parametru, identyfikator odwołuje się do zgodnej jednostki.
Jeśli identyfikator pasuje do zmiennej lokalnej, zmiennej statycznej lub stałej lokalnej i podano listę argumentów typu, wystąpi błąd czasu kompilacji. Jeśli identyfikator pasuje do parametru typu metody i podano listę argumentów typu, nie wystąpi dopasowanie i rozwiązanie będzie kontynuowane. Jeśli identyfikator pasuje do zmiennej lokalnej, dopasowana zmienna lokalna jest niejawną funkcją lub
Getakcesorem zwracaną zmiennąAddressOflokalną, a wyrażenie jest częścią wyrażenia wywołania, instrukcji wywołania lub wyrażenia, a następnie nie występuje dopasowanie i rozpoznawanie będzie kontynuowane.Wyrażenie jest klasyfikowane jako zmienna, jeśli jest zmienną lokalną, zmienną statyczną lub parametrem. Wyrażenie jest klasyfikowane jako typ, jeśli jest to parametr typu metody. Wyrażenie jest klasyfikowane jako wartość, jeśli jest stałą lokalną.
Dla każdego zagnieżdżonego typu zawierającego wyrażenie, zaczynając od najbardziej wewnętrznego i przechodzącego do najbardziej zewnętrznego, jeśli wyszukiwanie identyfikatora w typie powoduje dopasowanie z dostępnym elementem członkowskim:
- Jeśli pasujący element członkowski typu jest parametrem typu, wynik jest klasyfikowany jako typ i jest zgodnym parametrem typu. Jeśli podano listę argumentów typu, nie występuje dopasowanie i rozwiązanie będzie kontynuowane.
- W przeciwnym razie, jeśli typ jest natychmiast otaczającym typem, a odnośnik identyfikuje element członkowski typu nieudostępnianego, wynik jest taki sam jak dostęp do elementu członkowskiego formularza
Me.E(Of A), gdzieEjest identyfikatorem iAjest listą argumentów typu, jeśli istnieje. - W przeciwnym razie wynik jest dokładnie taki sam jak dostęp do elementu członkowskiego formularza
T.E(Of A), gdzieTjest typem zawierającym pasujący element członkowski,Ejest identyfikator iAjest listą argumentów typu, jeśli istnieje. W takim przypadku jest to błąd identyfikatora odwołującego się do elementu członkowskiego, który nie jest współużytkowany.
Dla każdej zagnieżdżonej przestrzeni nazw, począwszy od najbardziej wewnętrznej i przechodząc do najbardziej zewnętrznej przestrzeni nazw, wykonaj następujące czynności:
- Jeśli przestrzeń nazw zawiera dostępny typ o podanej nazwie i ma taką samą liczbę parametrów typu, jak podano na liście argumentów typu, jeśli istnieje, identyfikator odwołuje się do tego typu i jest klasyfikowany jako typ.
- W przeciwnym razie, jeśli nie podano listy argumentów typu, a przestrzeń nazw zawiera element członkowski przestrzeni nazw o podanej nazwie, identyfikator odwołuje się do tej przestrzeni nazw i jest klasyfikowany jako przestrzeń nazw.
- W przeciwnym razie, jeśli przestrzeń nazw zawiera jeden lub więcej dostępnych modułów standardowych, a odnośnik nazwy składowej identyfikatora generuje dostępne dopasowanie w dokładnie jednym standardowym module, wynik jest dokładnie taki sam jak dostęp do elementu członkowskiego formularza
M.E(Of A), gdzieMjest modułem standardowym zawierającym pasujący element członkowski,Ejest identyfikatorem iAjest listą argumentów typu, jeśli istnieje. Jeśli identyfikator jest zgodny z dostępnymi elementami członkowskimi typu w więcej niż jednym module standardowym, wystąpi błąd czasu kompilacji.
Jeśli plik źródłowy ma co najmniej jeden alias importu, a identyfikator jest zgodny z nazwą jednego z nich, identyfikator odwołuje się do tej przestrzeni nazw lub typu. Jeśli zostanie podana lista argumentów typu, wystąpi błąd czasu kompilacji.
Jeśli plik źródłowy zawierający odwołanie do nazwy ma co najmniej jeden import:
- Jeśli identyfikator pasuje do dokładnie jednej nazwy typu dostępnego z taką samą liczbą parametrów typu, jak podano na liście argumentów typu, jeśli istnieje lub element członkowski typu, identyfikator odnosi się do tego typu lub elementu członkowskiego typu. Jeśli identyfikator jest zgodny z więcej niż jednym importem nazwy dostępnego typu z taką samą liczbą parametrów typu, jak podano na liście argumentów typu, jeśli istnieje lub dostępny element członkowski typu, wystąpi błąd czasu kompilacji.
- W przeciwnym razie, jeśli nie podano listy argumentów typu i identyfikator pasuje do dokładnie jednej nazwy przestrzeni nazw z dostępnymi typami, identyfikator odwołuje się do tej przestrzeni nazw. Jeśli nie podano listy argumentów typu i identyfikator jest zgodny z więcej niż jednym importem nazwy przestrzeni nazw z dostępnymi typami, wystąpi błąd czasu kompilacji.
- W przeciwnym razie, jeśli import zawiera jeden lub więcej dostępnych modułów standardowych, a odnośnik nazwy składowej identyfikatora generuje dostępne dopasowanie w dokładnie jednym module standardowym, wynik jest dokładnie taki sam jak dostęp do elementu członkowskiego formularza
M.E(Of A), gdzieMjest modułem standardowym zawierającym pasujący element członkowski,Ejest identyfikatorem iAjest listą argumentów typu, jeśli istnieje. Jeśli identyfikator jest zgodny z dostępnymi elementami członkowskimi typu w więcej niż jednym module standardowym, wystąpi błąd czasu kompilacji.
Jeśli środowisko kompilacji definiuje co najmniej jeden alias importu, a identyfikator jest zgodny z nazwą jednego z nich, identyfikator odwołuje się do tej przestrzeni nazw lub typu. Jeśli zostanie podana lista argumentów typu, wystąpi błąd czasu kompilacji.
Jeśli środowisko kompilacji definiuje co najmniej jeden import:
- Jeśli identyfikator pasuje do dokładnie jednej nazwy typu dostępnego z taką samą liczbą parametrów typu, jak podano na liście argumentów typu, jeśli istnieje lub element członkowski typu, identyfikator odnosi się do tego typu lub elementu członkowskiego typu. Jeśli identyfikator jest zgodny z więcej niż jednym importem nazwy dostępnego typu z taką samą liczbą parametrów typu, jak podano na liście argumentów typu, jeśli istnieje lub element członkowski typu, wystąpi błąd czasu kompilacji.
- W przeciwnym razie, jeśli nie podano listy argumentów typu i identyfikator pasuje do dokładnie jednej nazwy przestrzeni nazw z dostępnymi typami, identyfikator odwołuje się do tej przestrzeni nazw. Jeśli nie podano listy argumentów typu i identyfikator jest zgodny z więcej niż jednym importem nazwy przestrzeni nazw z dostępnymi typami, wystąpi błąd czasu kompilacji.
- W przeciwnym razie, jeśli import zawiera jeden lub więcej dostępnych modułów standardowych, a odnośnik nazwy składowej identyfikatora generuje dostępne dopasowanie w dokładnie jednym module standardowym, wynik jest dokładnie taki sam jak dostęp do elementu członkowskiego formularza
M.E(Of A), gdzieMjest modułem standardowym zawierającym pasujący element członkowski,Ejest identyfikatorem iAjest listą argumentów typu, jeśli istnieje. Jeśli identyfikator jest zgodny z dostępnymi elementami członkowskimi typu w więcej niż jednym module standardowym, wystąpi błąd czasu kompilacji.
W przeciwnym razie nazwa nadana przez identyfikator jest niezdefiniowana.
Proste wyrażenie nazwy, które jest niezdefiniowane, jest błędem czasu kompilacji.
Zwykle nazwa może wystąpić tylko raz w określonej przestrzeni nazw. Jednak ponieważ przestrzenie nazw można zadeklarować w wielu zestawach .NET, istnieje możliwość, że dwa zestawy definiują typ o tej samej w pełni kwalifikowanej nazwie. W takim przypadku typ zadeklarowany w bieżącym zestawie plików źródłowych jest preferowany przez typ zadeklarowany w zewnętrznym zestawie .NET. W przeciwnym razie nazwa jest niejednoznaczna i nie ma możliwości uściślania nazwy.
Wyrażenia AddressOf
Wyrażenie AddressOf służy do tworzenia wskaźnika metody. Wyrażenie składa się ze słowa kluczowego AddressOf i wyrażenia, które musi być klasyfikowane jako grupa metod lub późny dostęp. Grupa metod nie może odwoływać się do konstruktorów.
Wynik jest klasyfikowany jako wskaźnik metody, z tym samym skojarzonym wyrażeniem docelowym i listą argumentów typu (jeśli istnieje) jako grupa metod.
AddressOfExpression
: 'AddressOf' Expression
;
Wyrażenia typów
Wyrażenie typu to GetType wyrażenie, TypeOf...Is wyrażenie, Is wyrażenie lub GetXmlNamespace wyrażenie.
TypeExpression
: GetTypeExpression
| TypeOfIsExpression
| IsExpression
| GetXmlNamespaceExpression
;
Wyrażenia GetType
Wyrażenie GetType składa się ze słowa kluczowego GetType i nazwy typu.
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*
;
Wyrażenie GetType jest klasyfikowane jako wartość, a jego wartość jest klasą odbicia (System.Type), która reprezentuje jego parametr GetTypeTypeName. Jeśli GetTypeTypeName jest parametrem typu, wyrażenie zwróci System.Type obiekt odpowiadający argumentowi typu podanemu dla parametru typu w czasie wykonywania.
Parametr GetTypeTypeName jest specjalny na dwa sposoby:
Może to być
System.Void, jedynym miejscem w języku, w którym może się odwoływać ta nazwa typu.Może to być skonstruowany typ ogólny z pominiętymi argumentami typu. Dzięki temu wyrażenie zwraca
GetTypeSystem.Typeobiekt odpowiadający samemu typowi ogólnemu.
W poniższym przykładzie GetType pokazano wyrażenie:
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
Wynikowe dane wyjściowe to:
Int32
Int32
String
Double[]
TypeOf... Wyrażenie is
Wyrażenie TypeOf...Is służy do sprawdzania, czy typ czasu wykonywania wartości jest zgodny z danym typem. Pierwszy operand musi być klasyfikowany jako wartość, nie może być ponownie sklasyfikowaną metodą lambda i musi być typu odwołania lub nieprzyciągnięty typ parametru typu. Drugi operand musi być nazwą typu. Wynik wyrażenia jest klasyfikowany jako wartość i jest wartością Boolean . Wyrażenie oblicza, True czy typ czasu wykonywania operandu ma tożsamość, wartość domyślną, odwołanie, tablicę, typ wartości lub konwersję parametru typu na typ, False w przeciwnym razie. Błąd czasu kompilacji występuje, jeśli nie istnieje konwersja między typem wyrażenia a określonym typem.
TypeOfIsExpression
: 'TypeOf' Expression 'Is' LineTerminator? TypeName
;
Wyrażenie is
Wyrażenie Is lub IsNot służy do porównywania równości odwołań.
IsExpression
: Expression 'Is' LineTerminator? Expression
| Expression 'IsNot' LineTerminator? Expression
;
Każde wyrażenie musi być klasyfikowane jako wartość, a typ każdego wyrażenia musi być typem odwołania, nieprzyciągniętym typem typu lub typem wartości dopuszczanej do wartości null. Jeśli typ jednego wyrażenia jest typem parametru typu bez ograniczeń lub typem wartości dopuszczanej do wartości null, jednak drugie wyrażenie musi być literałem Nothing.
Wynik jest klasyfikowany jako wartość i jest typowany jako Boolean. Operacja Is oblicza, True czy obie wartości odwołują się do tego samego wystąpienia lub obu wartości to Nothing, lub False w inny sposób. Operacja IsNot oblicza, False czy obie wartości odwołują się do tego samego wystąpienia lub obu wartości to Nothing, lub True w inny sposób.
GetXmlNamespace Expressions
Wyrażenie GetXmlNamespace składa się ze słowa kluczowego GetXmlNamespace i nazwy przestrzeni nazw XML zadeklarowanej przez plik źródłowy lub środowisko kompilacji.
GetXmlNamespaceExpression
: 'GetXmlNamespace' OpenParenthesis XMLNamespaceName? CloseParenthesis
;
Wyrażenie GetXmlNamespace jest klasyfikowane jako wartość, a jego wartość jest wystąpieniem System.Xml.Linq.XNamespace , które reprezentuje wartość XMLNamespaceName. Jeśli ten typ jest niedostępny, wystąpi błąd czasu kompilacji.
Przykład:
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
Wszystkie elementy między nawiasami są traktowane jako część nazwy przestrzeni nazw, więc reguły XML wokół takich elementów jak odstępy mają zastosowanie. Przykład:
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
Wyrażenie przestrzeni nazw XML można również pominąć, w takim przypadku wyrażenie zwraca obiekt reprezentujący domyślną przestrzeń nazw XML.
Wyrażenia dostępu do składowych
Wyrażenie dostępu do elementu członkowskiego służy do uzyskiwania dostępu do elementu członkowskiego jednostki.
MemberAccessExpression
: MemberAccessBase? Period IdentifierOrKeyword
( OpenParenthesis 'Of' TypeArgumentList CloseParenthesis )?
;
MemberAccessBase
: Expression
| NonArrayTypeName
| 'Global'
| 'MyClass'
| 'MyBase'
;
Dostęp do elementu członkowskiego formularza E.I(Of A), gdzie E jest wyrażeniem, nazwą typu nie tablicy, słowem kluczowym Globallub pominiętym i I jest identyfikatorem z opcjonalną listą Aargumentów typu , jest oceniany i klasyfikowany w następujący sposób:
Jeśli
Ezostanie pominięty, wyrażenie z natychmiast zawierającejWithinstrukcję zostanie zastąpioneEi zostanie wykonany dostęp do składowych. Jeśli nie ma instrukcji zawierającejWith, wystąpi błąd czasu kompilacji.Jeśli
Ejest klasyfikowana jako przestrzeń nazw lubEjest słowem kluczowymGlobal, wyszukiwanie elementu członkowskiego odbywa się w kontekście określonej przestrzeni nazw. JeśliIjest nazwą dostępnego elementu członkowskiego tej przestrzeni nazw z taką samą liczbą parametrów typu, jak podano na liście argumentów typu, jeśli istnieje, wynik jest tym elementem członkowskim. Wynik jest klasyfikowany jako przestrzeń nazw lub typ w zależności od elementu członkowskiego. W przeciwnym razie wystąpi błąd czasu kompilacji.Jeśli
Ejest typem lub wyrażeniem sklasyfikowanym jako typ, wyszukiwanie elementu członkowskiego odbywa się w kontekście określonego typu. JeśliIjest nazwą dostępnegoEelementu członkowskiego klasy ,E.Ijest oceniane i klasyfikowane w następujący sposób:- Jeśli
Ijest słowem kluczowymNewiEnie jest wyliczeniem, wystąpi błąd czasu kompilacji. - Jeśli
Iidentyfikuje typ z taką samą liczbą parametrów typu, jak podano na liście argumentów typu, jeśli istnieje, wynik jest tego typu. - Jeśli
Izidentyfikuje co najmniej jedną metodę, wynikiem jest grupa metod ze skojarzona lista argumentów typu i nie skojarzone wyrażenie docelowe. - Jeśli
Izidentyfikuje co najmniej jedną właściwość i nie podano listy argumentów typu, wynik jest grupą właściwości bez skojarzonego wyrażenia docelowego. - Jeśli
Iidentyfikuje zmienną współdzieloną i nie podano listy argumentów typu, wynik jest zmienną lub wartością. Jeśli zmienna jest tylko do odczytu, a odwołanie występuje poza współużytkowany konstruktor typu, w którym jest zadeklarowana zmiennaI, wynik jest wartością zmiennej udostępnionej w elemencieE. W przeciwnym razie wynik jest zmienną udostępnioną w plikuIE. - Jeśli
Izidentyfikuje zdarzenie udostępnione i nie podano listy argumentów typu, wynik jest dostępem do zdarzeń bez skojarzonego wyrażenia docelowego. - Jeśli
Iidentyfikuje stałą i nie podano listy argumentów typu, wynik jest wartością tej stałej. - Jeśli
Iidentyfikuje element członkowski wyliczenia i nie podano listy argumentów typu, wynik jest wartością tego elementu członkowskiego wyliczenia. - W przeciwnym razie
E.Ijest nieprawidłowym odwołaniem do składowej i występuje błąd kompilacji.
- Jeśli
Jeśli
Ejest klasyfikowana jako zmienna lub wartość, typ, którego wartością jestT, wyszukiwanie składowe jest wykonywane w kontekścieT. JeśliIjest nazwą dostępnegoTelementu członkowskiego klasy ,E.Ijest oceniane i klasyfikowane w następujący sposób:- Jeśli
Ijest słowem kluczowymNew,EjestMe,MyBaselubMyClass, i nie podano argumentów typu, wynik jest grupą metod reprezentującą konstruktory wystąpienia typuEz skojarzonym wyrażeniemEdocelowym i nie ma listy argumentów typu. W przeciwnym razie wystąpi błąd czasu kompilacji. - Jeśli
Izidentyfikuje co najmniej jedną metodę, w tym metody rozszerzenia, jeśliTnieObjectjest , wynikiem jest grupa metod ze skojarzona lista argumentów typu i skojarzone wyrażenieEdocelowe . - Jeśli
Izidentyfikuje co najmniej jedną właściwość i nie podano argumentów typu, wynik jest grupą właściwości ze skojarzonym wyrażeniemEdocelowym . - Jeśli
Iidentyfikuje zmienną współdzieloną lub zmienną wystąpienia i nie podano żadnych argumentów typu, wynik jest zmienną lub wartością. Jeśli zmienna jest tylko do odczytu, a odwołanie występuje poza konstruktorem klasy, w której zmienna jest zadeklarowana jako odpowiednia dla rodzaju zmiennej (udostępnionej lub wystąpienia), wynik jest wartością zmiennejIw obiekcie, do którego odwołuje sięEobiekt . JeśliTjest typem odwołania, wynikiem jest zmienna w obiekcie, do których odwołuje sięEobiektI. W przeciwnym razie, jeśliTjest typem wartości, a wyrażenieEjest klasyfikowane jako zmienna, wynik jest zmienną; w przeciwnym razie wynik jest wartością. - Jeśli
Izidentyfikuje zdarzenie i nie podano argumentów typu, wynik jest dostępem do zdarzeń ze skojarzonym wyrażeniemEdocelowym . - Jeśli
Iidentyfikuje stałą i nie podano argumentów typu, wynik jest wartością tej stałej. - Jeśli
Izostanie zidentyfikowany element członkowski wyliczenia i nie podano żadnych argumentów typu, wynik jest wartością tego elementu członkowskiego wyliczenia. - Jeśli
Twartość toObject, wynikiem jest wyszukiwanie elementów członkowskich z opóźnieniem sklasyfikowane jako dostęp do końca powiązany z listą argumentów typu i skojarzonym wyrażeniemEdocelowym .
- Jeśli
W przeciwnym razie
E.Ijest nieprawidłowym odwołaniem do składowej i występuje błąd kompilacji.
Dostęp do elementu członkowskiego formularza jest odpowiednikiem Me.I(Of A)elementu MyClass.I(Of A) , ale dostęp do niego wszystkich członków jest traktowany tak, jakby składowe nie były zastępowalne. W związku z tym dostęp do elementu członkowskiego nie będzie mieć wpływu na typ czasu wykonywania wartości, do której uzyskuje się dostęp do elementu członkowskiego.
Dostęp do elementu członkowskiego formularza MyBase.I(Of A) jest odpowiednikiem CType(Me, T).I(Of A) , gdzie T jest bezpośrednim typem podstawowym typu zawierającego wyrażenie dostępu do składowej. Wszystkie wywołania metody są traktowane tak, jakby wywoływana metoda nie jest zastępowana. Ta forma dostępu do składowych jest również nazywana dostępem podstawowym.
W poniższym przykładzie pokazano, jak Meelement i MyBaseMyClass są powiązane:
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
Ten kod zostanie wyświetlony:
MoreDerived.F
Derived.F
Derived.F
Gdy wyrażenie dostępu do elementu członkowskiego zaczyna się od słowa kluczowego Global, słowo kluczowe reprezentuje najbardziej zewnętrzną nienazwaną przestrzeń nazw, która jest przydatna w sytuacjach, gdy deklaracja w tle otaczającej przestrzeni nazw. Słowo Global kluczowe umożliwia "ucieczkę" do najbardziej zewnętrznej przestrzeni nazw w tej sytuacji. Przykład:
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
W powyższym przykładzie pierwsze wywołanie metody jest nieprawidłowe, ponieważ identyfikator System wiąże się z klasą System, a nie przestrzenią nazw System. Jedynym sposobem uzyskania dostępu System do przestrzeni nazw jest użycie Global funkcji ucieczki do najbardziej zewnętrznej przestrzeni nazw.
Jeśli dostęp do elementu członkowskiego jest współużytkowany, dowolne wyrażenie po lewej stronie okresu jest zbędne i nie jest oceniane, chyba że dostęp do elementu członkowskiego zostanie wykonany z opóźnieniem. Rozważmy na przykład następujący kod:
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
Drukuje The value of F is: 10 , ponieważ funkcja ReturnC nie musi być wywoływana, aby zapewnić wystąpienie C dostępu do udostępnionego elementu członkowskiego F.
Identyczne nazwy typów i składowych
Nie rzadko zdarza się, aby nazwać członków przy użyciu tej samej nazwy co ich typ. W takiej sytuacji może jednak wystąpić niewygodne ukrywanie nazwy:
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
W poprzednim przykładzie prosta nazwa Color w DefaultColor pliku wiąże się z właściwością wystąpienia zamiast typu. Ponieważ nie można odwołać się do elementu członkowskiego wystąpienia w udostępnionym elemencie członkowskim, zwykle jest to błąd.
Jednak specjalna reguła zezwala na dostęp do typu w tym przypadku. Jeśli podstawowe wyrażenie wyrażenia dostępu do składowej jest prostą nazwą i wiąże się z stałą, polem, właściwością, zmienną lokalną lub parametrem, którego typ ma taką samą nazwę, wyrażenie podstawowe może odwoływać się do elementu członkowskiego lub typu. Nie może to spowodować niejednoznaczności, ponieważ członkowie, do których można uzyskać dostęp poza jednym z nich, są takie same.
Wystąpienia domyślne
W niektórych sytuacjach klasy pochodzące z wspólnej klasy bazowej zwykle lub zawsze mają tylko jedno wystąpienie. Na przykład większość okien wyświetlanych w interfejsie użytkownika ma tylko jedno wystąpienie wyświetlane na ekranie w dowolnym momencie. Aby uprościć pracę z tymi typami klas, język Visual Basic może automatycznie generować domyślne wystąpienia klas, które zapewniają pojedyncze, łatwe odwołanie do wystąpienia dla każdej klasy.
Wystąpienia domyślne są zawsze tworzone dla rodziny typów, a nie dla jednego określonego typu. Dlatego zamiast tworzyć wystąpienie domyślne dla klasy Form1, która pochodzi z formularza, wystąpienia domyślne są tworzone dla wszystkich klas pochodzących z formularza. Oznacza to, że każda pojedyncza klasa pochodząca z klasy bazowej nie musi być specjalnie oznaczona, aby mieć wystąpienie domyślne.
Domyślne wystąpienie klasy jest reprezentowane przez właściwość wygenerowaną przez kompilator, która zwraca domyślne wystąpienie tej klasy. Właściwość wygenerowana jako element członkowski klasy o nazwie klasa grupy , która zarządza przydzielaniem i niszczeniem wystąpień domyślnych dla wszystkich klas pochodzących z konkretnej klasy bazowej. Na przykład wszystkie domyślne właściwości wystąpień klas pochodnych Form mogą być zbierane w MyForms klasie. Jeśli wystąpienie klasy grupy jest zwracane przez wyrażenie My.Forms, następujący kod uzyskuje dostęp do domyślnych wystąpień klas Form1 pochodnych i 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
Wystąpienia domyślne nie zostaną utworzone do momentu pierwszego odwołania do nich; Pobranie właściwości reprezentującej wystąpienie domyślne powoduje utworzenie domyślnego wystąpienia, jeśli jeszcze nie został utworzony lub został ustawiony na Nothingwartość . Aby umożliwić testowanie istnienia wystąpienia domyślnego, gdy wystąpienie domyślne jest obiektem docelowym Is operatora lub IsNot , wystąpienie domyślne nie zostanie utworzone. W związku z tym można sprawdzić, czy wystąpienie domyślne jest Nothing , czy inne odwołanie, bez powodowania utworzenia wystąpienia domyślnego.
Wystąpienia domyślne mają ułatwić odwoływanie się do wystąpienia domyślnego spoza klasy, która ma wystąpienie domyślne. Użycie wystąpienia domyślnego z klasy definiującej ją może spowodować zamieszanie co do tego, do którego wystąpienia jest odwoływane, tj. wystąpienie domyślne lub bieżące wystąpienie. Na przykład poniższy kod modyfikuje tylko wartość x w wystąpieniu domyślnym, mimo że jest wywoływana z innego wystąpienia. W związku z tym kod będzie wyświetlać wartość 5 zamiast 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
Aby zapobiec tego rodzaju nieporozumieniu, nieprawidłowe jest odwołanie się do wystąpienia domyślnego z poziomu metody wystąpienia typu wystąpienia domyślnego.
Domyślne wystąpienia i nazwy typów
Wystąpienie domyślne może być również dostępne bezpośrednio za pośrednictwem nazwy jego typu. W takim przypadku w każdym kontekście wyrażenia, w którym nazwa typu nie jest dozwolona, wyrażenie , gdzie E reprezentuje w pełni kwalifikowaną nazwę klasy z wystąpieniem domyślnym, jest zmieniany na E', gdzie E' reprezentuje wyrażenieE, które pobiera domyślną właściwość wystąpienia. Jeśli na przykład domyślne wystąpienia klas pochodnych z Form zezwalania na dostęp do wystąpienia domyślnego za pomocą nazwy typu, poniższy kod jest odpowiednikiem kodu w poprzednim przykładzie:
Module Main
Sub Main()
Form1.x = 10
Console.WriteLine(Form2.y)
End Sub
End Module
Oznacza to również, że wystąpienie domyślne, które jest dostępne za pośrednictwem nazwy jego typu, również można przypisać za pomocą nazwy typu. Na przykład następujący kod ustawia domyślne wystąpienie elementu Form1 na Nothingwartość :
Module Main
Sub Main()
Form1 = Nothing
End Sub
End Module
Należy pamiętać, że znaczenie E.I elementu oznacza klasę E i I reprezentuje współużytkowany element członkowski nie zmienia się. Takie wyrażenie nadal uzyskuje dostęp do udostępnionego elementu członkowskiego bezpośrednio z wystąpienia klasy i nie odwołuje się do wystąpienia domyślnego.
Klasy grup
Atrybut Microsoft.VisualBasic.MyGroupCollectionAttribute wskazuje klasę grupy dla rodziny wystąpień domyślnych. Atrybut ma cztery parametry:
Parametr
TypeToCollectokreśla klasę bazową dla grupy. Wszystkie klasy możliwe do utworzenia bez parametrów typu otwartego pochodzącego z typu o tej nazwie (niezależnie od parametrów typu) będą automatycznie miały wystąpienie domyślne.Parametr
CreateInstanceMethodNameokreśla metodę wywoływania w klasie grupy w celu utworzenia nowego wystąpienia we właściwości wystąpienia domyślnego.Parametr
DisposeInstanceMethodNameokreśla metodę wywoływania w klasie grupy w celu usunięcia domyślnej właściwości wystąpienia, jeśli domyślna właściwość wystąpienia ma przypisaną wartośćNothing.Parametr
DefaultInstanceAliasokreśla wyrażenieE', które ma zastąpić nazwę klasy, jeśli wystąpienia domyślne są dostępne bezpośrednio za pośrednictwem ich nazwy typu. Jeśli ten parametr jestNothinglub jest pustym ciągiem, wystąpienia domyślne tego typu grupy nie są dostępne bezpośrednio za pośrednictwem nazwy ich typu. (Uwaga. We wszystkich bieżących implementacjach językaDefaultInstanceAliasVisual Basic parametr jest ignorowany, z wyjątkiem kodu dostarczonego przez kompilator).
Wiele typów można zebrać w tej samej grupie, oddzielając nazwy typów i metod w pierwszych trzech parametrach przy użyciu przecinków. W każdym parametrze musi znajdować się taka sama liczba elementów, a elementy listy są dopasowywane w kolejności. Na przykład następująca deklaracja atrybutu zbiera typy pochodzące z C1klasy lub C2C3 do jednej grupy:
<Microsoft.VisualBasic.MyGroupCollection("C1, C2, C3", _
"CreateC1, CreateC2, CreateC3", _
"DisposeC1, DisposeC2, DisposeC3", "My.Cs")>
Public NotInheritable Class MyCs
...
End Class
Podpis metody create musi mieć postać Shared Function <Name>(Of T As {New, <Type>})(Instance Of T) As T. Metoda dispose musi mieć postać Shared Sub <Name>(Of T As <Type>)(ByRef Instance Of T). W związku z tym klasa grupy dla przykładu w poprzedniej sekcji może być zadeklarowana w następujący sposób:
<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
Jeśli plik źródłowy zadeklarował klasę Form1pochodną , wygenerowana klasa grupy byłaby równoważna:
<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
Kolekcja metod rozszerzenia
Metody rozszerzenia dla wyrażenia E.I dostępu do składowej są zbierane przez zebranie wszystkich metod rozszerzenia o nazwie I dostępnej w bieżącym kontekście:
- Najpierw sprawdzany jest każdy zagnieżdżony typ zawierający wyrażenie, zaczynając od najbardziej wewnętrznego i przechodzącego do najbardziej zewnętrznego.
- Następnie sprawdzana jest każda zagnieżdżona przestrzeń nazw, zaczynając od najbardziej wewnętrznej i przechodzącej do najbardziej zewnętrznej przestrzeni nazw.
- Następnie importy w pliku źródłowym są sprawdzane.
- Następnie sprawdzane są importy zdefiniowane przez środowisko kompilacji.
Metoda rozszerzenia jest zbierana tylko wtedy, gdy istnieje rozszerzająca konwersja natywna z typu wyrażenia docelowego na typ pierwszego parametru metody rozszerzenia. W przeciwieństwie do zwykłego powiązania wyrażenia nazwy wyszukiwanie zbiera wszystkie metody rozszerzenia; kolekcja nie zostanie zatrzymana po znalezieniu metody rozszerzenia. Przykład:
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
W tym przykładzie, mimo że N2C1Extensions.M1 znaleziono je przed N1C1Extensions.M1parametrem , są traktowane jako metody rozszerzenia. Po zebraniu wszystkich metod rozszerzenia są one następnie curried. Currying przyjmuje element docelowy wywołania metody rozszerzenia i stosuje go do wywołania metody rozszerzenia, co powoduje usunięcie nowego podpisu metody z pierwszym parametrem usuniętym (ponieważ został określony). Przykład:
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
W powyższym przykładzie wynikiem zastosowania metody vExt1.M jest podpis Sub M(y As Integer)metody .
Oprócz usunięcia pierwszego parametru metody rozszerzenia currying usuwa również wszystkie parametry typu metody, które są częścią typu pierwszego parametru. Podczas currying metody rozszerzenia z parametrem typu metody wnioskowanie typu jest stosowane do pierwszego parametru, a wynik jest stały dla dowolnych parametrów typu, które są wnioskowane. Jeśli wnioskowanie typu nie powiedzie się, metoda zostanie zignorowana. Przykład:
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
W powyższym przykładzie curried wynik zastosowania v do Ext1.M jest sygnaturą Sub M(Of U)(y As U)metody , ponieważ parametr T typu jest wnioskowany w wyniku curryingu i jest teraz stały. Ponieważ parametr U typu nie został wywnioskowany jako część curryingu, pozostaje otwartym parametrem. Podobnie, ponieważ parametr T typu jest wywnioskowany w wyniku zastosowania v do Ext2.Mparametru , typ parametru y staje się stały jako Integer. Nie zostanie on wywnioskowany jako żaden inny typ. Podczas currying podpisu wszystkie ograniczenia z wyjątkiem New ograniczeń są również stosowane. Jeśli ograniczenia nie są spełnione lub zależą od typu, który nie został wywnioskowany w ramach curryingu, metoda rozszerzenia jest ignorowana. Przykład:
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
Uwaga. Jedną z głównych przyczyn curryingu metod rozszerzenia jest umożliwienie wyrażeń zapytań wnioskowania typu iteracji przed oceną argumentów do metody wzorca zapytania. Ponieważ większość metod wzorca zapytania korzysta z wyrażeń lambda, które wymagają wnioskowania typu, znacznie upraszcza to proces obliczania wyrażenia zapytania.
W przeciwieństwie do dziedziczenia interfejsu normalnego dostępne są metody rozszerzeń rozszerzające dwa interfejsy, które nie odnoszą się do siebie, o ile nie mają tego samego podpisu curried:
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
Na koniec należy pamiętać, że metody rozszerzeń nie są brane pod uwagę podczas wykonywania późnego powiązania:
Module Test
Sub Main()
Dim o As Object = ...
' Ignores extension methods
o.M1()
End Sub
End Module
Wyrażenia dostępu do składowych słownika
Wyrażenie dostępu do składowych słownika służy do wyszukiwania elementu członkowskiego kolekcji. Dostęp do elementu członkowskiego słownika ma formę E!I, gdzie E jest wyrażeniem sklasyfikowanym jako wartość i I jest identyfikatorem.
DictionaryAccessExpression
: Expression? '!' IdentifierOrKeyword
;
Typ wyrażenia musi mieć właściwość domyślną indeksowaną przez pojedynczy String parametr. Wyrażenie E!I dostępu do składowych słownika jest przekształcane w wyrażenie E.D("I"), gdzie D jest domyślną właściwością E. Przykład:
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
Jeśli wykrzyknik jest określony bez wyrażenia, przyjmuje się wyrażenie z natychmiast zawierającej With instrukcję. Jeśli nie ma instrukcji zawierającej With , wystąpi błąd czasu kompilacji.
Wyrażenia wywołania
Wyrażenie wywołania składa się z obiektu docelowego wywołania i opcjonalnej listy argumentów.
InvocationExpression
: Expression ( OpenParenthesis ArgumentList? CloseParenthesis )?
;
ArgumentList
: PositionalArgumentList
| PositionalArgumentList Comma NamedArgumentList
| NamedArgumentList
;
PositionalArgumentList
: Expression? ( Comma Expression? )*
;
NamedArgumentList
: IdentifierOrKeyword ColonEquals Expression
( Comma IdentifierOrKeyword ColonEquals Expression )*
;
Wyrażenie docelowe musi być klasyfikowane jako grupa metod lub wartość, której typ jest typem delegata. Jeśli wyrażenie docelowe jest wartością, której typem jest typ delegata, element docelowy wyrażenia wywołania staje się grupą metod dla Invoke elementu członkowskiego typu delegata, a wyrażenie docelowe staje się skojarzonym wyrażeniem docelowym grupy metod.
Lista argumentów zawiera dwie sekcje: argumenty pozycyjne i nazwane argumenty.
Argumenty pozycyjne to wyrażenia i muszą poprzedzać wszystkie nazwane argumenty.
Nazwane argumenty zaczynają się od identyfikatora, który może być zgodny ze słowami kluczowymi, a następnie := wyrażeniem.
Jeśli grupa metod zawiera tylko jedną dostępną metodę, w tym metody wystąpienia i rozszerzenia, a ta metoda nie przyjmuje argumentów i jest funkcją, grupa metod jest interpretowana jako wyrażenie wywołania z pustą listą argumentów, a wynik jest używany jako element docelowy wyrażenia wywołania z podanymi listami argumentów. Przykład:
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
W przeciwnym razie rozwiązanie przeciążenia jest stosowane do metod w celu wybrania najbardziej odpowiedniej metody dla danej listy argumentów. Jeśli najbardziej odpowiednią metodą jest funkcja, wynik wyrażenia wywołania jest klasyfikowany jako wartość typ zwracana funkcji. Jeśli najbardziej odpowiednią metodą jest podroutine, wynik jest klasyfikowany jako void. Jeśli najbardziej odpowiednią metodą jest metoda częściowa, która nie ma treści, wyrażenie wywołania jest ignorowane, a wynik jest klasyfikowany jako void.
W przypadku wyrażenia wywołania wczesnego powiązanego argumenty są oceniane w kolejności, w której odpowiednie parametry są deklarowane w metodzie docelowej. W przypadku wyrażenia dostępu do elementu członkowskiego z późnym opóźnieniem są one oceniane w kolejności, w jakiej są wyświetlane w wyrażeniu dostępu do składowych: zobacz Sekcję Late-Bound Wyrażenia.
Rozpoznawanie metody przeciążonej:
Aby zapoznać się z rozpoznawaniem przeciążeń, specyfiką elementów członkowskich/typów przy użyciu listy argumentów, rodzajowości, stosowania do listy argumentów, przekazywania argumentów i wybierania argumentów opcjonalnych, metod warunkowych i wnioskowania argumentów typu: zobacz Rozpoznawanie przeciążenia sekcji.
Wyrażenia indeksu
Wyrażenie indeksu powoduje, że element tablicy lub ponownie klasyfikuje grupę właściwości do dostępu do właściwości. Wyrażenie indeksu składa się z wyrażenia w kolejności, nawiasu otwierającego, listy argumentów indeksu i nawiasu zamykającego.
IndexExpression
: Expression OpenParenthesis ArgumentList? CloseParenthesis
;
Element docelowy wyrażenia indeksu musi być klasyfikowany jako grupa właściwości lub wartość. Wyrażenie indeksu jest przetwarzane w następujący sposób:
Jeśli wyrażenie docelowe jest klasyfikowane jako wartość i jeśli jego typ nie jest typem tablicy,
ObjectlubSystem.Array, typ musi mieć właściwość domyślną. Indeks jest wykonywany w grupie właściwości, która reprezentuje wszystkie domyślne właściwości typu. Mimo że deklarowanie właściwości domyślnej bez parametrów w Visual Basic jest nieprawidłowe, inne języki mogą zezwalać na deklarowanie takiej właściwości. W związku z tym indeksowanie właściwości bez argumentów jest dozwolone.Jeśli wyrażenie powoduje wartość typu tablicy, liczba argumentów na liście argumentów musi być taka sama jak ranga typu tablicy i może nie zawierać nazwanych argumentów. Jeśli którykolwiek z indeksów jest nieprawidłowy w czasie wykonywania, zgłaszany
System.IndexOutOfRangeExceptionjest wyjątek. Każde wyrażenie musi być niejawnie konwertowane na typInteger. Wynikiem wyrażenia indeksu jest zmienna w określonym indeksie i jest klasyfikowana jako zmienna.Jeśli wyrażenie jest klasyfikowane jako grupa właściwości, rozpoznawanie przeciążenia służy do określenia, czy jedna z właściwości ma zastosowanie do listy argumentów indeksu. Jeśli grupa właściwości zawiera tylko jedną właściwość, która ma metodę
Getdostępu i jeśli ta metoda dostępu nie przyjmuje żadnych argumentów, grupa właściwości jest interpretowana jako wyrażenie indeksu z pustą listą argumentów. Wynik jest używany jako element docelowy bieżącego wyrażenia indeksu. Jeśli nie ma zastosowania, wystąpi błąd czasu kompilacji. W przeciwnym razie wyrażenie powoduje dostęp do właściwości ze skojarzonym wyrażeniem docelowym (jeśli istnieje) grupy właściwości.Jeśli wyrażenie jest klasyfikowane jako grupa właściwości z późną granicą lub jako wartość, której typ jest
ObjectlubSystem.Array, przetwarzanie wyrażenia indeksu jest odroczone do czasu wykonywania, a indeksowanie jest opóźnione. Wyrażenie powoduje, że dostęp do właściwości powiązanej z opóźnieniem został wpisany jakoObject. Skojarzone wyrażenie docelowe jest wyrażeniem docelowym, jeśli jest wartością lub skojarzonym wyrażeniem docelowym grupy właściwości. W czasie wykonywania wyrażenie jest przetwarzane w następujący sposób:Jeśli wyrażenie jest klasyfikowane jako grupa właściwości z opóźnieniem, wyrażenie może spowodować grupę metod, grupę właściwości lub wartość (jeśli element członkowski jest wystąpieniem lub zmienną współdzieloną). Jeśli wynikiem jest grupa metod lub grupa właściwości, rozwiązanie przeciążenia jest stosowane do grupy w celu określenia prawidłowej metody dla listy argumentów. Jeśli rozwiązywanie przeciążenia zakończy się niepowodzeniem
System.Reflection.AmbiguousMatchException, zostanie zgłoszony wyjątek. Następnie wynik jest przetwarzany jako dostęp do właściwości lub jako wywołanie, a wynik jest zwracany. Jeśli wywołanie ma wartość podrzędną, wynik toNothing.Jeśli typ czasu wykonywania wyrażenia docelowego jest typem tablicy lub
System.Array, wynikiem wyrażenia indeksu jest wartość zmiennej w określonym indeksie.W przeciwnym razie typ czasu wykonywania wyrażenia musi mieć właściwość domyślną, a indeks jest wykonywany w grupie właściwości reprezentującej wszystkie właściwości domyślne typu. Jeśli typ nie ma właściwości domyślnej,
System.MissingMemberExceptionzostanie zgłoszony wyjątek.
Nowe wyrażenia
Operator New służy do tworzenia nowych wystąpień typów. Istnieją cztery formy New wyrażeń:
Wyrażenia tworzenia obiektów służą do tworzenia nowych wystąpień typów klas i typów wartości.
Wyrażenia tworzenia tablic są używane do tworzenia nowych wystąpień typów tablic.
Wyrażenia tworzenia delegatów (które nie mają odrębnej składni z wyrażeń tworzenia obiektów) są używane do tworzenia nowych wystąpień typów delegatów.
Anonimowe wyrażenia tworzenia obiektów służą do tworzenia nowych wystąpień anonimowych typów klas.
NewExpression
: ObjectCreationExpression
| ArrayExpression
| AnonymousObjectCreationExpression
;
Wyrażenie New jest klasyfikowane jako wartość, a wynikiem jest nowe wystąpienie typu.
wyrażenia Object-Creation
Wyrażenie tworzenia obiektu służy do tworzenia nowego wystąpienia typu klasy lub typu struktury.
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
;
Typ wyrażenia tworzenia obiektu musi być typem klasy, typem struktury lub parametrem typu z ograniczeniem New i nie może być klasą MustInherit . Biorąc pod uwagę wyrażenie tworzenia obiektu formularza New T(A), gdzie T jest typem klasy lub typem struktury i A jest opcjonalną listą argumentów, rozdzielczość przeciążenia określa prawidłowy konstruktor T do wywołania. Parametr typu z ograniczeniem New jest uznawany za jeden, bez parametrów konstruktor. Jeśli żaden konstruktor nie jest wywoływany, wystąpi błąd czasu kompilacji; w przeciwnym razie wyrażenie powoduje utworzenie nowego wystąpienia T przy użyciu wybranego konstruktora. Jeśli nie ma argumentów, nawiasy mogą zostać pominięte.
Przydzielanie wystąpienia zależy od tego, czy wystąpienie jest typem klasy, czy typem wartości.
New Wystąpienia typów klas są tworzone na stercie systemu, podczas gdy nowe wystąpienia typów wartości są tworzone bezpośrednio na stosie.
Wyrażenie tworzenia obiektu może opcjonalnie określić listę inicjatorów składowych po argumentach konstruktora. Te inicjatory składowych są poprzedzone słowem kluczowym With, a lista inicjatorów jest interpretowana tak, jakby znajdowała się w kontekście instrukcji With . Na przykład biorąc pod uwagę klasę:
Class Customer
Dim Name As String
Dim Address As String
End Class
Kod:
Module Test
Sub Main()
Dim x As New Customer() With { .Name = "Bob Smith", _
.Address = "123 Main St." }
End Sub
End Module
Jest w przybliżeniu równoważne:
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
Każdy inicjator musi określić nazwę do przypisania, a nazwa musi być zmienną inną niżReadOnly wystąpienie lub właściwość typu, który jest konstruowany; dostęp do elementu członkowskiego nie będzie opóźniony, jeśli typ jest skonstruowany.Object Inicjatory mogą nie używać słowa kluczowego Key . Każdy element członkowski w typie może być inicjowany tylko raz. Jednak wyrażenia inicjatora mogą się odwoływać do siebie nawzajem. Przykład:
Module Test
Sub Main()
Dim x As New Customer() With { .Name = "Bob Smith", _
.Address = .Name & " St." }
End Sub
End Module
Inicjatory są przypisane od lewej do prawej, więc jeśli inicjator odwołuje się do elementu członkowskiego, który nie został jeszcze zainicjowany, zobaczy dowolną wartość zmiennej wystąpienia po uruchomieniu konstruktora:
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
Inicjatory mogą być zagnieżdżone:
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
Jeśli tworzony typ jest typem kolekcji i ma metodę wystąpienia o nazwie Add (w tym metody rozszerzenia i metody udostępnione), wyrażenie tworzenia obiektów może określić inicjator kolekcji poprzedzony prefiksem słowa kluczowego From. Wyrażenie tworzenia obiektu nie może określić inicjatora składowego i inicjatora kolekcji. Każdy element w inicjatorze kolekcji jest przekazywany jako argument do wywołania Add funkcji. Przykład:
Dim list = New List(Of Integer)() From { 1, 2, 3, 4 }
jest odpowiednikiem:
Dim list = New List(Of Integer)()
list.Add(1)
list.Add(2)
list.Add(3)
Jeśli element jest inicjatorem kolekcji, każdy element inicjatora kolekcji podrzędnej zostanie przekazany jako pojedynczy argument do Add funkcji. Na przykład:
Dim dict = Dictionary(Of Integer, String) From { { 1, "One" },{ 2, "Two" } }
jest odpowiednikiem:
Dim dict = New Dictionary(Of Integer, String)
dict.Add(1, "One")
dict.Add(2, "Two")
Ta ekspansja jest zawsze wykonywana i jest wykonywana tylko na jednym poziomie głębokości; następnie inicjatory podrzędne są traktowane jako literały tablicy. Przykład:
' 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 } } }
Wyrażenia tablicy
Wyrażenie tablicy służy do tworzenia nowego wystąpienia typu tablicy. Istnieją dwa typy wyrażeń tablicy: wyrażenia tworzenia tablicy i literały tablicy.
Wyrażenia tworzenia tablicy
Jeśli zostanie podany modyfikator inicjowania rozmiaru tablicy, wynikowy typ tablicy jest uzyskiwany przez usunięcie każdego z poszczególnych argumentów z listy argumentów inicjowania rozmiaru tablicy. Wartość każdego argumentu określa górną granicę odpowiedniego wymiaru w nowo przydzielonym wystąpieniu tablicy. Jeśli wyrażenie ma inicjator kolekcji niepustej, każdy argument na liście argumentów musi być stałą, a ranga i długość wymiarów określona przez listę wyrażeń musi być zgodna z argumentami inicjatora kolekcji.
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 }
Jeśli modyfikator inicjowania rozmiaru tablicy nie jest podany, nazwa typu musi być typem tablicy, a inicjator kolekcji musi być pusty lub mieć taką samą liczbę poziomów zagnieżdżania co ranga określonego typu tablicy. Wszystkie elementy na najbardziej wewnętrznym poziomie zagnieżdżania muszą być niejawnie konwertowane na typ elementu tablicy i muszą być klasyfikowane jako wartość. Liczba elementów w każdym zagnieżdżonym inicjatorze kolekcji musi być zawsze zgodna z rozmiarem innych kolekcji na tym samym poziomie. Poszczególne długości wymiarów są wnioskowane z liczby elementów w każdym z odpowiednich poziomów zagnieżdżania inicjatora kolekcji. Jeśli inicjator kolekcji jest pusty, długość każdego wymiaru wynosi zero.
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 } }
Najbardziej oddalony poziom zagnieżdżania inicjatora kolekcji odpowiada lewemu wymiarowi tablicy, a najbardziej wewnętrzny poziom zagnieżdżania odpowiada najbardziej po prawej stronie wymiaru. Przykład:
Dim array As Integer(,) = _
{ { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, { 8, 9 } }
Jest odpowiednikiem następujących elementów:
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
Jeśli inicjator kolekcji jest pusty (czyli taki, który zawiera nawiasy klamrowe, ale nie lista inicjatora) i granice wymiarów inicjowanej tablicy są znane, pusty inicjator kolekcji reprezentuje wystąpienie tablicy o określonym rozmiarze, w którym wszystkie elementy zostały zainicjowane do wartości domyślnej typu elementu. Jeśli nie są znane granice wymiarów inicjowanej tablicy, pusty inicjator kolekcji reprezentuje wystąpienie tablicy, w którym wszystkie wymiary mają rozmiar zero.
Ranga i długość każdego wymiaru wystąpienia tablicy są stałe przez cały okres istnienia wystąpienia. Innymi słowy, nie można zmienić rangi istniejącego wystąpienia tablicy ani zmienić rozmiaru jego wymiarów.
Literały tablicy
Literał tablicy określa tablicę, której typ elementu, ranga i granice są wnioskowane z kombinacji kontekstu wyrażenia i inicjatora kolekcji. Jest to wyjaśnione w sekcji Wyrażenie reklasyfikacji.
ArrayExpression
: ArrayCreationExpression
| ArrayLiteralExpression
;
ArrayCreationExpression
: 'New' NonArrayTypeName ArrayNameModifier CollectionInitializer
;
ArrayLiteralExpression
: CollectionInitializer
;
Przykład:
' 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}}
Format i wymagania inicjatora kolekcji w literału tablicy są dokładnie takie same jak w przypadku inicjatora kolekcji w wyrażeniu tworzenia tablicy.
Uwaga. Literał tablicy nie tworzy tablicy w i w sobie; Zamiast tego jest to ponowna klasyfikacja wyrażenia w wartość, która powoduje utworzenie tablicy. Na przykład konwersja CType(new Integer() {1,2,3}, Short()) nie jest możliwa, ponieważ nie ma konwersji z Integer() na Short(); ale wyrażenie jest możliwe, ponieważ najpierw ponownie klasyfikuje literał tablicy do wyrażenia CType({1,2,3},Short())New Short() {1,2,3}tworzenia tablicy .
wyrażenia Delegate-Creation
Wyrażenie tworzenia delegata służy do tworzenia nowego wystąpienia typu delegata. Argument wyrażenia tworzenia delegata musi być wyrażeniem sklasyfikowanym jako wskaźnik metody lub metodą lambda.
Jeśli argument jest wskaźnikiem metody, jedna z metod, do których odwołuje się wskaźnik metody, musi mieć zastosowanie do podpisu typu delegata. Metoda M ma zastosowanie do typu D delegata, jeśli:
MniePartialjest lub ma ciało.Zarówno
Mfunkcje, jak iD, lubDjest podroutine.MiDmają taką samą liczbę parametrów.Typy parametrów
Mkażdego z nich mają konwersję z typu odpowiadającego mu typu parametruD, a ich modyfikatory (tj.ByRef,ByVal) są zgodne.Zwracany
Mtyp , jeśli istnieje, ma konwersję na zwracanyDtyp .
Jeśli wskaźnik metody odwołuje się do dostępu z opóźnieniem, przyjmuje się, że dostęp związany z opóźnieniem jest związany z funkcją, która ma taką samą liczbę parametrów jak typ delegata.
Jeśli nie są używane ścisłe semantyka i istnieje tylko jedna metoda przywoływana przez wskaźnik metody, ale nie ma zastosowania ze względu na fakt, że nie ma żadnych parametrów, a typ delegata nie, metoda jest uważana za odpowiednią, a parametry lub wartość zwracana są po prostu ignorowane. Przykład:
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
Uwaga. To złagodzenie jest dozwolone tylko wtedy, gdy ścisłe semantyka nie są używane z powodu metod rozszerzeń. Ponieważ metody rozszerzenia są brane pod uwagę tylko wtedy, gdy zwykła metoda nie miała zastosowania, możliwe jest, aby metoda wystąpienia bez parametrów ukrywała metodę rozszerzenia z parametrami w celu budowy delegata.
Jeśli do typu delegata ma zastosowanie więcej niż jedna metoda, do której odwołuje się wskaźnik metody, rozpoznawanie przeciążenia służy do wybierania między metodami kandydata. Typy parametrów delegata są używane jako typy argumentów na potrzeby rozpoznawania przeciążenia. Jeśli żaden kandydat metody nie jest najbardziej odpowiedni, wystąpi błąd czasu kompilacji. W poniższym przykładzie zmienna lokalna jest inicjowana za pomocą delegata, który odwołuje się do drugiej Square metody, ponieważ ta metoda jest bardziej odpowiednia do podpisu i zwracanego DoubleFunctypu .
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
Gdyby druga Square metoda nie była obecna, zostałaby wybrana pierwsza Square metoda. Jeśli środowisko kompilacji lub Option Strictprzez program określa ścisłą semantykę, występuje błąd czasu kompilacji, jeśli najbardziej konkretna metoda przywoływana przez wskaźnik metody jest węższa niż sygnatura delegata. Metoda M jest uważana za węższą niż typ D delegata, jeśli:
Typ parametru
Mma konwersję rozszerzającą do odpowiedniego typu parametruD.Lub zwracany typ , jeśli istnieje,
Mma konwersję zawężającą do zwracanegoDtypu .
Jeśli argumenty typu są skojarzone ze wskaźnikiem metody, brane są pod uwagę tylko metody o tej samej liczbie argumentów typu. Jeśli nie są skojarzone argumenty typu ze wskaźnikiem metody, wnioskowanie typu jest używane podczas dopasowywania podpisów do metody ogólnej. W przeciwieństwie do innego normalnego wnioskowania typu zwracany typ delegata jest używany podczas wnioskowania argumentów typu, ale typy zwracane nie są nadal brane pod uwagę podczas określania najmniej ogólnego przeciążenia. W poniższym przykładzie przedstawiono oba sposoby podawania argumentu typu do wyrażenia tworzenia delegata:
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
W powyższym przykładzie wystąpienie typu delegata innego niż ogólny zostało utworzone przy użyciu metody ogólnej. Istnieje również możliwość utworzenia wystąpienia skonstruowanego typu delegata przy użyciu metody ogólnej. Przykład:
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
Jeśli argumentem wyrażenia delegata tworzenia jest metoda lambda, metoda lambda musi mieć zastosowanie do podpisu typu delegata. Metoda L lambda ma zastosowanie do typu D delegata, jeśli:
Jeśli
Lparametr ma parametry,Dma taką samą liczbę parametrów. (JeśliLnie ma parametrów, parametryDsą ignorowane).Typy parametrów każdego z
Lnich mają konwersję na typ odpowiedniego typu parametruD, a ich modyfikatory (tj.ByRef,ByVal) są zgodne.Jeśli
Djest funkcją, zwracanyLtyp ma konwersję na zwracany typD. (JeśliDjest podroutyną, zwracana wartośćLjest ignorowana).
Jeśli pominięto typ parametru parametru L , typ odpowiedniego parametru w D pliku jest wnioskowany; jeśli parametr L ma tablicę lub modyfikatory nazw dopuszczających wartość null, wynik błędu czasu kompilacji. Po udostępnieniu wszystkich typów parametrów L typ wyrażenia w metodzie lambda jest wnioskowany. Przykład:
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
W niektórych sytuacjach, gdy sygnatura delegata nie jest dokładnie zgodna z metodą lambda lub podpisem metody, program .NET Framework może nie obsługiwać natywnego tworzenia delegata. W takiej sytuacji wyrażenie metody lambda jest używane do dopasowywania dwóch metod. Przykład:
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
Wynikiem wyrażenia delegującego jest wystąpienie delegata, które odwołuje się do zgodnej metody ze skojarzonym wyrażeniem docelowym (jeśli istnieje) z wyrażenia wskaźnika metody. Jeśli wyrażenie docelowe jest wpisywane jako typ wartości, typ wartości jest kopiowany na stertę systemową, ponieważ delegat może wskazywać tylko metodę obiektu na stercie. Metoda i obiekt, do którego odwołuje się delegat, pozostają stałe przez cały okres istnienia delegata. Innymi słowy, nie można zmienić obiektu docelowego lub obiektu delegata po jego utworzeniu.
Anonimowe wyrażenia Object-Creation
Wyrażenie tworzenia obiektu z inicjatorami składowymi może również całkowicie pominąć nazwę typu.
AnonymousObjectCreationExpression
: 'New' ObjectMemberInitializer
;
W takim przypadku typ anonimowy jest konstruowany na podstawie typów i nazw elementów członkowskich zainicjowanych jako część wyrażenia. Przykład:
Module Test
Sub Main()
Dim Customer = New With { .Name = "John Smith", .Age = 34 }
Console.WriteLine(Customer.Name)
End Sub
End Module
Typ utworzony przez anonimowe wyrażenie tworzenia obiektów jest klasą, która nie ma nazwy, dziedziczy bezpośrednio z Objectklasy i ma zestaw właściwości o tej samej nazwie co elementy członkowskie przypisane do na liście inicjatorów składowych. Typ każdej właściwości jest wnioskowany przy użyciu tych samych reguł co wnioskowanie typu zmiennej lokalnej. Wygenerowane typy anonimowe zastępują ToStringrównież wartość , zwracając ciąg reprezentujący wszystkie elementy członkowskie i ich wartości. (Dokładny format tego ciągu wykracza poza zakres tej specyfikacji).
Domyślnie właściwości generowane przez typ anonimowy to odczyt-zapis. Można oznaczyć właściwość typu anonimowego jako tylko do odczytu przy użyciu Key modyfikatora. Modyfikator Key określa, że pole może służyć do unikatowego identyfikowania wartości reprezentowanej przez typ anonimowy. Oprócz tworzenia właściwości tylko do Equals odczytu, powoduje również zastąpienie typu anonimowego i GetHashCode zaimplementowanie interfejsu System.IEquatable(Of T) (wypełnianie typu anonimowego dla elementu T). Składowe są definiowane w następujący sposób:
Function Equals(obj As Object) As Boolean i Function Equals(val As T) As Boolean są implementowane przez zweryfikowanie, że dwa wystąpienia mają ten sam typ, a następnie porównują każdy Key element członkowski przy użyciu polecenia Object.Equals. Jeśli wszystkie Key elementy członkowskie są równe, Equals zwraca wartość True, w przeciwnym razie Equals zwraca wartość False.
Function GetHashCode() As Integer jest zaimplementowany w taki sposób, że jeśli Equals ma wartość true dla dwóch wystąpień typu anonimowego, GetHashCode zostanie zwrócona ta sama wartość. Skrót zaczyna się od wartości inicjatora, a następnie, w Key kolejności, mnoży skrót przez 31 i dodaje Key wartość skrótu elementu członkowskiego (podaną przez GetHashCode), jeśli element członkowski nie jest typem odniesienia lub typem wartości dopuszczanej do wartości null z wartością Nothing.
Na przykład typ utworzony w instrukcji :
Dim zipState = New With { Key .ZipCode = 98112, .State = "WA" }
tworzy klasę, która wygląda mniej więcej tak (chociaż dokładna implementacja może się różnić):
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
Aby uprościć sytuację tworzenia typu anonimowego na podstawie pól innego typu, nazwy pól można wywnioskować bezpośrednio z wyrażeń w następujących przypadkach:
Proste wyrażenie
xnazwy wywnioskuje nazwęx.Wyrażenie
x.ydostępu do elementu członkowskiego wywnioskuje nazwęy.Wyrażenie
x!yodnośnika słownika wywnioskuje nazwęy.Wywołanie lub wyrażenie indeksu bez argumentów
x()wywnioskuje nazwęx.Wyrażenie
x.<y>dostępu do składowych XML ,x.@yx...<y>, wywnioskuje nazwęy.Wyrażenie dostępu elementu członkowskiego XML, które jest elementem docelowym wyrażenia
x.<y>.zdostępu elementu członkowskiego, wywnioskuje nazwęz.Wyrażenie dostępu elementu członkowskiego XML, które jest elementem docelowym wywołania lub wyrażenia indeksu bez argumentów
x.<y>.z(), wywnioskuje nazwęz.Wyrażenie dostępu elementu członkowskiego XML, które jest celem wywołania lub wyrażenia
x.<y>(0)indeksu, wywnioskuje nazwęy.
Inicjator jest interpretowany jako przypisanie wyrażenia do wnioskowanej nazwy. Na przykład następujące inicjatory są równoważne:
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
Jeśli nazwa elementu członkowskiego jest wnioskowana, że powoduje konflikt z istniejącym elementem członkowskim typu, takim jak GetHashCode, wystąpi błąd czasu kompilacji. W przeciwieństwie do zwykłych inicjatorów składowych, anonimowe wyrażenia tworzenia obiektów nie zezwalają inicjatorom składowym na odwoływanie cykliczne ani odwoływanie się do elementu członkowskiego przed jego zainicjowaniem. Przykład:
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
Jeśli dwa anonimowe wyrażenia tworzenia klas występują w tej samej metodzie i dają ten sam wynikowy kształt — jeśli kolejność właściwości, nazwy właściwości i typy właściwości są zgodne — będą one odwoływać się do tej samej klasy anonimowej. Zakres metody wystąpienia lub współużytkowanej zmiennej składowej z inicjatorem jest konstruktorem, w którym jest inicjowana zmienna.
Uwaga. Istnieje możliwość, że kompilator może zdecydować się na dalsze ujednolicenie typów anonimowych, takich jak na poziomie zestawu, ale nie można na tym etapie polegać na tym.
Wyrażenia rzutowe
Wyrażenie rzutowania zmusza wyrażenie do danego typu. Określone wyrażenia rzutu słów kluczowych są współrzędne do typów pierwotnych. Trzy ogólne słowa kluczowe rzutu, CTypei TryCastDirectCast, przekształcają wyrażenie w typ.
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 i TryCast mają specjalne zachowania. W związku z tym obsługują tylko konwersje natywne. Ponadto typ docelowy wyrażenia nie może być typem TryCast wartości. Operatory konwersji zdefiniowane przez użytkownika nie są brane pod uwagę, gdy DirectCast są używane lub TryCast używane. (Uwaga. Zestaw konwersji, który DirectCast i TryCast obsługa są ograniczone, ponieważ implementują konwersje "natywnej CLR". DirectCast Celem jest zapewnienie funkcjonalności instrukcji "rozpakowywanie", podczas gdy celem TryCast jest zapewnienie funkcjonalności instrukcji "isinst". Ponieważ są mapowane na instrukcje CLR, obsługa konwersji nieobsługiwanych bezpośrednio przez CLR pokona zamierzony cel.
DirectCast Konwertuje wyrażenia, które są typizowane inaczej Object niż CType. Podczas konwertowania wyrażenia typuObject, którego typ czasu wykonywania jest typem wartości pierwotnej, zgłasza System.InvalidCastException wyjątek, DirectCast jeśli określony typ nie jest taki sam jak typ czasu wykonywania wyrażenia lub System.NullReferenceException jeśli wyrażenie zwróci wartość Nothing. (Uwaga. Jak wspomniano powyżej, DirectCast mapuje bezpośrednio na instrukcję CLR "rozpatruj", gdy typ wyrażenia to Object. Z kolei CType zamienia się w wywołanie pomocnika środowiska uruchomieniowego w celu przeprowadzenia konwersji, dzięki czemu konwersje między typami pierwotnymi mogą być obsługiwane. W przypadku, gdy Object wyrażenie jest konwertowane na typ wartości pierwotnej, a typ rzeczywistego wystąpienia jest zgodny z typem docelowym, DirectCast będzie znacznie szybszy niż CType.)
TryCast Konwertuje wyrażenia, ale nie zgłasza wyjątku, jeśli nie można przekonwertować wyrażenia na typ docelowy. Zamiast tego spowoduje to, TryCastNothing że wyrażenie nie może zostać przekonwertowane w czasie wykonywania. (Uwaga. Jak wspomniano powyżej, TryCast mapuje bezpośrednio na instrukcję CLR "isinst". Łącząc sprawdzanie typu i konwersję na jedną operację, TryCast może być tańsze niż wykonanie TypeOf ... Is operacji, a następnie CType.)
Przykład:
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
Jeśli żadna konwersja nie istnieje z typu wyrażenia do określonego typu, wystąpi błąd czasu kompilacji. W przeciwnym razie wyrażenie jest klasyfikowane jako wartość, a wynikiem jest wartość wygenerowana przez konwersję.
Wyrażenia operatorów
Istnieją dwa rodzaje operatorów.
Operatory jednoargumentowe przyjmują jeden operand i używają notacji prefiksu (na przykład -x).
Operatory binarne przyjmują dwa operandy i używają notacji przyrostkowej (na przykład x + y). Z wyjątkiem operatorów relacyjnych, które zawsze powodują Boolean, operator zdefiniowany dla określonego typu powoduje, że ten typ. Operandy operatora muszą być zawsze klasyfikowane jako wartość; wynik wyrażenia operatora jest klasyfikowany jako wartość.
OperatorExpression
: ArithmeticOperatorExpression
| RelationalOperatorExpression
| LikeOperatorExpression
| ConcatenationOperatorExpression
| ShortCircuitLogicalOperatorExpression
| LogicalOperatorExpression
| ShiftOperatorExpression
| AwaitOperatorExpression
;
Pierwszeństwo i asocjatywność operatorów
Gdy wyrażenie zawiera wiele operatorów binarnych, pierwszeństwo operatorów kontroluje kolejność oceniania poszczególnych operatorów binarnych. Na przykład wyrażenie x + y * z jest oceniane jako x + (y * z) , ponieważ * operator ma wyższy priorytet niż + operator. W poniższej tabeli wymieniono operatory binarne w kolejności malejącej pierwszeństwa:
| Kategoria | Operatorzy |
|---|---|
| Podstawowy | Wszystkie wyrażenia nieoperacyjne |
| Czekać | Await |
| Potęgowanie | ^ |
| Negacja jednoargumentowa |
+, - |
| Multiplikatywny |
*, / |
| Dzielenie liczb całkowitych | \ |
| Modulo | Mod |
| Dodatek |
+, - |
| Łączenie | & |
| Zmiana |
<<, >> |
| Relacyjne |
=, <>, , <, <=>, >=, , IsLikeIsNot |
| Negacja logiczna | Not |
| Logiczne AND |
And, AndAlso |
| Alternatywa logiczna |
Or, OrElse |
| Logiczny XOR | Xor |
Gdy wyrażenie zawiera dwa operatory o tym samym pierwszeństwie, skojarzenie operatorów kontroluje kolejność wykonywania operacji. Wszystkie operatory binarne są skojarzone z lewej strony, co oznacza, że operacje są wykonywane od lewej do prawej. Pierwszeństwo i kojarzenie można kontrolować przy użyciu wyrażeń nawiasów.
Operandy obiektów
Oprócz zwykłych typów obsługiwanych przez każdy operator, wszystkie operatory obsługują operandy typu Object. Operatory stosowane do Object operandów są obsługiwane podobnie do wywołań metod wykonanych na Object wartościach: można wybrać wywołanie metody powiązanej z opóźnieniem, w tym przypadku typ czasu wykonywania operandów, a nie typ czasu kompilacji, określa ważność i typ operacji. Jeśli ścisłe semantyka są określone przez środowisko kompilacji lub przez Option Strict, wszelkie operatory z operandami typu Object powodują błąd czasu kompilacji, z wyjątkiem TypeOf...IsIs operatorów i IsNot .
Gdy rozpoznawanie operatora określa, że operacja powinna być wykonywana z opóźnieniem, wynikiem operacji jest zastosowanie operatora do typów operandów, jeśli typy czasu wykonywania operandów są typami obsługiwanymi przez operator. Wartość Nothing jest traktowana jako wartość domyślna typu innego operandu w wyrażeniu operatora binarnego. W wyrażeniu operatora jednoargumentowego lub jeśli oba operandy znajdują się Nothing w wyrażeniu operatora binarnego, typ operacji to Integer lub jedyny typ wyniku operatora, jeśli operator nie spowoduje .Integer Wynikiem operacji jest zawsze rzutowanie z powrotem do Objectelementu . Jeśli typy operandów nie mają prawidłowego operatora, zgłaszany System.InvalidCastException jest wyjątek. Konwersje w czasie wykonywania są wykonywane bez względu na to, czy są niejawne, czy jawne.
Jeśli wynik operacji binarnej liczbowej spowoduje wygenerowanie wyjątku przepełnienia (niezależnie od tego, czy sprawdzanie przepełnienia liczby całkowitej jest włączone, czy wyłączone), typ wyniku zostanie podwyższony do następnego szerszego typu liczbowego, jeśli to możliwe. Rozważmy na przykład następujący kod:
Module Test
Sub Main()
Dim o As Object = CObj(CByte(2)) * CObj(CByte(255))
Console.WriteLine(o.GetType().ToString() & " = " & o)
End Sub
End Module
Wyświetla on następujący wynik:
System.Int16 = 512
Jeśli do przechowywania liczby nie jest dostępny szerszy typ liczbowy, System.OverflowException zgłaszany jest wyjątek.
Rozdzielczość operatora
Biorąc pod uwagę typ operatora i zestaw operandów, rozdzielczość operatora określa, który operator ma być używany dla operandów. Podczas rozpoznawania operatorów operatory zdefiniowane przez użytkownika będą traktowane jako pierwsze, wykonując następujące kroki:
Najpierw zbierane są wszystkie operatory kandydatów. Operatory kandydatów to wszystkie operatory zdefiniowane przez użytkownika określonego typu operatora w typie źródłowym i wszystkie operatory zdefiniowane przez użytkownika określonego typu w typie docelowym. Jeśli typ źródła i typ docelowy są powiązane, typowe operatory są traktowane tylko raz.
Następnie rozdzielczość przeciążenia jest stosowana do operatorów i operandów w celu wybrania najbardziej konkretnego operatora. W przypadku operatorów binarnych może to spowodować późne wywołanie powiązane.
Podczas zbierania operatorów kandydatów dla typu T?używane są zamiast tego operatory typu T .
TWszystkie operatory zdefiniowane przez użytkownika, które obejmują tylko typy wartości innych niż null, również są zniesione. Operator lifted używa wersji dopuszczanej do wartości null dowolnego typu wartości, z wyjątkiem zwracanych typów IsTrue i IsFalse (które muszą być Boolean). Operatory zniesione są oceniane przez przekonwertowanie operandów na wersję niepustą, a następnie ocenę operatora zdefiniowanego przez użytkownika, a następnie przekonwertowanie typu wyniku na jego wersję dopuszczającą wartość null. Jeśli operand ether ma Nothingwartość , wynik wyrażenia jest wartością Nothing typizowane jako wersja dopuszczana do wartości null typu wyniku. Przykład:
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
Jeśli operator jest operatorem binarnym, a jeden z operandów jest typem referencyjnym, operator jest również podnosiny, ale każde powiązanie z operatorem generuje błąd. Przykład:
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
Uwaga. Ta reguła istnieje, ponieważ rozważano, czy chcemy dodać typy referencyjne propagujące wartości null w przyszłej wersji, w tym przypadku zachowanie w przypadku operatorów binarnych między dwoma typami ulegnie zmianie.
Podobnie jak w przypadku konwersji, operatory zdefiniowane przez użytkownika są zawsze preferowane przez operatory zniesione.
Podczas rozpoznawania przeciążonych operatorów mogą występować różnice między klasami zdefiniowanymi w języku Visual Basic i zdefiniowanymi w innych językach:
W innych językach ,
NotAndiOrmoże być przeciążony zarówno jako operatory logiczne, jak i operatory bitowe. Po zaimportowaniu z zestawu zewnętrznego formularz jest akceptowany jako prawidłowe przeciążenie dla tych operatorów. Jednak w przypadku typu, który definiuje zarówno operatory logiczne, jak i bitowe, należy rozważyć tylko implementację bitową.W innych językach
>>i<<może być przeciążony zarówno jako podpisane operatory, jak i niepodpisane operatory. Po zaimportowaniu z zestawu zewnętrznego formularz jest akceptowany jako prawidłowe przeciążenie. Jednak w przypadku typu, który definiuje zarówno podpisane, jak i niepodpisane operatory, zostanie uwzględniona tylko podpisana implementacja.Jeśli żaden operator zdefiniowany przez użytkownika nie jest najbardziej specyficzny dla operandów, zostaną uwzględnione operatory wewnętrzne. Jeśli żaden operator wewnętrzny nie jest zdefiniowany dla operandów, a żaden operand ma typ Object, operator zostanie rozpoznany pod koniec ograniczenia; w przeciwnym razie wynik błędu czasu kompilacji.
W poprzednich wersjach języka Visual Basic, jeśli był dokładnie jeden operand typu Object i nie ma odpowiednich operatorów zdefiniowanych przez użytkownika i nie ma odpowiednich operatorów wewnętrznych, był to błąd. Od wersji Visual Basic 11 jest ona teraz rozwiązywana z opóźnieniem. Przykład:
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
TypT, który ma operator wewnętrzny, definiuje również ten sam operator dla .T? Wynik operatora on T? będzie taki sam jak w Tprzypadku , z tą różnicą, że jeśli którykolwiek operand ma Nothingwartość , wynik operatora będzie Nothing (tj. wartość null jest propagowana). Na potrzeby rozpoznawania typu operacji ? element jest usuwany z dowolnych operandów, które je mają, typ operacji jest określany i jest ? dodawany do typu operacji, jeśli którykolwiek z operandów były typami wartości dopuszczających wartość null. Przykład:
Dim v1? As Integer = 10
Dim v2 As Long = 20
' Type of operation will be Long?
Console.WriteLine(v1 + v2)
Każdy operator wyświetla listę typów wewnętrznych, dla których jest definiowana, oraz typ operacji wykonywanej na podstawie typów operandów. Wynik operacji wewnętrznej jest zgodny z następującymi ogólnymi regułami:
Jeśli wszystkie operandy mają ten sam typ, a operator jest zdefiniowany dla typu, nie wystąpi konwersja i zostanie użyty operator dla tego typu.
Każdy operand, którego typ nie jest zdefiniowany dla operatora, jest konwertowany przy użyciu następujących kroków, a operator jest rozpoznawany względem nowych typów:
Operand jest konwertowany na następny najszerszy typ zdefiniowany zarówno dla operatora, jak i operandu, i do którego jest niejawnie konwertowany.
Jeśli nie ma takiego typu, operand jest konwertowany na następny najwęższy typ zdefiniowany zarówno dla operatora, jak i operandu, i do którego jest niejawnie konwertowany.
Jeśli nie ma takiego typu lub konwersja nie może wystąpić, wystąpi błąd czasu kompilacji.
W przeciwnym razie operandy są konwertowane na szersze typy operandów i operator dla tego typu. Jeśli węższy typ operandu nie może zostać niejawnie przekonwertowany na szerszy typ operatora, wystąpi błąd czasu kompilacji.
Pomimo tych ogólnych zasad w tabelach wyników operatorów istnieje jednak wiele specjalnych przypadków.
Uwaga. Ze względów formatowania operator type tables skraca wstępnie zdefiniowane nazwy do pierwszych dwóch znaków. Tak więc wyrażenie "By" to Byte, "UI" to UInteger, "St" is Stringitp. "Err" oznacza, że nie ma zdefiniowanej operacji dla danego typu operandu.
Operatory arytmetyczne
Operatory *arytmetyczne to operatory , /, \Mod^, , . +-
ArithmeticOperatorExpression
: UnaryPlusExpression
| UnaryMinusExpression
| AdditionOperatorExpression
| SubtractionOperatorExpression
| MultiplicationOperatorExpression
| DivisionOperatorExpression
| ModuloOperatorExpression
| ExponentOperatorExpression
;
Operacje arytmetyczne zmiennoprzecinkowe mogą być wykonywane z wyższą precyzją niż typ wyniku operacji. Na przykład niektóre architektury sprzętowe obsługują typ zmiennoprzecinkowa "rozszerzony" lub "długi podwójny" z większą dokładnością i dokładnością niż Double typ, a niejawnie wykonują wszystkie operacje zmiennoprzecinkowe przy użyciu tego typu o wyższej precyzji. Architektury sprzętowe można wykonywać w celu wykonywania operacji zmiennoprzecinkowych z mniejszą precyzją tylko przy nadmiernym koszcie na wydajność; zamiast wymagać implementacji, aby przejąć zarówno wydajność, jak i precyzję, visual Basic umożliwia użycie większej precyzji dla wszystkich operacji zmiennoprzecinkowych. Poza dostarczaniem bardziej precyzyjnych wyników rzadko ma to jakiekolwiek wymierne skutki. Jednak w wyrażeniach formularza x * y / z, gdzie mnożenie daje wynik, który znajduje się poza Double zakresem, ale kolejny podział przynosi tymczasowy wynik z powrotem do Double zakresu, fakt, że wyrażenie jest oceniane w formacie wyższego zakresu, może spowodować wygenerowanie skończonego wyniku zamiast nieskończoności.
Jednoargumentowy operator plus
UnaryPlusExpression
: '+' Expression
;
Jednoargumentowy operator plus jest zdefiniowany dla Bytetypów , , SByte, IntegerULongShortUShortLongSingleUIntegerDoublei .Decimal
Typ operacji:
| Bo | SB | Przez | Sh | USA | W | UI | Lo | UL | De | Si | Robić | Da | Ch | St | Ob |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Sh | SB | Przez | Sh | USA | In | interfejs użytkownika | Lo | UL | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob |
Jednoargumentowy operator minus
UnaryMinusExpression
: '-' Expression
;
Jednoargumentowy operator minus jest zdefiniowany dla następujących typów:
SByte, Short, Integer i Long. Wynik jest obliczany przez odjęcie operandu od zera. Jeśli sprawdzanie przepełnienia liczby całkowitej jest włączone, a wartość operandu jest maksymalną ujemną SBytewartością , Short, Integerlub Long, zgłaszany System.OverflowException jest wyjątek. W przeciwnym razie, jeśli wartość operandu jest maksymalną ujemną SBytewartością , Short, Integerlub Long, wynik jest taki sam, a przepełnienie nie jest zgłaszane.
Single i Double. Wynik jest wartością operandu z odwróconym znakiem, w tym wartościami 0 i Nieskończonością. Jeśli operand ma wartość NaN, wynik jest również wartością NaN.
Decimal. Wynik jest obliczany przez odjęcie operandu od zera.
Typ operacji:
| Bo | SB | Przez | Sh | USA | W | UI | Lo | UL | De | Si | Robić | Da | Ch | St | Ob |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Sh | SB | Sh | Sh | In | In | Lo | Lo | De | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob |
Operator dodawania
Operator dodawania oblicza sumę dwóch operandów.
AdditionOperatorExpression
: Expression '+' LineTerminator? Expression
;
Operator dodawania jest definiowany dla następujących typów:
Byte,SByte,UShort,Short,UInteger,Integer,ULongiLong. Jeśli sprawdzanie przepełnienia liczby całkowitej jest włączone, a suma znajduje się poza zakresem typu wyniku, zgłaszanySystem.OverflowExceptionjest wyjątek. W przeciwnym razie przepełnienia nie są zgłaszane, a wszystkie znaczące bity o wysokiej kolejności wyniku są odrzucane.SingleiDouble. Suma jest obliczana zgodnie z regułami arytmetyki IEEE 754.Decimal. Jeśli wynikowa wartość jest zbyt duża, aby reprezentować w formacie dziesiętnym,System.OverflowExceptionzgłaszany jest wyjątek. Jeśli wartość wyniku jest zbyt mała do reprezentowania w formacie dziesiętny, wynik wynosi 0.String. DwaStringoperandy są połączone ze sobą.Date. TypSystem.DateTimedefiniuje przeciążone operatory dodawania. PonieważSystem.DateTimejest odpowiednikiem typu wewnętrznegoDate, te operatory są również dostępne w typieDate.
Typ operacji:
| Bo | SB | Przez | Sh | USA | W | UI | Lo | UL | De | Si | Robić | Da | Ch | St | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Bo | Sh | SB | Sh | Sh | In | In | Lo | Lo | De | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob |
| SB | SB | Sh | Sh | In | In | Lo | Lo | De | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | |
| Przez | Przez | Sh | USA | In | interfejs użytkownika | Lo | UL | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | ||
| Sh | Sh | In | In | Lo | Lo | De | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | |||
| USA | USA | In | interfejs użytkownika | Lo | UL | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | ||||
| W | In | Lo | Lo | De | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | |||||
| UI | interfejs użytkownika | Lo | UL | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | ||||||
| Lo | Lo | De | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | |||||||
| UL | UL | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | ||||||||
| De | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | |||||||||
| Si | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | ||||||||||
| Robić | Zrób | Błądzić | Błądzić | Zrób | Ob | |||||||||||
| Da | St | Błądzić | St | Ob | ||||||||||||
| Ch | St | St | Ob | |||||||||||||
| St | St | Ob | ||||||||||||||
| Ob | Ob |
Operator odejmowania
Operator odejmowania odejmuje drugi operand z pierwszego operandu.
SubtractionOperatorExpression
: Expression '-' LineTerminator? Expression
;
Operator odejmowania jest definiowany dla następujących typów:
Byte,SByte,UShort,Short,UInteger,Integer,ULongiLong. Jeśli sprawdzanie przepełnienia liczby całkowitej jest włączone i różnica wykracza poza zakres typu wyniku, zgłaszanySystem.OverflowExceptionjest wyjątek. W przeciwnym razie przepełnienia nie są zgłaszane, a wszystkie znaczące bity o wysokiej kolejności wyniku są odrzucane.SingleiDouble. Różnica jest obliczana zgodnie z regułami arytmetyki IEEE 754.Decimal. Jeśli wynikowa wartość jest zbyt duża, aby reprezentować w formacie dziesiętnym,System.OverflowExceptionzgłaszany jest wyjątek. Jeśli wartość wyniku jest zbyt mała do reprezentowania w formacie dziesiętny, wynik wynosi 0.Date. TypSystem.DateTimedefiniuje przeciążone operatory odejmowania. PonieważSystem.DateTimejest odpowiednikiem typu wewnętrznegoDate, te operatory są również dostępne w typieDate.
Typ operacji:
| Bo | SB | Przez | Sh | USA | W | UI | Lo | UL | De | Si | Robić | Da | Ch | St | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Bo | Sh | SB | Sh | Sh | In | In | Lo | Lo | De | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob |
| SB | SB | Sh | Sh | In | In | Lo | Lo | De | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | |
| Przez | Przez | Sh | USA | In | interfejs użytkownika | Lo | UL | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | ||
| Sh | Sh | In | In | Lo | Lo | De | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | |||
| USA | USA | In | interfejs użytkownika | Lo | UL | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | ||||
| W | In | Lo | Lo | De | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | |||||
| UI | interfejs użytkownika | Lo | UL | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | ||||||
| Lo | Lo | De | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | |||||||
| UL | UL | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | ||||||||
| De | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | |||||||||
| Si | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | ||||||||||
| Robić | Zrób | Błądzić | Błądzić | Zrób | Ob | |||||||||||
| Da | Błądzić | Błądzić | Błądzić | Błądzić | ||||||||||||
| Ch | Błądzić | Błądzić | Błądzić | |||||||||||||
| St | Zrób | Ob | ||||||||||||||
| Ob | Ob |
Operator mnożenia
Operator mnożenia oblicza iloczyn dwóch operandów.
MultiplicationOperatorExpression
: Expression '*' LineTerminator? Expression
;
Operator mnożenia jest definiowany dla następujących typów:
Byte,SByte,UShort,Short,UInteger,Integer,ULongiLong. Jeśli sprawdzanie przepełnienia liczby całkowitej jest włączone, a produkt znajduje się poza zakresem typu wyników, zgłaszanySystem.OverflowExceptionjest wyjątek. W przeciwnym razie przepełnienia nie są zgłaszane, a wszystkie znaczące bity o wysokiej kolejności wyniku są odrzucane.SingleiDouble. Produkt jest obliczany zgodnie z regułami arytmetyki IEEE 754.Decimal. Jeśli wynikowa wartość jest zbyt duża, aby reprezentować w formacie dziesiętnym,System.OverflowExceptionzgłaszany jest wyjątek. Jeśli wartość wyniku jest zbyt mała do reprezentowania w formacie dziesiętny, wynik wynosi 0.
Typ operacji:
| Bo | SB | Przez | Sh | USA | W | UI | Lo | UL | De | Si | Robić | Da | Ch | St | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Bo | Sh | SB | Sh | Sh | In | In | Lo | Lo | De | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob |
| SB | SB | Sh | Sh | In | In | Lo | Lo | De | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | |
| Przez | Przez | Sh | USA | In | interfejs użytkownika | Lo | UL | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | ||
| Sh | Sh | In | In | Lo | Lo | De | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | |||
| USA | USA | In | interfejs użytkownika | Lo | UL | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | ||||
| W | In | Lo | Lo | De | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | |||||
| UI | interfejs użytkownika | Lo | UL | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | ||||||
| Lo | Lo | De | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | |||||||
| UL | UL | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | ||||||||
| De | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | |||||||||
| Si | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | ||||||||||
| Robić | Zrób | Błądzić | Błądzić | Zrób | Ob | |||||||||||
| Da | Błądzić | Błądzić | Błądzić | Błądzić | ||||||||||||
| Ch | Błądzić | Błądzić | Błądzić | |||||||||||||
| St | Zrób | Ob | ||||||||||||||
| Ob | Ob |
Operatory dzielenia
Operatory dzielenia obliczają iloraz dwóch operandów. Istnieją dwa operatory dzielenia: zwykły (zmiennoprzecinkowy) operator dzielenia i operator dzielenia całkowitego.
DivisionOperatorExpression
: FPDivisionOperatorExpression
| IntegerDivisionOperatorExpression
;
FPDivisionOperatorExpression
: Expression '/' LineTerminator? Expression
;
IntegerDivisionOperatorExpression
: Expression '\\' LineTerminator? Expression
;
Operator dzielenia regularnego jest definiowany dla następujących typów:
SingleiDouble. Iloraz jest obliczany zgodnie z regułami arytmetyki IEEE 754.Decimal. Jeśli wartość prawego operandu wynosi zero, zgłaszanySystem.DivideByZeroExceptionjest wyjątek. Jeśli wynikowa wartość jest zbyt duża, aby reprezentować w formacie dziesiętnym,System.OverflowExceptionzgłaszany jest wyjątek. Jeśli wartość wyniku jest zbyt mała, aby reprezentować w formacie dziesiętny, wynik jest zerowy. Skala wyniku, przed zaokrągleniami, jest najbliżej preferowanej skali, która zachowa wynik równy dokładnemu wynikowi. Preferowana skala to skala pierwszego operandu mniej skali drugiego operandu.
Zgodnie z normalnymi regułami rozpoznawania operatorów regularne dzielenie wyłącznie między operandami typów, takich jak Byte, Short, Integer, i Long spowodowałoby przekonwertowanie obu operandów na typ Decimal. Jednak podczas wykonywania rozpoznawania operatora dla operatora dzielenia, gdy żaden typ nie jest Decimal, Double jest uważany za węższy niż Decimal. Ta konwencja jest przestrzegana, ponieważ Double dzielenie jest bardziej wydajne niż Decimal dzielenie.
Typ operacji:
| Bo | SB | Przez | Sh | USA | W | UI | Lo | UL | De | Si | Robić | Da | Ch | St | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Bo | Zrób | Zrób | Zrób | Zrób | Zrób | Zrób | Zrób | Zrób | Zrób | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob |
| SB | Zrób | Zrób | Zrób | Zrób | Zrób | Zrób | Zrób | Zrób | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | |
| Przez | Zrób | Zrób | Zrób | Zrób | Zrób | Zrób | Zrób | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | ||
| Sh | Zrób | Zrób | Zrób | Zrób | Zrób | Zrób | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | |||
| USA | Zrób | Zrób | Zrób | Zrób | Zrób | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | ||||
| W | Zrób | Zrób | Zrób | Zrób | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | |||||
| UI | Zrób | Zrób | Zrób | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | ||||||
| Lo | Zrób | Zrób | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | |||||||
| UL | Zrób | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | ||||||||
| De | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | |||||||||
| Si | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | ||||||||||
| Robić | Zrób | Błądzić | Błądzić | Zrób | Ob | |||||||||||
| Da | Błądzić | Błądzić | Błądzić | Błądzić | ||||||||||||
| Ch | Błądzić | Błądzić | Błądzić | |||||||||||||
| St | Zrób | Ob | ||||||||||||||
| Ob | Ob |
Operator dzielenia liczby całkowitej jest definiowany dla , Byte, ShortUIntegerUShortIntegerSByte, ULong, i .Long Jeśli wartość prawego operandu wynosi zero, zgłaszany System.DivideByZeroException jest wyjątek. Podział zaokrągla wynik do zera, a wartość bezwzględna wyniku jest największą możliwą liczbą całkowitą mniejszą niż wartość bezwzględna ilorazu dwóch operandów. Wynik jest zerowy lub dodatni, gdy dwa operandy mają ten sam znak, a zero lub ujemne, gdy dwa operandy mają przeciwstawne znaki. Jeśli lewy operand jest maksymalną ujemną wartością SByte, , ShortIntegerlub Long, a prawy operand to -1, występuje przepełnienie. Jeśli sprawdzanie przepełnienia całkowitego jest włączone, System.OverflowException zgłaszany jest wyjątek. W przeciwnym razie przepełnienie nie jest zgłaszane, a wynik jest wartością lewego operandu.
Uwaga. Ponieważ dwa operandy dla niepodpisanych typów zawsze będą zerowe lub dodatnie, wynik jest zawsze zerowy lub dodatni. W wyniku wyrażenia zawsze będzie mniejsze niż lub równe największemu z dwóch operandów, nie jest możliwe przepełnienie. W związku z tym sprawdzanie przepełnienia liczby całkowitej nie jest wykonywane dla dzielenia liczb całkowitych z dwoma niepodpisanymi liczbami całkowitymi. Wynikiem jest typ operandu po lewej stronie.
Typ operacji:
| Bo | SB | Przez | Sh | USA | W | UI | Lo | UL | De | Si | Robić | Da | Ch | St | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Bo | Sh | SB | Sh | Sh | In | In | Lo | Lo | Lo | Lo | Lo | Lo | Błądzić | Błądzić | Lo | Ob |
| SB | SB | Sh | Sh | In | In | Lo | Lo | Lo | Lo | Lo | Lo | Błądzić | Błądzić | Lo | Ob | |
| Przez | Przez | Sh | USA | In | interfejs użytkownika | Lo | UL | Lo | Lo | Lo | Błądzić | Błądzić | Lo | Ob | ||
| Sh | Sh | In | In | Lo | Lo | Lo | Lo | Lo | Lo | Błądzić | Błądzić | Lo | Ob | |||
| USA | USA | In | interfejs użytkownika | Lo | UL | Lo | Lo | Lo | Błądzić | Błądzić | Lo | Ob | ||||
| W | In | Lo | Lo | Lo | Lo | Lo | Lo | Błądzić | Błądzić | Lo | Ob | |||||
| UI | interfejs użytkownika | Lo | UL | Lo | Lo | Lo | Błądzić | Błądzić | Lo | Ob | ||||||
| Lo | Lo | Lo | Lo | Lo | Lo | Błądzić | Błądzić | Lo | Ob | |||||||
| UL | UL | Lo | Lo | Lo | Błądzić | Błądzić | Lo | Ob | ||||||||
| De | Lo | Lo | Lo | Błądzić | Błądzić | Lo | Ob | |||||||||
| Si | Lo | Lo | Błądzić | Błądzić | Lo | Ob | ||||||||||
| Robić | Lo | Błądzić | Błądzić | Lo | Ob | |||||||||||
| Da | Błądzić | Błądzić | Błądzić | Błądzić | ||||||||||||
| Ch | Błądzić | Błądzić | Błądzić | |||||||||||||
| St | Lo | Ob | ||||||||||||||
| Ob | Ob |
Mod Operator
Operator Mod (modulo) oblicza resztę dzielenia między dwoma operandami.
ModuloOperatorExpression
: Expression 'Mod' LineTerminator? Expression
;
Operator Mod jest definiowany dla następujących typów:
Byte,SByte,UShortShort,UInteger,IntegerULongiLong. Wynikiemx Mod yjest wartość wygenerowana przezx - (x \ y) * y. Jeśliywartość to zero, zgłaszanySystem.DivideByZeroExceptionjest wyjątek. Operator modulo nigdy nie powoduje przepełnienia.SingleiDouble. Pozostała część jest obliczana zgodnie z regułami arytmetyki IEEE 754.Decimal. Jeśli wartość prawego operandu wynosi zero, zgłaszanySystem.DivideByZeroExceptionjest wyjątek. Jeśli wynikowa wartość jest zbyt duża, aby reprezentować w formacie dziesiętnym,System.OverflowExceptionzgłaszany jest wyjątek. Jeśli wartość wyniku jest zbyt mała, aby reprezentować w formacie dziesiętny, wynik jest zerowy.
Typ operacji:
| Bo | SB | Przez | Sh | USA | W | UI | Lo | UL | De | Si | Robić | Da | Ch | St | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Bo | Sh | SB | Sh | Sh | In | In | Lo | Lo | De | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob |
| SB | SB | Sh | Sh | In | In | Lo | Lo | De | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | |
| Przez | Przez | Sh | USA | In | interfejs użytkownika | Lo | UL | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | ||
| Sh | Sh | In | In | Lo | Lo | De | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | |||
| USA | USA | In | interfejs użytkownika | Lo | UL | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | ||||
| W | In | Lo | Lo | De | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | |||||
| UI | interfejs użytkownika | Lo | UL | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | ||||||
| Lo | Lo | De | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | |||||||
| UL | UL | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | ||||||||
| De | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | |||||||||
| Si | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | ||||||||||
| Robić | Zrób | Błądzić | Błądzić | Zrób | Ob | |||||||||||
| Da | Błądzić | Błądzić | Błądzić | Błądzić | ||||||||||||
| Ch | Błądzić | Błądzić | Błądzić | |||||||||||||
| St | Zrób | Ob | ||||||||||||||
| Ob | Ob |
Operator wykładnika
Operator wykładniczy oblicza pierwszy operand podniesiony do potęgi drugiego operandu.
ExponentOperatorExpression
: Expression '^' LineTerminator? Expression
;
Operator wykładnika jest definiowany dla typu Double. Wartość jest obliczana zgodnie z regułami arytmetyki IEEE 754.
Typ operacji:
| Bo | SB | Przez | Sh | USA | W | UI | Lo | UL | De | Si | Robić | Da | Ch | St | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Bo | Zrób | Zrób | Zrób | Zrób | Zrób | Zrób | Zrób | Zrób | Zrób | Zrób | Zrób | Zrób | Błądzić | Błądzić | Zrób | Ob |
| SB | Zrób | Zrób | Zrób | Zrób | Zrób | Zrób | Zrób | Zrób | Zrób | Zrób | Zrób | Błądzić | Błądzić | Zrób | Ob | |
| Przez | Zrób | Zrób | Zrób | Zrób | Zrób | Zrób | Zrób | Zrób | Zrób | Zrób | Błądzić | Błądzić | Zrób | Ob | ||
| Sh | Zrób | Zrób | Zrób | Zrób | Zrób | Zrób | Zrób | Zrób | Zrób | Błądzić | Błądzić | Zrób | Ob | |||
| USA | Zrób | Zrób | Zrób | Zrób | Zrób | Zrób | Zrób | Zrób | Błądzić | Błądzić | Zrób | Ob | ||||
| W | Zrób | Zrób | Zrób | Zrób | Zrób | Zrób | Zrób | Błądzić | Błądzić | Zrób | Ob | |||||
| UI | Zrób | Zrób | Zrób | Zrób | Zrób | Zrób | Błądzić | Błądzić | Zrób | Ob | ||||||
| Lo | Zrób | Zrób | Zrób | Zrób | Zrób | Błądzić | Błądzić | Zrób | Ob | |||||||
| UL | Zrób | Zrób | Zrób | Zrób | Błądzić | Błądzić | Zrób | Ob | ||||||||
| De | Zrób | Zrób | Zrób | Błądzić | Błądzić | Zrób | Ob | |||||||||
| Si | Zrób | Zrób | Błądzić | Błądzić | Zrób | Ob | ||||||||||
| Robić | Zrób | Błądzić | Błądzić | Zrób | Ob | |||||||||||
| Da | Błądzić | Błądzić | Błądzić | Błądzić | ||||||||||||
| Ch | Błądzić | Błądzić | Błądzić | |||||||||||||
| St | Zrób | Ob | ||||||||||||||
| Ob | Ob |
Operatory relacyjne
Operatory relacyjne porównują wartości ze sobą. Operatory porównania to =, , <>, ><, , <=i >=.
RelationalOperatorExpression
: Expression '=' LineTerminator? Expression
| Expression '<' '>' LineTerminator? Expression
| Expression '<' LineTerminator? Expression
| Expression '>' LineTerminator? Expression
| Expression '<' '=' LineTerminator? Expression
| Expression '>' '=' LineTerminator? Expression
;
Wszystkie operatory relacyjne powodują Boolean wartość.
Operatory relacyjne mają następujące ogólne znaczenie:
=Operator sprawdza, czy dwa operandy są równe.Operator
<>sprawdza, czy dwa operandy nie są równe.<Operator sprawdza, czy pierwszy operand jest mniejszy niż drugi operand.Operator
>sprawdza, czy pierwszy operand jest większy niż drugi operand.Operator
<=sprawdza, czy pierwszy operand jest mniejszy niż lub równy drugiemu operandowi.Operator
>=sprawdza, czy pierwszy operand jest większy, czy równy drugiemu operandowi.
Operatory relacyjne są definiowane dla następujących typów:
Boolean. Operatory porównują wartości prawdy dwóch operandów.Truewartość jest uważana za mniejszą niżFalse, która jest zgodna z ich wartościami liczbowymi.Byte,SByte,UShort,Short,UInteger,Integer,ULongiLong. Operatory porównują wartości liczbowe dwóch operacji całkowitych.SingleiDouble. Operatory porównują operandy zgodnie z regułami standardu IEEE 754.Decimal. Operatory porównują wartości liczbowe dwóch operandów dziesiętnych.Date. Operatory zwracają wynik porównywania dwóch wartości daty/godziny.Char. Operatory zwracają wynik porównywania dwóch wartości Unicode.String. Operatory zwracają wynik porównywania dwóch wartości przy użyciu porównania binarnego lub porównania tekstu. Użyte porównanie jest określane przez środowisko kompilacji i instrukcjęOption Compare. Porównanie binarne określa, czy wartość liczbowa Unicode każdego znaku w każdym ciągu jest taka sama. Porównanie tekstu wykonuje porównanie tekstu Unicode na podstawie bieżącej kultury używanej w programie .NET Framework. Podczas porównywania ciągów wartość null jest równoważna literału""ciągu .
Typ operacji:
| Bo | SB | Przez | Sh | USA | W | UI | Lo | UL | De | Si | Robić | Da | Ch | St | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Bo | Bo | SB | Sh | Sh | In | In | Lo | Lo | De | De | Si | Zrób | Błądzić | Błądzić | Bo | Ob |
| SB | SB | Sh | Sh | In | In | Lo | Lo | De | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | |
| Przez | Przez | Sh | USA | In | interfejs użytkownika | Lo | UL | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | ||
| Sh | Sh | In | In | Lo | Lo | De | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | |||
| USA | USA | In | interfejs użytkownika | Lo | UL | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | ||||
| W | In | Lo | Lo | De | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | |||||
| UI | interfejs użytkownika | Lo | UL | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | ||||||
| Lo | Lo | De | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | |||||||
| UL | UL | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | ||||||||
| De | De | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | |||||||||
| Si | Si | Zrób | Błądzić | Błądzić | Zrób | Ob | ||||||||||
| Robić | Zrób | Błądzić | Błądzić | Zrób | Ob | |||||||||||
| Da | Da | Błądzić | Da | Ob | ||||||||||||
| Ch | Ch | St | Ob | |||||||||||||
| St | St | Ob | ||||||||||||||
| Ob | Ob |
Like Operator
Operator Like określa, czy ciąg pasuje do danego wzorca.
LikeOperatorExpression
: Expression 'Like' LineTerminator? Expression
;
Operator Like jest definiowany String dla typu. Pierwszy operand to pasowany ciąg, a drugi operand jest wzorcem zgodnym z. Wzorzec składa się z znaków Unicode. Następujące sekwencje znaków mają specjalne znaczenie:
Znak
?pasuje do dowolnego pojedynczego znaku.Znak
*pasuje do zera lub większej liczby znaków.Znak
#pasuje do dowolnej pojedynczej cyfry (0–9).Lista znaków otoczonych nawiasami kwadratowymi (
[ab...]) pasuje do dowolnego pojedynczego znaku na liście.Lista znaków otoczonych nawiasami kwadratowymi i poprzedzona wykrzyknikiem (
[!ab...]) pasuje do żadnego pojedynczego znaku, który nie znajduje się na liście znaków.Dwa znaki na liście znaków oddzielone łącznikiem (
-) określają zakres znaków Unicode rozpoczynający się od pierwszego znaku i kończący się drugim znakiem. Jeśli drugi znak nie znajduje się później w kolejności sortowania niż pierwszy znak, wystąpi wyjątek czasu wykonywania. Łącznik, który pojawia się na początku lub na końcu listy znaków, określa się sam.
Aby dopasować znaki specjalne lewego nawiasu kwadratowego (), znak zapytania ([?), znak numeru (#) i gwiazdkę (*), nawiasy muszą je ująć. Prawy nawias kwadratowy (]) nie może być używany w grupie, aby dopasować się do siebie, ale może być używany poza grupą jako pojedynczy znak. Sekwencja [] znaków jest uważana za literał ""ciągu .
Należy pamiętać, że porównania znaków i kolejność list znaków są zależne od typu używanych porównań. Jeśli są używane porównania binarne, porównania znaków i kolejność są oparte na wartościach liczbowych Unicode. Jeśli są używane porównania tekstu, porównania znaków i kolejność są oparte na bieżących ustawieniach regionalnych używanych w programie .NET Framework.
W niektórych językach znaki specjalne w alfabetze reprezentują dwa oddzielne znaki i na odwrót. Na przykład kilka języków używa znaku æ do reprezentowania znaków a i e ich wyświetlania razem, podczas gdy znaki ^ i O mogą służyć do reprezentowania znaku Ô. W przypadku używania porównań tekstowych Like operator rozpoznaje takie równoważności kulturowe. W takim przypadku wystąpienie pojedynczego znaku specjalnego w wzorcu lub ciągu pasuje do równoważnej sekwencji dwuznakowej w drugim ciągu. Podobnie pojedynczy znak specjalny we wzorcu umieszczonym w nawiasach kwadratowych (samodzielnie, na liście lub w zakresie) pasuje do równoważnej sekwencji dwuznakowej w ciągu i odwrotnie.
W wyrażeniuLike, w którym oba operandy są Nothing lub jeden operand ma wewnętrzną konwersję na String , Nothing a drugi operand jest Nothingtraktowany tak, jakby był pustym literałem ""ciągu .
Typ operacji:
| Bo | SB | Przez | Sh | USA | W | UI | Lo | UL | De | Si | Robić | 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 | |
| Przez | St | St | St | St | St | St | St | St | St | St | St | St | St | Ob | ||
| Sh | 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 | ||||
| W | 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 | ||||||||||
| Robić | St | St | St | St | Ob | |||||||||||
| Da | St | St | St | Ob | ||||||||||||
| Ch | St | St | Ob | |||||||||||||
| St | St | Ob | ||||||||||||||
| Ob | Ob |
Operator łączenia
ConcatenationOperatorExpression
: Expression '&' LineTerminator? Expression
;
Operator łączenia jest definiowany dla wszystkich typów wewnętrznych, w tym wersji z możliwością wartości null typów wartości wewnętrznych. Jest on również zdefiniowany do łączenia między typami wymienionymi powyżej i System.DBNull, który jest traktowany Nothing jako ciąg. Operator łączenia konwertuje wszystkie operandy na String; w wyrażeniu wszystkie konwersje, które String mają być rozszerzane, niezależnie od tego, czy są używane ścisłe semantyki. Wartość System.DBNull jest konwertowana na literał Nothing wpisany jako String. Typ wartości dopuszczającej wartość null, którego wartość jest Nothing również konwertowana na literał Nothing wpisany jako String, zamiast zgłaszać błąd czasu wykonywania.
Operacja łączenia powoduje utworzenie ciągu, który jest łączeniem dwóch operandów w kolejności od lewej do prawej. Wartość Nothing jest traktowana tak, jakby była pustym literałem ""ciągu .
Typ operacji:
| Bo | SB | Przez | Sh | USA | W | UI | Lo | UL | De | Si | Robić | 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 | |
| Przez | St | St | St | St | St | St | St | St | St | St | St | St | St | Ob | ||
| Sh | 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 | ||||
| W | 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 | ||||||||||
| Robić | St | St | St | St | Ob | |||||||||||
| Da | St | St | St | Ob | ||||||||||||
| Ch | St | St | Ob | |||||||||||||
| St | St | Ob | ||||||||||||||
| Ob | Ob |
Operatory logiczne
Operatory And, Not, Ori Xor są nazywane operatorami logicznymi.
LogicalOperatorExpression
: 'Not' Expression
| Expression 'And' LineTerminator? Expression
| Expression 'Or' LineTerminator? Expression
| Expression 'Xor' LineTerminator? Expression
;
Operatory logiczne są oceniane w następujący sposób:
BooleanDla typu:Operacja logiczna
Andjest wykonywana na dwóch operandach.Operacja logiczna
Notjest wykonywana na operandie.Operacja logiczna
Orjest wykonywana na dwóch operandach.Logiczna operacja wykluczająca
Orjest wykonywana na dwóch operandach.
W przypadku
Bytetypów ,SByte,UIntegerLongUShortShortIntegerULongi wszystkich wyliczonych określona operacja jest wykonywana na każdym bitzie binarnej reprezentacji dwóch operandów:And: bit wynikowy to 1, jeśli obie bity mają wartość 1; w przeciwnym razie bit wynikowy wynosi 0.Not: bit wynikowy to 1, jeśli bit ma wartość 0; w przeciwnym razie bit wynikowy wynosi 1.Or: bit wynikowy to 1, jeśli bit ma wartość 1; w przeciwnym razie bit wynikowy wynosi 0.Xor: Bit wynikowy to 1, jeśli bit ma wartość 1, ale nie obie bity; w przeciwnym razie bit wynikowy to 0 (czyli 1Xor0 = 1, 1Xor1 = 0).
Gdy operatory
Andlogiczne iOrsą zniesione dla typuBoolean?, są rozszerzone w celu objęcia trzywartej logiki logicznej w następujący sposób:Andzwraca wartość true, jeśli oba operandy są prawdziwe; false, jeśli jeden z operandów ma wartość false;Nothinginaczej.Orzwraca wartość true, jeśli którykolwiek operand ma wartość true; wartość false to oba operandy są fałszywe;Nothinginaczej.
Przykład:
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
Uwaga. W idealnym przypadku operatory And logiczne i Or zostaną zniesione przy użyciu trzywartej logiki dla dowolnego typu, który może być używany w wyrażeniu logicznym (tj. typ IsTrue implementujący i ), w taki sam sposób, AndAlso jak i IsFalseOrElse zwarcie w dowolnym typie, który może być używany w wyrażeniu logicznym. Niestety, trzywartościowe podnoszenie jest stosowane tylko do Boolean?typów zdefiniowanych przez użytkownika, które chcą mieć trzywartościową logikę, musi to zrobić ręcznie, definiując And operatory i Or dla ich wersji dopuszczające wartość null.
Nie ma możliwości przepełnienia podczas tych operacji. Wyliczone operatory typów wykonują operację bitową dla bazowego typu wyliczonego typu, ale zwracana wartość jest typem wyliczanym.
Nie typ operacji:
| Bo | SB | Przez | Sh | USA | W | UI | Lo | UL | De | Si | Robić | Da | Ch | St | Ob |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Bo | SB | Przez | Sh | USA | In | interfejs użytkownika | Lo | UL | Lo | Lo | Lo | Błądzić | Błądzić | Lo | Ob |
I, lub, Xor typ operacji:
| Bo | SB | Przez | Sh | USA | W | UI | Lo | UL | De | Si | Robić | Da | Ch | St | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Bo | Bo | SB | Sh | Sh | In | In | Lo | Lo | Lo | Lo | Lo | Lo | Błądzić | Błądzić | Bo | Ob |
| SB | SB | Sh | Sh | In | In | Lo | Lo | Lo | Lo | Lo | Lo | Błądzić | Błądzić | Lo | Ob | |
| Przez | Przez | Sh | USA | In | interfejs użytkownika | Lo | UL | Lo | Lo | Lo | Błądzić | Błądzić | Lo | Ob | ||
| Sh | Sh | In | In | Lo | Lo | Lo | Lo | Lo | Lo | Błądzić | Błądzić | Lo | Ob | |||
| USA | USA | In | interfejs użytkownika | Lo | UL | Lo | Lo | Lo | Błądzić | Błądzić | Lo | Ob | ||||
| W | In | Lo | Lo | Lo | Lo | Lo | Lo | Błądzić | Błądzić | Lo | Ob | |||||
| UI | interfejs użytkownika | Lo | UL | Lo | Lo | Lo | Błądzić | Błądzić | Lo | Ob | ||||||
| Lo | Lo | Lo | Lo | Lo | Lo | Błądzić | Błądzić | Lo | Ob | |||||||
| UL | UL | Lo | Lo | Lo | Błądzić | Błądzić | Lo | Ob | ||||||||
| De | Lo | Lo | Lo | Błądzić | Błądzić | Lo | Ob | |||||||||
| Si | Lo | Lo | Błądzić | Błądzić | Lo | Ob | ||||||||||
| Robić | Lo | Błądzić | Błądzić | Lo | Ob | |||||||||||
| Da | Błądzić | Błądzić | Błądzić | Błądzić | ||||||||||||
| Ch | Błądzić | Błądzić | Błądzić | |||||||||||||
| St | Lo | Ob | ||||||||||||||
| Ob | Ob |
Operatory logiczne zwarciowe
Operatory AndAlso i OrElse to wersje zwarciowe operatorów logicznych And i Or .
ShortCircuitLogicalOperatorExpression
: Expression 'AndAlso' LineTerminator? Expression
| Expression 'OrElse' LineTerminator? Expression
;
Ze względu na ich zachowanie zwarcie, drugi operand nie jest obliczany w czasie wykonywania, jeśli wynik operatora jest znany po ocenie pierwszego operandu.
Operatory logiczne zwarcie są oceniane w następujący sposób:
Jeśli pierwszy operand operacji
AndAlsozwracaFalsewartość lub zwraca wartość True z operatoraIsFalse, wyrażenie zwraca swój pierwszy operand. W przeciwnym razie drugi operand jest obliczany, a operacja logicznaAndjest wykonywana na dwóch wynikach.Jeśli pierwszy operand operacji
OrElsezwracaTruewartość lub zwraca wartość True z operatoraIsTrue, wyrażenie zwraca swój pierwszy operand. W przeciwnym razie drugi operand jest obliczany, a operacja logicznaOrjest wykonywana na dwóch wynikach.
Operatory AndAlso i OrElse są definiowane dla typu Boolean, lub dla dowolnego typu T , który przeciąża następujące operatory:
Public Shared Operator IsTrue(op As T) As Boolean
Public Shared Operator IsFalse(op As T) As Boolean
a także przeciążenie odpowiedniego And operatora lub Or :
Public Shared Operator And(op1 As T, op2 As T) As T
Public Shared Operator Or(op1 As T, op2 As T) As T
Podczas oceniania AndAlso operatorów lub OrElse pierwszy operand jest oceniany tylko raz, a drugi operand nie jest obliczany lub obliczany dokładnie raz. Rozważmy na przykład następujący kod:
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
Wyświetla on następujący wynik:
And: False True
Or: True False
AndAlso: False
OrElse: True
W zniesionej formie operatorów AndAlso i OrElse , jeśli pierwszy operand był wartością null Boolean?, drugi operand jest obliczany, ale wynik jest zawsze wartością null Boolean?.
Typ operacji:
| Bo | SB | Przez | Sh | USA | W | UI | Lo | UL | De | Si | Robić | Da | Ch | St | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Błądzić | Błądzić | Bo | Ob |
| SB | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Błądzić | Błądzić | Bo | Ob | |
| Przez | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Błądzić | Błądzić | Bo | Ob | ||
| Sh | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Błądzić | Błądzić | Bo | Ob | |||
| USA | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Błądzić | Błądzić | Bo | Ob | ||||
| W | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Błądzić | Błądzić | Bo | Ob | |||||
| UI | Bo | Bo | Bo | Bo | Bo | Bo | Błądzić | Błądzić | Bo | Ob | ||||||
| Lo | Bo | Bo | Bo | Bo | Bo | Błądzić | Błądzić | Bo | Ob | |||||||
| UL | Bo | Bo | Bo | Bo | Błądzić | Błądzić | Bo | Ob | ||||||||
| De | Bo | Bo | Bo | Błądzić | Błądzić | Bo | Ob | |||||||||
| Si | Bo | Bo | Błądzić | Błądzić | Bo | Ob | ||||||||||
| Robić | Bo | Błądzić | Błądzić | Bo | Ob | |||||||||||
| Da | Błądzić | Błądzić | Błądzić | Błądzić | ||||||||||||
| Ch | Błądzić | Błądzić | Błądzić | |||||||||||||
| St | Bo | Ob | ||||||||||||||
| Ob | Ob |
Operatory przesunięcia
Operatory << binarne i >> wykonują operacje przesunięcia bitowego.
ShiftOperatorExpression
: Expression '<' '<' LineTerminator? Expression
| Expression '>' '>' LineTerminator? Expression
;
Operatory są definiowane dla Bytetypów , , SByte, UIntegerUShortShort, , ULongIntegeri Long . W przeciwieństwie do innych operatorów binarnych typ wyniku operacji przesunięcia jest określany tak, jakby operator był operatorem jednoargumentowym z tylko lewym operandem. Typ prawego operandu musi być niejawnie konwertowany na Integer i nie jest używany w określaniu typu wyniku operacji.
Operator << powoduje przesunięcie bitów w pierwszym operandzie w lewo o liczbę miejsc określonych przez ilość przesunięcia. Bity o wysokiej kolejności poza zakresem typu wyników są odrzucane, a pozycje bitów o niskiej kolejności są wypełniane zero.
Operator >> powoduje przesunięcie bitów w pierwszym operandzie w prawo do liczby miejsc określonych przez ilość przesunięcia. Bity o niskiej kolejności są odrzucane, a pozycje bitów o wysokiej kolejności są ustawione na zero, jeśli lewy operand jest dodatni lub do jednego, jeśli ujemny. Jeśli lewy operand ma typ Byte, UShort, UIntegerlub ULong puste bity o wysokiej kolejności są wypełnione zero.
Operatory przesunięcia przesuwają bity bazowej reprezentacji pierwszego operandu przez ilość drugiego operandu. Jeśli wartość drugiego operandu jest większa niż liczba bitów w pierwszym operandzie lub jest ujemna, wartość przesunięcia jest obliczana w RightOperand And SizeMask następujący sposób SizeMask :
| LeftOperand, typ | Maska rozmiaru |
|---|---|
Byte, SByte |
7 (&H7) |
UShort, Short |
15 (&HF) |
UInteger, Integer |
31 (&H1F) |
ULong, Long |
63 (&H3F) |
Jeśli wartość przesunięcia wynosi zero, wynik operacji jest identyczny z wartością pierwszego operandu. Nie ma możliwości przepełnienia podczas tych operacji.
Typ operacji:
| Bo | SB | Przez | Sh | USA | W | UI | Lo | UL | De | Si | Robić | Da | Ch | St | Ob |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Sh | SB | Przez | Sh | USA | In | interfejs użytkownika | Lo | UL | Lo | Lo | Lo | Błądzić | Błądzić | Lo | Ob |
Wyrażenia logiczne
Wyrażenie logiczne to wyrażenie, które można przetestować, aby sprawdzić, czy jest ono prawdziwe, czy jest fałszywe.
BooleanExpression
: Expression
;
Typ T może być używany w wyrażeniu logicznym, jeśli w kolejności preferencji:
TjestBooleanlubBoolean?Tma konwersję rozszerzającą naBooleanTma konwersję rozszerzającą naBoolean?Tdefiniuje dwa pseudo operatory iIsTrueIsFalse.Tma konwersję zawężającą doBoolean?tej, która nie obejmuje konwersji zBooleannaBoolean?.Tma konwersję zawężającą doBoolean.
Uwaga. Warto zauważyć, że jeśli Option Strict jest wyłączona, wyrażenie, które ma konwersję zawężającą, Boolean zostanie zaakceptowane bez błędu czasu kompilacji, ale język nadal preferuje IsTrue operator, jeśli istnieje. Dzieje się tak, ponieważ Option Strict zmienia się tylko to, co jest i nie jest akceptowane przez język, i nigdy nie zmienia rzeczywistego znaczenia wyrażenia.
IsTrue W związku z tym należy zawsze preferować konwersję zawężającą, niezależnie od Option Strict.
Na przykład następująca klasa nie definiuje konwersji rozszerzającej na Boolean. W rezultacie jego użycie w instrukcji If powoduje wywołanie IsTrue operatora.
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
Jeśli wyrażenie warunkowe jest wpisywane jako lub konwertowane na Boolean lub Boolean?, jest prawdziwe, jeśli wartość jest True i fałsz w przeciwnym razie.
W przeciwnym razie wyrażenie logiczne wywołuje IsTrue operator i zwraca True wartość, jeśli operator zwrócił Truewartość ; w przeciwnym razie jest to fałsz (ale nigdy nie wywołuje IsFalse operatora).
W poniższym przykładzie Integer występuje konwersja zawężająca na Booleanwartość , więc wartość null Integer? ma konwersję zawężającą do obu Boolean? wartości (dając wartość null Boolean) i do Boolean (co zgłasza wyjątek). Preferowana jest konwersja zawężająca na Boolean? , a więc wartość "i" jako wyrażenie logiczne to False.
Dim i As Integer? = Nothing
If i Then Console.WriteLine()
Wyrażenia lambda
Wyrażenie lambda definiuje anonimową metodę nazywaną metodą lambda. Metody lambda ułatwiają przekazywanie metod "wbudowanych" do innych metod, które przyjmują typy delegatów.
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'
;
Przykład:
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
zostanie wydrukowany:
2 4 6 8
Wyrażenie lambda rozpoczyna się od opcjonalnych modyfikatorów Async lub Iterator, a następnie słowa kluczowego Function lub Sub i listy parametrów. Nie można zadeklarować Optional parametrów w wyrażeniu lambda ani ParamArray nie mogą mieć atrybutów. W przeciwieństwie do metod regularnych pominięcie typu parametru dla metody lambda nie powoduje automatycznego wnioskowania Object. Zamiast tego, gdy metoda lambda jest ponownie sklasyfikowana, pominięte typy parametrów i ByRef modyfikatory są wnioskowane z typu docelowego. W poprzednim przykładzie wyrażenie lambda mogło zostać zapisane jako Function(x) x * 2, i wywnioskowałby typ xInteger metody lambda, gdy metoda lambda została użyta do utworzenia wystąpienia typu delegata IntFunc . W przeciwieństwie do wnioskowania zmiennej lokalnej, jeśli parametr metody lambda pomija typ, ale zawiera tablicę lub modyfikator nazw dopuszczający wartość null, występuje błąd czasu kompilacji.
Zwykłe wyrażenie lambda jest jedno z ani AsyncIterator modyfikatorami.
Wyrażenie lambda iteratora jest jednym z Iterator modyfikatorem i bez Async modyfikatora. Musi to być funkcja. W przypadku ponownej klasyfikacji wartości można ją przeklasyfikować tylko na wartość typu delegata, którego zwracany typ to IEnumerator, lub IEnumerablelub IEnumerator(Of T)IEnumerable(Of T) dla niektórych Tparametrów , i który nie ma parametrów ByRef.
Asynchroniczne wyrażenie lambda jest jednym z Async modyfikatorem i bez Iterator modyfikatora. Sub lambda asynchronicznego może być przeklasyfikowana tylko do wartości typu delegata podrzędnego bez parametrów ByRef. Funkcja async lambda może zostać sklasyfikowana tylko na wartość typu delegata funkcji, którego zwracany typ to Task lub Task(Of T) dla niektórych Tparametrów , i który nie ma parametrów ByRef.
Wyrażenia lambda mogą być wyrażeniami jednowierszowymi lub wielowierszowymi. Wyrażenia lambda jednowierszowe Function zawierają pojedyncze wyrażenie, które reprezentuje wartość zwróconą z metody lambda. Wyrażenia lambda jednowierszowe Sub zawierają pojedynczą instrukcję bez zamykającego StatementTerminatorwyrażenia . Przykład:
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
Konstrukcje lambda jednowierszowe wiążą się mniej ściśle niż wszystkie inne wyrażenia i instrukcje. W związku z tym na przykład wartość "Function() x + 5" jest równoważna wartości ",Function() (x+5)" a nie "(Function() x) + 5". Aby uniknąć niejednoznaczności, jednowierszowe Sub wyrażenie lambda może nie zawierać instrukcji Dim ani instrukcji deklaracji etykiety. Ponadto, chyba że jest ujęta w nawiasy, jednowierszowe Sub wyrażenie lambda może nie być natychmiast po dwukropku ":", operator dostępu do składowej ".", operator dostępu do składowych słownika "!" lub nawias otwarty "(". Nie może zawierać żadnej instrukcji bloku (, , , WhileFor, Do, Using) ani ResumeOnError . SyncLock, If...EndIfWith
Uwaga. W wyrażeniu Function(i) x=ilambda treść jest interpretowana jako wyrażenie (które sprawdza, czy x i i są równe). Jednak w wyrażeniu Sub(i) x=ilambda treść jest interpretowana jako instrukcja (która przypisuje i do xelementu ).
Wyrażenie lambda wielowierszowe zawiera blok instrukcji i musi kończyć się odpowiednią End instrukcją (tj. End Function lub End Sub). Podobnie jak w przypadku metod regularnych, instrukcje i SubEnd instrukcje metody lambda Function wielowierszowej muszą znajdować się we własnych wierszach. Przykład:
' 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
Wyrażenia lambda wielowierszowe Function mogą deklarować typ zwracany, ale nie mogą umieszczać na nim atrybutów. Jeśli wyrażenie lambda wielowierszowe Function nie deklaruje typu zwracanego, ale zwracany typ może zostać wywnioskowany z kontekstu, w którym jest używane wyrażenie lambda, jest używany ten zwracany typ. W przeciwnym razie zwracany typ funkcji jest obliczany w następujący sposób:
W regularnym wyrażeniu lambda typ zwracany jest dominującym typem wyrażeń we wszystkich
Returninstrukcjach w bloku instrukcji.W wyrażeniu lambda asynchronicznego typ zwracany jest
Task(Of T)miejscem, w którymTjest dominującym typem wyrażeń we wszystkichReturninstrukcjach w bloku instrukcji.W wyrażeniu lambda iteratora zwracany typ to
IEnumerable(Of T)gdzieTjest dominującym typem wyrażeń we wszystkichYieldinstrukcjach w bloku instrukcji.
Przykład:
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
We wszystkich przypadkach, jeśli nie ma Return instrukcji (odpowiednio Yield) lub jeśli nie ma wśród nich dominującego typu, a używane są ścisłe semantyka, występuje błąd czasu kompilacji; w przeciwnym razie dominujący typ jest niejawnie Object.
Należy pamiętać, że typ zwracany jest obliczany ze wszystkich Return instrukcji, nawet jeśli nie są osiągalne. Przykład:
' Return type is Double
Dim x = Function()
Return 10
Return 10.50
End Function
Nie ma niejawnej zmiennej zwracanej, ponieważ nie ma nazwy zmiennej.
Bloki instrukcji wewnątrz wyrażeń lambda wielowierszowych mają następujące ograniczenia:
On Errorinstrukcje iResumesą niedozwolone, chociażTryinstrukcje są dozwolone.Nie można zadeklarować statycznych ustawień lokalnych w wyrażeniach lambda wielowierszowych.
Nie można rozgałęzić w bloku instrukcji wielowierszowego wyrażenia lambda, chociaż w nim mają zastosowanie normalne reguły rozgałęziania. Przykład:
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
Wyrażenie lambda jest w przybliżeniu równoważne metodzie anonimowej zadeklarowanej w typie zawierającym. Początkowy przykład jest w przybliżeniu odpowiednikiem:
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
Zamknięcia
Wyrażenia lambda mają dostęp do wszystkich zmiennych w zakresie, w tym zmiennych lokalnych lub parametrów zdefiniowanych w wyrażeniach zawierających metodę i lambda. Gdy wyrażenie lambda odwołuje się do zmiennej lokalnej lub parametru, wyrażenie lambda przechwytuje zmienną, która jest odwoływana do zamknięcia. Zamknięcie to obiekt, który znajduje się na stercie zamiast na stosie, a gdy zmienna zostanie przechwycona, wszystkie odwołania do zmiennej są przekierowywane do zamknięcia. Dzięki temu wyrażenia lambda mogą nadal odwoływać się do zmiennych lokalnych i parametrów, nawet po zakończeniu procesu zawierającego metodę. Przykład:
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
jest w przybliżeniu równoważne:
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
Zamknięcie przechwytuje nową kopię zmiennej lokalnej za każdym razem, gdy wprowadza blok, w którym zadeklarowana jest zmienna lokalna, ale nowa kopia jest inicjowana z wartością poprzedniej kopii, jeśli istnieje. Przykład:
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
Drukuje
1 2 3 4 5 6 7 8 9 10
Zamiast
9 9 9 9 9 9 9 9 9 9
Ponieważ zamknięcia muszą być inicjowane podczas wprowadzania bloku, nie wolno GoTo wchodzić do bloku z zamknięciem spoza tego bloku, chociaż może wejść do Resume bloku z zamknięciem. Przykład:
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
Ponieważ nie można ich przechwycić do zamknięcia, następujące nie mogą pojawić się wewnątrz wyrażenia lambda:
Parametry odwołania.
Wyrażenia wystąpień (
Me,MyClass,MyBase), jeśli typMenie jest klasą.
Elementy członkowskie anonimowego wyrażenia tworzenia typów, jeśli wyrażenie lambda jest częścią wyrażenia . Przykład:
' Error: Lambda cannot refer to anonymous type field
Dim x = New With { .a = 12, .b = Function() .a }
ReadOnly zmienne wystąpienia w konstruktorach wystąpień lub ReadOnly współużytkowanych zmiennych w konstruktorach udostępnionych, w których zmienne są używane w kontekście innym niż wartość. Przykład:
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
Wyrażenia zapytań
Wyrażenie zapytania to wyrażenie, które stosuje serię operatorów zapytań do elementów kolekcji z możliwością wykonywania zapytań. Na przykład następujące wyrażenie przyjmuje kolekcję Customer obiektów i zwraca nazwy wszystkich klientów w stanie Waszyngton:
Dim names = _
From cust In Customers _
Where cust.State = "WA" _
Select cust.Name
Wyrażenie zapytania musi zaczynać się od From operatora lub i może kończyć się dowolnym operatorem Aggregate zapytania. Wynik wyrażenia zapytania jest klasyfikowany jako wartość; typ wyniku wyrażenia zależy od typu wyniku ostatniego operatora zapytania w wyrażeniu.
QueryExpression
: FromOrAggregateQueryOperator QueryOperator*
;
FromOrAggregateQueryOperator
: FromQueryOperator
| AggregateQueryOperator
;
QueryOperator
: FromQueryOperator
| AggregateQueryOperator
| SelectQueryOperator
| DistinctQueryOperator
| WhereQueryOperator
| OrderByQueryOperator
| PartitionQueryOperator
| LetQueryOperator
| GroupByQueryOperator
| JoinOrGroupJoinQueryOperator
;
JoinOrGroupJoinQueryOperator
: JoinQueryOperator
| GroupJoinQueryOperator
;
Zmienne zakresu
Niektóre operatory zapytań wprowadzają specjalny rodzaj zmiennej nazywanej zmienną zakresu. Zmienne zakresu nie są rzeczywistymi zmiennymi; Zamiast tego reprezentują one poszczególne wartości podczas oceny zapytania w kolekcjach wejściowych.
CollectionRangeVariableDeclarationList
: CollectionRangeVariableDeclaration ( Comma CollectionRangeVariableDeclaration )*
;
CollectionRangeVariableDeclaration
: Identifier ( 'As' TypeName )? 'In' LineTerminator? Expression
;
ExpressionRangeVariableDeclarationList
: ExpressionRangeVariableDeclaration ( Comma ExpressionRangeVariableDeclaration )*
;
ExpressionRangeVariableDeclaration
: Identifier ( 'As' TypeName )? Equals Expression
;
Zmienne zakresu są ograniczone od operatora wprowadzającego zapytania na końcu wyrażenia zapytania lub do operatora zapytania, takiego jak Select ukrywa je. Na przykład w poniższym zapytaniu
Dim waCusts = _
From cust As Customer In Customers _
Where cust.State = "WA"
From Operator zapytania wprowadza zmienną cust zakresu typizowane jako Customer reprezentującą każdego klienta w Customers kolekcji. Poniższy Where operator zapytania odwołuje się następnie do zmiennej cust zakresu w wyrażeniu filtru, aby określić, czy filtrować pojedynczego klienta z wynikowej kolekcji.
Istnieją dwa typy zmiennych zakresu: zmienne zakresu kolekcji i zmienne zakresu wyrażeń. Zmienne zakresu kolekcji przyjmują swoje wartości z elementów kolekcji, których dotyczy zapytanie. Wyrażenie kolekcji w deklaracji zmiennej zakresu kolekcji musi być klasyfikowane jako wartość, której typ jest możliwy do wykonywania zapytań. Jeśli typ zmiennej zakresu kolekcji zostanie pominięty, zostanie on wywnioskowany jako typ elementu wyrażenia kolekcji lub Object jeśli wyrażenie kolekcji nie ma typu elementu (tj. definiuje tylko metodę Cast ). Jeśli wyrażenie kolekcji nie jest możliwe do wykonywania zapytań (tj. nie można wywnioskować typu elementu kolekcji), wynik błędu czasu kompilacji.
Zmienna zakresu wyrażeń to zmienna zakresu, której wartość jest obliczana przez wyrażenie, a nie kolekcję. W poniższym przykładzie Select operator zapytania wprowadza zmienną zakresu wyrażeń o nazwie cityState obliczoną z dwóch pól:
Dim cityStates = _
From cust As Customer In Customers _
Select cityState = cust.City & "," & cust.State _
Where cityState.Length() < 10
Zmienna zakresu wyrażeń nie jest wymagana do odwołowania się do innej zmiennej zakresu, chociaż taka zmienna może mieć wątpliwą wartość. Wyrażenie przypisane do zmiennej zakresu wyrażeń musi być klasyfikowane jako wartość i musi być niejawnie konwertowane na typ zmiennej zakresu, jeśli zostanie podana.
Tylko w operatorze Let może być określona zmienna zakresu wyrażeń. W innych operatorach lub jeśli jego typ nie jest określony, wnioskowanie typu zmiennej lokalnej jest używane do określania typu zmiennej zakresu.
Zmienna zakresu musi przestrzegać reguł deklarowania zmiennych lokalnych w odniesieniu do cieniowania. W związku z tym zmienna zakresu nie może ukryć nazwy zmiennej lokalnej lub parametru w otaczającej metodzie lub innej zmiennej zakresu (chyba że operator zapytania ukrywa wszystkie bieżące zmienne zakresu w zakresie).
Typy z możliwością wykonywania zapytań
Wyrażenia zapytania są implementowane przez tłumaczenie wyrażenia na wywołania do dobrze znanych metod w typie kolekcji. Te dobrze zdefiniowane metody definiują typ elementu kolekcji, a także typy wyników operatorów zapytań wykonywanych w kolekcji. Każdy operator zapytania określa metodę lub metody, na które zwykle tłumaczony jest operator zapytania, chociaż określone tłumaczenie jest zależne od implementacji. Metody są podane w specyfikacji przy użyciu ogólnego formatu, który wygląda następująco:
Function Select(selector As Func(Of T, R)) As CR
Następujące zasady dotyczą metod:
Metoda musi być wystąpieniem lub rozszerzeniem typu kolekcji i musi być dostępna.
Metoda może być ogólna, pod warunkiem, że można wywnioskować wszystkie argumenty typu.
Metoda może być przeciążona, w tym przypadku rozpoznawanie przeciążenia służy do określenia dokładnie używanej metody.
Inny typ delegata może być używany zamiast typu delegata
Func, pod warunkiem, że ma ten sam podpis, w tym typ zwracany, co pasującyFunctyp.Typ
System.Linq.Expressions.Expression(Of D)może być używany zamiast typu delegata, pod warunkiem, żeDjest to typ delegataFunc, który ma ten sam podpis, w tym typ zwracany jako pasującyFunctyp.Typ
Treprezentuje typ elementu kolekcji danych wejściowych. Wszystkie metody zdefiniowane przez typ kolekcji muszą mieć ten sam typ elementu wejściowego dla typu kolekcji, aby można było wykonywać zapytania.Typ
Sreprezentuje typ elementu drugiej kolekcji wejściowej w przypadku operatorów zapytań, które wykonują sprzężenia.Typ
Kreprezentuje typ klucza w przypadku operatorów zapytań, które mają zestaw zmiennych zakresu, które działają jako klucze.Typ
Nreprezentuje typ, który jest używany jako typ liczbowy (chociaż nadal może być typem zdefiniowanym przez użytkownika, a nie wewnętrznym typem liczbowym).Typ reprezentuje typ
B, który może być używany w wyrażeniu logicznym.Typ
Rreprezentuje typ elementu kolekcji wyników, jeśli operator zapytania generuje kolekcję wyników.Rzależy od liczby zmiennych zakresu w zakresie po zakończeniu operatora zapytania. Jeśli pojedyncza zmienna zakresu jest w zakresie,Rjest to typ tej zmiennej zakresu. W przykładzieDim custNames = From c In Customers Select c.Namewynikiem zapytania będzie typ kolekcji z typem
Stringelementu . Jeśli zakres zawiera wiele zmiennych zakresu,Rjest to anonimowy typ, który zawiera wszystkie zmienne zakresu w zakresie jakoKeypola. W przykładzie:Dim custNames = From c In Customers, o In c.Orders Select Name = c.Name, ProductName = o.ProductNamewynik zapytania będzie typem kolekcji z typem elementu typu anonimowego z właściwością tylko do odczytu o nazwie typu i właściwością tylko do odczytu o nazwie
NameProductNametypuString.StringW wyrażeniu zapytania typy anonimowe wygenerowane w celu zawierania zmiennych zakresu są przezroczyste, co oznacza, że zmienne zakresu są zawsze dostępne bez kwalifikacji. Na przykład w poprzednim przykładzie zmienne
czakresu iomogą być dostępne bez kwalifikacji operatoraSelectzapytania, mimo że typ elementu kolekcji danych wejściowych był typem anonimowym.Typ
CXreprezentuje typ kolekcji, niekoniecznie typ kolekcji wejściowej, którego typ elementu jest typem .X
Typ kolekcji z możliwością wykonywania zapytań musi spełniać jeden z następujących warunków w kolejności preferencji:
Musi zdefiniować zgodną
Selectmetodę.Musi mieć jedną z następujących metod
Function AsEnumerable() As CT Function AsQueryable() As CTktóre można wywołać w celu uzyskania kolekcji z możliwością wykonywania zapytań. Jeśli podano obie metody,
AsQueryablepreferowane jest użycie metodyAsEnumerable.Musi mieć metodę
Function Cast(Of T)() As CTktóre można wywołać za pomocą typu zmiennej zakresu w celu utworzenia kolekcji z możliwością wykonywania zapytań.
Ponieważ określenie typu elementu kolekcji odbywa się niezależnie od wywołania metody rzeczywistej, nie można określić zastosowania określonych metod. W związku z tym podczas określania typu elementu kolekcji, jeśli istnieją metody wystąpień, które pasują do dobrze znanych metod, wszystkie metody rozszerzenia zgodne z dobrze znanymi metodami są ignorowane.
Tłumaczenie operatora zapytania odbywa się w kolejności, w której operatory zapytań występują w wyrażeniu. Nie jest konieczne, aby obiekt kolekcji implementował wszystkie metody wymagane przez wszystkie operatory zapytań, chociaż każdy obiekt kolekcji musi przynajmniej obsługiwać Select operator zapytania. Jeśli wymagana metoda nie jest obecna, wystąpi błąd czasu kompilacji. W przypadku powiązania dobrze znanych nazw metod metody metody inne niż metody są ignorowane w celu wielokrotnego dziedziczenia w interfejsach i powiązaniu metody rozszerzenia, chociaż semantyka cieniowania nadal ma zastosowanie. Przykład:
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
Domyślny indeksator zapytań
Każdy typ kolekcji z możliwością wykonywania zapytań, którego typ elementu jest T i nie ma jeszcze właściwości domyślnej, jest uważany za domyślną właściwość następującego formularza ogólnego:
Public ReadOnly Default Property Item(index As Integer) As T
Get
Return Me.ElementAtOrDefault(index)
End Get
End Property
Właściwość domyślna może być odwoływana tylko przy użyciu domyślnej składni dostępu do właściwości; nie można odwoływać się do właściwości domyślnej według nazwy. Przykład:
Dim customers As IEnumerable(Of Customer) = ...
Dim customerThree = customers(2)
' Error, no such property
Dim customerFour = customers.Item(4)
Jeśli typ kolekcji nie ma elementu ElementAtOrDefault członkowskiego, wystąpi błąd czasu kompilacji.
Z operatora zapytania
Operator From zapytania wprowadza zmienną zakresu kolekcji, która reprezentuje poszczególne elementy członkowskie kolekcji do odpytowania.
FromQueryOperator
: LineTerminator? 'From' LineTerminator? CollectionRangeVariableDeclarationList
;
Na przykład wyrażenie zapytania:
From c As Customer In Customers ...
można traktować jako równoważne
For Each c As Customer In Customers
...
Next c
Gdy From operator zapytania deklaruje wiele zmiennych zakresu kolekcji lub nie jest pierwszym From operatorem zapytania w wyrażeniu zapytania, każda nowa zmienna zakresu kolekcji jest sprzężona krzyżowo z istniejącym zestawem zmiennych zakresu. Wynikiem jest to, że zapytanie jest oceniane na krzyżowo iloczyn wszystkich elementów w sprzężonych kolekcjach. Na przykład wyrażenie:
From c In Customers _
From e In Employees _
...
można traktować jako równoważne:
For Each c In Customers
For Each e In Employees
...
Next e
Next c
i jest dokładnie równoważne:
From c In Customers, e In Employees ...
Zmienne zakresu wprowadzone w poprzednich operatorach zapytań mogą być używane w późniejszym From operatorze zapytania. Na przykład w poniższym wyrażeniu zapytania drugi From operator zapytania odwołuje się do wartości pierwszej zmiennej zakresu:
From c As Customer In Customers _
From o As Order In c.Orders _
Select c.Name, o
Wiele zmiennych zakresu w From operatorze zapytania lub wielu From operatorach zapytań jest obsługiwanych tylko wtedy, gdy typ kolekcji zawiera jedną lub obie następujące metody:
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
Kod
Dim xs() As Integer = ...
Dim ys() As Integer = ...
Dim zs = From x In xs, y In ys ...
jest zwykle tłumaczony na
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})...
Uwaga.
From nie jest słowem zastrzeżonym.
Operator kwerendy sprzężenia
Join Operator zapytania łączy istniejące zmienne zakresu z nową zmienną zakresu kolekcji, tworząc pojedynczą kolekcję, której elementy zostały połączone na podstawie wyrażenia równości.
JoinQueryOperator
: LineTerminator? 'Join' LineTerminator? CollectionRangeVariableDeclaration
JoinOrGroupJoinQueryOperator? LineTerminator? 'On' LineTerminator? JoinConditionList
;
JoinConditionList
: JoinCondition ( 'And' LineTerminator? JoinCondition )*
;
JoinCondition
: Expression 'Equals' LineTerminator? Expression
;
Przykład:
Dim customersAndOrders = _
From cust In Customers _
Join ord In Orders On cust.ID Equals ord.CustomerID
Wyrażenie równości jest bardziej ograniczone niż wyrażenie równości regularnej:
Oba wyrażenia muszą być klasyfikowane jako wartość.
Oba wyrażenia muszą odwoływać się do co najmniej jednej zmiennej zakresu.
Zmienna zakresu zadeklarowana w operatorze zapytania sprzężenia musi być przywoływane przez jedno z wyrażeń, a to wyrażenie nie może odwoływać się do żadnych innych zmiennych zakresu.
Jeśli typy dwóch wyrażeń nie są dokładnie tym samym typem, wówczas
Jeśli operator równości jest zdefiniowany dla dwóch typów, oba wyrażenia są niejawnie konwertowane na nie, a nie , a następnie
Objectkonwertują oba wyrażenia na ten typ.W przeciwnym razie, jeśli istnieje typ dominujący, na który można niejawnie przekonwertować oba wyrażenia, przekonwertuj oba wyrażenia na ten typ.
W przeciwnym razie wystąpi błąd czasu kompilacji.
Wyrażenia są porównywane przy użyciu wartości skrótu (tj. przez wywołanie GetHashCode()metody ), a nie przy użyciu operatorów równości w celu zwiększenia wydajności.
Join Operator zapytania może wykonywać wiele sprzężeń lub warunków równości w tym samym operatorze.
Join Operator zapytania jest obsługiwany tylko wtedy, gdy typ kolekcji zawiera metodę:
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
Kod
Dim xs() As Integer = ...
Dim ys() As Integer = ...
Dim zs = From x In xs _
Join y In ys On x Equals y _
...
jest zwykle tłumaczony na
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})...
Uwaga.Join, On i Equals nie są zastrzeżonych wyrazów.
Zezwalaj operatorowi kwerendy
Operator Let zapytania wprowadza zmienną zakresu wyrażeń. Umożliwia to obliczanie wartości pośredniej raz, która będzie używana wiele razy w późniejszych operatorach zapytań.
LetQueryOperator
: LineTerminator? 'Let' LineTerminator? ExpressionRangeVariableDeclarationList
;
Przykład:
Dim taxedPrices = _
From o In Orders _
Let tax = o.Price * 0.088 _
Where tax > 3.50 _
Select o.Price, tax, total = o.Price + tax
można traktować jako równoważne:
For Each o In Orders
Dim tax = o.Price * 0.088
...
Next o
Let Operator zapytania jest obsługiwany tylko wtedy, gdy typ kolekcji zawiera metodę:
Function Select(selector As Func(Of T, R)) As CR
Kod
Dim xs() As Integer = ...
Dim zs = From x In xs _
Let y = x * 10 _
...
jest zwykle tłumaczony na
Dim xs() As Integer = ...
Dim zs = _
xs.Select(Function(x As Integer) New With {x, .y = x * 10})...
Wybieranie operatora zapytania
Operator Select zapytania jest jak Let operator zapytania, który wprowadza zmienne zakresu wyrażeń, Select jednak operator zapytania ukrywa obecnie dostępne zmienne zakresu zamiast dodawać do nich. Ponadto typ zmiennej zakresu wyrażeń wprowadzony przez Select operator zapytania jest zawsze wnioskowany przy użyciu reguł wnioskowania typu zmiennej lokalnej; nie można określić jawnego typu i jeśli nie można wywnioskować żadnego typu, wystąpi błąd czasu kompilacji.
SelectQueryOperator
: LineTerminator? 'Select' LineTerminator? ExpressionRangeVariableDeclarationList
;
Na przykład w zapytaniu:
Dim smiths = _
From cust In Customers _
Select name = cust.name _
Where name.EndsWith("Smith")
Where Operator kwerendy ma dostęp tylko do zmiennej zakresu wprowadzonej Selectname przez operator; gdyby Where operator próbował odwołać custsię, wystąpił błąd czasu kompilacji.
Zamiast jawnie określać nazwy zmiennych zakresu, Select operator zapytania może wywnioskować nazwy zmiennych zakresu, używając tych samych reguł co wyrażenia tworzenia obiektów typu anonimowego. Przykład:
Dim custAndOrderNames = _
From cust In Customers, ord In cust.Orders _
Select cust.name, ord.ProductName _
Where name.EndsWith("Smith")
Jeśli nie podano nazwy zmiennej zakresu i nie można wywnioskować nazwy, wystąpi błąd czasu kompilacji.
Select Jeśli operator zapytania zawiera tylko jedno wyrażenie, nie wystąpi błąd, jeśli nie można wywnioskować nazwy tej zmiennej zakresu, ale zmienna zakresu jest beznazwytna. Przykład:
Dim custAndOrderNames = _
From cust In Customers, ord In cust.Orders _
Select cust.Name & " bought " & ord.ProductName _
Take 10
Jeśli operator zapytania ma niejednoznaczność Select między przypisaniem nazwy do zmiennej zakresu a wyrażeniem równości, preferowane jest przypisanie nazwy. Przykład:
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)
Każde wyrażenie w operatorze Select zapytania musi być klasyfikowane jako wartość.
Select Operator zapytania jest obsługiwany tylko wtedy, gdy typ kolekcji zawiera metodę:
Function Select(selector As Func(Of T, R)) As CR
Kod
Dim xs() As Integer = ...
Dim zs = From x In xs _
Select x, y = x * 10 _
...
jest zwykle tłumaczony na
Dim xs() As Integer = ...
Dim zs = _
xs.Select(Function(x As Integer) New With {x, .y = x * 10})...
Distinct Query Operator
Distinct Operator zapytania ogranicza wartości w kolekcji tylko do tych z unikatowymi wartościami, zgodnie z ustaleniem, porównując typ elementu pod kątem równości.
DistinctQueryOperator
: LineTerminator? 'Distinct' LineTerminator?
;
Na przykład zapytanie:
Dim distinctCustomerPrice = _
From cust In Customers, ord In cust.Orders _
Select cust.Name, ord.Price _
Distinct
Zwróci tylko jeden wiersz dla każdej odrębnej pary nazwy klienta i ceny zamówienia, nawet jeśli klient ma wiele zamówień z tą samą ceną.
Distinct Operator zapytania jest obsługiwany tylko wtedy, gdy typ kolekcji zawiera metodę:
Function Distinct() As CT
Kod
Dim xs() As Integer = ...
Dim zs = From x In xs _
Distinct _
...
jest zwykle tłumaczony na
Dim xs() As Integer = ...
Dim zs = xs.Distinct()...
Uwaga.
Distinct nie jest słowem zastrzeżonym.
Gdzie operator zapytania
Where Operator zapytania ogranicza wartości w kolekcji do tych, które spełniają określony warunek.
WhereQueryOperator
: LineTerminator? 'Where' LineTerminator? BooleanExpression
;
Where Operator zapytania przyjmuje wyrażenie logiczne, które jest obliczane dla każdego zestawu wartości zmiennych zakresu. Jeśli wartość wyrażenia jest prawdziwa, wartości są wyświetlane w kolekcji wyjściowej, w przeciwnym razie wartości są pomijane. Na przykład wyrażenie zapytania:
From cust In Customers, ord In Orders _
Where cust.ID = ord.CustomerID _
...
można traktować jako odpowiednik zagnieżdżonej pętli
For Each cust In Customers
For Each ord In Orders
If cust.ID = ord.CustomerID Then
...
End If
Next ord
Next cust
Where Operator zapytania jest obsługiwany tylko wtedy, gdy typ kolekcji zawiera metodę:
Function Where(predicate As Func(Of T, B)) As CT
Kod
Dim xs() As Integer = ...
Dim zs = From x In xs _
Where x < 10 _
...
jest zwykle tłumaczony na
Dim xs() As Integer = ...
Dim zs = _
xs.Where(Function(x As Integer) x < 10)...
Uwaga.
Where nie jest słowem zastrzeżonym.
Operatory zapytań partycji
PartitionQueryOperator
: LineTerminator? 'Take' LineTerminator? Expression
| LineTerminator? 'Take' 'While' LineTerminator? BooleanExpression
| LineTerminator? 'Skip' LineTerminator? Expression
| LineTerminator? 'Skip' 'While' LineTerminator? BooleanExpression
;
Operator Take zapytania powoduje wyświetlenie pierwszych n elementów kolekcji. W przypadku użycia z While modyfikatorem Take operator zwraca pierwsze n elementy kolekcji, które spełniają wyrażenie logiczne. Operator Skip pomija pierwsze n elementy kolekcji, a następnie zwraca pozostałą część kolekcji. W połączeniu While z modyfikatorem Skip operator pomija pierwsze n elementy kolekcji, które spełniają wyrażenie logiczne, a następnie zwraca resztę kolekcji. Wyrażenia w operatorze Take zapytania lub Skip muszą być klasyfikowane jako wartość.
Take Operator zapytania jest obsługiwany tylko wtedy, gdy typ kolekcji zawiera metodę:
Function Take(count As N) As CT
Skip Operator zapytania jest obsługiwany tylko wtedy, gdy typ kolekcji zawiera metodę:
Function Skip(count As N) As CT
Take While Operator zapytania jest obsługiwany tylko wtedy, gdy typ kolekcji zawiera metodę:
Function TakeWhile(predicate As Func(Of T, B)) As CT
Skip While Operator zapytania jest obsługiwany tylko wtedy, gdy typ kolekcji zawiera metodę:
Function SkipWhile(predicate As Func(Of T, B)) As CT
Kod
Dim xs() As Integer = ...
Dim zs = From x In xs _
Skip 10 _
Take 5 _
Skip While x < 10 _
Take While x > 5 _
...
jest zwykle tłumaczony na
Dim xs() As Integer = ...
Dim zs = _
xs.Skip(10). _
Take(5). _
SkipWhile(Function(x) x < 10). _
TakeWhile(Function(x) x > 5)...
Uwaga.
Take i Skip nie są słowami zarezerwowanymi.
Order By Query Operator
Operator Order By zapytania porządkuje wartości wyświetlane w zmiennych zakresu.
OrderByQueryOperator
: LineTerminator? 'Order' 'By' LineTerminator? OrderExpressionList
;
OrderExpressionList
: OrderExpression ( Comma OrderExpression )*
;
OrderExpression
: Expression Ordering?
;
Ordering
: 'Ascending' | 'Descending'
;
Operator Order By zapytania przyjmuje wyrażenia określające wartości klucza, które mają być używane do porządkowania zmiennych iteracji. Na przykład następujące zapytanie zwraca produkty posortowane według ceny:
Dim productsByPrice = _
From p In Products _
Order By p.Price _
Select p.Name
Kolejność może być oznaczona jako Ascending, w tym przypadku mniejsze wartości pochodzą przed większymi wartościami lub Descending, w tym przypadku większe wartości pochodzą przed mniejszymi wartościami. Ustawieniem domyślnym dla kolejności, jeśli nie określono żadnego z nich, jest Ascending. Na przykład następujące zapytanie zwraca produkty posortowane według ceny z najdroższym produktem:
Dim productsByPriceDesc = _
From p In Products _
Order By p.Price Descending _
Select p.Name
Operator Order By zapytania może określać wiele wyrażeń do porządkowania, w tym przypadku kolekcja jest uporządkowana w sposób zagnieżdżony. Na przykład następujące zapytanie wysyła do klientów zamówienia według stanu, a następnie według miasta w każdym stanie, a następnie za pomocą kodu pocztowego w każdym mieście:
Dim customersByLocation = _
From c In Customers _
Order By c.State, c.City, c.ZIP _
Select c.Name, c.State, c.City, c.ZIP
Wyrażenia w Order By operatorze zapytania muszą być klasyfikowane jako wartość.
Order By Operator zapytania jest obsługiwany tylko wtedy, gdy typ kolekcji zawiera jedną lub obie z następujących metod:
Function OrderBy(keySelector As Func(Of T, K)) As CT
Function OrderByDescending(keySelector As Func(Of T, K)) As CT
Typ CT zwracany musi być uporządkowaną kolekcją. Uporządkowana kolekcja to typ kolekcji, który zawiera jedną lub obie metody:
Function ThenBy(keySelector As Func(Of T, K)) As CT
Function ThenByDescending(keySelector As Func(Of T, K)) As CT
Kod
Dim xs() As Integer = ...
Dim zs = From x In xs _
Order By x Ascending, x Mod 2 Descending _
...
jest zwykle tłumaczony na
Dim xs() As Integer = ...
Dim zs = _
xs.OrderBy(Function(x) x).ThenByDescending(Function(x) x Mod 2)...
Uwaga. Ponieważ operatory zapytań po prostu mapują składnię na metody implementujące określoną operację zapytania, zachowanie kolejności nie jest określane przez język i jest określane przez implementację samego operatora. Jest to bardzo podobne do operatorów zdefiniowanych przez użytkownika, że implementacja przeciążenia operatora dodawania dla typu liczbowego zdefiniowanego przez użytkownika może nie wykonywać żadnych czynności przypominających dodanie. Oczywiście, aby zachować przewidywalność, implementacja czegoś, co nie jest zgodne z oczekiwaniami użytkowników, nie jest zalecane.
Uwaga.
Order i By nie są słowami zarezerwowanymi.
Grupuj według operatora zapytania
Operator Group By zapytania grupuje zmienne zakresu w zakresie na podstawie co najmniej jednego wyrażenia, a następnie tworzy nowe zmienne zakresu na podstawie tych grup.
GroupByQueryOperator
: LineTerminator? 'Group' ( LineTerminator? ExpressionRangeVariableDeclarationList )?
LineTerminator? 'By' LineTerminator? ExpressionRangeVariableDeclarationList
LineTerminator? 'Into' LineTerminator? ExpressionRangeVariableDeclarationList
;
Na przykład następujące zapytanie grupuje wszystkich klientów według State, a następnie oblicza liczbę i średni wiek każdej grupy:
Dim averageAges = _
From cust In Customers _
Group By cust.State _
Into Count(), Average(cust.Age)
Operator Group By zapytania ma trzy klauzule: klauzulę opcjonalną Group , By klauzulę i klauzulę Into . Klauzula Group ma taką samą składnię i efekt jak Select operator zapytania, z tą różnicą, że ma wpływ tylko na zmienne zakresu dostępne w klauzuli Into , a nie klauzulę By . Przykład:
Dim averageAges = _
From cust In Customers _
Group cust.Age By cust.State _
Into Count(), Average(Age)
Klauzula By deklaruje zmienne zakresu wyrażeń, które są używane jako wartości klucza w operacji grupowania. Klauzula Into umożliwia deklarowanie zmiennych zakresu wyrażeń, które obliczają agregacje dla każdej z grup utworzonych przez klauzulę By . W klauzuli Into zmienna zakresu wyrażeń może być przypisana tylko wyrażenie, które jest wywołaniem metody funkcji agregującej. Funkcja agregacji jest funkcją typu kolekcji grupy (która nie musi być tym samym typem kolekcji oryginalnej kolekcji), która wygląda jak każda z następujących metod:
Function _name_() As _type_
Function _name_(selector As Func(Of T, R)) As R
Jeśli funkcja agregacji przyjmuje argument delegata, wyrażenie wywołania może mieć wyrażenie argumentu, które musi być klasyfikowane jako wartość. Wyrażenie argumentu może używać zmiennych zakresu, które są w zakresie; w wywołaniu funkcji agregującej te zmienne zakresu reprezentują wartości w tworzonej grupie, a nie wszystkie wartości w kolekcji. Na przykład w oryginalnym przykładzie w tej sekcji Average funkcja oblicza średnią wieku klientów na stan, a nie dla wszystkich klientów razem.
Wszystkie typy kolekcji są uważane za funkcję agregacji Group zdefiniowaną na niej, która nie przyjmuje żadnych parametrów i po prostu zwraca grupę. Inne standardowe funkcje agregujące, które mogą udostępniać typ kolekcji, to:
Count i LongCount, które zwracają liczbę elementów w grupie lub liczbę elementów w grupie, które spełniają wyrażenie logiczne.
Count i LongCount są obsługiwane tylko wtedy, gdy typ kolekcji zawiera jedną z metod:
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, która zwraca sumę wyrażenia we wszystkich elementach w grupie.
Sum jest obsługiwany tylko wtedy, gdy typ kolekcji zawiera jedną z metod:
Function Sum() As N
Function Sum(selector As Func(Of T, N)) As N
Min funkcja zwraca minimalną wartość wyrażenia we wszystkich elementach w grupie.
Min jest obsługiwany tylko wtedy, gdy typ kolekcji zawiera jedną z metod:
Function Min() As N
Function Min(selector As Func(Of T, N)) As N
Max, która zwraca maksymalną wartość wyrażenia we wszystkich elementach w grupie.
Max jest obsługiwany tylko wtedy, gdy typ kolekcji zawiera jedną z metod:
Function Max() As N
Function Max(selector As Func(Of T, N)) As N
Average, która zwraca średnią wyrażenia we wszystkich elementach w grupie.
Average jest obsługiwany tylko wtedy, gdy typ kolekcji zawiera jedną z metod:
Function Average() As N
Function Average(selector As Func(Of T, N)) As N
Any, który określa, czy grupa zawiera elementy członkowskie, czy wyrażenie logiczne jest prawdziwe dla dowolnego elementu w grupie.
Any Zwraca wartość, która może być używana w wyrażeniu logicznym i jest obsługiwana tylko wtedy, gdy typ kolekcji zawiera jedną z metod:
Function Any() As B
Function Any(predicate As Func(Of T, B)) As B
All, który określa, czy wyrażenie logiczne ma wartość true dla wszystkich elementów w grupie.
All Zwraca wartość, która może być używana w wyrażeniu logicznym i jest obsługiwana tylko wtedy, gdy typ kolekcji zawiera metodę:
Function All(predicate As Func(Of T, B)) As B
Po operatorze Group By zapytania zmienne zakresu wcześniej w zakresie są ukryte, a zmienne zakresu wprowadzone przez By klauzule i Into są dostępne.
Group By Operator zapytania jest obsługiwany tylko wtedy, gdy typ kolekcji zawiera metodę:
Function GroupBy(keySelector As Func(Of T, K), _
resultSelector As Func(Of K, CT, R)) As CR
Deklaracje zmiennych zakresu w klauzuli Group są obsługiwane tylko wtedy, gdy typ kolekcji zawiera metodę:
Function GroupBy(keySelector As Func(Of T, K), _
elementSelector As Func(Of T, S), _
resultSelector As Func(Of K, CS, R)) As CR
Kod
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) _
...
jest zwykle tłumaczony na
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)})...
Uwaga.Group, By, i Into nie są zastrzeżonych wyrazów.
Operator zapytania agregowania
Aggregate Operator zapytania wykonuje podobną funkcję jako Group By operator, z tą różnicą, że umożliwia agregowanie grup, które zostały już utworzone. Ponieważ grupa została już utworzona, Into klauzula Aggregate operatora zapytania nie ukrywa zmiennych zakresu w zakresie (w ten sposób Aggregate jest bardziej podobna Letdo , i Group By jest bardziej podobna do ).Select
AggregateQueryOperator
: LineTerminator? 'Aggregate' LineTerminator? CollectionRangeVariableDeclaration QueryOperator*
LineTerminator? 'Into' LineTerminator? ExpressionRangeVariableDeclarationList
;
Na przykład następujące zapytanie agreguje sumę wszystkich zamówień złożonych przez klientów w Waszyngtonie:
Dim orderTotals = _
From cust In Customers _
Where cust.State = "WA" _
Aggregate order In cust.Orders _
Into Sum(order.Total)
Wynikiem tego zapytania jest kolekcja, której typ elementu jest typem anonimowym z właściwością o nazwie typed as Customer i właściwością o nazwie custSum typed jako Integer.
W przeciwieństwie do Group Bymetody można umieścić dodatkowe operatory zapytań między klauzulami Aggregate i Into . Między klauzulą a końcem AggregateInto klauzuli można użyć wszystkich zmiennych zakresu w zakresie, w tym tych zadeklarowanych przez klauzulę Aggregate . Na przykład następujące zapytanie agreguje sumę wszystkich zamówień złożonych przez klientów w Waszyngtonie przed 2006 r.:
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)
Operator Aggregate może również służyć do uruchamiania wyrażenia zapytania. W takim przypadku wynikiem wyrażenia zapytania będzie pojedyncza wartość obliczona przez klauzulę Into . Na przykład następujące zapytanie oblicza sumę wszystkich sum zamówień przed 1 stycznia 2006 r.:
Dim ordersTotal = _
Aggregate order In Orders _
Where order.Date <= #01/01/2006# _
Into Sum(order.Total)
Wynikiem zapytania jest pojedyncza Integer wartość.
Aggregate Operator zapytania jest zawsze dostępny (chociaż funkcja agregacji musi być również dostępna, aby wyrażenie było prawidłowe). Kod
Dim xs() As Integer = ...
Dim zs = _
Aggregate x In xs _
Where x < 5 _
Into Sum()
jest zwykle tłumaczony na
Dim xs() As Integer = ...
Dim zs = _
xs.Where(Function(x) x < 5).Sum()
Uwaga.
Aggregate i Into nie są słowami zarezerwowanymi.
Operator zapytania dołączania do grupy
Operator Group Join zapytania łączy funkcje operatorów Join zapytań i Group By w jeden operator.
Group Join Łączy dwie kolekcje na podstawie pasujących kluczy wyodrębnionych z elementów, grupując wszystkie elementy po prawej stronie sprzężenia, które pasują do określonego elementu po lewej stronie sprzężenia. W związku z tym operator generuje zestaw wyników hierarchicznych.
GroupJoinQueryOperator
: LineTerminator? 'Group' 'Join' LineTerminator? CollectionRangeVariableDeclaration
JoinOrGroupJoinQueryOperator? LineTerminator? 'On' LineTerminator? JoinConditionList
LineTerminator? 'Into' LineTerminator? ExpressionRangeVariableDeclarationList
;
Na przykład następujące zapytanie tworzy elementy zawierające nazwę pojedynczego klienta, grupę wszystkich zamówień i łączną kwotę wszystkich tych zamówień:
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
Wynikiem zapytania jest kolekcja, której typ elementu jest typem anonimowym z trzema właściwościami: Name, typd jako String, Orders wpisany jako kolekcja, której typ elementu to Order, i OrdersTotal, wpisz jako Integer.
Group Join Operator zapytania jest obsługiwany tylko wtedy, gdy typ kolekcji zawiera metodę:
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
Kod
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 _
...
jest zwykle tłumaczony na
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})...
Uwaga.Group, Join, i Into nie są zastrzeżonych wyrazów.
Wyrażenia warunkowe
Wyrażenie warunkowe If testuje wyrażenie i zwraca wartość.
ConditionalExpression
: 'If' OpenParenthesis BooleanExpression Comma Expression Comma Expression CloseParenthesis
| 'If' OpenParenthesis Expression Comma Expression CloseParenthesis
;
IIF Jednak w przeciwieństwie do funkcji środowiska uruchomieniowego wyrażenie warunkowe oblicza tylko jego operandy w razie potrzeby. Na przykład wyrażenie If(c Is Nothing, c.Name, "Unknown") nie zgłosi wyjątku, jeśli wartość c to Nothing. Wyrażenie warunkowe ma dwie formy: jedną, która przyjmuje dwa operandy i jeden, który przyjmuje trzy operandy.
Jeśli podano trzy operandy, wszystkie trzy wyrażenia muszą być klasyfikowane jako wartości, a pierwszy operand musi być wyrażeniem logicznym. Jeśli wynik wyrażenia ma wartość true, drugie wyrażenie będzie wynikiem operatora, w przeciwnym razie trzecie wyrażenie będzie wynikiem operatora. Typ wyniku wyrażenia jest dominującym typem między typami drugiego i trzeciego wyrażenia. Jeśli nie ma dominującego typu, wystąpi błąd czasu kompilacji.
Jeśli podano dwa operandy, oba operandy muszą być klasyfikowane jako wartości, a pierwszy operand musi być typem odwołania lub typem wartości dopuszczanej do wartości null.
If(x, y) Wyrażenie jest następnie oceniane tak, jakby było wyrażeniem If(x IsNot Nothing, x, y), z dwoma wyjątkami. Najpierw pierwsze wyrażenie jest zawsze obliczane tylko raz, a drugie, jeśli typ drugiego operandu jest typem wartości niepustej, a typ pierwszego operandu jest ? usuwany z typu pierwszego operandu podczas określania typu dominującego typu wyrażenia. Przykład:
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
W obu formach wyrażenia, jeśli operand ma Nothingwartość , jego typ nie jest używany do określania typu dominującego. W przypadku wyrażenia If(<expression>, Nothing, Nothing)typ dominujący jest uznawany za Object.
Wyrażenia literału XML
Wyrażenie literału XML reprezentuje wartość XML (eXtensible Markup Language) 1.0.
XMLLiteralExpression
: XMLDocument
| XMLElement
| XMLProcessingInstruction
| XMLComment
| XMLCDATASection
;
Wynikiem wyrażenia literału XML jest wartość typowana jako jeden z typów z System.Xml.Linq przestrzeni nazw. Jeśli typy w tej przestrzeni nazw nie są dostępne, wyrażenie literału XML spowoduje błąd czasu kompilacji. Wartości są generowane za pomocą wywołań konstruktorów przetłumaczonych na podstawie wyrażenia literału XML. Na przykład kod:
Dim book As System.Xml.Linq.XElement = _
<book title="My book"></book>
jest w przybliżeniu odpowiednikiem kodu:
Dim book As System.Xml.Linq.XElement = _
New System.Xml.Linq.XElement( _
"book", _
New System.Xml.Linq.XAttribute("title", "My book"))
Wyrażenie literału XML może mieć postać dokumentu XML, elementu XML, instrukcji przetwarzania XML, komentarza XML lub sekcji CDATA.
Uwaga. Ta specyfikacja zawiera tylko wystarczająco dużo opisu XML, aby opisać zachowanie języka Visual Basic. Więcej informacji na temat kodu XML można znaleźć na stronie http://www.w3.org/TR/REC-xml/.
Reguły leksykalne
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>'
;
Wyrażenia literału XML są interpretowane przy użyciu reguł leksykalnych XML zamiast reguł leksykalnych zwykłego kodu Języka Visual Basic. Dwa zestawy reguł zwykle różnią się następującymi sposobami:
Białe znaki są istotne w kodzie XML. W rezultacie gramatyka wyrażeń literałów XML jawnie określa, gdzie dozwolone jest białe znaki. Białe znaki nie są zachowywane, z wyjątkiem sytuacji, gdy występują w kontekście danych znaków w elemecie. Przykład:
' The following element preserves no whitespace Dim e1 = _ <customer> <name>Bob</> </> ' The following element preserves all of the whitespace Dim e2 = _ <customer> Bob </>Biały znak XML jest znormalizowany zgodnie ze specyfikacją XML.
W pliku XML jest uwzględniana wielkość liter. Słowa kluczowe muszą dokładnie odpowiadać wielkości liter lub wystąpi błąd czasu kompilacji.
Terminatory wierszy są uznawane za białe znaki w formacie XML. W rezultacie w wyrażeniach literału XML nie są potrzebne żadne znaki kontynuacji wiersza.
Kod XML nie akceptuje znaków o pełnej szerokości. Jeśli są używane znaki o pełnej szerokości, wystąpi błąd czasu kompilacji.
Wyrażenia osadzone
Wyrażenia literału XML mogą zawierać wyrażenia osadzone. Wyrażenie osadzone to wyrażenie języka Visual Basic, które jest oceniane i używane do wypełniania co najmniej jednej wartości w lokalizacji wyrażenia osadzonego.
XMLEmbeddedExpression
: '<' '%' '=' LineTerminator? Expression LineTerminator? '%' '>'
;
Na przykład następujący kod umieszcza ciąg John Smith jako wartość elementu XML:
Dim name as String = "John Smith"
Dim element As System.Xml.Linq.XElement = <customer><%= name %></customer>
Wyrażenia można osadzać w wielu kontekstach. Na przykład następujący kod generuje element o nazwie customer:
Dim name As String = "customer"
Dim element As System.Xml.Linq.XElement = <<%= name %>>John Smith</>
Każdy kontekst, w którym można użyć wyrażenia osadzonego, określa typy, które zostaną zaakceptowane. Jeśli w kontekście części wyrażenia osadzonego wyrażenia stosowane są normalne reguły leksykalne dla kodu języka Visual Basic, tak aby na przykład były używane kontynuacje wierszy:
' Visual Basic expression uses line continuation, XML does not
Dim element As System.Xml.Linq.XElement = _
<<%= name & _
name %>>John
Smith</>
Dokumenty XML
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
;
Dokument XML powoduje wpisaną wartość jako System.Xml.Linq.XDocument. W przeciwieństwie do specyfikacji XML 1.0 dokumenty XML w wyrażeniach literału XML są wymagane do określenia prologu dokumentu XML; Wyrażenia literału XML bez prologu dokumentu XML są interpretowane jako ich pojedyncza jednostka. Przykład:
Dim doc As System.Xml.Linq.XDocument = _
<?xml version="1.0"?>
<?instruction?>
<customer>Bob</>
Dim pi As System.Xml.Linq.XProcessingInstruction = _
<?instruction?>
Dokument XML może zawierać wyrażenie osadzone, którego typ może być dowolnym typem; jednak w czasie wykonywania obiekt musi spełniać wymagania konstruktora XDocument lub wystąpi błąd czasu wykonywania.
W przeciwieństwie do zwykłego kodu XML wyrażenia dokumentów XML nie obsługują identyfikatorów DTD (deklaracje typów dokumentów). Ponadto atrybut kodowania, jeśli zostanie podany, zostanie zignorowany, ponieważ kodowanie wyrażenia literału Xml jest zawsze takie samo jak kodowanie samego pliku źródłowego.
Uwaga. Mimo że atrybut kodowania jest ignorowany, nadal jest prawidłowy atrybut w celu zachowania możliwości uwzględnienia jakichkolwiek prawidłowych dokumentów Xml 1.0 w kodzie źródłowym.
Elementy XML
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+ ';'
;
Element XML powoduje, że wartość jest typowana jako System.Xml.Linq.XElement. W przeciwieństwie do zwykłego kodu XML elementy XML mogą pominąć nazwę w tagu zamykającym, a bieżący najbardziej zagnieżdżony element zostanie zamknięty. Przykład:
Dim name = <name>Bob</>
Deklaracje atrybutów w elemecie XML powodują, że wartości są wpisywane jako System.Xml.Linq.XAttribute. Wartości atrybutów są znormalizowane zgodnie ze specyfikacją XML. Gdy wartość atrybutu jest Nothing atrybutem nie zostanie utworzona, więc wyrażenie wartości atrybutu nie będzie musiało być sprawdzane pod kątem Nothing. Przykład:
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 %>/>
Elementy i atrybuty XML mogą zawierać zagnieżdżone wyrażenia w następujących miejscach:
Nazwa elementu, w którym przypadku wyrażenie osadzone musi być wartością typu niejawnie konwertowanego na System.Xml.Linq.XName. Przykład:
Dim name = <<%= "name" %>>Bob</>
Nazwa atrybutu elementu, w tym przypadku wyrażenie osadzone musi być wartością typu niejawnie konwertowanego na System.Xml.Linq.XName. Przykład:
Dim name = <name <%= "length" %>="3">Bob</>
Wartość atrybutu elementu, w tym przypadku wyrażenie osadzone może być wartością dowolnego typu. Przykład:
Dim name = <name length=<%= 3 %>>Bob</>
Atrybut elementu, w którym to przypadku osadzone wyrażenie może być wartością dowolnego typu. Przykład:
Dim name = <name <%= new XAttribute("length", 3) %>>Bob</>
Zawartość elementu, w tym przypadku wyrażenie osadzone może być wartością dowolnego typu. Przykład:
Dim name = <name><%= "Bob" %></>
Jeśli typ wyrażenia osadzonego to Object(), tablica zostanie przekazana jako paramarray do konstruktora XElement .
Przestrzenie nazw XML
Elementy XML mogą zawierać deklaracje przestrzeni nazw XML zdefiniowane przez specyfikację przestrzeni nazw XML 1.0.
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
;
Ograniczenia dotyczące definiowania przestrzeni nazw xml i xmlns są wymuszane i będą powodować błędy czasu kompilacji. Deklaracje przestrzeni nazw XML nie mogą mieć wyrażenia osadzonego dla ich wartości; podana wartość musi być niepustym literałem ciągu. Przykład:
' 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</>
Uwaga. Ta specyfikacja zawiera tylko wystarczająco dużo opisu przestrzeni nazw XML, aby opisać zachowanie języka Visual Basic. Więcej informacji na temat przestrzeni nazw XML można znaleźć na stronie http://www.w3.org/TR/REC-xml-names/.
Nazwy elementów i atrybutów XML można zakwalifikować przy użyciu nazw przestrzeni nazw. Przestrzenie nazw są powiązane tak jak w zwykłym formacie XML, z wyjątkiem, że wszystkie importy przestrzeni nazw zadeklarowane na poziomie pliku są uznawane za zadeklarowane w kontekście zawierającym deklarację, która jest sama ujęta przez wszystkie importy przestrzeni nazw zadeklarowane przez środowisko kompilacji. Jeśli nie można odnaleźć nazwy przestrzeni nazw, wystąpi błąd czasu kompilacji. Przykład:
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
Przestrzenie nazw XML zadeklarowane w elemecie nie mają zastosowania do literałów XML wewnątrz osadzonych wyrażeń. Przykład:
' Error: Namespace prefix 'db' is not declared
Dim customer = _
<db:customer xmlns:db="http://example.org/database">
<%= <db:customer>Bob</> %>
</>
Uwaga. Wynika to z faktu, że osadzone wyrażenie może być dowolne, w tym wywołanie funkcji. Jeśli wywołanie funkcji zawiera wyrażenie literału XML, nie jest jasne, czy programiści oczekiwaliby zastosowania lub zignorowania przestrzeni nazw XML.
Instrukcje przetwarzania XML
Instrukcja przetwarzania XML powoduje wpisaną wartość jako System.Xml.Linq.XProcessingInstruction. Instrukcje przetwarzania XML nie mogą zawierać wyrażeń osadzonych, ponieważ są one prawidłową składnią w instrukcji przetwarzania.
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 ">">'
;
Komentarze XML
Komentarz XML powoduje wpisaną wartość jako System.Xml.Linq.XComment. Komentarze XML nie mogą zawierać wyrażeń osadzonych, ponieważ są one prawidłową składnią w komentarzu.
XMLComment
: '<' '!' '-' '-' XMLCommentCharacter* '-' '-' '>'
;
XMLCommentCharacter
: '<Any XMLCharacter except dash (0x002D)>'
| '-' '<Any XMLCharacter except dash (0x002D)>'
;
Sekcje CDATA
Sekcja CDATA powoduje wpisaną wartość jako System.Xml.Linq.XCData. Sekcje CDATA nie mogą zawierać wyrażeń osadzonych, ponieważ są one prawidłową składnią w sekcji CDATA.
XMLCDATASection
: '<' '!' ( 'CDATA' '[' XMLCDATASectionString? ']' )? '>'
;
XMLCDATASectionString
: '<Any XMLString that does not contain the string "]]>">'
;
Wyrażenia dostępu do składowych XML
Wyrażenie dostępu do elementu członkowskiego XML uzyskuje dostęp do elementów członkowskich wartości XML.
XMLMemberAccessExpression
: Expression '.' LineTerminator? '<' XMLQualifiedName '>'
| Expression '.' LineTerminator? '@' LineTerminator? '<' XMLQualifiedName '>'
| Expression '.' LineTerminator? '@' LineTerminator? IdentifierOrKeyword
| Expression '.' '.' '.' LineTerminator? '<' XMLQualifiedName '>'
;
Istnieją trzy typy wyrażeń dostępu składowych XML:
Dostęp do elementu, w którym nazwa XML jest zgodna z pojedynczą kropką. Przykład:
Dim customer = _ <customer> <name>Bob</> </> Dim customerName = customer.<name>.ValueMapowania dostępu do elementu do funkcji:
Function Elements(name As System.Xml.Linq.XName) As _ System.Collections.Generic.IEnumerable(Of _ System.Xml.Linq.XNode)Powyższy przykład jest więc odpowiednikiem:
Dim customerName = customer.Elements("name").ValueDostęp do atrybutu, w którym identyfikator języka Visual Basic jest zgodny z kropką i znakiem przy znaku lub nazwa XML następuje kropką i znakiem. Przykład:
Dim customer = <customer age="30"/> Dim customerAge = customer.@ageMapowania dostępu atrybutów do funkcji:
Function AttributeValue(name As System.Xml.Linq.XName) as StringPowyższy przykład jest więc odpowiednikiem:
Dim customerAge = customer.AttributeValue("age")Uwaga.
AttributeValueMetoda rozszerzenia (a także powiązana właściwośćValuerozszerzenia ) nie jest obecnie zdefiniowana w żadnym zestawie. Jeśli wymagane są elementy członkowskie rozszerzenia, są one automatycznie definiowane w tworzonym zestawie.Dostęp malejący, w którym nazwy XML są zgodne z trzema kropkami. Przykład:
Dim company = _ <company> <customers> <customer>Bob</> <customer>Mary</> <customer>Joe</> </> </> Dim customers = company...<customer>Dostęp malejąco mapuje do funkcji:
Function Descendents(name As System.Xml.Linq.XName) As _ System.Collections.Generic.IEnumerable(Of _ System.Xml.Linq.XElement)Powyższy przykład jest więc odpowiednikiem:
Dim customers = company.Descendants("customer")
Podstawowe wyrażenie wyrażenia dostępu elementu członkowskiego XML musi być wartością i musi być typu:
Jeśli element lub malejąco uzyskuje dostęp,
System.Xml.Linq.XContainertyp pochodny lubSystem.Collections.Generic.IEnumerable(Of T)typ pochodny, gdzieTjestSystem.Xml.Linq.XContainerlub typ pochodny.Jeśli dostęp do atrybutu lub
System.Xml.Linq.XElementtyp pochodny lubSystem.Collections.Generic.IEnumerable(Of T)typ pochodny, gdzieTjestSystem.Xml.Linq.XElementlub typem pochodnym.
Nazwy w wyrażeniach dostępu do składowych XML nie mogą być puste. Mogą być kwalifikowane przestrzeni nazw przy użyciu dowolnych przestrzeni nazw zdefiniowanych przez importy. Przykład:
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
Odstępy nie są dozwolone po kropkach w wyrażeniu dostępu do składowych XML lub między nawiasami kątowymi i nazwą. Przykład:
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 >
Jeśli typy w System.Xml.Linq przestrzeni nazw nie są dostępne, wyrażenie dostępu do składowych XML spowoduje błąd czasu kompilacji.
Await Operator
Operator await jest powiązany z metodami asynchronicznych, które opisano w sekcji Metody asynchroniczne.
AwaitOperatorExpression
: 'Await' Expression
;
Await jest słowem zarezerwowanym, jeśli natychmiast otaczającej metodę lub wyrażenie lambda, w którym się pojawia, ma Async modyfikator, a jeśli Await pojawia się po tym Async modyfikatorze; nie jest zarezerwowany w innym miejscu. Nie jest również zastrzeżony w dyrektywach preprocesora. Operator await jest dozwolony tylko w treści wyrażenia metody lub lambda, gdzie jest słowem zarezerwowanym. W obrębie metody bezpośrednio otaczającej lub lambda wyrażenie await może nie występować wewnątrz treści Catch obiektu lub Finally bloku, ani wewnątrz treści SyncLock instrukcji, ani wewnątrz wyrażenia zapytania.
Operator await przyjmuje pojedyncze wyrażenie, które musi być klasyfikowane jako wartość i którego typ musi być oczekiwanym typem lub Object. Jeśli jego typ jest Object , wszystkie przetwarzanie jest odroczone do czasu wykonywania. Mówi się, że typ C jest oczekiwany, jeśli wszystkie następujące elementy są prawdziwe:
Czawiera dostępne wystąpienie lub metodę rozszerzenia o nazwieGetAwaiter, która nie zawiera argumentów i zwraca jakiś typE;Ezawiera czytelne wystąpienie lub właściwość rozszerzenia o nazwieIsCompleted, która nie przyjmuje żadnych argumentów i ma typ Logiczny;Ezawiera dostępne wystąpienie lub metodę rozszerzenia o nazwieGetResult, która nie przyjmuje żadnych argumentów;Eimplementuje elementSystem.Runtime.CompilerServices.INotifyCompletionlubICriticalNotifyCompletion.
Jeśli GetResult element to Sub, wyrażenie await jest klasyfikowane jako void. W przeciwnym razie wyrażenie await jest klasyfikowane jako wartość, a jego typ jest zwracanym typem GetResult metody.
Oto przykład klasy, która może być oczekiwana:
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
Uwaga. Autorzy bibliotek są zalecane, aby postępować zgodnie ze wzorcem wywoływanym przez nich delegatem kontynuacji w taki sam SynchronizationContext sposób, jak OnCompleted w przypadku wywołania samego siebie. Ponadto delegat wznowienia nie powinien być wykonywany synchronicznie w OnCompleted metodzie, ponieważ może to prowadzić do przepełnienia stosu: zamiast tego delegat powinien zostać w kolejce do kolejnego wykonania.
Gdy przepływ sterowania osiągnie Await operator, zachowanie jest następujące.
Wywoływana
GetAwaiterjest metoda operandu await. Wynik tego wywołania jest określany jako oczekujące.Właściwość obiektu awaiter
IsCompletedjest pobierana. Jeśli wynik ma wartość true, wykonaj następujące działania:- Wywoływana
GetResultjest metoda awaiter. JeśliGetResultbyła funkcją, wartość wyrażenia await jest zwracaną wartością tej funkcji.
- Wywoływana
Jeśli właściwość IsCompleted nie jest prawdziwa, wówczas:
Albo
ICriticalNotifyCompletion.UnsafeOnCompletedjest wywoływany w obiekcie awaiter (jeśli typEosoby oczekującej implementujeICriticalNotifyCompletion) lubINotifyCompletion.OnCompleted(w przeciwnym razie). W obu przypadkach przekazuje delegat wznowienia skojarzony z bieżącym wystąpieniem metody asynchronicznej.Punkt kontrolny bieżącego wystąpienia metody asynchronicznej jest zawieszony, a przepływ sterowania jest wznawiany w bieżącym obiekcie wywołującym (zdefiniowanym w sekcji Metody asynchroniczne).
Jeśli później jest wywoływany delegat wznowienia,
- delegat wznowienia po raz pierwszy przywraca
System.Threading.Thread.CurrentThread.ExecutionContextto, co było w tym czasieOnCompleted, zostało wywołane, - następnie wznowi przepływ sterowania w punkcie kontrolnym wystąpienia metody asynchronicznej (zobacz Sekcję Metody asynchroniczne),
- gdzie wywołuje metodę
GetResultobiektu awaiter, jak w 2.1 powyżej.
- delegat wznowienia po raz pierwszy przywraca
Jeśli operand await ma typ Object, to zachowanie jest odroczone do czasu wykonania:
- Krok 1 jest osiągany przez wywołanie metody GetAwaiter() bez argumentów; w związku z tym może wiązać się ze środowiskiem uruchomieniowym z metodami wystąpienia, które przyjmują parametry opcjonalne.
- Krok 2 jest osiągany przez pobranie właściwości IsCompleted() bez argumentów i próbę konwersji wewnętrznej na wartość logiczną.
- Krok 3.a jest osiągany przez próbę
TryCast(awaiter, ICriticalNotifyCompletion), a jeśli to się nie powiedzie, toDirectCast(awaiter, INotifyCompletion).
Delegat wznowienia przekazany w wersji 3.a może być wywoływany tylko raz. Jeśli jest wywoływany więcej niż raz, zachowanie jest niezdefiniowane.
Visual Basic language spec