12 Uitdrukkingen
12.1 Algemeen
Een expressie is een reeks operatoren en operanden. Met deze component worden de syntaxis, volgorde van evaluatie van operanden en operatoren en de betekenis van expressies gedefinieerd.
12.2 Uitdrukkingclassificaties
12.2.1 Algemeen
Het resultaat van een expressie wordt geclassificeerd als een van de volgende:
- Een waarde. Elke waarde heeft een gekoppeld type.
- Een variabele. Tenzij anders opgegeven, wordt een variabele expliciet getypt en heeft een gekoppeld type, namelijk het gedeclareerde type van de variabele. Een impliciet getypte variabele heeft geen gekoppeld type.
- Een letterlijke waarde van null. Een expressie met deze classificatie kan impliciet worden geconverteerd naar een verwijzingstype of null-waardetype.
- Een anonieme functie. Een expressie met deze classificatie kan impliciet worden geconverteerd naar een compatibel type delegeaat of expressieboomtype.
- Een tuple. Elke tuple heeft een vast aantal elementen, elk met een expressie en een optionele tuple-elementnaam.
- Toegang tot een eigenschap. Elke eigenschapstoegang heeft een gekoppeld type, namelijk het type eigenschap. Bovendien kan een eigenschapstoegang een bijbehorende instantie-expressie hebben. Wanneer een accessor van een exemplaareigenschapstoegang wordt aangeroepen, wordt het resultaat van het evalueren van de exemplaarexpressie het exemplaar dat wordt vertegenwoordigd door
this
(§12.8.14). - Toegang tot een indexeerfunctie. Elke indexeerfunctietoegang heeft een gekoppeld type, namelijk het elementtype van de indexeerfunctie. Bovendien heeft een indexeerfunctietoegang een bijbehorende exemplaarexpressie en een bijbehorende argumentenlijst. Wanneer een toegangsfunctie van een indexeerfunctie wordt aangeroepen, wordt het resultaat van het evalueren van de exemplaarexpressie het exemplaar dat wordt vertegenwoordigd door
this
(§12.8.14) en wordt het resultaat van het evalueren van de argumentenlijst de parameterlijst van de aanroep. - Niets. Dit gebeurt wanneer de expressie een aanroep van een methode is met een retourtype van
void
. Een expressie die als niets wordt geclassificeerd, is alleen geldig in de context van een statement_expression (§13.7) of als de hoofdtekst van een lambda_expression (§12.19).
Voor expressies die optreden als subexpressies van grotere expressies, met de genoteerde beperkingen, kan het resultaat ook worden geclassificeerd als een van de volgende:
- Een naamruimte. Een expressie met deze classificatie kan alleen worden weergegeven als de linkerkant van een member_access (§12.8.7). In een andere context veroorzaakt een expressie die is geclassificeerd als een naamruimte een compileertijdfout.
- Een type. Een expressie met deze classificatie kan alleen worden weergegeven als de linkerkant van een member_access (§12.8.7). In een andere context veroorzaakt een expressie die is geclassificeerd als een type een compileertijdfout.
- Een methodegroep, een set overbelaste methoden die het gevolg zijn van een ledenopzoekactie (§12.5). Een methodegroep kan een bijbehorende exemplaarexpressie en een lijst met bijbehorende typeargumenten hebben. Wanneer een exemplaarmethode wordt aangeroepen, wordt het resultaat van het evalueren van de exemplaarexpressie het exemplaar dat wordt vertegenwoordigd door
this
(§12.8.14). Een methodegroep is toegestaan in een invocation_expression (§12.8.10) of een delegate_creation_expression (§12.8.17.6) en kan impliciet worden geconverteerd naar een compatibel gemachtigdetype (§10.8). In een andere context veroorzaakt een expressie die is geclassificeerd als een methodegroep een compilatiefout. - Toegang tot een evenement Elke gebeurtenistoegang heeft een gekoppeld type, namelijk het type gebeurtenis. Bovendien kan een gebeurtenistoegang een bijbehorende exemplaarexpressie hebben. Een gebeurtenistoegang kan worden weergegeven als de linkeroperand van de operatoren
+=
en-=
(§12.21.5). In een andere context veroorzaakt een expressie die is geclassificeerd als gebeurtenistoegang een compilatiefout. Wanneer een accessor van een exemplaargebeurtenistoegang wordt aangeroepen, wordt het resultaat van het evalueren van de exemplaarexpressie het exemplaar dat wordt vertegenwoordigd doorthis
(§12.8.14). - Een throw-expressie, die in verschillende contexten kan worden gebruikt om een uitzondering in een expressie te genereren. Een throw-expressie kan worden geconverteerd door een impliciete conversie naar elk type.
Een toegang tot eigenschappen of indexertoegang wordt altijd opnieuw geclassificeerd als een waarde door de get-accessor of set-accessor aan te roepen. De specifieke toegangsfunctie wordt bepaald door de context van de toegang tot de eigenschap of indexeerfunctie: als de toegang het doel van een toewijzing is, wordt de ingestelde toegangsfunctie aangeroepen om een nieuwe waarde toe te wijzen (§12.21.2). Anders wordt de get accessor aangeroepen om de huidige waarde te verkrijgen (§12.2.2).
Een exemplaartoegangsfunctie is een eigenschapstoegang voor een exemplaar, een gebeurtenistoegang op een exemplaar of een indexeerfunctietoegang.
12.2.2 Waarden van expressies
De meeste constructies die betrekking hebben op een expressie, vereisen uiteindelijk dat de expressie een waarde aangeeft. Als de werkelijke expressie in dergelijke gevallen een naamruimte, een type, een methodegroep of niets aangeeft, treedt er een compilatietijdfout op. Als de expressie echter een eigenschapstoegang, een indexeerfunctietoegang of een variabele aangeeft, wordt de waarde van de eigenschap, indexeerfunctie of variabele impliciet vervangen:
- De waarde van een variabele is gewoon de waarde die momenteel is opgeslagen in de opslaglocatie die wordt geïdentificeerd door de variabele. Een variabele wordt beschouwd als definitief toegewezen (§9.4) voordat de waarde ervan kan worden verkregen, of anders treedt er een compilatiefout op.
- De waarde van een eigenschapstoegangsexpressie wordt verkregen door de get accessor van de eigenschap aan te roepen. Als de eigenschap geen get-accessor heeft, ontstaat er een compilatiefout. Anders wordt een functielid aanroep (§12.6.6) uitgevoerd en wordt het resultaat van de aanroep de waarde van de eigenschapstoegangsexpressie.
- De waarde van een indexeerfunctie-toegangsexpressie wordt verkregen door de get-accessor van de indexeerfunctie aan te roepen. Als de indexer geen gettoegangsmethode heeft, ontstaat er een fout tijdens de compilatie. Anders wordt een functielid aanroep (§12.6.6) uitgevoerd met de argumentenlijst die is gekoppeld aan de toegangsexpressie van de indexeerfunctie en wordt het resultaat van de aanroep de waarde van de toegangsexpressie van de indexeerfunctie.
- De waarde van een tuple-expressie wordt verkregen door een impliciete tupleconversie (§10.2.13) toe te passen op het type tuple-expressie. Het is een fout om de waarde van een tuple-expressie te achterhalen die geen type heeft.
12.3 Statische en dynamische binding
12.3.1 Algemeen
Binding is het proces van het bepalen waarnaar een bewerking verwijst, op basis van het type of de waarde van expressies (argumenten, operanden, ontvangers). De binding van een methode-aanroep wordt bijvoorbeeld bepaald op basis van het type ontvanger en argumenten. De binding van een operator wordt bepaald op basis van het type operanden.
In C# wordt de binding van een bewerking meestal bepaald tijdens het compileren, op basis van het compileertijdtype van de subexpressies. Als een expressie een fout bevat, wordt de fout ook gedetecteerd en gerapporteerd tijdens het compileren. Deze methode wordt statische bindinggenoemd.
Als een expressie echter een dynamische expressie is (d.w.z. heeft het type dynamic
) geeft dit aan dat elke binding waaraan deze deelneemt, moet zijn gebaseerd op het runtimetype in plaats van het type dat het tijdens het compileren heeft. De binding van een dergelijke bewerking wordt daarom uitgesteld tot de tijd waarop de bewerking moet worden uitgevoerd tijdens het uitvoeren van het programma. Dit wordt dynamische bindinggenoemd.
Wanneer een bewerking dynamisch is gebonden, wordt tijdens het compileren weinig of geen controle uitgevoerd. Als de runtimebinding mislukt, worden fouten gerapporteerd als uitzonderingen tijdens runtime.
De volgende bewerkingen in C# zijn onderhevig aan binding:
- Toegang tot leden:
e.M
- Aanroep van methode:
e.M(e₁,...,eᵥ)
- Aanroep delegeren:
e(e₁,...,eᵥ)
- Toegang tot elementen:
e[e₁,...,eᵥ]
- Object maken: nieuwe
C(e₁,...,eᵥ)
- Overbelaste unaire operatoren:
+
,-
,!
(alleen logische negatie),~
,++
,--
,true
,false
- Overbelaste binaire operatoren:
+
,-
,*
,/
,%
,&
,&&
,|
,||
,??
,^
,<<
,>>
,==
,!=
,>
,<
,>=
,<=
- Toewijzingsoperatoren:
=
,= ref
,+=
,-=
,*=
,/=
,%=
,&=
,|=
,^=
,<<=
,>>=
- Impliciete en expliciete conversies
Wanneer er geen dynamische expressies betrokken zijn, wordt in C# standaard ingesteld op statische binding, wat betekent dat de compileertijdtypen van subexpressies worden gebruikt in het selectieproces. Wanneer een van de subexpressies in de bovenstaande bewerkingen echter een dynamische expressie is, is de bewerking in plaats daarvan dynamisch gebonden.
Het is een compilatietijdfout als een methode-aanroep dynamisch is gebonden en een van de parameters, inclusief de ontvanger, invoerparameters zijn.
12.3.2 Bindingstijd
Statische binding vindt plaats tijdens het compileren, terwijl dynamische binding plaatsvindt tijdens runtime. In de volgende subclauses verwijst de term binding-time verwijst naar compileertijd of runtime, afhankelijk van wanneer de binding plaatsvindt.
Voorbeeld van: Hieronder ziet u de noties van statische en dynamische binding en bindingstijd:
object o = 5; dynamic d = 5; Console.WriteLine(5); // static binding to Console.WriteLine(int) Console.WriteLine(o); // static binding to Console.WriteLine(object) Console.WriteLine(d); // dynamic binding to Console.WriteLine(int)
De eerste twee aanroepen zijn statisch gebonden: de overbelasting van
Console.WriteLine
wordt gekozen op basis van het type compileertijd van hun argument. De bindingstijd wordt dus compilatietijd.De derde aanroep is dynamisch gebonden: de overbelasting van
Console.WriteLine
wordt gekozen op basis van het runtimetype van het argument. Dit gebeurt omdat het argument een dynamische expressie is: het type compileertijd is dynamisch. De bindingstijd voor de derde aanroep is dus uitvoeringstijd.einde voorbeeld
12.3.3 Dynamische binding
Deze subclause is informatief.
Met dynamische binding kunnen C#-programma's communiceren met dynamische objecten, d.w.: objecten die niet voldoen aan de normale regels van het C#-typesysteem. Dynamische objecten kunnen objecten zijn uit andere programmeertalen met verschillende typen systemen, of ze kunnen objecten zijn die programmatisch zijn ingesteld om hun eigen bindingsemantiek voor verschillende bewerkingen te implementeren.
Het mechanisme waarmee een dynamisch object zijn eigen semantiek implementeert, is door de implementatie gedefinieerd. Een bepaalde interface, opnieuw door implementatie gedefinieerd, wordt geïmplementeerd door dynamische objecten om de C#-runtime te signaleren dat ze speciale semantiek hebben. Wanneer bewerkingen op een dynamisch object dus dynamisch zijn gebonden, nemen ze hun eigen bindingsemantiek over, in plaats van C# zoals opgegeven in deze specificatie.
Hoewel het doel van dynamische binding is om interoperation met dynamische objecten mogelijk te maken, staat C# dynamische binding toe op alle objecten, ongeacht of ze dynamisch zijn of niet. Dit maakt een soepelere integratie van dynamische objecten mogelijk, omdat de resultaten van bewerkingen op deze objecten mogelijk niet zelf dynamische objecten zijn, maar nog steeds van een type onbekend zijn voor de programmeur tijdens het compileren. Dynamische binding kan ook helpen bij het elimineren van foutgevoelige op reflectie gebaseerde code, zelfs wanneer de betrokken objecten geen dynamische objecten zijn.
12.3.4 Typen subexpressies
Wanneer een bewerking statisch is gebonden, wordt het type subexpressie (bijvoorbeeld een ontvanger en argument, een index of een operand) altijd beschouwd als het type compileertijd van die expressie.
Wanneer een bewerking dynamisch is gebonden, wordt het type subexpressie op verschillende manieren bepaald, afhankelijk van het type compileertijd van de subexpressie:
- Een subexpressie van het dynamische type compileertijd wordt beschouwd als het type van de werkelijke waarde waarnaar de expressie tijdens runtime evalueert
- Een subexpressie waarvan het compileertijdtype een typeparameter is, wordt beschouwd als het type waaraan de typeparameter bij uitvoering is gebonden.
- Anders wordt de subexpressie geacht zijn compileertijd-type te hebben.
12.4 Operatoren
12.4.1 Algemeen
Expressies worden samengesteld op basis van operanden en operators. De operatoren van een expressie geven aan welke bewerkingen moeten worden toegepast op de operanden.
voorbeeld: voorbeelden van operators zijn onder andere
+
,-
,*
,/
ennew
. Voorbeelden van operanden zijn letterlijke waarden, velden, lokale variabelen en expressies. einde voorbeeld
Er zijn drie soorten operators:
- Unaire operatoren. De unaire operatoren nemen één operand en gebruiken een voorvoegsel notatie (zoals
–x
) of achtervoegsel notatie (zoalsx++
). - Binaire operatoren. De binaire operatoren nemen twee operanden en gebruiken allemaal infix-notatie (zoals
x + y
). - Ternaire operator. Slechts één ternaire operator,
?:
, bestaat; het duurt drie operanden en maakt gebruik van infix notatie (c ? x : y
).
De beoordelingsvolgorde van operators in een expressie wordt bepaald door de prioriteit en associatieve van de operatoren (§12.4.2).
Operanden in een expressie worden van links naar rechts geëvalueerd.
voorbeeld: in
F(i) + G(i++) * H(i)
wordt de methodeF
aangeroepen met behulp van de oude waarde vani
, wordt de methodeG
aangeroepen met de oude waarde vani
en tot slot wordt de methodeH
aangeroepen met de nieuwe waarde i. Dit is gescheiden van en niet gerelateerd aan de prioriteit van de operator. einde voorbeeld
Bepaalde operators kunnen worden overbelast. Bij overbelasting van operatoren (§12.4.3) kunnen door de gebruiker gedefinieerde operator-implementaties worden opgegeven voor bewerkingen waarbij een of beide operanden van een door de gebruiker gedefinieerde klasse of struct-type zijn.
12.4.2 Operatorprioriteit en associativiteit
Wanneer een expressie meerdere operators bevat, bepaalt de prioriteit van de operators de volgorde waarin de afzonderlijke operators worden geëvalueerd.
Opmerking: de expressie
x + y * z
wordt bijvoorbeeld geëvalueerd alsx + (y * z)
omdat de operator*
een hogere prioriteit heeft dan de operator binaire+
. eindnotitie
De prioriteit van een operator wordt bepaald door de definitie van de bijbehorende grammaticaproductie.
Opmerking: een additive_expression bestaat bijvoorbeeld uit een reeks multiplicative_expressiongescheiden door
+
- of-
operatoren, waardoor de operatoren+
en-
lagere prioriteit hebben dan de operatoren*
,/
en%
. eindnotitie
Opmerking: de volgende tabel bevat een overzicht van alle operatoren in volgorde van prioriteit van hoog naar laag:
subclausule categorie Operators §12.8 Primair x.y
x?.y
f(x)
a[x]
a?[x]
x++
x--
x!
new
typeof
default
checked
unchecked
delegate
stackalloc
§12.9 Unaire +
-
!x
~
++x
--x
(T)x
await x
§12.10 Multiplicatieve *
/
%
§12.10 Toevoeging +
-
§12.11 Verschuiving <<
>>
§12.12 Relationele en type-testen <
>
<=
>=
is
as
§12.12 Gelijkheid ==
!=
§12.13 Logische AND &
§12.13 Logische XOR ^
§12.13 Logische OR \|
§12.14 Voorwaardelijk EN &&
§12.14 Voorwaardelijk OF \|\|
§12.15 en §12.16 Null-coalescing en gooi-expressie ??
throw x
§12.18 Voorwaardelijk ?:
§12.21 en §12.19 Toewijzing en lambda-expressie =
= ref
*=
/=
%=
+=
-=
<<=
>>=
&=
^=
\|=
=>
eindnotitie
Wanneer een operand plaatsvindt tussen twee operators met dezelfde prioriteit, bepaalt de associativiteit van de operators de volgorde waarin de bewerkingen worden uitgevoerd:
- Met uitzondering van de toewijzingsoperatoren en de null-coalescentie-operator zijn alle binaire operatoren links-associatief, wat betekent dat bewerkingen van links naar rechts worden uitgevoerd.
voorbeeld van:
x + y + z
wordt geëvalueerd als(x + y) + z
. einde voorbeeld - De toewijzingsoperatoren, de samensnookoperator null en de voorwaardelijke operator (
?:
) zijn rechts-associatieve, wat betekent dat bewerkingen van rechts naar links worden uitgevoerd.voorbeeld van:
x = y = z
wordt geëvalueerd alsx = (y = z)
. einde voorbeeld
Prioriteit en associativiteit kunnen worden beheerd met haakjes.
voorbeeld:
x + y * z
vermenigvuldigt eersty
metz
en voegt vervolgens het resultaat toe aanx
, maar(x + y) * z
voegt eerstx
eny
toe en vermenigvuldigt vervolgens het resultaat metz
. einde voorbeeld
12.4.3 Operator-overbelasting
Alle unaire en binaire operators hebben vooraf gedefinieerde implementaties. Daarnaast kunnen door de gebruiker gedefinieerde implementaties worden ingevoerd door operatordeclaraties (§15.10) in klassen en structs op te geven. Door de gebruiker gedefinieerde operator-implementaties hebben altijd voorrang op vooraf gedefinieerde operator-implementaties: Alleen wanneer er geen toepasselijke door de gebruiker gedefinieerde operator-implementaties bestaan, worden de vooraf gedefinieerde implementaties van operatoren beschouwd, zoals beschreven in §12.4.4 en §12.4.5.
De overbelaste unaire operators zijn:
+ - !
(alleen logische ontkenning) ~ ++ -- true false
Opmerking: Hoewel
true
enfalse
niet expliciet worden gebruikt in expressies (en daarom niet zijn opgenomen in de prioriteitstabel in §12.4.2), worden ze beschouwd als operatoren omdat ze in verschillende expressiecontexten worden aangeroepen : Booleaanse expressies (§12.24) en expressies met betrekking tot de voorwaardelijk (§12.18) en voorwaardelijke logische operatoren (§12.14). eindnotitie
Opmerking: de operator null-forgiving (postfix
!
, §12.8.9) is geen overbelastingsbare operator. eindnotitie
De overbelastingsbare binaire operatoren zijn:
+ - * / % & | ^ << >> == != > < <= >=
Alleen de hierboven genoemde operators kunnen overbelast worden. Met name is het niet mogelijk om toegang tot leden, methodeaanroepen of de operators =
, &&
, ||
, ??
, ?:
, =>
, checked
, unchecked
, new
, typeof
, default
, as
en is
te overbelasten.
Wanneer een binaire operator overbelast is, wordt de bijbehorende samengestelde toewijzingsoperator, indien aanwezig, ook impliciet overbelast.
Voorbeeld: een overbelasting van operatoren
*
is ook een overbelasting van operatoren*=
. Dit wordt verder beschreven in §12.21. einde voorbeeld
De toewijzingsoperator zelf (=)
kan niet worden overbelast. Een toewijzing voert altijd een eenvoudige opslag van een waarde uit in een variabele (§12.21.2).
Cast-bewerkingen, zoals (T)x
, worden overbelast door door de gebruiker gedefinieerde conversies (§10,5).
Opmerking: door de gebruiker gedefinieerde conversies hebben geen invloed op het gedrag van de operators voor
is
ofas
. eindnotitie
Toegang tot elementen, zoals a[x]
, wordt niet beschouwd als een overloadbare operator. In plaats daarvan wordt door de gebruiker gedefinieerde indexering ondersteund via indexeerfuncties (§15,9).
In expressies wordt naar operators verwezen met behulp van operator-notatie en in declaraties wordt naar operators verwezen met behulp van functionele notatie. In de volgende tabel ziet u de relatie tussen operator- en functionele notaties voor unaire en binaire operatoren. In het eerste item geeft «op» een overbelastingsbare unary-voorvoegseloperator aan. In de tweede vermelding geeft «op» de unary postfix ++
en --
operators aan. In de derde vermelding geeft «op» elke overbelastingsbare binaire operator aan.
Opmerking: Voor een voorbeeld van het overbelasten van de operatoren
++
en--
, zie §15.10.2. eindnotitie
Operator-notatie | functionele notatie |
---|---|
«op» x |
operator «op»(x) |
x «op» |
operator «op»(x) |
x «op» y |
operator «op»(x, y) |
Door de gebruiker gedefinieerde operatordeclaraties vereisen altijd ten minste één van de parameters van het klasse- of structtype dat de operatordeclaratie bevat.
Opmerking: het is dus niet mogelijk dat een door de gebruiker gedefinieerde operator dezelfde handtekening heeft als een vooraf gedefinieerde operator. eindnotitie
Door de gebruiker gedefinieerde operatordeclaraties kunnen de syntaxis, prioriteit of associativiteit van een operator niet wijzigen.
voorbeeld: de operator
/
is altijd een binaire operator, heeft altijd het prioriteitsniveau dat is opgegeven in §12.4.2en is altijd links-associatief. einde voorbeeld
Opmerking: hoewel het mogelijk is voor een door de gebruiker gedefinieerde operator om berekeningen uit te voeren, worden implementaties die andere resultaten opleveren dan de implementaties die intuïtief worden verwacht sterk afgeraden. Een implementatie van operator
==
moet bijvoorbeeld de twee operanden voor gelijkheid vergelijken en een geschiktbool
resultaat retourneren. eindnotitie
De beschrijvingen van individuele exploitanten in §12.9 tot en met §12.21 de vooraf gedefinieerde implementaties van de operators en eventuele aanvullende regels opgeven die van toepassing zijn op elke operator. De beschrijvingen maken gebruik van de termen unaire operator overbelastingsresolutie, binaire operator overbelastingsresolutie, numerieke promotieen definities van opgeheven operatoren die in de volgende subclauses worden gevonden.
12.4.4 Oplossing voor unaire operatoroverbelasting
Een werking van het formulier «op» x
of x «op»
, waarbij «op» een overbelaste unaire operator is en x
een expressie van het type X
is, wordt als volgt verwerkt:
- De set door de gebruiker gedefinieerde kandidaatoperators die door
X
worden verstrekt voor de bewerkingoperator «op»(x)
wordt bepaald aan de hand van de regels van §12.4.6. - Als de set door de gebruiker gedefinieerde kandidaatoperators niet leeg is, wordt dit de set kandidaatoperators voor de bewerking. Anders vormen de vooraf gedefinieerde binaire
operator «op»
-implementaties, inclusief hun aangepaste vormen, de verzameling kandidaatoperatoren voor de bewerking. De vooraf gedefinieerde implementaties van een bepaalde operator worden opgegeven in de beschrijving van de operator. De vooraf gedefinieerde operators die worden geleverd door een enum- of delegatetype, worden alleen opgenomen in deze set wanneer het bindingstijdtype (of het onderliggende type als het een nullable type is) van een operand het enum- of delegatetype is. - De regels voor overbelastingsoplossing van §12.6.4 worden toegepast op de set kandidaatoperatoren om de beste operator te selecteren met betrekking tot de lijst met argumenten
(x)
, en deze operator wordt het resultaat van het overbelastingsoplossingsproces. Als overbelastingsresolutie niet één beste operator selecteert, treedt er een bindingstijdfout op.
12.4.5 Binaire operator overbelastingsresolutie
Een werking van het formulier x «op» y
, waarbij «op» een overbelastingbare binaire operator is, x
een expressie van het type X
is en y
een expressie van het type Y
is, wordt als volgt verwerkt:
- De set door de gebruiker gedefinieerde kandidaatoperators die worden geleverd door
X
enY
voor de bewerkingoperator «op»(x, y)
wordt bepaald. De set bestaat uit de vereniging van de doorX
enY
verstrekte kandidaat-operators, die elk worden bepaald volgens de regels van §12.4.6. Voor de gecombineerde set worden kandidaten als volgt samengevoegd:- Als
X
enY
identiteit converteerbaar zijn of alsX
enY
zijn afgeleid van een gemeenschappelijk basistype, worden gedeelde kandidaatoperators slechts eenmaal in de gecombineerde set uitgevoerd. - Als er een identiteitsconversie is tussen
X
enY
, heeft een operator«op»Y
vanY
hetzelfde retourtype als een«op»X
geleverd doorX
en de operandtypen van«op»Y
een identiteitsconversie hebben naar de bijbehorende operandtypen van«op»X
dan vindt er slechts«op»X
plaats in de set.
- Als
- Als de set door de gebruiker gedefinieerde kandidaatoperators niet leeg is, wordt dit de set kandidaatoperators voor de bewerking. Anders vormen de vooraf gedefinieerde binaire
operator «op»
-implementaties, inclusief hun aangepaste vormen, de verzameling kandidaatoperatoren voor de bewerking. De vooraf gedefinieerde implementaties van een bepaalde operator worden opgegeven in de beschrijving van de operator. Voor voorgedefinieerde enum- en delegate-operatoren zijn de enige operators die worden beschouwd die door een enum- of delegatetype worden geleverd dat het bindingstijdtype van een van de operanden is. - De regels voor overbelastingsoplossing van §12.6.4 worden toegepast op de set kandidaatoperatoren om de beste operator te selecteren met betrekking tot de lijst met argumenten
(x, y)
, en deze operator wordt het resultaat van het overbelastingsoplossingsproces. Als overbelastingsresolutie niet één beste operator selecteert, treedt er een bindingstijdfout op.
12.4.6 Door de gebruiker gedefinieerde kandidaatoperators
Gezien een type T
en een bewerking operator «op»(A)
, waarbij «op» een overbelastingsbare operator is en A
een lijst met argumenten is, wordt de set door de gebruiker gedefinieerde kandidaatoperators die door T
worden verstrekt voor operator-«op»(A)
als volgt bepaald:
- Het type
T₀
bepalen. AlsT
een nullable waarde type is, dan isT₀
het onderliggende type; anders isT₀
gelijk aanT
. - Voor alle
operator «op»
declaraties inT₀
en alle opgeheven vormen van dergelijke exploitanten, indien ten minste één exploitant van toepassing is (§12.6.4.2) met betrekking tot de lijst met argumentenA
, bestaat de set kandidaat-exploitanten uit alle toepasselijke exploitanten inT₀
. - Als
T₀
andersobject
is, is de set van kandidaat-operators leeg. - Anders is de set kandidaat-operators die worden geleverd door
T₀
de set kandidaat-operators die worden geleverd door de directe basisklasse vanT₀
, of de effectieve basisklasse vanT₀
alsT₀
een typeparameter is.
12.4.7 Numerieke promoties
12.4.7.1 Algemeen
Deze subclause is informatief.
§12.4.7 en de subclauses ervan zijn een samenvatting van het gecombineerde effect van:
- de regels voor impliciete numerieke conversies (§10.2.3);
- de regels voor een betere omzetting (§12.6.4.7); en
- de beschikbare rekenkundige (§12.10), relationele (§12.12) en integraal logische operatoren (§12.13.2).
Numerieke promotie bestaat uit het automatisch uitvoeren van bepaalde impliciete conversies van de operanden van de vooraf gedefinieerde unaire en binaire numerieke operatoren. Numerieke promotie is geen afzonderlijk mechanisme, maar een effect van het toepassen van overbelastingsresolutie op de vooraf gedefinieerde operators. Numerieke promotie heeft specifiek geen invloed op de evaluatie van door de gebruiker gedefinieerde operators, hoewel door de gebruiker gedefinieerde operators kunnen worden geïmplementeerd om vergelijkbare effecten te vertonen.
Als voorbeeld van numerieke promotie kunt u rekening houden met de vooraf gedefinieerde implementaties van de binaire *
-operator:
int operator *(int x, int y);
uint operator *(uint x, uint y);
long operator *(long x, long y);
ulong operator *(ulong x, ulong y);
float operator *(float x, float y);
double operator *(double x, double y);
decimal operator *(decimal x, decimal y);
Wanneer overbelastingsoplossingsregels (§12.6.4) worden toegepast op deze set operators, is het effect het selecteren van de eerste operatoren waarvoor impliciete conversies bestaan uit de operandtypen.
voorbeeld: voor de bewerking
b * s
, waarbijb
eenbyte
is ens
eenshort
is, selecteert overbelastingsresolutieoperator *(int, int)
als de beste operator. Het effect is dus datb
ens
worden geconverteerd naarint
, en het type van het resultaat isint
. Voor de bewerkingi * d
, waarbiji
eenint
is end
eendouble
is, selecteertoverload
resolutieoperator *(double, double)
als de beste operator. einde voorbeeld
einde van informatieve tekst.
12.4.7.2 Unaire numerieke promoties
Deze subclause is informatief.
Unaire numerieke promotie vindt plaats voor de operanden van de vooraf gedefinieerde +
, -
en ~
unaire operatoren. Unaire numerieke promotie bestaat eenvoudig uit het converteren van operanden van het type sbyte
, byte
, short
, ushort
of char
om int
te typen . Daarnaast converteert de unaire numerieke promotie operanden van het type uint
naar type long
voor de unaire – operator.
einde van informatieve tekst.
12.4.7.3 Binaire numerieke promoties
Deze subclause is informatief.
Binaire numerieke promotie vindt plaats voor de operanden van de vooraf gedefinieerde +
, -
, *
, /
, %
, &
, |
, ^
, ==
, !=
, >
, <
, >=
en <=
binaire operatoren. Binaire numerieke promotie converteert impliciet beide operanden naar een gemeenschappelijk type dat, in het geval van de niet-relationele operatoren, ook het resultaattype van de bewerking wordt. Binaire numerieke promotie bestaat uit het toepassen van de volgende regels, in de volgorde waarin ze hier worden weergegeven:
- Als een operand van het type
decimal
is, wordt de andere operand geconverteerd naar het typedecimal
of treedt er een bindingstijdfout op als de andere operand van het typefloat
ofdouble
is. - Als een van beide operanden van het type
double
is, wordt de andere operand geconverteerd naar het typedouble
. - Als een van beide operanden van het type
float
is, wordt de andere operand geconverteerd naar het typefloat
. - Als een operand van het type
ulong
is, wordt de andere operand geconverteerd naar het typeulong
of treedt er een bindingstijdfout op als de andere operand vantype sbyte
,short
,int
oflong
is. - Als een van beide operanden van het type
long
is, wordt de andere operand geconverteerd naar het typelong
. - Als een operand van het type
uint
is en de andere operand van het typesbyte
,short
ofint
, worden beide operanden geconverteerd naar het typelong
. - Als een van beide operanden van het type
uint
is, wordt de andere operand geconverteerd naar het typeuint
. - Anders worden beide operanden geconverteerd naar het type
int
.
Opmerking: De eerste regel verbiedt bewerkingen die het
decimal
-type combineren met de typendouble
enfloat
. De regel volgt uit het feit dat er geen impliciete conversies zijn tussen hetdecimal
type en dedouble
enfloat
typen. eindnotitie
Opmerking: houd er ook rekening mee dat het niet mogelijk is dat een operand van het type
ulong
is wanneer de andere operand van een ondertekend integraal type is. De reden hiervoor is dat er geen integraal type bestaat dat het volledige bereik vanulong
en de ondertekende integrale typen kan vertegenwoordigen. eindnotitie
In beide bovenstaande gevallen kan een cast-expressie worden gebruikt om één operand expliciet te converteren naar een type dat compatibel is met de andere operand.
Voorbeeld: In de volgende code
decimal AddPercent(decimal x, double percent) => x * (1.0 + percent / 100.0);
er treedt een bindingstijdfout op omdat een
decimal
niet kan worden vermenigvuldigd met eendouble
. De fout wordt opgelost door de tweede operand expliciet te converteren naardecimal
, als volgt:decimal AddPercent(decimal x, double percent) => x * (decimal)(1.0 + percent / 100.0);
einde voorbeeld
einde van informatieve tekst.
12.4.8 Gelifte operatoren
Verhoogde operators staan vooraf gedefinieerde en door de gebruiker gedefinieerde operators toe die werken op niet-nullable waardetypen om ook met nullable vormen van deze typen te worden gebruikt. Lifted operators zijn samengesteld uit vooraf gedefinieerde en door de gebruiker gedefinieerde operators die voldoen aan bepaalde vereisten, zoals hieronder beschreven.
- Voor de unaire operatoren
+
,++
,-
,--
,!
(logische negatie) en~
, bestaat er een opgeheven vorm van een operator als de operand- en resultaattypen beide niet-nullbare waardetypen zijn. Het opgeheven formulier wordt samengesteld door één?
modificator toe te voegen aan operand- en resultaattypen. De gelifte operator produceert eennull
-waarde als de operandnull
is. Anders haalt de lifted operator de operand uit, past de onderliggende operator toe en verpakt het resultaat. - Voor de binaire operatoren
+
,-
,*
,/
,%
,&
,|
,^
,<<
en>>
, bestaat er een opgeheven vorm van een operator als de operand- en resultaattypen allemaal niet-nullbare waardetypen zijn. Het opgeheven formulier wordt samengesteld door één?
modifier toe te voegen aan elke operand en het resultaattype. De opgetilde operator produceert eennull
waarde als een of beide operandennull
zijn (een uitzondering: de operatoren&
en|
van het typebool?
, zoals beschreven in §12.13.5). Anders haalt de opgetilde operator de operanden uit, past de onderliggende operator toe en verpakt het resultaat. - Voor de gelijkheidsoperatoren
==
en!=
bestaat er een getilde vorm van een operator als de operandtypen zowel niet-nulwaarde-typen zijn, én het resultaattype isbool
. Het opgeheven formulier wordt samengesteld door één?
modifier toe te voegen aan elk operandtype. De gelifte operator beschouwt tweenull
-waarden als gelijk en eennull
-waarde als ongelijk aan elke waarde die geennull
is. Als beide operanden niet-null
zijn, haalt de lifted operator de operanden uit en past de onderliggende operator toe om hetbool
resultaat te produceren. - Voor de relationele operatoren
<
,>
,<=
en>=
, bestaat er een opgeheven vorm van een operator als de operandtypen zowel niet-nullable waardetypen zijn als het resultaattype isbool
. Het opgeheven formulier wordt samengesteld door één?
modifier toe te voegen aan elk operandtype. De gelifte operator levert de waardefalse
als een of beide operandennull
zijn. Anders pakt de lifted operator de operanden uit en past de onderliggende bewerking toe om het resultaatbool
te verkrijgen.
12.5 Ledenzoekactie
12.5.1 Algemeen
Een ledenopzoekactie is het proces waarbij de betekenis van een naam in de context van een type wordt vastgesteld. Een lidzoekactie kan optreden als onderdeel van het evalueren van een simple_name (§12.8.4) of een member_access (§12.8.7) in een expressie. Als de simple_name of member_access optreedt als de primary_expression van een invocation_expression (§12.8.10.2), wordt het lid aangeroepen.
Als een lid een methode of gebeurtenis is, of als het een constante, veld of eigenschap is van een gemachtigdetype (§20) of het type dynamic
(§8.2.4), wordt het lid aanroepbaar geacht.
Bij het opzoeken van leden wordt niet alleen rekening gebracht met de naam van een lid, maar ook met het aantal parameters van het type dat het lid heeft en of het lid toegankelijk is. Voor het opzoeken van leden hebben algemene methoden en geneste algemene typen het aantal typeparameters aangegeven in hun respectieve declaraties en alle andere leden hebben nultypeparameters.
Het opzoeken van een lid met de naam N
met K
typeargumenten in een type T
wordt als volgt verwerkt:
- Eerst wordt een set toegankelijke leden met de naam
N
bepaald:- Als
T
een typeparameter is, is de set de samenvoeging van de sets toegankelijke leden met de naamN
in elk van de typen die zijn opgegeven als een primaire beperking of secundaire beperking (§15.2.5) voorT
, samen met de set toegankelijke leden met de naamN
inobject
. - Anders bestaat de set uit alle toegankelijke leden (§7,5) met de naam
N
inT
, inclusief overgenomen leden en de toegankelijke leden met de naamN
inobject
. AlsT
een samengesteld type is, wordt de set leden verkregen door typeargumenten te vervangen zoals beschreven in §15.3.3. Leden met eenoverride
modifier worden uitgesloten van de set.
- Als
- Als
K
nul is, worden alle geneste typen waarvan de declaraties typeparameters bevatten, verwijderd. AlsK
niet nul is, worden alle leden met een ander aantal typeparameters verwijderd. WanneerK
nul is, worden methoden met typeparameters niet verwijderd, omdat het typedeductieproces (§12.6.3) mogelijk de typeargumenten kan afleiden. - Als het lid vervolgens wordt aangeroepen, worden alle niet-aanroepbare leden uit de set verwijderd.
- Vervolgens worden leden die door andere leden zijn verborgen, uit de set verwijderd. Voor elk lid
S.M
in de set, waarbijS
het type is waarin het lidM
wordt gedeclareerd, worden de volgende regels toegepast:- Als
M
een constant, veld, eigenschap, gebeurtenis of opsommingslid is, worden alle leden die zijn gedeclareerd in een basistypeS
uit de set verwijderd. - Als
M
een typedeclaratie is, worden alle niet-typen die zijn gedeclareerd in een basistype vanS
uit de set verwijderd en worden alle typedeclaraties met hetzelfde aantal typeparameters verwijderd alsM
gedeclareerd in een basistype vanS
uit de set worden verwijderd. - Als
M
een methode is, worden alle niet-methodeleden die zijn gedeclareerd in een basistype vanS
uit de set verwijderd.
- Als
- Vervolgens worden interfaceleden die door klasseleden worden verborgen, uit de set verwijderd. Deze stap heeft alleen een effect als
T
een typeparameter is enT
zowel een effectieve basisklasse heeft danobject
als een niet-lege effectieve interfaceset (§15.2.5). Voor elk lidS.M
in de set, waarbijS
het type is waarin het lidM
wordt gedeclareerd, worden de volgende regels toegepast alsS
een andere klassedeclaratie is danobject
:- Als
M
een constante, veld, eigenschap, gebeurtenis, opsommingslid of typedeclaratie is, worden alle leden die in een interfacedeclaratie zijn gedeclareerd, verwijderd uit de set. - Als
M
een methode is, worden alle niet-methodeleden die zijn gedeclareerd in een interfacedeclaratie uit de set verwijderd en worden alle methoden met dezelfde handtekening alsM
die zijn gedeclareerd in een interfacedeclaratie uit de set verwijderd.
- Als
- Ten slotte wordt het resultaat van de zoekactie bepaald nadat verborgen leden zijn verwijderd:
- Als de set bestaat uit één lid dat geen methode is, is dit lid het resultaat van de opzoekactie.
- Als de set alleen methoden bevat, is deze groep methoden het resultaat van de opzoekactie.
- Anders is de zoekactie dubbelzinnig en treedt er een bindingstijdfout op.
Voor lidzoekacties in typen anders dan typeparameters en interfaces, evenals lidzoekacties in interfaces die strikt sprake zijn van enkele overerving (waarbij elke interface in de overnameketen precies nul of één directe basisinterface heeft), is het effect van de opzoekregels simpelweg dat afgeleide leden basiselementen met dezelfde naam of handtekening verbergen. Dergelijke opzoekingen met enkele overerving zijn nooit dubbelzinnig. De dubbelzinnigheden die mogelijk kunnen ontstaan door lidzoekopdrachten in interfaces met meerdere overnames, worden beschreven in §18.4.6.
Opmerking: Deze fase houdt slechts rekening met één soort dubbelzinnigheid. Als het opzoeken van leden resulteert in een methodegroep, kan het gebruik van een methodegroep mislukken vanwege dubbelzinnigheid, zoals beschreven in §12.6.4.1 en §12.6.6.2. eindnotitie
12.5.2 Basistypen
Voor het opzoeken van leden wordt een type T
geacht te bestaan uit de volgende basistypen:
- Als
T
isobject
ofdynamic
, heeftT
geen basistype. - Als
T
een enum_typeis, zijn de basistypen vanT
de klassetypenSystem.Enum
,System.ValueType
enobject
. - Als
T
een struct_typeis, zijn de basistypen vanT
de klassetypenSystem.ValueType
enobject
.Opmerking: een nullable_value_type is een struct_type (§8.3.1). eindnotitie
- Als
T
een class_typeis, zijn de basistypen vanT
de basisklassen vanT
, inclusief het klassetypeobject
. - Als
T
een interface_typeis, zijn de basistypen vanT
de basisinterfaces vanT
en het klassetypeobject
. - Als
T
een array_typeis, zijn de basistypen vanT
de klassetypenSystem.Array
enobject
. - Als
T
een delegate_typeis, zijn de basistypen vanT
de klassetypenSystem.Delegate
enobject
.
12.6 Functieleden
12.6.1 Algemeen
Functieleden zijn leden die uitvoerbare instructies bevatten. Functieleden zijn altijd leden van typen en kunnen geen lid zijn van naamruimten. C# definieert de volgende categorieën functieleden:
- Methoden
- Eigenschappen
- Gebeurtenissen
- Indexeerders
- Door de gebruiker gedefinieerde operators
- Exemplaarconstructors
- Statische constructors
- Finaliseerders
Met uitzondering van finalizers en statische constructors (die niet expliciet kunnen worden aangeroepen), worden de instructies in functieleden uitgevoerd via aanroepen van functieleden. De werkelijke syntaxis voor het schrijven van een aanroep van een functielid is afhankelijk van de specifieke categorie van het functielid.
De argumentenlijst (§12.6.2) van een aanroep van een functielid bevat werkelijke waarden of variabele verwijzingen voor de parameters van het functielid.
Aanroepen van algemene methoden kunnen gebruikmaken van typedeductie om de set typeargumenten te bepalen die aan de methode moeten worden doorgegeven. Dit proces wordt beschreven in §12.6.3.
Aanroepen van methoden, indexeerfuncties, operators en instantieconstructors maken gebruik van overbelastingsresolutie om te bepalen welke van een kandidaat-set functieleden moet worden aangeroepen. Dit proces wordt beschreven in §12.6.4.
Zodra een bepaald functielid tijdens bindingstijd is geïdentificeerd, mogelijk via overbelastingsresolutie, wordt het werkelijke runtimeproces voor het aanroepen van het functielid beschreven in §12.6.6.
Opmerking: de volgende tabel bevat een overzicht van de verwerking die plaatsvindt in constructies met de zes categorieën functieleden die expliciet kunnen worden aangeroepen. In de tabel geven
e
,x
,y
envalue
expressies aan die zijn geclassificeerd als variabelen of waarden,T
een expressie aangeeft die is geclassificeerd als een type,F
de eenvoudige naam van een methode is enP
de eenvoudige naam van een eigenschap is.
Bouwen Voorbeeld Beschrijving Methode-aanroep F(x, y)
Overbelastingsresolutie wordt toegepast om de beste methode F
in de bevatte klasse of structuur te selecteren. De methode wordt aangeroepen met de argumentenlijst(x, y)
. Als de methode nietstatic
is, is de instantie-expressiethis
.T.F(x, y)
Overbelastingsresolutie wordt toegepast om de beste methode te selecteren F
in de klasse of structT
. Er treedt een fout op tijdens de bindtijd als de methode nietstatic
is. De methode wordt aangeroepen met de argumentenlijst(x, y)
.e.F(x, y)
Overbelastingresolutie wordt toegepast om de beste methode F
in de klasse, struct of interface te selecteren die is opgegeven door het typee
. Er treedt een bindingstijdfout op als de methode isstatic
. De methode wordt aangeroepen met de exemplaarexpressiee
en de lijst met argumenten(x, y)
.Toegang tot eigenschappen P
De get accessor van de eigenschap P
in de bevatde klasse of struct wordt aangeroepen. Er treedt een compilatiefout op alsP
schrijf-alleen is. AlsP
nietstatic
is, is de exemplaarexpressiethis
.P = value
De set accessor van de eigenschap P
in de bevatde klasse of struct wordt aangeroepen met de lijst met argumenten(value)
. Er treedt een compilatiefout op alsP
alleen-lezen is. AlsP
nietstatic
is, is de exemplaarexpressiethis
.T.P
De get accessor van de eigenschap P
in de klasse of structT
wordt aangeroepen. Er treedt een compilatiefout op alsP
nietstatic
is of alsP
alleen-voor-schrijven is.T.P = value
De set accessor van de eigenschap P
in de klasse of structT
wordt aangeroepen met de argumentenlijst(value)
. Er treedt een compilatiefout op alsP
nietstatic
is of alsP
alleen lezen is.e.P
De get accessor van de eigenschap P
in de klasse, struct of interface die wordt opgegeven door het typeE
wordt aangeroepen met de exemplaarexpressiee
. Er treedt een bindingstijdfout op alsP
static
is of alsP
alleen voor schrijven is.e.P = value
De settoegangsfunctie van de eigenschap P
in de klasse, struct of interface die wordt opgegeven door het typeE
wordt aangeroepen met de exemplaarexpressiee
en de lijst met argumenten(value)
. Er treedt een bindingstijdfout op alsP
isstatic
of alsP
alleen-lezen is.Toegang tot gebeurtenissen E += value
De add accessor van de gebeurtenis E
in de bevatde klasse of struct wordt aangeroepen. AlsE
nietstatic
is, is de exemplaarexpressiethis
.E -= value
De verwijdertoegangsfunctie van de gebeurtenis E
in de bevattende Klasse of struct wordt aangeroepen. AlsE
nietstatic
is, is de exemplaarexpressiethis
.T.E += value
De add accessor van de gebeurtenis E
in de klasse of structT
wordt aangeroepen. Er treedt een bindingstijdfout op alsE
niet isstatic
.T.E -= value
De remove-toegangsfunctie van de gebeurtenis E
in de klasse of structT
wordt aangeroepen. Er treedt een bindingstijdfout op alsE
niet isstatic
.e.E += value
De add accessor van de gebeurtenis E
in de klasse, struct of interface die wordt opgegeven door het typeE
, wordt aangeroepen met de expressie van de instantiee
. Er treedt een bindingstijdfout op alsE
isstatic
.e.E -= value
De verwijdertoegangsmethode van de gebeurtenis E
in de klasse, struct of interface, zoals gegeven door het typeE
, wordt aangeroepen met de exemplaarexpressiee
. Er treedt een bindingstijdfout op alsE
isstatic
.Toegang tot indexeerfunctie e[x, y]
Overbelasting resolutie is van toepassing voor het selecteren van de beste indexer in de klasse, struct of interface die wordt gegeven door het type e
. De get accessor van de indexeerfunctie wordt aangeroepen met de exemplaarexpressiee
en de argumentenlijst(x, y)
. Er treedt een bindingstijdfout op als de indexeerfunctie alleen-schrijven is.e[x, y] = value
Overbelasting resolutie is van toepassing voor het selecteren van de beste indexer in de klasse, struct of interface die wordt gegeven door het type e
. De set accessor van de indexeerfunctie wordt aangeroepen met de exemplaarexpressiee
en de argumentenlijst(x, y, value)
. Er treedt een bindingstijdfout op als de indexeerfunctie alleen-lezen is.Operator aanroepen -x
Overbelastingsresolutie wordt toegepast om de beste unaire operator in de klasse of struct te selecteren die is opgegeven door het type x
. De geselecteerde operator wordt aangeroepen met de lijst met argumenten(x)
.x + y
Overbelastingsresolutie wordt toegepast om de beste binaire operator te selecteren in de klassen of structs die worden gegeven door de typen x
eny
. De geselecteerde operator wordt aangeroepen met de lijst met argumenten(x, y)
.Aanroep van exemplaarconstructor new T(x, y)
Overbelastingsresolutie wordt toegepast om de beste instantieconstructor in de klasse of struct T
te selecteren. De instantieconstructor wordt aangeroepen met de lijst met argumenten(x, y)
.eindnotitie
12.6.2 Argumentlijsten
12.6.2.1 Algemeen
Elk functielid en gemachtigde aanroep bevat een lijst met argumenten, die werkelijke waarden of variabele verwijzingen biedt voor de parameters van het functielid. De syntaxis voor het opgeven van de argumentenlijst van een aanroep van een functielid is afhankelijk van de categorie functielid:
- Voor bijvoorbeeld constructors, methoden, indexeerfuncties en gemachtigden worden de argumenten opgegeven als een argument_list, zoals hieronder wordt beschreven. Voor indexeerders, bij het aanroepen van de set-accessor, bevat de argumentenlijst ook de expressie die is opgegeven als de rechteroperand van de toewijzingsoperator.
Opmerking: dit extra argument wordt niet gebruikt voor overload-resolutie, alleen tijdens het aanroepen van de set-accessor. eindnotitie
- Voor eigenschappen is de lijst met argumenten leeg bij het aanroepen van de get-accessor en bestaat deze uit de expressie die is opgegeven als de rechteroperand van de toewijzingsoperator bij het aanroepen van de set-accessor.
- Voor gebeurtenissen bestaat de lijst met argumenten uit de expressie die is opgegeven als de rechteroperand van de operator
+=
of-=
. - Voor door de gebruiker gedefinieerde operators bestaat de lijst met argumenten uit één operand van de unaire operator of de twee operanden van de binaire operator.
De argumenten van eigenschappen (§15,7) en gebeurtenissen (§15,8) worden altijd doorgegeven als waardeparameters (§15.6.2.2). De argumenten van door de gebruiker gedefinieerde operatoren (§15.10) worden altijd doorgegeven als waardeparameters (§15.6.2.2) of invoerparameters (§9.2.8). De argumenten van indexeerfuncties (§15,9) worden altijd doorgegeven als waardeparameters (§15.6.2.2), invoerparameters (§9.2.8), of parametermatrices (§15.6.2.4). Uitvoer- en referentieparameters worden niet ondersteund voor deze categorieën functieleden.
De argumenten van een instantieconstructor, methode, indexeerfunctie of gemachtigde aanroep worden opgegeven als een argument_list:
argument_list
: argument (',' argument)*
;
argument
: argument_name? argument_value
;
argument_name
: identifier ':'
;
argument_value
: expression
| 'in' variable_reference
| 'ref' variable_reference
| 'out' variable_reference
;
Een argument_list bestaat uit een of meer arguments, gescheiden door komma's. Elk argument bestaat uit een optionele argument_name gevolgd door een argument_value. Een argument met een argument_name wordt een benoemd argumentgenoemd, terwijl een argument zonder argument_name een positioneel argumentis.
De argument_value kan een van de volgende vormen aannemen:
- Een expressie, waarmee wordt aangegeven dat het argument wordt doorgegeven als waardeparameter of wordt omgezet in een invoerparameter en vervolgens wordt doorgegeven als die, zoals bepaald door (§12.6.4.2 en wordt beschreven in §12.6.2.3.
- Het trefwoord
in
gevolgd door een variable_reference (§9,5), waarmee wordt aangegeven dat het argument wordt doorgegeven als invoerparameter (§15.6.2.3.2). Een variabele moet zeker worden toegewezen (§9.4) voordat deze kan worden doorgegeven als invoerparameter. - Het trefwoord
ref
gevolgd door een variable_reference (§9,5), waarmee wordt aangegeven dat het argument wordt doorgegeven als referentieparameter (§15.6.2.3.3). Een variabele moet zeker worden toegewezen (§9.4) voordat deze kan worden doorgegeven als referentieparameter. - Het trefwoord
out
gevolgd door een variable_reference (§9,5), waarmee wordt aangegeven dat het argument wordt doorgegeven als een uitvoerparameter (§15.6.2.3.4). Een variabele wordt beschouwd als definitief toegewezen (§9.4) na een functielidaanroep waarin de variabele wordt doorgegeven als een uitvoerparameter.
Het formulier bepaalt de modus voor het doorgeven van parameters van het argument: respectievelijk waarde, invoer, verwijzingof uitvoer. Zoals hierboven vermeld, kan een argument met de modus voor het doorgeven van waarden echter worden omgezet in een argument met de invoerdoorgiftemodus.
Het doorgeven van een vluchtig veld (§15.5.4) als invoer-, uitvoer- of verwijzingsparameter veroorzaakt een waarschuwing, omdat het veld mogelijk niet als vluchtig wordt behandeld door de aangeroepen methode.
12.6.2.2 Corresponderende parameters
Voor elk argument in een argumentenlijst moet er een bijbehorende parameter zijn in het functielid of de gedelegeerde die wordt aangeroepen.
De parameterlijst die in het volgende wordt gebruikt, wordt als volgt bepaald:
- Voor virtuele methoden en indexeringsfuncties die in klassen zijn gedefinieerd, wordt de parameterlijst gekozen uit de eerste declaratie of override van het functielid dat wordt gevonden door te beginnen met het statische type van de ontvanger en door te zoeken in de basisklassen.
- Voor gedeeltelijke methoden wordt de parameterlijst van de declaratie voor gedeeltelijke methoden gebruikt.
- Voor alle andere functieleden en gemachtigden is er slechts één lijst met parameters. Dit is de lijst die wordt gebruikt.
De positie van een argument of parameter wordt gedefinieerd als het aantal argumenten of parameters dat eraan voorafgaat in de argumentenlijst of parameterlijst.
De bijbehorende parameters voor argumenten voor functieleden worden als volgt ingesteld:
- Argumenten in het argument_list van exemplaarconstructors, methoden, indexers en delegeren:
- Een positioneel argument waarbij een parameter zich op dezelfde positie in de parameterlijst bevindt, komt overeen met die parameter, tenzij de parameter een parametermatrix is en het functielid wordt aangeroepen in de uitgevouwen vorm.
- Een positioneel argument van een functielid met een parametermatrix die wordt aangeroepen in de uitgevouwen vorm, die plaatsvindt op of na de positie van de parametermatrix in de parameterlijst, komt overeen met een element in de parametermatrix.
- Een benoemd argument komt overeen met de parameter van dezelfde naam in de parameterlijst.
- Bij het aanroepen van de settoegangsfunctie komt de expressie die is opgegeven als de rechteroperand van de toewijzingsoperator, overeen met de impliciete
value
parameter van de declaratie van de settoegangsfunctie.
- Bij het aanroepen van de get-accessor zijn er geen argumenten voor eigenschappen. Wanneer u de settoegangsfunctie aanroept, komt de expressie die is opgegeven als de rechteroperand van de toewijzingsoperator overeen met de impliciete waardeparameter van de declaratie van de settoegangsfunctie.
- Voor door de gebruiker gedefinieerde unaire operators (inclusief conversies) komt de enkele operand overeen met de enkele parameter van de operatordeclaratie.
- Voor door de gebruiker gedefinieerde binaire operatoren komt de linkeroperand overeen met de eerste parameter en komt de rechteroperand overeen met de tweede parameter van de operatordeclaratie.
- Een niet-benoemd argument komt overeen met geen parameter wanneer deze zich achter een argument met een buiten positie bevindt of een benoemd argument dat overeenkomt met een parametermatrix.
Opmerking: hiermee voorkomt u dat
void M(bool a = true, bool b = true, bool c = true);
wordt aangeroepen doorM(c: false, valueB);
. Het eerste argument wordt buiten positie gebruikt (het argument wordt gebruikt op de eerste positie, maar de parameter met de naamc
zich op de derde positie bevindt), dus de volgende argumenten moeten worden benoemd. Met andere woorden, niet-gevolgde benoemde argumenten zijn alleen toegestaan wanneer de naam en de positie resulteren in het vinden van dezelfde corresponderende parameter. eindnotitie
12.6.2.3 Runtime-evaluatie van argumentlijsten
Tijdens de uitvoering van de aanroep van een functielid (§12.6.6), worden de expressies of variabele verwijzingen van een lijst met argumenten als volgt geëvalueerd, van links naar rechts:
Als voor een waardeargument de doorgangsmodus van de parameter een waarde is
de argumentexpressie wordt geëvalueerd en er wordt een impliciete conversie (§10.2) uitgevoerd op het bijbehorende parametertype. De resulterende waarde wordt de initiële waarde van de waardeparameter in de aanroep van het functielid.
anders is de passeermodus van de parameter invoer. Als het argument een variabele verwijzing is en er een identiteitsconversie bestaat (§10.2.2) tussen het type van het argument en het type van de parameter, wordt de resulterende opslaglocatie de opslaglocatie die wordt vertegenwoordigd door de parameter in de aanroep van het functielid. Anders wordt er een opslaglocatie gemaakt met hetzelfde type als die van de bijbehorende parameter. De argumentexpressie wordt geëvalueerd en er wordt een impliciete conversie (§10.2) uitgevoerd naar het bijbehorende parametertype. De resulterende waarde wordt opgeslagen in die opslaglocatie. Deze opslaglocatie wordt vertegenwoordigd door de invoerparameter in de aanroep van het functielid.
Voorbeeld: Gegeven de volgende declaraties en methodeaanroepen:
static void M1(in int p1) { ... } int i = 10; M1(i); // i is passed as an input argument M1(i + 5); // transformed to a temporary input argument
In de
M1(i)
methodeaanroep wordti
zelf doorgegeven als invoerargument, omdat deze is geclassificeerd als een variabele en hetzelfde type heeftint
als de invoerparameter. In deM1(i + 5)
methodeaanroep wordt een niet-benoemdeint
variabele gemaakt, geïnitialiseerd met de waarde van het argument en vervolgens doorgegeven als invoerargument. Zie §12.6.4.2 en §12.6.4.4.einde voorbeeld
Voor een invoer-, uitvoer- of verwijzingsargument wordt de variabelereferentie geëvalueerd en wordt de resulterende opslaglocatie de opslaglocatie die wordt vertegenwoordigd door de parameter in de aanroep van het functielid. Voor een invoer- of verwijzingsargument wordt de variabele zeker toegewezen aan het punt van de methodeaanroep. Als de variabeleverwijzing wordt opgegeven als een uitvoerargument of een matrixelement van een reference_typeis, wordt er een runtimecontrole uitgevoerd om ervoor te zorgen dat het elementtype van de matrix identiek is aan het type van de parameter. Als deze controle mislukt, wordt er een
System.ArrayTypeMismatchException
gegenereerd.
Opmerking: deze runtimecontrole is vereist vanwege de covariantie van de matrix (§17,6). eindnotitie
Voorbeeld: In de volgende code
class Test { static void F(ref object x) {...} static void Main() { object[] a = new object[10]; object[] b = new string[10]; F(ref a[0]); // Ok F(ref b[1]); // ArrayTypeMismatchException } }
de tweede aanroep van
F
zorgt ervoor dat eenSystem.ArrayTypeMismatchException
wordt gegenereerd omdat het werkelijke elementtype vanb
string
is en nietobject
.einde voorbeeld
Methoden, indexeerfuncties en instantieconstructors kunnen de meest rechtse parameter declareren als een parametermatrix (§15.6.2.4). Dergelijke functieleden worden aangeroepen in hun normale vorm of in hun uitgebreide vorm, afhankelijk van wat van toepassing is (§12.6.4.2):
- Wanneer een functielid met een parametermatrix wordt aangeroepen in de normale vorm, is het argument voor de parametermatrix een enkele expressie die impliciet kan worden omgezet (§10.2) voor het parametermatrixtype. In dit geval fungeert de parametermatrix precies als een waardeparameter.
- Wanneer een functielid met een parametermatrix wordt aangeroepen in de uitgevouwen vorm, geeft de aanroep nul of meer positionele argumenten op voor de parametermatrix, waarbij elk argument een expressie is die impliciet kan worden omgezet (§10.2) aan het elementtype van de parametermatrix. In dit geval maakt de aanroep een exemplaar van het parametertabeltype met een lengte die overeenkomt met het aantal argumenten, initialiseert de elementen van de tabelinstantie met de opgegeven argumentwaarden en wordt de zojuist aangemaakte tabelinstantie gebruikt als het werkelijke argument.
De expressies van een lijst met argumenten worden altijd in tekstvolgorde geëvalueerd.
Voorbeeld: Daarom het voorbeeld
class Test { static void F(int x, int y = -1, int z = -2) => Console.WriteLine($"x = {x}, y = {y}, z = {z}"); static void Main() { int i = 0; F(i++, i++, i++); F(z: i++, x: i++); } }
produceert de uitvoer
x = 0, y = 1, z = 2 x = 4, y = -1, z = 3
einde voorbeeld
Wanneer een functielid met een parametermatrix wordt aangeroepen in de uitgevouwen vorm met ten minste één uitgevouwen argument, wordt de aanroep verwerkt alsof een expressie voor het maken van een matrix met een matrixinitiizer (§12.8.17.5) rond de uitgebreide argumenten is ingevoegd. Er wordt een lege matrix doorgegeven wanneer er geen argumenten zijn voor de parametermatrix; het is niet opgegeven of de verwijzing die is doorgegeven aan een nieuw toegewezen of bestaande lege matrix is.
Voorbeeld: Gegeven de declaratie
void F(int x, int y, params object[] args);
de volgende aanroepen van de uitgebreide vorm van de methode
F(10, 20, 30, 40); F(10, 20, 1, "hello", 3.0);
precies overeenkomen met
F(10, 20, new object[] { 30, 40 }); F(10, 20, new object[] { 1, "hello", 3.0 });
einde voorbeeld
Wanneer argumenten worden weggelaten van een functielid met bijbehorende optionele parameters, worden de standaardargumenten van de declaratie van het functielid impliciet doorgegeven. (Dit kan betrekking hebben op het maken van een opslaglocatie, zoals hierboven beschreven.)
Opmerking: omdat deze altijd constant zijn, heeft de evaluatie geen invloed op de evaluatie van de resterende argumenten. eindnotitie
12.6.3 Type inferentie
12.6.3.1 Algemeen
Wanneer een algemene methode wordt aangeroepen zonder typeargumenten op te geven, probeert een typedeductie proces argumenten voor de aanroep af te stellen. Door de aanwezigheid van typedeductie kan een handigere syntaxis worden gebruikt voor het aanroepen van een algemene methode en kan de programmeur voorkomen dat redundante typegegevens worden opgegeven.
voorbeeld van:
class Chooser { static Random rand = new Random(); public static T Choose<T>(T first, T second) => rand.Next(2) == 0 ? first : second; } class A { static void M() { int i = Chooser.Choose(5, 213); // Calls Choose<int> string s = Chooser.Choose("apple", "banana"); // Calls Choose<string> } }
Door middel van typedeductie worden de typeargumenten
int
enstring
bepaald van de argumenten tot de methode.einde voorbeeld
Typedeductie vindt plaats als onderdeel van de bindingstijdverwerking van een methode-aanroep (§12.8.10.2) en vindt plaats vóór de overbelastingsresolutiestap van de aanroep. Wanneer een bepaalde methodegroep wordt opgegeven in een methode-aanroep en er geen typeargumenten worden opgegeven als onderdeel van de aanroep van de methode, wordt typedeductie toegepast op elke algemene methode in de methodegroep. Als de type-inferentie slaagt, worden de afgeleide typeargumenten gebruikt om de typen van argumenten voor de volgende overbelastingsoplossing te bepalen. Als overbelastingsresolutie een algemene methode kiest als de methode die moet worden aangeroepen, worden de uitgestelde typeargumenten gebruikt als de typeargumenten voor de aanroep. Als typedeductie voor een bepaalde methode mislukt, neemt die methode niet deel aan overbelastingsresolutie. De fout van typedeductie, op en van zichzelf, veroorzaakt geen bindingstijdfout. Het leidt echter vaak tot een bindingstijdfout wanneer overbelastingsoplossing geen toepasselijke methoden kan vinden.
Als elk opgegeven argument niet overeenkomt met precies één parameter in de methode (§12.6.2.2), of als er een niet-optionele parameter zonder corresponderend argument is, mislukt deductie onmiddellijk. Anders wordt ervan uitgegaan dat de algemene methode de volgende handtekening heeft:
Tₑ M<X₁...Xᵥ>(T₁ p₁ ... Tₓ pₓ)
Met een methodeoproep van het formulier M(E₁ ...Eₓ)
de taak van typedeductie is het vinden van unieke typeargumenten S₁...Sᵥ
voor elk van de typeparameters X₁...Xᵥ
, zodat de aanroep M<S₁...Sᵥ>(E₁...Eₓ)
geldig wordt.
Het proces van typedeductie wordt hieronder beschreven als een algoritme. Een conforme compiler kan worden geïmplementeerd met behulp van een alternatieve benadering, mits deze in alle gevallen hetzelfde resultaat bereikt.
Tijdens het proces van inferentie wordt elke typeparameter Xᵢ
ofwel vastgelegd op een bepaald type Sᵢ
, ofwel niet-vastgelegd met een bijbehorende set van beperkingen. Elk van de beperkingen is een bepaald type T
. In eerste instantie is elke typevariabele Xᵢ
niet vastgelegd en heeft geen grenzen.
Typedeductie vindt plaats in fasen. Elke fase probeert argumenten voor meer typevariabelen af te stellen op basis van de bevindingen van de vorige fase. In de eerste fase worden enkele initiële afleidingen van grenzen gemaakt, terwijl in de tweede fase typevariabelen worden vastgelegd op specifieke typen en meer grenzen worden afgeleid. De tweede fase moet mogelijk een aantal keren worden herhaald.
Opmerking: Type deductie wordt ook gebruikt in andere contexten, waaronder voor het converteren van methodegroepen (§12.6.3.14) en het vinden van het beste gemeenschappelijke type expressies (§12.6.3.15). eindnotitie
12.6.3.2 De eerste fase
Voor elk van de methodeargumenten Eᵢ
:
- Als
Eᵢ
een anonieme functie is, wordt een expliciete parametertypedeductie (§12.6.3.8) gemaakt vanEᵢ
totTᵢ
- Als
Eᵢ
een typeU
heeft en de bijbehorende parameter een waardeparameter (§15.6.2.2) is, wordt een ondergrensafleiding (§12.6.3.10) gemaakt vanU
totTᵢ
. - Anders, als
Eᵢ
een typeU
heeft en de bijbehorende parameter een referentieparameter is (§15.6.2.3.3) of uitvoerparameter (§15.6.6.3 2.3.4) wordt vervolgens een exacte deductie (§12.6.3.9) gemaakt vanU
totTᵢ
. - Als
Eᵢ
een typeU
heeft en de bijbehorende parameter een invoerparameter is (§15.6.2.3.2) enEᵢ
een invoerargument is, wordt een exacte deductie (§12.6.3.9) gemaakt vanU
totTᵢ
. - Als
Eᵢ
een typeU
heeft en de bijbehorende parameter een invoerparameter is (§15.6.2.3.2) wordt een ondergrensdeductie (§12.6.3.10) gemaakt vanU
totTᵢ
. - Anders wordt er geen deductie gemaakt voor dit argument.
12.6.3.3 De tweede fase
De tweede fase gaat als volgt:
- Alle niet-gefixeerde variabelen typevariabelen
Xᵢ
die niet afhankelijk zijn van (§12.6.3.6) alleXₑ
zijn vast (§12.6.3.12). - Als er geen dergelijke typevariabelen bestaan, worden alle niet-vaste typevariabelen
Xᵢ
vastgezet waarvoor alle volgende voorwaarden gelden:- Er is ten minste één typevariabele
Xₑ
die afhankelijk is vanXᵢ
-
Xᵢ
heeft een niet-lege set grenzen
- Er is ten minste één typevariabele
- Als er geen dergelijke typevariabelen bestaan en er nog steeds niet-vastgefixeerde typevariabelen zijn, mislukt deductie.
- Anders, als er verder geen niet-opgeloste typevariabelen bestaan, dan slaagt de type-inferentie.
- Anders geldt dat voor alle argumenten
Eᵢ
met het bijbehorende parametertypeTᵢ
waarbij de uitvoertypen (§12.6.3.5) niet-opgeloste variabelen typevariabelenXₑ
bevatten, maar de invoertypen (§12.6.3.4) niet, een uitvoertypedeductie (§12.6.3.7) wordt gemaakt vanEᵢ
totTᵢ
. Vervolgens wordt de tweede fase herhaald.
12.6.3.4 Invoertypen
Als E
een methodegroep of impliciet getypte anonieme functie is en T
een gedelegeerdetype of expressieboomtype is, dan zijn alle parametertypen van T
invoertypen vanE
met het typeT
.
12.6.3.5 Uitvoertypen
Als E
een methodegroep of een anonieme functie is en T
een type gedelegeerde of expressiestructuur is, is het retourtype van T
een uitvoertype vanE
met het typeT
.
12.6.3.6 Afhankelijkheid
Een niet-opgeloste typevariabele Xᵢ
is rechtstreeks afhankelijk van een niet-opgeloste variabele typevariabele Xₑ
als voor een bepaald argument Eᵥ
type Tᵥ
Xₑ
voorkomt in een invoertype van Eᵥ
met het type Tᵥ
en Xᵢ
optreedt in een uitvoertype van Eᵥ
met het type Tᵥ
.
Xₑ
is afhankelijk vanXᵢ
als Xₑ
rechtstreeks afhankelijk is vanXᵢ
of dat Xᵢ
rechtstreeks afhankelijk is vanXᵥ
en Xᵥ
afhankelijk is vanXₑ
. Dus "hangt af van" is de transitieve maar niet reflexieve sluiting van "hangt rechtstreeks af van".
12.6.3.7 Uitvoertypededucties
Een uitvoertypedeductie wordt gemaakt van een expressie E
om een type T op de volgende manier te:
- Als
E
een anonieme functie is met afgeleid retourtypeU
(§12.6.3.13) enT
een gedelegeerd type of expressieboomtype is met retourtypeTₓ
, dan wordt een ondergrensinference (§12.6.3.10) gemaakt vanU
totTₓ
. - Als
E
een methodegroep is enT
een gedelegeerd type of expressiebombomitetype is met parametertypenT₁...Tᵥ
en retourtypeTₓ
, en overbelastingsresolutie vanE
met de typenT₁...Tᵥ
een enkele methode oplevert met retourtypeU
, dan wordt een inferentie van de ondergrens gemaakt vanU
totTₓ
. - Als
E
een expressie is met het typeU
, dan wordt er een ondergrens deductie gemaakt vanU
naarT
. - Anders worden er geen deducties gemaakt.
12.6.3.8 Expliciete parametertypeafleidingen
Een expliciete parametertypedeductie wordt gemaakt van een expressie E
:
- Als
E
een expliciet getypte anonieme functie is met parametertypenU₁...Uᵥ
enT
een type gedelegeerde of expressiestructuur is met parametertypenV₁...Vᵥ
wordt voor elkeUᵢ
een exacte deductie (§12.6.3.9) gemaakt vanUᵢ
tot de bijbehorendeVᵢ
.
12.6.3.9 Exacte deducties
Een exacte afleidingvan een type U
tot een type V
wordt als volgt gemaakt:
- Als
V
een van de niet-vastgesteldeXᵢ
is, wordtU
toegevoegd aan de set van exacte limieten voorXᵢ
. - Anders worden sets
V₁...Vₑ
enU₁...Uₑ
bepaald door te controleren of een van de volgende gevallen van toepassing is:-
V
is een matrixtypeV₁[...]
enU
is een matrixtypeU₁[...]
van dezelfde rang -
V
is het typeV₁?
enU
het typeU₁
-
V
is een samengesteld typeC<V₁...Vₑ>
enU
een samengesteld typeC<U₁...Uₑ>
Als een van deze gevallen van toepassing is, wordt een exacte deductie gemaakt van elkeUᵢ
naar de bijbehorendeVᵢ
.
-
- Anders worden er geen deducties gemaakt.
12.6.3.10 Ondergrensdeducties
Een ondergrensdeductie van een type U
:
- Als
V
een van de niet-vastgesteldeXᵢ
is, wordtU
toegevoegd aan de set van ondergrenzen voorXᵢ
. - Als
V
het typeV₁?
is enU
is het typeU₁?
, wordt er een ondergrensinferentie gemaakt vanU₁
totV₁
. - Anders worden sets
U₁...Uₑ
enV₁...Vₑ
bepaald door te controleren of een van de volgende gevallen van toepassing is:-
V
is een matrixtypeV₁[...]
enU
is een matrixtypeU₁[...]
van dezelfde rang -
V
is een vanIEnumerable<V₁>
,ICollection<V₁>
,IReadOnlyList<V₁>>
,IReadOnlyCollection<V₁>
ofIList<V₁>
enU
een enkeldimensionaal matrixtypeU₁[]
-
V
is een samengesteldclass
,struct
,interface
ofdelegate
typeC<V₁...Vₑ>
en er is een uniek typeC<U₁...Uₑ>
datU
(of, alsU
een typeparameter
is, de effectieve basisklasse of een lid van de effectieve interfaceset) identiek is aan,inherits
van (direct of indirect) of implementeert (direct of indirect)C<U₁...Uₑ>
. - (De beperking 'uniekheid' betekent dat in het geval van interface
C<T>{} class U: C<X>, C<Y>{}
er geen deductie wordt gemaakt bij het afleiden vanU
totC<T>
, omdatU₁
X
ofY
kan zijn.)
Als een van deze gevallen van toepassing is, wordt van elkeUᵢ
een deductie gemaakt op de bijbehorendeVᵢ
als volgt: - Als
Uᵢ
geen referentietype is, wordt er een exacte inferentie gemaakt - Anders, als
U
een arraytype is, wordt er een ondergrensinferentie gemaakt - Als
V
C<V₁...Vₑ>
is, dan is deductie afhankelijk van dei-th
typeparameter vanC
:- Als deze covariant is, wordt er een ondergrensinferentie gemaakt.
- Als het contravariant is, wordt er een bovengrensdeductie gemaakt.
- Als deze invariant is, wordt er een exacte deductie gemaakt.
-
- Anders worden er geen deducties gemaakt.
12.6.3.11 Bovengrensdeducties
Een bovengrensdeductie van een type U
:
- Als
V
een van de niet-vastgesteldeXᵢ
is, wordtU
toegevoegd aan de set bovengrens voorXᵢ
. - Anders worden sets
V₁...Vₑ
enU₁...Uₑ
bepaald door te controleren of een van de volgende gevallen van toepassing is:-
U
is een matrixtypeU₁[...]
enV
is een matrixtypeV₁[...]
van dezelfde rang -
U
is een vanIEnumerable<Uₑ>
,ICollection<Uₑ>
,IReadOnlyList<Uₑ>
,IReadOnlyCollection<Uₑ>
ofIList<Uₑ>
enV
een enkeldimensionaal matrixtypeVₑ[]
-
U
is het typeU1?
enV
het typeV1?
-
U
is het type klasse, struct, interface of gemachtigdeC<U₁...Uₑ>
enV
is eenclass, struct, interface
ofdelegate
type datidentical
is,inherits
van (direct of indirect) of implementeert (direct of indirect) een uniek typeC<V₁...Vₑ>
- (De 'uniekheidseis' betekent dat gezien een interface
C<T>{} class V<Z>: C<X<Z>>, C<Y<Z>>{}
er geen afleiding wordt gemaakt vanC<U₁>
naarV<Q>
. Afleidingen worden niet gedaan vanU₁
naarX<Q>
of naarY<Q>
.)
Als een van deze gevallen van toepassing is, wordt van elkeUᵢ
een deductie gemaakt op de bijbehorendeVᵢ
als volgt: - Als
Uᵢ
geen referentietype is, wordt er een exacte inferentie gemaakt - Als
V
een matrixtype is, wordt er anders een bovengrensafleiding gemaakt. - Als
U
C<U₁...Uₑ>
is, dan is deductie afhankelijk van dei-th
typeparameter vanC
:- Als het covariant is, wordt er een bovengrensdeductie gemaakt.
- Als het contravariant is, wordt er een ondergrensdeductie gemaakt.
- Als deze invariant is, wordt er een exacte deductie gemaakt.
-
- Anders worden er geen deducties gemaakt.
12.6.3.12 Repareren
Een niet-opgeloste typevariabele Xᵢ
met een set grenzen is als volgt vaste:
- De set kandidaattypen
Uₑ
begint als de set van alle typen in de set van grenzen voorXᵢ
. - Elke grens voor
Xᵢ
wordt op zijn beurt onderzocht: voor elke exacte grens vanXᵢ
alle typenUₑ
die niet identiek zijn aanU
uit de kandidaatset worden verwijderd. Voor elke ondergrensU
vanXᵢ
worden alle typenUₑ
waarvoor er geen impliciete conversie vanU
is, uit de kandidaatset verwijderd. Voor elke bovengrens vanXᵢ
alle typenUₑ
waaruit geen impliciete conversie naarU
wordt verwijderd uit de kandidaatset. - Als er onder de overgebleven kandidaattypen
Uₑ
een uniek typeV
is waarop een impliciete conversie van alle andere kandidaattypen mogelijk is, wordtXᵢ
vastgezet opV
. - Anders faalt de typeafleiding.
12.6.3.13 Afgeleid retourtype
Het uitgestelde retourtype van een anonieme functie F
wordt gebruikt tijdens typedeductie en overbelastingsresolutie. Het afgeleide retourtype kan alleen worden bepaald voor een anonieme functie waarbij alle parametertypen bekend zijn, omdat ze expliciet worden opgegeven, via een anonieme functieconversie of afgeleid tijdens typedeductie bij een omsluitende generieke methodeaanroep.
De afgeleide effectieve retourtype wordt als volgt bepaald:
- Als de hoofdtekst van
F
een uitdrukking is die een type heeft, dan is het afgeleide effectieve retourtype vanF
het type van die uitdrukking. - Als het lichaam van
F
een blok is en de verzameling expressies in dereturn
instructies van het blok een meest gangbaar typeT
heeft (§12.6.3.15), dan is het afgeleide effectieve retourtype vanF
T
. - Anders kan een effectief retourtype niet worden afgeleid voor
F
.
Het afgeleide retourtype wordt als volgt bepaald:
- Als
F
asynchroon is en de hoofdtekst vanF
een expressie is die als niets is geclassificeerd (§12.2), of een blok waarin geenreturn
instructies expressies hebben, is het uitgestelde retourtype«TaskType»
(§15.15.1). - Als
F
asynchroon is en een uitgestelde effectieve retourtypeT
heeft, is het uitgestelde retourtype«TaskType»<T>»
(§15.15.1). - Als
F
niet-asynchroon is en een afgeleid effectief retourtypeT
heeft, is het afgeleide retourtypeT
. - Anders kan een retourtype niet worden afgeleid voor
F
.
voorbeeld: als voorbeeld van typedeductie waarbij anonieme functies betrokken zijn, kunt u de
Select
extensiemethode overwegen die is gedeclareerd in de klasseSystem.Linq.Enumerable
:namespace System.Linq { public static class Enumerable { public static IEnumerable<TResult> Select<TSource,TResult>( this IEnumerable<TSource> source, Func<TSource,TResult> selector) { foreach (TSource element in source) { yield return selector(element); } } } }
Ervan uitgaande dat de
System.Linq
naamruimte is geïmporteerd met eenusing namespace
instructie, en gegeven een klasseCustomer
met eenName
-eigenschap van typestring
, kan deSelect
-methode worden gebruikt om de namen van een lijst met klanten te selecteren:List<Customer> customers = GetCustomerList(); IEnumerable<string> names = customers.Select(c => c.Name);
De aanroep van de extensiemethode (§12.8.10.3) van
Select
wordt verwerkt door de aanroep te herschrijven naar een statische methode-aanroep:IEnumerable<string> names = Enumerable.Select(customers, c => c.Name);
Omdat typeargumenten niet expliciet zijn opgegeven, wordt typedeductie gebruikt om de typeargumenten af te stellen. Ten eerste is het argument van de klant gerelateerd aan de bronparameter, waardoor
TSource
moet wordenCustomer
. Vervolgens wordtc
met behulp van het hierboven beschreven deductieproces van het anonieme functietype het typeCustomer
gegeven, en wordt de expressiec.Name
gerelateerd aan het retourtype van de selectieparameter, waarbijTResult
wordt afgeleid alsstring
. De aanroep is dus gelijk aanSequence.Select<Customer,string>(customers, (Customer c) => c.Name)
en het resultaat is van het type
IEnumerable<string>
.In het volgende voorbeeld ziet u hoe deductie van anonieme functietypen het mogelijk maakt om typegegevens te 'stromen' tussen argumenten in een algemene methode-aanroep. Gegeven de volgende methode en aanroeping:
class A { static Z F<X,Y,Z>(X value, Func<X,Y> f1, Func<Y,Z> f2) { return f2(f1(value)); } static void M() { double hours = F("1:15:30", s => TimeSpan.Parse(s), t => t.TotalHours); } }
typedeductie voor de aanroep gaat als volgt: Eerst is het argument "1:15:30" gerelateerd aan de waardeparameter, waardoor
X
moet wordenstring
. Vervolgens krijgt de parameter van de eerste anonieme functie,s
, het afgeleide typestring
en is de expressieTimeSpan.Parse(s)
gerelateerd aan het retourtype vanf1
, waardoorY
wordt geïnterpreteerd alsSystem.TimeSpan
. Ten slotte krijgt de parameter van de tweede anonieme functie,t
, het uitgestelde typeSystem.TimeSpan
en de expressiet.TotalHours
is gerelateerd aan het retourtype vanf2
, waardoorZ
moet wordendouble
. Het resultaat van de aanroep is dus van het typedouble
.einde voorbeeld
12.6.3.14 Typedeductie voor conversie van methodegroepen
Net als bij aanroepen van algemene methoden wordt typedeductie ook toegepast wanneer een methodegroep M
met een algemene methode wordt geconverteerd naar een bepaald type gedelegeerde D
(§10,8). Gegeven van een methode
Tₑ M<X₁...Xᵥ>(T₁ x₁ ... Tₑ xₑ)
en de methodegroep M
wordt toegewezen aan het delegeertype D
, is de taak van type-inferentie om typeargumenten te vinden S₁...Sᵥ
, zodat de expressie:
M<S₁...Sᵥ>
wordt compatibel (§20.2) met D
.
In tegenstelling tot het algoritme voor typedeductie voor algemene methode-aanroepen, zijn er in dit geval alleen argumenten typen, geen argument expressies. Er zijn met name geen anonieme functies en dus geen noodzaak voor meerdere fasen van deductie.
In plaats daarvan worden alle Xᵢ
beschouwd als niet-vastgemaakteen wordt een ondergrensdeductie gemaakt van elk argumenttype Uₑ
van D
. Als er voor geen van de Xᵢ
limieten werden gevonden, mislukt type-inferentie. Anders worden alle Xᵢ
vaste aan overeenkomende Sᵢ
, wat het resultaat is van typedeductie.
12.6.3.15 Het vinden van het beste algemene type van een set expressies
In sommige gevallen moet een gemeenschappelijk type worden afgeleid voor een set expressies. In het bijzonder worden de elementtypen van impliciet getypte matrices en de retourtypen van anonieme functies met blok lichamen op deze manier gevonden.
Het meest voorkomende type voor een set expressies E₁...Eᵥ
wordt als volgt bepaald:
- Er wordt een nieuwe niet-vast typevariabele geïntroduceerd
X
. - Voor elke expressie
Ei
een uitvoertypedeductie (§12.6.3.7) wordt van daaruit uitgevoerd naarX
. -
X
is vaste (§12.6.3.12), en het resulterende type is het meest gangbare type. - Anders mislukt inferentie.
Opmerking: Deze inferentie is intuïtief gelijk aan het aanroepen van een methode
void M<X>(X x₁ ... X xᵥ)
met deEᵢ
als argumenten en het afleiden vanX
. eindnotitie
12.6.4 Overbelastingsresolutie
12.6.4.1 Algemeen
Overbelastingsresolutie is een mechanisme voor bindingstijd voor het selecteren van het beste functielid om een lijst met argumenten en een set kandidaatfunctieleden aan te roepen. Overbelastingsresolutie selecteert het functielid dat moet worden aangeroepen in de volgende afzonderlijke contexten binnen C#:
- Aanroep van een methode die is genoemd in een invocation_expression (§12.8.10).
- Aanroep van een instantieconstructor met de naam in een object_creation_expression (§12.8.17.2).
- Aanroep van een toegangsfunctie voor een indexeerfunctie via een element_access (§12.8.12).
- Aanroepen van een vooraf gedefinieerde of door de gebruiker gedefinieerde operator waarnaar wordt verwezen in een expressie (§12.4.4 en §12.4.5).
Elk van deze contexten definieert de set kandidaatfunctieleden en de lijst met argumenten op een eigen unieke manier. De set kandidaten voor een methode-aanroep bevat bijvoorbeeld geen methoden die zijn gemarkeerd als override (§12.5), en methoden in een basisklasse zijn geen kandidaten als een methode in een afgeleide klasse van toepassing is (§12.8.10.2).
Zodra de leden van de kandidaatfunctie en de argumentenlijst zijn geïdentificeerd, is de selectie van het beste functielid in alle gevallen hetzelfde:
- Ten eerste wordt de set kandidaatfunctieleden gereduceerd tot die functieleden die van toepassing zijn op de opgegeven argumentenlijst (§12.6.4.2). Als deze gereduceerde set leeg is, treedt er een compilatietijdfout op.
- Vervolgens bevindt het beste functielid uit de set van toepasselijke kandidaatfunctieleden zich. Als de set slechts één functielid bevat, is dat functielid het beste functielid. Anders is het beste functielid het ene functielid dat beter is dan alle andere functieleden met betrekking tot de opgegeven argumentenlijst, mits elk functielid wordt vergeleken met alle andere functieleden die gebruikmaken van de regels in §12.6.4.3. Als er niet precies één functielid is dat beter is dan alle andere functieleden, is de aanroep van het functielid niet eenduidig en treedt er een bindingstijdfout op.
De volgende subclauses definiëren de exacte betekenis van de termen toepasselijke functielid en beter functielid.
12.6.4.2 Toepasselijk functielid
Een functielid wordt geacht een van toepassing functielid te zijn met betrekking tot een lijst met argumenten A
wanneer alle volgende waar zijn:
- Elk argument in
A
komt overeen met een parameter in de declaratie van het functielid, zoals beschreven in §12.6.2.2, ten hoogste één argument komt overeen met elke parameter en een parameter waarvoor geen argument overeenkomt, is een optionele parameter. - Voor elk argument in
A
is de parameterdoorgiftemodus van het argument identiek aan de parameterdoorgiftemodus van de bijbehorende parameter en- voor een waardeparameter of een parametermatrix bestaat een impliciete conversie (§10.2) van de argumentexpressie tot het type van de bijbehorende parameter, of
- voor een verwijzings- of uitvoerparameter is er een identiteitsconversie tussen het type argumentexpressie (indien aanwezig) en het type van de bijbehorende parameter, of
- voor een invoerparameter wanneer het bijbehorende argument de wijzigingsfunctie
in
heeft, is er een identiteitsconversie tussen het type van de argumentexpressie (indien van toepassing) en het type van de bijbehorende parameter, of - voor een invoerparameter wanneer het bijbehorende argument de
in
modifier weglaat, bestaat er een impliciete conversie (§10.2) van de argumentexpressie tot het type van de bijbehorende parameter.
Voor een functielid dat een parametermatrix bevat, als het functielid van toepassing is volgens de bovenstaande regels, wordt gezegd dat het van toepassing is in de normale vorm. Als een functielid dat een parametermatrix bevat, niet van toepassing is in de normale vorm, kan het functielid in plaats daarvan van toepassing zijn in de uitgevouwen vorm:
- Het uitgevouwen formulier wordt samengesteld door de parametermatrix in de functieliddeclaratie te vervangen door nul of meer waardeparameters van het elementtype van de parametermatrix, zodat het aantal argumenten in de argumentenlijst
A
overeenkomt met het totale aantal parameters. AlsA
minder argumenten heeft dan het aantal vaste parameters in de declaratie van het functielid, kan de uitgevouwen vorm van het functielid niet worden samengesteld en is dus niet van toepassing. - Anders is het uitgevouwen formulier van toepassing als voor elk argument in
A
een van de volgende waar is:- de parameterdoorgiftemodus van het argument is identiek aan de parameterdoorgiftemodus van de bijbehorende parameter en:
- voor een parameter met vaste waarde of een waardeparameter die door de uitbreiding is gemaakt, bestaat er een impliciete conversie (§10.2) van de argumentexpressie tot het type van de bijbehorende parameter; of
- voor een by-reference-parameter is het type argumentexpressie identiek aan het type van de bijbehorende parameter.
- de parameterdoorgiftemodus van het argument is waarde en de parameterdoorgiftemodus van de bijbehorende parameter is invoer en een impliciete conversie (§10.2) bestaat uit de argumentexpressie tot het type van de bijbehorende parameter.
- de parameterdoorgiftemodus van het argument is identiek aan de parameterdoorgiftemodus van de bijbehorende parameter en:
Wanneer de impliciete conversie van het argumenttype naar het parametertype van een invoerparameter een dynamische impliciete conversie is (§10.2.10), zijn de resultaten niet gedefinieerd.
Voorbeeld: Gegeven de volgende declaraties en methodeaanroepen:
public static void M1(int p1) { ... } public static void M1(in int p1) { ... } public static void M2(in int p1) { ... } public static void Test() { int i = 10; uint ui = 34U; M1(in i); // M1(in int) is applicable M1(in ui); // no exact type match, so M1(in int) is not applicable M1(i); // M1(int) and M1(in int) are applicable M1(i + 5); // M1(int) and M1(in int) are applicable M1(100u); // no implicit conversion exists, so M1(int) is not applicable M2(in i); // M2(in int) is applicable M2(i); // M2(in int) is applicable M2(i + 5); // M2(in int) is applicable }
einde voorbeeld
- Een statische methode is alleen van toepassing als de methodegroep het resultaat is van een simple_name of een member_access via een type.
- Een instantiemethode is alleen van toepassing als de methodegroep het resultaat is van een simple_name, een member_access via een variabele of waarde of een base_access.
- Als de methodegroep het resultaat is van een simple_name, is een instantiemethode alleen van toepassing als
this
toegang is toegestaan §12.8.14.
- Als de methodegroep het resultaat is van een simple_name, is een instantiemethode alleen van toepassing als
- Wanneer de methodegroep het resultaat is van een member_access die kan worden gebruikt via een exemplaar of een type zoals beschreven in §12.8.7.2, zijn zowel exemplaar- als statische methoden van toepassing.
- Een algemene methode waarvan de typeargumenten (expliciet opgegeven of afgeleid) niet aan hun beperkingen voldoen, is niet van toepassing.
- In de context van een methodegroepconversie bestaat er een identiteitsconversie (§10.2.2) of een impliciete verwijzingsconversie (§10.2.8) van het retourtype van de methode naar het retourtype van de gemachtigde. Anders is de kandidaatmethode niet van toepassing.
12.6.4.3 Beter functielid
Voor het bepalen van het betere functielid wordt een lijst met gestreepte argumenten A
samengesteld die alleen de argumentexpressies zelf bevat in de volgorde waarin ze worden weergegeven in de oorspronkelijke lijst met argumenten en eventuele out
of ref
argumenten weglaat.
Parameterslijsten voor elk van de kandidaatfunctieleden worden op de volgende manier samengesteld:
- Het uitgevouwen formulier wordt gebruikt als het functielid alleen van toepassing was in het uitgevouwen formulier.
- Optionele parameters zonder bijbehorende argumenten worden verwijderd uit de lijst met parameters
- Verwijzings- en uitvoerparameters worden verwijderd uit de lijst met parameters
- De parameters worden opnieuw gerangschikt, zodat ze zich op dezelfde positie bevinden als het bijbehorende argument in de lijst met argumenten.
Gezien een lijst met argumenten A
met een set argumentexpressies {E₁, E₂, ..., Eᵥ}
en twee toepasselijke functieleden Mᵥ
en Mₓ
met parametertypen {P₁, P₂, ..., Pᵥ}
en {Q₁, Q₂, ..., Qᵥ}
, wordt Mᵥ
gedefinieerd als een beter functielid dan Mₓ
als
- voor elk argument is de impliciete conversie van
Eᵥ
naarQᵥ
niet beter dan de impliciete conversie vanEᵥ
naarPᵥ
, en - voor ten minste één argument is de conversie van
Eᵥ
naarPᵥ
beter dan de conversie vanEᵥ
naarQᵥ
.
Als de parametertypereeksen {P₁, P₂, ..., Pᵥ}
en {Q₁, Q₂, ..., Qᵥ}
gelijkwaardig zijn (dat wil bijvoorbeeld dat elke Pᵢ
een identiteitsconversie heeft naar de bijbehorende Qᵢ
), worden de volgende regels voor tie-breaking toegepast om het betere lid van de functie te bepalen.
- Als
Mᵢ
een niet-generieke methode is enMₑ
een algemene methode is, isMᵢ
beter danMₑ
. - Als
Mᵢ
in de normale vorm van toepassing is enMₑ
een parametermatrix heeft en alleen van toepassing is in de uitgevouwen vorm, isMᵢ
beter danMₑ
. - Als beide methoden parametermatrices hebben en alleen van toepassing zijn in hun uitgevouwen vormen en als de parametermatrix van
Mᵢ
minder elementen heeft dan de parametermatrix vanMₑ
, isMᵢ
beter danMₑ
. - Als
Mᵥ
meer specifieke parametertypen heeft danMₓ
, isMᵥ
beter danMₓ
. Laat{R1, R2, ..., Rn}
en{S1, S2, ..., Sn}
de niet-geïnstantieerde en niet-uitgevouwen parametertypen vanMᵥ
enMₓ
voorstellen.Mᵥ
parametertypen zijn specifieker danMₓ
s als voor elke parameterRx
niet minder specifiek is danSx
, en voor ten minste één parameter isRx
specifieker danSx
:- Een typeparameter is minder specifiek dan een niet-typeparameter.
- Recursief is een samengesteld type specifieker dan een ander samengesteld type (met hetzelfde aantal typeargumenten) als ten minste één typeargument specifieker is en geen typeargument minder specifiek is dan het bijbehorende typeargument in het andere.
- Een matrixtype is specifieker dan een ander matrixtype (met hetzelfde aantal dimensies) als het elementtype van de eerste specifieker is dan het elementtype van de tweede.
- Anders geldt dat als één lid een niet-gelifte operator is en de andere een gelifte operator, de niet-gelifte beter is.
- Als geen van beide functieleden beter is gebleken en alle parameters van
Mᵥ
een corresponderend argument hebben, terwijl standaardargumenten moeten worden vervangen door ten minste één optionele parameter inMₓ
, isMᵥ
beter danMₓ
. - Als voor ten minste één parameter
Mᵥ
de betere keuze voor het doorgeven van parameters gebruikt (§12.6.4.4) dan de bijbehorende parameter inMₓ
en geen van de parameters inMₓ
de betere keuze voor parameterdoorgifte danMᵥ
, isMᵥ
beter danMₓ
. - Anders is geen functielid beter.
12.6.4.4 Betere parameter-doorvoermethode
Het is toegestaan om overeenkomstige parameters in twee overbelaste methoden alleen te laten verschillen door de parameterdoorgiftemodus op voorwaarde dat een van de twee parameters de modus voor het doorgeven van waarden heeft, als volgt:
public static void M1(int p1) { ... }
public static void M1(in int p1) { ... }
Gezien int i = 10;
, volgens §12.6.4.2, leiden de aanroepen M1(i)
en M1(i + 5)
ertoe dat beide overbelastingen van toepassing zijn. In dergelijke gevallen is de methode met de parameter-passingsmodus van waarde de betere keuze voor de parameterdoorgiftemodus.
Opmerking: er is geen keuze nodig voor argumenten van invoer-, uitvoer- of verwijzingsmodi, omdat deze argumenten alleen overeenkomen met exact dezelfde parameterdoorgiftemodi. eindnotitie
12.6.4.5 Betere conversie van expressie
Gezien een impliciete conversie C₁
die wordt geconverteerd van een expressie E
naar een type T₁
en een impliciete conversie C₂
die converteert van een expressie E
naar een type T₂
, is C₁
een betere conversie dan C₂
als een van de volgende bewaringen geldt:
-
E
exact overeenkomt metT₁
enE
komt niet exact overeen metT₂
(§12.6.4.6) -
E
komt exact overeen met zowelT₁
alsT₂
, enT₁
is een beter conversiedoel danT₂
(§12.6.4.7) -
E
is een methodegroep (§12.2), isT₁
compatibel (§20.4) met de ene beste methode uit de methodegroep voor conversieC₁
enT₂
is niet compatibel met de enige beste methode uit de methodegroep voor conversieC₂
12.6.4.6 Exact overeenkomende expressie
Gezien een expressie E
en een type T
, komt E
exact overeen metT
als een van de volgende waarden geldt:
-
E
heeft een typeS
en er bestaat een identiteitsconversie vanS
totT
-
E
is een anonieme functie,T
is een delegaat typeD
of een expressieboomtypeExpression<D>
en geldt een van de volgende:- Er bestaat een afgeleid retourtype
X
voorE
in de context van de parameterlijst vanD
(§12.6.3.12) en er bestaat een identiteitsconversie vanX
tot het retourtypeD
-
E
is eenasync
lambda zonder retourwaarde, enD
heeft een retourtype dat een niet-generieke«TaskType»
is -
E
is niet-asynchroon enD
heeft een retourtypeY
, ofE
is asynchroon enD
heeft een retourtype«TaskType»<Y>
(§15.15.1), en een van de volgende voorwaarden:- De hoofdtekst van
E
is een expressie die exact overeenkomt metY
- De hoofdtekst van
E
is een blok waarin elke retourinstructie een expressie retourneert die exact overeenkomt metY
- De hoofdtekst van
- Er bestaat een afgeleid retourtype
12.6.4.7 Beter conversiedoelstelling
Voor de twee typen T₁
en T₂
is T₁
een beter conversiedoel dan T₂
als een van de volgende voorwaarden geldt:
- Er bestaat een impliciete conversie van
T₁
naarT₂
en er bestaat geen impliciete conversie vanT₂
naarT₁
-
T₁
is«TaskType»<S₁>
(§15.15.1),T₂
is«TaskType»<S₂>
en isS₁
een beter conversiedoel danS₂
-
T₁
is«TaskType»<S₁>
(§15.15.1),T₂
is«TaskType»<S₂>
enT₁
is meer gespecialiseerd danT₂
-
T₁
isS₁
ofS₁?
waarbijS₁
een ondertekend integraal type is enT₂
isS₂
ofS₂?
waarbijS₂
een niet-ondertekend integraaltype is. Specifiek:-
S₁
issbyte
enS₂
isbyte
,ushort
,uint
ofulong
-
S₁
isshort
enS₂
isushort
,uint
ofulong
-
S₁
isint
enS₂
isuint
ofwelulong
-
S₁
islong
enS₂
isulong
-
12.6.4.8 Overloading in algemene klassen
Opmerking: hoewel handtekeningen zoals aangegeven uniek zijn (§8.6), is het mogelijk dat vervanging van typeargumenten identieke handtekeningen oplevert. In een dergelijke situatie kiest overbelastingsoplossing de meest specifieke (§12.6.4.3) van de oorspronkelijke handtekeningen (vóór vervanging van typeargumenten), als deze bestaat en meldt anders een fout. eindnotitie
voorbeeld: in de volgende voorbeelden ziet u overbelastingen die geldig en ongeldig zijn volgens deze regel:
public interface I1<T> { ... } public interface I2<T> { ... } public abstract class G1<U> { public abstract int F1(U u); // Overload resolution for G<int>.F1 public abstract int F1(int i); // will pick non-generic public abstract void F2(I1<U> a); // Valid overload public abstract void F2(I2<U> a); } abstract class G2<U,V> { public abstract void F3(U u, V v); // Valid, but overload resolution for public abstract void F3(V v, U u); // G2<int,int>.F3 will fail public abstract void F4(U u, I1<V> v); // Valid, but overload resolution for public abstract void F4(I1<V> v, U u); // G2<I1<int>,int>.F4 will fail public abstract void F5(U u1, I1<V> v2); // Valid overload public abstract void F5(V v1, U u2); public abstract void F6(ref U u); // Valid overload public abstract void F6(out V v); }
einde voorbeeld
12.6.5 Compileertijdcontrole van dynamische aanroep van leden
Hoewel overbelastingsresolutie van een dynamisch gebonden bewerking plaatsvindt tijdens runtime, is het soms mogelijk om tijdens het compileren de lijst met functieleden te kennen waaruit een overbelasting wordt gekozen:
- Voor een delegatie-aanroep (§12.8.10.4) is de lijst een enkel functielid met dezelfde parameterlijst als die van de delegate_type van de aanroep.
- Voor een methodeaanroep (§12.8.10.2) op een type of op een waarde waarvan het statische type niet dynamisch is, is de set toegankelijke methoden in de methodegroep bekend tijdens het compileren.
- Voor een expressie voor het maken van objecten (§12.8.17.2) is de set toegankelijke constructors in het type bekend tijdens het compileren.
- Voor toegang tot een indexeerfunctie (§12.8.12.3) is de set toegankelijke indexeerfuncties in de ontvanger bekend tijdens het compileren.
In deze gevallen wordt er een beperkte compilatietijdcontrole uitgevoerd op elk lid van de bekende set functieleden, om te zien of er met zekerheid kan worden vastgesteld dat deze nooit tijdens de uitvoeringstijd wordt aangeroepen. Voor elk functielid F
een gewijzigde parameter en lijst met argumenten zijn samengesteld:
- Als
F
een algemene methode en typeargumenten zijn opgegeven, worden deze eerst vervangen door de typeparameters in de parameterlijst. Als er echter geen typeargumenten zijn opgegeven, vindt er geen vervanging plaats. - Vervolgens wordt elke parameter waarvan het type is geopend (dat wil bijvoorbeeld een typeparameter bevatten; zie §8.4.3) wordt verwijderd, samen met de bijbehorende parameter(en).
Voor F
om de controle te doorstaan, moeten alle volgende voorwaarden gelden:
- De gewijzigde parameterlijst voor
F
is van toepassing op de lijst met gewijzigde argumenten in termen van §12.6.4.2. - Alle samengestelde typen in de gewijzigde parameterlijst voldoen aan hun beperkingen (§8.4.5).
- Als de typeparameters van
F
in de bovenstaande stap zijn vervangen, worden aan hun beperkingen voldaan. - Als
F
een statische methode is, mag de methodegroep niet ontstaan uit een member_access waarvan tijdens het compileren bekend is dat de ontvanger een variabele of waarde is. - Als
F
een instantiemethode is, heeft de methodegroep niet geresulteerd uit een member_access waarvan de ontvanger op het moment van compileren bekend is dat het een type is.
Als er geen kandidaat aan deze test is geslaagd, treedt er een compilatiefout op.
12.6.6 Functielid aanroepen
12.6.6.1 Algemeen
In dit subclause wordt het proces beschreven dat tijdens de uitvoering plaatsvindt om een bepaald functielid aan te roepen. Er wordt vanuit gegaan dat een bindingstijdproces al heeft bepaald dat het specifieke lid moet worden aangeroepen, mogelijk door overbelastingsresolutie toe te passen op een set kandidaatfunctieleden.
Voor het beschrijven van het aanroepproces worden functieleden onderverdeeld in twee categorieën:
- Statische functieleden. Dit zijn statische methoden, statische eigenschapstoegangsors en door de gebruiker gedefinieerde operators. Statische functieleden zijn altijd niet-virtueel.
- Leden van de instantiefunctie. Dit zijn instantiemethoden, instantieconstructors, instantie-eigenschapstoegangsmethoden en indexeertoevallers. Functieleden van een instantie zijn niet-virtueel of virtueel en worden altijd aangeroepen op een specifiek exemplaar. Het exemplaar wordt berekend door een exemplaarexpressie en wordt toegankelijk binnen het functielid als
this
(§12.8.14). Voor een exemplaarconstructor wordt de exemplaarexpressie beschouwd als het zojuist toegewezen object.
De uitvoering van een aanroep van een functielid bestaat uit de volgende stappen, waarbij M
het functielid is en, als M
een exemplaarlid is, E
de exemplaarexpressie is:
Als
M
een statisch functielid is:- De lijst met argumenten wordt geëvalueerd zoals beschreven in §12.6.2.
-
M
wordt aangeroepen.
Als het type
E
een waardetypeV
is, enM
is gedeclareerd of overschreven inV
:-
E
wordt geëvalueerd. Als deze evaluatie een uitzondering veroorzaakt, worden er geen verdere stappen uitgevoerd. Voor een instantieconstructor bestaat deze evaluatie uit het toewijzen van opslag (meestal uit een uitvoeringsstack) voor het nieuwe object. In dit geval wordtE
geclassificeerd als een variabele. - Als
E
niet is geclassificeerd als een variabele of alsV
geen leesbare struct is (§16.2.2) enE
een van de volgende is:- een invoerparameter (§15.6.2.3.2) of
- een
readonly
veld (§15.5.3), of - een
readonly
verwijzingsvariabele of retourwaarde (§9,7)
vervolgens wordt een tijdelijke lokale variabele van het type
E
gemaakt en wordt de waarde vanE
aan die variabele toegewezen.E
wordt vervolgens opnieuw geclassificeerd als een verwijzing naar die tijdelijke lokale variabele. De tijdelijke variabele is toegankelijk alsthis
binnenM
, maar niet op een andere manier. Dus alleen wanneerE
kan worden geschreven, kan de beller de wijzigingen observeren dieM
aanthis
aanbrengt.- De lijst met argumenten wordt geëvalueerd zoals beschreven in §12.6.2.
-
M
wordt aangeroepen. De variabele waarnaar wordt verwezen doorE
wordt de variabele waarnaar wordt verwezen doorthis
.
-
Anders:
-
E
wordt geëvalueerd. Als deze evaluatie een uitzondering veroorzaakt, worden er geen verdere stappen uitgevoerd. - De lijst met argumenten wordt geëvalueerd zoals beschreven in §12.6.2.
- Als het type
E
een value_typeis, wordt een boxing-conversie (§10.2.9) uitgevoerd omE
te converteren naar een class_type, en wordtE
in de volgende stappen als die class_type beschouwd. Als de value_type een enum_typeis, wordt de class_type andersSystem.Enum;
, wordt dezeSystem.ValueType
. - De waarde van
E
is gecontroleerd op geldigheid. Als de waarde vanE
null is, wordt er eenSystem.NullReferenceException
gegenereerd en worden er geen verdere stappen uitgevoerd. - De implementatie van het functielid dat moet worden aangeroepen, wordt bepaald:
- Als het binding-time type van
E
een interface is, is het functielid dat moet worden aangeroepen de implementatie vanM
geleverd door het runtime-type van de instantie waarE
naar verwijst. Dit functielid wordt bepaald door de interfacetoewijzingsregels (§18.6.5) toe te passen om de implementatie vanM
te bepalen die wordt geleverd door het runtimetype van het exemplaar waarnaar wordt verwezen doorE
. - Anders, als
M
een virtueel functielid is, dan is het functielid dat aangeroepen moet worden de implementatie vanM
, geleverd door het uitvoeringstype van het exemplaar waarnaarE
verwijst. Dit functielid wordt bepaald door de regels toe te passen voor het bepalen van de meest afgeleide implementatie (§15.6.4) vanM
met betrekking tot het uitvoeringstype van het exemplaar waarnaar wordt verwezen doorE
. - Anders is
M
het niet-virtuele functielid en is het functielid dat moet worden aangeroepenM
zelf.
- Als het binding-time type van
- De implementatie van het functielid die in de bovenstaande stap is bepaald, wordt aangeroepen. Het object waarnaar wordt verwezen door
E
wordt het object waarnaar door dit wordt verwezen.
-
Het resultaat van de aanroep van een instantieconstructor (§12.8.17.2) is de waarde die is gemaakt. Het resultaat van de aanroep van een ander functielid is de eventueel geretourneerde waarde (§13.10.5) uit het lichaam.
12.6.6.2 Aanroepen op gewrapte instanties
Een functielid dat is geïmplementeerd in een value_type kan worden aangeroepen via een boxed exemplaar van die value_type in de volgende situaties:
- Wanneer het functielid een overriding is van een methode die is geërfd van het class_type en wordt aangeroepen via een instance expressie van dat class_type.
Opmerking: de class_type is altijd een van
System.Object
,System.ValueType
ofSystem.Enum
. eindnotitie - Wanneer het functielid een implementatie is van een interfacefunctielid en wordt aangeroepen via een exemplaarexpressie van een interface_type.
- Wanneer het functielid wordt aangeroepen via een gemachtigde.
In deze situaties wordt het boxed-exemplaar beschouwd als een variabele van het type value_type, en deze variabele wordt de variabele waarnaar wordt verwezen binnen de aanroep van het functielid.
Opmerking: dit betekent met name dat wanneer een functielid wordt aangeroepen op een boxed exemplaar, het mogelijk is dat het functielid de waarde in het boxed-exemplaar wijzigt. eindnotitie
12.7 Deconstructie
Deconstructie is een proces waarbij een expressie wordt omgezet in een tuple van afzonderlijke expressies. Deconstructie wordt gebruikt wanneer het doel van een eenvoudige toewijzing een tuple-expressie is, om waarden te verkrijgen die aan de elementen van die tuple moeten worden toegekend.
Een expressie E
wordt gedeconstrueerd tot een tuple-expressie met n
elementen op de volgende manier:
- Als
E
een tuple-expressie is metn
elementen, is het resultaat van de deconstructie de expressieE
zichzelf. - Anders, als
E
een tupletype(T1, ..., Tn)
metn
elementen heeft, dan wordtE
geëvalueerd in een tijdelijke variabele__v
en is het resultaat van de deconstructie de expressie(__v.Item1, ..., __v.Itemn)
. - Als de expressie
E.Deconstruct(out var __v1, ..., out var __vn)
tijdens het compileren wordt omgezet in een unieke instantie of extensiemethode, wordt die expressie geëvalueerd en is het resultaat van de deconstructie de expressie(__v1, ..., __vn)
. Een dergelijke methode wordt een deconstructorgenoemd. - Anders kan
E
niet worden gedeconstrueerd.
Hier verwijzen __v
en __v1, ..., __vn
naar onzichtbare en ontoegankelijke tijdelijke variabelen.
Opmerking: een expressie van het type
dynamic
kan niet worden gedeconstrueerd. eindnotitie
12.8 Primaire expressies
12.8.1 Algemeen
Primaire expressies bevatten de eenvoudigste vormen van expressies.
primary_expression
: primary_no_array_creation_expression
| array_creation_expression
;
primary_no_array_creation_expression
: literal
| interpolated_string_expression
| simple_name
| parenthesized_expression
| tuple_expression
| member_access
| null_conditional_member_access
| invocation_expression
| element_access
| null_conditional_element_access
| this_access
| base_access
| post_increment_expression
| post_decrement_expression
| null_forgiving_expression
| object_creation_expression
| delegate_creation_expression
| anonymous_object_creation_expression
| typeof_expression
| sizeof_expression
| checked_expression
| unchecked_expression
| default_value_expression
| nameof_expression
| anonymous_method_expression
| pointer_member_access // unsafe code support
| pointer_element_access // unsafe code support
| stackalloc_expression
;
Opmerking: deze grammaticaregels zijn niet gereed voor ANTLR, omdat ze deel uitmaken van een reeks wederzijds recursieve regels (
primary_expression
,primary_no_array_creation_expression
,member_access
,invocation_expression
,element_access
,post_increment_expression
,post_decrement_expression
,null_forgiving_expression
,pointer_member_access
enpointer_element_access
) die niet door ANTLR worden verwerkt. Standaardtechnieken kunnen worden gebruikt om de grammatica te transformeren en de wederzijdse linker recursie te verwijderen. Dit is niet gedaan omdat niet alle parseringsstrategieën dit vereisen (bijvoorbeeld een LALR-parser niet) en dit zou de structuur en beschrijving verdoezelen. eindnotitie
pointer_member_access (§23.6.3) en pointer_element_access (§23.6.4) zijn alleen beschikbaar in onveilige code (§23).
Primaire uitdrukkingen worden verdeeld tussen array_creation_expressionen primary_no_array_creation_expression. Door array_creation_expression op deze manier te behandelen, in plaats van deze samen met de andere eenvoudige expressievormen weer te geven, kan de grammatica verwarrende code zoals... uitsluiten.
object o = new int[3][1];
wat anders zou worden geïnterpreteerd als
object o = (new int[3])[1];
12.8.2 Letterlijke tekens
Een primary_expression die bestaat uit een letterlijke (§6.4,5) wordt geclassificeerd als een waarde.
12.8.3 Geïnterpoleerde tekenreeksexpressies
Een interpolated_string_expression bestaat uit $
, $@
of @$
, direct gevolgd door tekst binnen "
tekens. Binnen de tekst tussen aanhalingstekens zijn er nul of meer interpolaties, begrensd door de tekens {
en }
, die elk een expressie en optionele opmaakspecificaties bevatten.
Geïnterpoleerde tekenreeksexpressies hebben twee vormen; regular (interpolated_regular_string_expression) en verbatim (interpolated_verbatim_string_expression); die lexisch vergelijkbaar zijn met, maar semantisch verschillen van, de twee vormen van letterlijke tekenreeksen (§6.4.5.6).
interpolated_string_expression
: interpolated_regular_string_expression
| interpolated_verbatim_string_expression
;
// interpolated regular string expressions
interpolated_regular_string_expression
: Interpolated_Regular_String_Start Interpolated_Regular_String_Mid?
('{' regular_interpolation '}' Interpolated_Regular_String_Mid?)*
Interpolated_Regular_String_End
;
regular_interpolation
: expression (',' interpolation_minimum_width)?
Regular_Interpolation_Format?
;
interpolation_minimum_width
: constant_expression
;
Interpolated_Regular_String_Start
: '$"'
;
// the following three lexical rules are context sensitive, see details below
Interpolated_Regular_String_Mid
: Interpolated_Regular_String_Element+
;
Regular_Interpolation_Format
: ':' Interpolated_Regular_String_Element+
;
Interpolated_Regular_String_End
: '"'
;
fragment Interpolated_Regular_String_Element
: Interpolated_Regular_String_Character
| Simple_Escape_Sequence
| Hexadecimal_Escape_Sequence
| Unicode_Escape_Sequence
| Open_Brace_Escape_Sequence
| Close_Brace_Escape_Sequence
;
fragment Interpolated_Regular_String_Character
// Any character except " (U+0022), \\ (U+005C),
// { (U+007B), } (U+007D), and New_Line_Character.
: ~["\\{}\u000D\u000A\u0085\u2028\u2029]
;
// interpolated verbatim string expressions
interpolated_verbatim_string_expression
: Interpolated_Verbatim_String_Start Interpolated_Verbatim_String_Mid?
('{' verbatim_interpolation '}' Interpolated_Verbatim_String_Mid?)*
Interpolated_Verbatim_String_End
;
verbatim_interpolation
: expression (',' interpolation_minimum_width)?
Verbatim_Interpolation_Format?
;
Interpolated_Verbatim_String_Start
: '$@"'
| '@$"'
;
// the following three lexical rules are context sensitive, see details below
Interpolated_Verbatim_String_Mid
: Interpolated_Verbatim_String_Element+
;
Verbatim_Interpolation_Format
: ':' Interpolated_Verbatim_String_Element+
;
Interpolated_Verbatim_String_End
: '"'
;
fragment Interpolated_Verbatim_String_Element
: Interpolated_Verbatim_String_Character
| Quote_Escape_Sequence
| Open_Brace_Escape_Sequence
| Close_Brace_Escape_Sequence
;
fragment Interpolated_Verbatim_String_Character
: ~["{}] // Any character except " (U+0022), { (U+007B) and } (U+007D)
;
// lexical fragments used by both regular and verbatim interpolated strings
fragment Open_Brace_Escape_Sequence
: '{{'
;
fragment Close_Brace_Escape_Sequence
: '}}'
;
Zes van de hierboven gedefinieerde lexicale regels zijn als volgt contextgevoelige:
regel | contextuele vereisten |
---|---|
Interpolated_Regular_String_Mid | Alleen erkend na een Interpolated_Regular_String_Start, tussen eventuele volgende interpolaties en vóór de overeenkomstige Interpolated_Regular_String_End. |
Regular_Interpolation_Format | Alleen herkend binnen een regular_interpolation en wanneer de beginkomma (:) is niet genest binnen een haakje (haakjes/accolades/vierkant). |
Interpolated_Regular_String_End | Wordt alleen herkend na een Interpolated_Regular_String_Start en alleen indien tussenliggende tokens Interpolated_Regular_String_Mids zijn of tokens die deel uit kunnen maken van regular_interpolations, inclusief tokens voor alle interpolated_regular_string_expressions die binnen dergelijke interpolaties zijn verwerkt. |
Interpolated_Verbatim_String_MidVerbatim_Interpolation_FormatInterpolated_Verbatim_String_End | De erkenning van deze drie regels volgt op die van de overeenkomstige bovenstaande regels, waarbij elke vermelde reguliere grammaticaregel wordt vervangen door de bijbehorende exacte. |
Opmerking: de bovenstaande regels zijn contextgevoelig omdat de definities overlappen met die van andere tokens in de taal. eindnotitie
Opmerking: de bovenstaande grammatica is niet gereed voor ANTLR vanwege de contextgevoelige lexicale regels. Net als bij andere lexergenerators ondersteunt ANTLR contextgevoelige lexicale regels, bijvoorbeeld met behulp van de lexicale modi, maar dit is een implementatiedetail en daarom geen deel van deze specificatie. eindnotitie
Een interpolated_string_expression wordt geclassificeerd als een waarde. Als deze direct wordt geconverteerd naar System.IFormattable
of System.FormattableString
met een impliciete geïnterpoleerde tekenreeksconversie (§10.2.5), heeft de geïnterpoleerde tekenreeksexpressie dat type. Anders is het van het type string
.
Opmerking: de verschillen tussen de mogelijke typen een interpolated_string_expression kunnen worden bepaald uit de documentatie voor
System.String
(§C.2) enSystem.FormattableString
(§C.3). eindnotitie
De betekenis van een interpolatie, zowel regular_interpolation als verbatim_interpolation, is het opmaken van de waarde van de expressie als een string
volgens de indeling die is opgegeven door de Regular_Interpolation_Format of Verbatim_Interpolation_Format, of volgens een standaardindeling voor het type expressie. De opgemaakte tekenreeks wordt vervolgens gewijzigd door de interpolation_minimum_width, indien van toepassing, om de definitieve string
te creëren die in de interpolated_string_expressionwordt geïnterpoleerd.
Opmerking: hoe de standaardindeling voor een type wordt bepaald, wordt beschreven in de documentatie voor
System.String
(§C.2) enSystem.FormattableString
(§C.3). Beschrijvingen van standaardindelingen, die identiek zijn voor Regular_Interpolation_Format en Verbatim_Interpolation_Format, vindt u in de documentatie voorSystem.IFormattable
(§C.4) en in andere typen in de standaardbibliotheek (§C). eindnotitie
In een interpolation_minimum_width heeft de constant_expression een impliciete conversie naar int
. Laat de veldbreedte de absolute waarde van deze constante expressie zijn en laat de uitlijning overeenkomen met het teken (positief of negatief) van de waarde van deze constante expressie:
- Als de waarde van de veldbreedte kleiner is dan of gelijk is aan de lengte van de opgemaakte tekenreeks, wordt de opgemaakte tekenreeks niet gewijzigd.
- Anders wordt de opgemaakte tekenreeks opgevuld met spatietekens, zodat de lengte gelijk is aan de veldbreedte:
- Als de uitlijning positief is, wordt de opgemaakte tekenreeks rechts uitgelijnd door de opvulling aan te passen,
- Anders wordt deze links uitgelijnd door de opvulling toe te voegen.
De algemene betekenis van een interpolated_string_expression, inclusief de bovenstaande opmaak en opvulling van interpolaties, wordt gedefinieerd door een conversie van de expressie naar een methodeaanroep: als het type expressie wordt System.IFormattable
of System.FormattableString
die methode is System.Runtime.CompilerServices.FormattableStringFactory.Create
(§C.3) die een waarde van het type System.FormattableString
retourneert ; anders wordt het type string
en wordt de methode string.Format
(§C.2) die een waarde van het type string
retourneert .
In beide gevallen bestaat de argumentenlijst van de aanroep uit een letterlijke tekenreeks met notatietekenreeks letterlijke met notatiespecificaties voor elke interpolatie en een argument voor elke expressie die overeenkomt met de indelingsspecificaties.
De letterlijke notatietekenreeks wordt als volgt samengesteld, waarbij N
het aantal interpolaties is in de interpolated_string_expression . De letterlijke notatietekenreeks bestaat uit, in volgorde:
- De tekens van de Interpolated_Regular_String_Start of Interpolated_Verbatim_String_Start
- De tekens van de Interpolated_Regular_String_Mid of Interpolated_Verbatim_String_Mid, indien aanwezig.
- Als
N ≥ 1
voor elk getalI
van0
totN-1
.- Een tijdelijke aanduidingsspecificatie:
- Een linker accolade (
{
) teken - De decimale weergave van
I
- Als de bijbehorende regular_interpolation of verbatim_interpolation vervolgens een interpolation_minimum_widthheeft, wordt er een komma (
,
) gevolgd door de decimale weergave van de waarde van de constant_expression - De tekens van de Regular_Interpolation_Format of Verbatim_Interpolation_Format, indien van toepassing, van de bijbehorende regular_interpolation of verbatim_interpolation
- Een rechter accolade (
}
) karakter
- Een linker accolade (
- De tekens van de Interpolated_Regular_String_Mid of Interpolated_Verbatim_String_Mid die onmiddellijk na de bijbehorende interpolatie komen, indien van toepassing
- Een tijdelijke aanduidingsspecificatie:
- Ten slotte de tekens van de Interpolated_Regular_String_End of Interpolated_Verbatim_String_End.
De volgende argumenten zijn de expressies uit de interpolaties, indien van toepassing, in volgorde.
Wanneer een interpolated_string_expression meerdere interpolaties bevat, worden de expressies in deze interpolaties geëvalueerd in tekstvolgorde van links naar rechts.
voorbeeld van:
In dit voorbeeld worden de volgende formatspecificaties gebruikt:
- de
X
-indelingsspecificatie waarmee gehele getallen als hoofdletter-hexadecimaal worden opgemaakt, - de standaardindeling voor een
string
waarde is de waarde zelf, - positieve uitlijningswaarden die rechts uitlijnen binnen de opgegeven minimale veldbreedte,
- negatieve uitlijningswaarden die links uitvullen binnen de opgegeven minimale veldbreedte,
- gedefinieerde constanten voor de interpolation_minimum_widthen
- dat
{{
en}}
respectievelijk zijn opgemaakt als{
en}
.
Gegeven:
string text = "red";
int number = 14;
const int width = -4;
Dan:
geïnterpoleerde tekenreeksexpressie |
equivalente betekenis als string |
waarde |
---|---|---|
$"{text}" |
string.Format("{0}", text) |
"red" |
$"{{text}}" |
string.Format("{{text}}) |
"{text}" |
$"{ text , 4 }" |
string.Format("{0,4}", text) |
" red" |
$"{ text , width }" |
string.Format("{0,-4}", text) |
"red " |
$"{number:X}" |
string.Format("{0:X}", number) |
"E" |
$"{text + '?'} {number % 3}" |
string.Format("{0} {1}", text + '?', number % 3) |
"red? 2" |
$"{text + $"[{number}]"}" |
string.Format("{0}", text + string.Format("[{0}]", number)) |
"red[14]" |
$"{(number==0?"Zero":"Non-zero")}" |
string.Format("{0}", (number==0?"Zero":"Non-zero")) |
"Non-zero" |
einde voorbeeld
12.8.4 Eenvoudige namen
Een simple_name bestaat uit een id, eventueel gevolgd door een lijst met typeargumenten:
simple_name
: identifier type_argument_list?
;
Een simple_name heeft de vorm I
of de vorm I<A₁, ..., Aₑ>
, waarbij I
een enkele identificator is en I<A₁, ..., Aₑ>
een optionele type-argumentenlijstis. Als er geen type_argument_list is opgegeven, wordt e
als nul beschouwd. De simple_name wordt als volgt geëvalueerd en geclassificeerd:
- Als
e
nul is en de simple_name wordt weergegeven in een lokale ruimte voor variabeledeclaratie (§7.3) die rechtstreeks een lokale variabele, parameter of constante met naamI
bevat, verwijst de simple_name naar die lokale variabele, parameter of constante en wordt geclassificeerd als een variabele of waarde. - Als
e
nul is en de simple_name wordt weergegeven in een algemene methodedeclaratie, maar buiten de kenmerken van de method_declaration, en als die declaratie een typeparameter met de naamI
bevat, verwijst de simple_name naar die typeparameter. - Anders geldt voor elk exemplaartype
T
(§15.3.2), te beginnen met het exemplaartype van de onmiddellijk insluitende typedeclaratie en door te gaan met het exemplaartype van elke insluitende klasse- of structdeclaratie (indien van toepassing):- Als
e
nul is en de declaratie vanT
een typeparameter met de naamI
bevat, verwijst de simple_name naar die typeparameter. - Anders, als een ledenopzoeking (§12,5) van
I
inT
mete
typeargumenten een overeenkomst oplevert:- Als
T
het exemplaartype is van het onmiddellijk ingesloten klasse- of structtype en de zoekactie een of meer methoden identificeert, is het resultaat een methodegroep met een bijbehorende exemplaarexpressie vanthis
. Als een lijst met typeargumenten is opgegeven, wordt deze gebruikt bij het aanroepen van een algemene methode (§12.8.10.2). - Als
T
het exemplaartype is van de onmiddellijk insluitende klasse of struct, als het zoeken een exemplaarlid identificeert en als de verwijzing optreedt in het blok van een exemplaarconstructor, een instantievemethode of een instantietoegangsmethode (§12.2.1), is het resultaat hetzelfde als een lidtoegang (§12.8.7) van de vormthis.I
. Dit kan alleen gebeuren wanneere
nul is. - Anders is het resultaat hetzelfde als een lidtoegang (§12.8.7) van het formulier
T.I
ofT.I<A₁, ..., Aₑ>
.
- Als
- Als
- Anders worden voor elke naamruimte
N
, beginnend met de naamruimte waarin de eenvoudige naam voorkomt, door te gaan met elke insluitende naamruimte (indien van toepassing) en eindigend met de globale naamruimte, worden de volgende stappen geëvalueerd totdat een entiteit is gevonden.- Als
e
nul is enI
de naam van een naamruimte inN
is, dan:- Als de locatie waar de simple_name plaatsvindt, wordt ingesloten door een naamruimtedeclaratie voor
N
en de naamruimtedeclaratie een extern_alias_directive of using_alias_directive bevat die de naamI
koppelt aan een naamruimte of type, is de simple_name niet eenduidig en treedt er een compilatietijdfout op. - Anders verwijst de simple_name naar de naamruimte met de naam
I
inN
.
- Als de locatie waar de simple_name plaatsvindt, wordt ingesloten door een naamruimtedeclaratie voor
- Anders, als
N
een toegankelijk type bevat met de naamI
en typeparameters voore
, dan:- Als
e
nul is en de locatie waar de simple_name plaatsvindt, wordt ingesloten door een naamruimtedeclaratie voorN
en bevat de naamruimtedeclaratie een extern_alias_directive of using_alias_directive die de naamI
koppelt aan een naamruimte of type, is de simple_name dubbelzinnig en treedt er een compilatietijdfout op. - Anders verwijst de namespace_or_type_name naar het type dat is samengesteld met de opgegeven typeargumenten.
- Als
- Anders, als de locatie waar de simple_name plaatsvindt, wordt ingesloten door een naamruimtedeclaratie voor
N
:- Als
e
nul is en de naamruimtedeclaratie een extern_alias_directive of using_alias_directive bevat die de naamI
koppelt aan een geïmporteerde naamruimte of -type, verwijst de simple_name naar die naamruimte of dat type. - Anders, als de naamruimten die door de using_namespace_directives van de declaratie van de naamruimte zijn geïmporteerd, precies één type bevatten met de naam
I
ene
type parameters, verwijst de simple_name naar dat type gevormd met de gespecificeerde typeargumenten. - Als de naamruimten die zijn geïmporteerd door de using_namespace_directives van de naamruimtedeclaratie meer dan één type bevatten met de naam
I
ene
typeparameters, is de simple_name niet eenduidig en treedt er een compilatiefout op.
- Als
Opmerking: deze hele stap is exact parallel aan de bijbehorende stap in de verwerking van een namespace_or_type_name (§7.8). eindnotitie
- Als
- Als
e
nul is enI
de identificator_
is, is de simple_name een eenvoudige verworpene, een vorm van declaratie-uitdrukking (§12.17). - Anders is de simple_name niet gedefinieerd en treedt er een compilatietijdfout op.
12.8.5 Haakjes geplaatste expressies
Een parenthesized_expression bestaat uit een expressie tussen haakjes.
parenthesized_expression
: '(' expression ')'
;
Een parenthesized_expression wordt geëvalueerd door de expressie tussen de haakjes te evalueren. Als de expressie tussen de haakjes een naamruimte of type aangeeft, treedt er een compilatietijdfout op. Anders is het resultaat van de parenthesized_expression het resultaat van de evaluatie van de ingesloten expressie.
12.8.6 Tuple-expressies
Een tuple_expression vertegenwoordigt een tuple en bestaat uit twee of meer door komma's gescheiden en optioneel benoemde expressietussen haakjes. Een deconstruction_expression is een verkorte syntaxis voor een tuple die impliciet getypte declaratie-uitdrukkingen bevat.
tuple_expression
: '(' tuple_element (',' tuple_element)+ ')'
| deconstruction_expression
;
tuple_element
: (identifier ':')? expression
;
deconstruction_expression
: 'var' deconstruction_tuple
;
deconstruction_tuple
: '(' deconstruction_element (',' deconstruction_element)+ ')'
;
deconstruction_element
: deconstruction_tuple
| identifier
;
Een tuple_expression wordt geclassificeerd als een tuple.
Een deconstruction_expressionvar (e1, ..., en)
is een afkorting voor de tuple_expression(var e1, ..., var en)
en volgt hetzelfde gedrag. Dit geldt recursief voor geneste deconstruction_tuples in de deconstruction_expression. Elke identificator die binnen een deconstruction_expression wordt genest, introduceert dus een declaratie-uitdrukking (§12.17). Als gevolg hiervan kan een deconstruction_expression zich alleen aan de linkerkant van een eenvoudige opdracht voordoen.
Een tuple-expressie heeft een type als en alleen als elk van de elementexpressies Ei
een type Ti
heeft. Het type moet een tupletype van dezelfde ariteit zijn als de tuple-expressie, waarbij elk element wordt gegeven door het volgende:
- Als het tuple-element in de overeenkomstige positie een naam heeft
Ni
, wordt het tupeltypeelementTi Ni
. - Als
Ei
in de vorm vanNi
ofE.Ni
ofE?.Ni
is, dan wordt het tuplet-type-elementTi Ni
, tenzij een van de volgende voorwaarden geldt:.- Een ander element van de tuple-expressie heeft de naam
Ni
of - Een ander tuple-element zonder naam heeft een tuple-elementexpressie van de vorm
Ni
ofE.Ni
ofE?.Ni
, of -
Ni
is van de vormItemX
, waarbijX
een reeks niet-0
geïnitieerde decimalen is die de positie van een tuple-element kunnen vertegenwoordigen enX
niet de positie van het element vertegenwoordigt.
- Een ander element van de tuple-expressie heeft de naam
- Anders moet het tupel-type-element
Ti
zijn.
Een tuple-expressie wordt geëvalueerd door elk van de elementexpressies te evalueren in volgorde van links naar rechts.
Een tuple-waarde kan worden verkregen uit een tuple-expressie door deze te converteren naar een tupletype (§10.2.2.13), door deze opnieuw te classificeren als een waarde (§12.2.2)) of door deze te maken als doel van een deconstructerende toewijzing (§12.21.2).
voorbeeld van:
(int i, string) t1 = (i: 1, "One"); (long l, string) t2 = (l: 2, null); var t3 = (i: 3, "Three"); // (int i, string) var t4 = (i: 4, null); // Error: no type
In dit voorbeeld zijn alle vier de tuple-expressies geldig. De eerste twee,
t1
ent2
, gebruiken niet het type tuple-expressie, maar passen in plaats daarvan een impliciete tupleconversie toe. In het geval vant2
is de impliciete tuple-conversie afhankelijk van de impliciete conversies van2
naarlong
en vannull
naarstring
. De derde tuple-expressie heeft een type(int i, string)
en kan daarom opnieuw worden geclassificeerd als een waarde van dat type. De declaratie vant4
daarentegen is een fout: de tuple-expressie heeft geen type omdat het tweede element geen type heeft.if ((x, y).Equals((1, 2))) { ... };
In dit voorbeeld zal je zien dat tuples soms kunnen leiden tot meerdere lagen van haakjes, vooral wanneer de tuple-expressie het enige argument is bij een methodeaanroep.
einde voorbeeld
12.8.7 Toegang tot leden
12.8.7.1 Algemeen
Een member_access bestaat uit een primary_expression, een predefined_typeof een qualified_alias_member, gevolgd door een ".
" token, gevolgd door een identifier, eventueel gevolgd door een type_argument_list.
member_access
: primary_expression '.' identifier type_argument_list?
| predefined_type '.' identifier type_argument_list?
| qualified_alias_member '.' identifier type_argument_list?
;
predefined_type
: 'bool' | 'byte' | 'char' | 'decimal' | 'double' | 'float' | 'int'
| 'long' | 'object' | 'sbyte' | 'short' | 'string' | 'uint' | 'ulong'
| 'ushort'
;
De qualified_alias_member productie wordt gedefinieerd in §14.8.
Een member_access is of de vorm E.I
of van de vorm E.I<A₁, ..., Aₑ>
, waarbij E
een primary_expression, predefined_type of qualified_alias_member is,I
een enkele identifier is, en <A₁, ..., Aₑ>
een optionele type_argument_listis. Als er geen type_argument_list is opgegeven, wordt e
als nul beschouwd.
Een member_access met een primary_expression van het type dynamic
is dynamisch gebonden (§12.3.3). In dit geval classificeert de compiler de lidtoegang als een eigenschapstoegang van het type dynamic
. De onderstaande regels om de betekenis van de member_access te bepalen, worden vervolgens toegepast tijdens runtime, met behulp van het runtimetype in plaats van het compileertijdtype van de primary_expression. Als deze runtime-classificatie leidt tot een methodegroep, dan zal de toegang tot het lid bestaan uit de primary_expression binnen een invocation_expression.
De member_access wordt als volgt geëvalueerd en geclassificeerd:
- Als
e
nul is enE
een naamruimte is enE
een geneste naamruimte met naamI
bevat, is het resultaat die naamruimte. - Als
E
een naamruimte is enE
een toegankelijk type met een naamI
enK
typeparameters bevat, is het resultaat dat het type is samengesteld met de opgegeven typeargumenten. - Als
E
is geclassificeerd als een type, alsE
geen typeparameter is en als een lidzoekactie (§12,5) vanI
inE
metK
typeparameters een overeenkomst produceert, wordtE.I
als volgt geëvalueerd en geclassificeerd:Opmerking: wanneer het resultaat van een dergelijke lidzoekactie een methodegroep is en
K
nul is, kan de methodegroep methoden met typeparameters bevatten. Hierdoor kunnen dergelijke methoden worden overwogen voor het afleiden van het typeargument. eindnotitie- Als
I
een type identificeert, is het resultaat dat type samengesteld met gegeven typeargumenten. - Als
I
een of meer methoden identificeert, is het resultaat een methodegroep zonder bijbehorende exemplaarexpressie. - Als
I
een statische eigenschap identificeert, is het resultaat een eigenschapstoegang zonder bijbehorende exemplaarexpressie. - Als
I
een statisch veld identificeert:- Als het veld alleen-lezen is en de verwijzing plaatsvindt buiten de statische constructor van de klasse of struct waarin het veld wordt gedeclareerd, dan is het resultaat een waarde, namelijk die van het statische veld
I
inE
. - Anders is het resultaat een variabele, namelijk het statische veld
I
inE
.
- Als het veld alleen-lezen is en de verwijzing plaatsvindt buiten de statische constructor van de klasse of struct waarin het veld wordt gedeclareerd, dan is het resultaat een waarde, namelijk die van het statische veld
- Als
I
een statische gebeurtenis identificeert:- Als de verwijzing plaatsvindt in de klasse of instructie waarin de gebeurtenis wordt gedeclareerd en de gebeurtenis is gedeclareerd zonder event_accessor_declarations (§15.8.1), wordt
E.I
precies verwerkt alsofI
een statisch veld zijn. - Anders is het resultaat een gebeurtenistoegang zonder bijbehorende exemplaarexpressie.
- Als de verwijzing plaatsvindt in de klasse of instructie waarin de gebeurtenis wordt gedeclareerd en de gebeurtenis is gedeclareerd zonder event_accessor_declarations (§15.8.1), wordt
- Als
I
een constante identificeert, is het resultaat een waarde, namelijk de waarde van die constante. - Als
I
een opsommingslid identificeert, is het resultaat een waarde, namelijk de waarde van dat opsommingslid. - Anders is
E.I
een ongeldige lidverwijzing en treedt er een compilatiefout op.
- Als
- Als
E
een eigenschapstoegang, indexeerfunctietoegang, variabele of waarde is, waarvan het typeT
is en een lidzoekactie (§12,5) vanI
inT
metK
typeargumenten een overeenkomst oplevert, wordtE.I
als volgt geëvalueerd en geclassificeerd:- Als
E
een eigenschap of indexeerfunctietoegang is, wordt de waarde van de toegang tot de eigenschap of indexeerfunctie verkregen (§12.2.2) en wordt E opnieuw geclassificeerd als een waarde. - Als
I
een of meer methoden identificeert, is het resultaat een methodegroep met een bijbehorende exemplaarexpressie vanE
. - Als
I
een exemplaareigenschap identificeert, is het resultaat een eigenschapstoegang met een bijbehorende exemplaarexpressie vanE
en een gekoppeld type dat het type van de eigenschap is. AlsT
een klassetype is, wordt het bijbehorende type gekozen uit de eerste declaratie of override van de eigenschap die wordt gevonden door te beginnen metT
en vervolgens door de basisklassen te zoeken. - Als
T
een class_type is enI
een exemplaarveld van die class_typeidentificeert:- Als de waarde van
E
null
is, wordt er eenSystem.NullReferenceException
gegenereerd. - Als het veld anderszins readonly is en de verwijzing buiten de instantieconstructor van de klasse waarin het veld gedeclareerd wordt, dan is het resultaat een waarde, namelijk de waarde van het veld
I
in het object die doorE
wordt gerefereerd. - Anders is het resultaat een variabele, namelijk het veld
I
in het object waarnaar wordt verwezen doorE
.
- Als de waarde van
- Als
T
een struct_type is enI
een exemplaarveld van die struct_typeidentificeert:- Als
E
een waarde is, of als het veld alleen-lezen is en de verwijzing plaatsvindt buiten een instantieconstructor van de structuur waarin het veld is gedeclareerd, dan is het resultaat een waarde, namelijk die van het veldI
binnen het doorE
gegeven structuurexemplaar. - Anders is het resultaat een variabele, namelijk het veld
I
in het struct-exemplaar dat is gegeven doorE
.
- Als
- Als
I
een instantie-gebeurtenis identificeert:- Als de verwijzing plaatsvindt in de klasse of de struct waarin de gebeurtenis wordt gedeclareerd en de gebeurtenis is gedeclareerd zonder event_accessor_declarations (§15.8.1), en de verwijzing niet voorkomt als de linkerkant van
a +=
of-=
operator, wordtE.I
precies verwerkt alsofI
een exemplaarveld is. - Anders is het resultaat een gebeurtenistoegang met een bijbehorende exemplaarexpressie van
E
.
- Als de verwijzing plaatsvindt in de klasse of de struct waarin de gebeurtenis wordt gedeclareerd en de gebeurtenis is gedeclareerd zonder event_accessor_declarations (§15.8.1), en de verwijzing niet voorkomt als de linkerkant van
- Als
- Anders wordt geprobeerd om
E.I
te verwerken als een aanroep van een uitbreidingsmethode (§12.8.10.3). Als dit mislukt, isE.I
een ongeldige lidverwijzing en treedt er een bindingstijdfout op.
12.8.7.2 Identieke eenvoudige namen en typenamen
Als bij een lidtoegang van de vorm E.I
E
een enkele identificator is, en als de betekenis van E
als een simple_name (§12.8.4) een constante, veld, eigenschap, lokale variabele of parameter is met hetzelfde type als de betekenis van E
als een type_name (§7.8.1), dan zijn beide interpretaties van E
toegestaan. Het opzoeken van leden van E.I
is nooit ondubbelzinnig, aangezien I
in beide gevallen noodzakelijkerwijs tot het type E
behoort. Met andere woorden, de regel maakt simpelweg toegang tot de statische leden en geneste typen van E
mogelijk waar anders een compilatiefout zou zijn opgetreden.
voorbeeld van:
struct Color { public static readonly Color White = new Color(...); public static readonly Color Black = new Color(...); public Color Complement() => new Color(...); } class A { public «Color» Color; // Field Color of type Color void F() { Color = «Color».Black; // Refers to Color.Black static member Color = Color.Complement(); // Invokes Complement() on Color field } static void G() { «Color» c = «Color».White; // Refers to Color.White static member } }
Uitsluitend voor verklarende doeleinden, binnen de
A
-klasse, worden de exemplaren van deColor
-identificator die naar hetColor
-type verwijzen, gemarkeerd met«...»
, terwijl degenen die naar hetColor
-veld verwijzen dat niet worden.einde voorbeeld
12.8.8 Null-voorwaardelijke lidtoegang
Een null_conditional_member_access is een voorwaardelijke versie van member_access (§12.8.7) en is een bindingstijdfout als het resultaattype is void
. Zie voor een null-voorwaardelijke expressie waarin het resultaattype kan worden void
(§12.8.11).
Een null_conditional_member_access bestaat uit een primary_expression gevolgd door de twee tokens "?
" en ".
", gevolgd door een -id met een optionele type_argument_list, gevolgd door nul of meer dependent_accessdie door een null_forgiving_operatorkunnen worden gevolgd.
null_conditional_member_access
: primary_expression '?' '.' identifier type_argument_list?
(null_forgiving_operator? dependent_access)*
;
dependent_access
: '.' identifier type_argument_list? // member access
| '[' argument_list ']' // element access
| '(' argument_list? ')' // invocation
;
null_conditional_projection_initializer
: primary_expression '?' '.' identifier type_argument_list?
;
Een null_conditional_member_access expressie E
heeft de vorm P?.A
. De betekenis van E
wordt als volgt bepaald:
Als het type
P
een null-waardetype is:Laat
T
het typeP.Value.A
zijn.Als
T
een typeparameter is waarvan niet bekend is of het een verwijzingstype of een waarde-type dat niet null is, optreedt er een fout tijdens de compilatietijd.Als
T
een niet-null-waardetype is, wordt het typeE
T?
en is de betekenis vanE
hetzelfde als de betekenis van:((object)P == null) ? (T?)null : P.Value.A
Behalve dat
P
slechts één keer wordt geëvalueerd.Anders is het type
E
T
en is de betekenis vanE
hetzelfde als de betekenis van:((object)P == null) ? (T)null : P.Value.A
Behalve dat
P
slechts één keer wordt geëvalueerd.
Anders:
Laat
T
het type expressieP.A
zijn.Als
T
een typeparameter is waarvan niet bekend is of het een verwijzingstype of een waarde-type dat niet null is, optreedt er een fout tijdens de compilatietijd.Als
T
een niet-null-waardetype is, wordt het typeE
T?
en is de betekenis vanE
hetzelfde als de betekenis van:((object)P == null) ? (T?)null : P.A
Behalve dat
P
slechts één keer wordt geëvalueerd.Anders is het type
E
T
en is de betekenis vanE
hetzelfde als de betekenis van:((object)P == null) ? (T)null : P.A
Behalve dat
P
slechts één keer wordt geëvalueerd.
Opmerking: In een expressie van het formulier:
P?.A₀?.A₁
Als
P
naarnull
evalueert, worden nochA₀
nochA₁
geëvalueerd. Hetzelfde geldt als een expressie een reeks null_conditional_member_access of null_conditional_element_access§12.8.13 bewerkingen is.eindnotitie
Een null_conditional_projection_initializer is een beperking van null_conditional_member_access en heeft dezelfde semantiek. Het gebeurt alleen als een projectie-initialisatiefunctie in een anonieme expressie voor het maken van objecten (§12.8.17.7).
12.8.9 Null-forgiving-expressies
12.8.9.1 Algemeen
De waarde, het type, de classificatie (§12.2) en de veilige context van een expressie (§16.4.12) is de waarde, het type, de classificatie en de veilige context van de primary_expression.
null_forgiving_expression
: primary_expression null_forgiving_operator
;
null_forgiving_operator
: '!'
;
Opmerking: de logische negatieoperators voor het postfix null-forgiving en voorvoegsel (§12.9.4), terwijl deze worden vertegenwoordigd door hetzelfde lexicale token (!
), zijn verschillend. Alleen de laatstgenoemde kan worden overschreven (§15.10), de definitie van de null-forgiving-operator is vastgesteld.
eindnotitie
Het is een compilatiefout om de null-vergevingsoperator meer dan één keer toe te passen op dezelfde uitdrukking, ongeacht tussenliggende haakjes.
voorbeeld: de volgende zijn allemaal ongeldig:
var p = q!!; // error: applying null_forgiving_operator more than once var s = ( ( m(t) ! ) )! // error: null_forgiving_operator applied twice to m(t)
einde voorbeeld
De rest van deze subclause en de volgende subclauses zijn voorwaardelijk normatief.
Een compiler die statische null-statusanalyse uitvoert (§8.9.5) moet voldoen aan de volgende specificatie.
De null-vergevingsgezinde operator is een pseudo-bewerking op compilatietijd die wordt gebruikt om de compiler te voorzien van informatie voor de statische analyse van de null-status. Het heeft twee toepassingen: om de vastberadenheid van een compiler te overschrijven dat een expressie misschien null-; en om een compiler te overschrijven die een waarschuwing geeft met betrekking tot null-baarheid.
Het toepassen van de null-forgiving operator op een expressie waarvoor de statische null-analyse van een compiler geen waarschuwingen geeft, is geen fout.
12.8.9.2 Het overschrijven van een 'misschien null'-bepaling
In sommige gevallen kan de statische null-statusanalyse van een compiler bepalen dat een expressie de null-status heeft misschien null- en een diagnostische waarschuwing geeft wanneer andere informatie aangeeft dat de expressie niet null kan zijn. Het toepassen van de null-vergevingsoperator op een dergelijke expressie informeert de statische null-statusanalyse van de compiler dat de null-status zich in bevindt en niet in null-. Hierdoor wordt de diagnostische waarschuwing voorkomen en kan dit invloed hebben op lopende analyses.
Voorbeeld: Houd rekening met het volgende:
#nullable enable public static void M() { Person? p = Find("John"); // returns Person? if (IsValid(p)) { Console.WriteLine($"Found {p!.Name}"); // p can't be null } } public static bool IsValid(Person? person) => person != null && person.Name != null;
Als
IsValid
true
retourneert, kanp
veilig worden gedereferentieerd om toegang te krijgen tot de eigenschapName
en kan de waarschuwing "dereferencing van een mogelijk nullwaarde" worden onderdrukt met behulp van!
.einde voorbeeld
Voorbeeld: De null-forgiving operator moet voorzichtig worden gebruikt; overweeg het volgende:
#nullable enable int B(int? x) { int y = (int)x!; // quash warning, throw at runtime if x is null return y; }
Hier wordt de null-forgiving-operator toegepast op een waardetype en onderdrukt deze eventuele waarschuwingen voor
x
. Alsx
tijdens runtime echternull
is, treedt er een uitzondering op omdatnull
niet naarint
kan worden gecast.einde voorbeeld
12.8.9.3 Overschrijven van andere null-analysewaarschuwingen
Naast het overschrijven van misschien null- bepaling zoals hierboven, kunnen er andere omstandigheden zijn waarin het wenselijk is om de statische null-statusanalyse van een compiler te overschrijven waarvoor een expressie een of meer waarschuwingen vereist. Het toepassen van de operator null-forgiving op een dergelijke expressie vraagt dat een compiler geen waarschuwingen voor de expressie uitgeeft. Als reactie kan een compiler ervoor kiezen om geen waarschuwingen uit te geven en kan het ook zijn verdere analyse wijzigen.
Voorbeeld: Houd rekening met het volgende:
#nullable enable public static void Assign(out string? lv, string? rv) { lv = rv; } public string M(string? t) { string s; Assign(out s!, t ?? "«argument was null»"); return s; }
De typen van methode
Assign
's parameters,lv
&rv
, zijnstring?
, waarbijlv
een uitvoerparameter is en het een eenvoudige toewijzing uitvoert.Methode
M
geeft de variabeles
van het typestring
door als uitvoerparameter vanAssign
. De gebruikte compiler geeft een waarschuwing omdats
geen nullable variabele is. Aangezien het tweede argument vanAssign
niet null kan zijn, wordt de operator null-forgiving gebruikt om de waarschuwing te vernietigen.einde voorbeeld
Einde van voorwaardelijk normatieve tekst.
12.8.10 Aanroepexpressies
12.8.10.1 Algemeen
Een invocation_expression wordt gebruikt om een methode aan te roepen.
invocation_expression
: primary_expression '(' argument_list? ')'
;
De primary_expression kan een null_forgiving_expression zijn als en alleen als het een delegate_typeheeft.
Een invocation_expression is dynamisch gebonden (§12.3.3) indien ten minste één van de volgende bewaringen bevat:
- De primary_expression heeft het compilatietijdstype
dynamic
. - Ten minste één argument van de optionele argument_list heeft het type compileertijd
dynamic
.
In dit geval classificeert de compiler de invocation_expression als een waarde van het type dynamic
. De onderstaande regels om de betekenis van de invocation_expression te bepalen, worden vervolgens tijdens de uitvoering toegepast met behulp van het uitvoeringstijdtype in plaats van het compileertijdtype van de primary_expression en argumenten met het compileertijdtype dynamic
. Als het primary_expression geen compileertijdtype heeft dynamic
, ondergaat de methodeaanroep een beperkte compileertijdcontrole, zoals beschreven in §12.6.5.
De primary_expression van een invocation_expression moet een methodegroep of een waarde van een delegate_typezijn. Als de primary_expression een methodegroep is, is de invocation_expression een methode-aanroep (§12.8.10.2). Als de primary_expression een waarde van een delegate_typeis, is de invocation_expression een gemachtigde aanroep (§12.8.10.4). Als de primary_expression geen methodegroep of een waarde van een delegate_typeis, treedt er een bindingstijdfout op.
De optionele argument_list (§12.6.2) bevat waarden of variabele verwijzingen voor de parameters van de methode.
Het resultaat van het evalueren van een invocation_expression wordt als volgt geclassificeerd:
- Als de invocation_expression een methode zonder waarde aanroept (§15.6.1) of een machtiging zonder waarde aanroept, is er geen resultaat. Een expressie die als niets is geclassificeerd, is alleen toegestaan in de context van een statement_expression (§13.7) of als hoofdtekst van een lambda_expression (§12.19). Anders treedt er een bindingstijdfout op.
- Anders, als de invocation_expression een methode als 'returns-by-ref' (§15.6.1) of een 'returns-by-ref' gedelegeerde aanroept, is het resultaat een variabele met een type dat overeenkomt met het retourtype van de methode of gedelegeerde. Als het gaat om de aanroep van een instantiemethode en de ontvanger van een klassetype
T
is, wordt het bijbehorende type gekozen uit de eerste declaratie of overschrijving van de methode die wordt gevonden door te beginnen metT
en de basisklassen door te zoeken. - Anders roept de invocation_expression een methode aan die een waarde retourneert (§15.6.1) of een delegate die een waarde retourneert, en het resultaat is een waarde met het type dat overeenkomt met het retourtype van de methode of de delegate. Als het gaat om de aanroep van een instantiemethode en de ontvanger van een klassetype
T
is, wordt het bijbehorende type gekozen uit de eerste declaratie of overschrijving van de methode die wordt gevonden door te beginnen metT
en de basisklassen door te zoeken.
12.8.10.2 Methode-aanroepen
Voor een methode-aanroep moet de primary_expression van de invocation_expression een methodegroep zijn. De methodegroep identificeert de ene methode die moet worden aangeroepen of de set overbelaste methoden waaruit een specifieke methode moet worden gekozen die moet worden aangeroepen. In het laatste geval is de bepaling van de specifieke methode die moet worden aangeroepen, gebaseerd op de context die wordt geboden door de typen argumenten in de argument_list.
De bindingstijdverwerking van een methodeaanroep van het formulier M(A)
, waarbij M
een methodegroep is (mogelijk inclusief een type_argument_list), en A
een optionele argument_listis, bestaat uit de volgende stappen:
- De set kandidaatmethoden voor de methode-aanroep wordt samengesteld. Voor elke methode
F
gekoppeld aan de methodegroepM
:- Als
F
niet-algemeen is, isF
een kandidaat wanneer:-
M
heeft geen lijst met typeargumenten en -
F
geldt voorA
(§12.6.4.2).
-
- Als
F
algemeen is enM
geen lijst met typeargumenten heeft, isF
een kandidaat wanneer:- Type deductie (§12.6.3) slaagt, waarbij een lijst met typeargumenten voor de aanroep wordt afgeleid en
- Zodra de uitgestelde typeargumenten worden vervangen door de overeenkomstige parametertypeparameters, voldoen alle samengestelde typen in de parameterlijst van
F
aan hun beperkingen (§8.4.5) en is de parameterlijst vanF
van toepassing opA
(§12.6.4.2)
- Als
F
algemeen is enM
een lijst met typeargumenten bevat, isF
een kandidaat wanneer:-
F
hetzelfde aantal parameters van het methodetype heeft als die zijn opgegeven in de lijst met typeargumenten en - Zodra de typeargumenten worden vervangen door de overeenkomstige parametertypeparameters, voldoen alle samengestelde typen in de parameterlijst van
F
aan hun beperkingen (§8.4.5), en is de parameterlijst vanF
van toepassing met betrekking totA
(§12.6.4.2).
-
- Als
- De set kandidaatmethoden wordt beperkt tot alleen methoden van de meest afgeleide typen: voor elke methode
C.F
in de set, waarbijC
het type is waarin de methodeF
wordt gedeclareerd, worden alle methoden die zijn gedeclareerd in een basistype vanC
uit de set verwijderd. AlsC
bovendien een ander klassetype is danobject
, worden alle methoden die in een interfacetype zijn gedeclareerd, uit de set verwijderd.Opmerking: deze laatste regel heeft alleen een effect wanneer de methodegroep het resultaat was van een opzoekactie van een lid op een typeparameter met een andere effectieve basisklasse dan
object
en een niet-lege effectieve interfaceset. eindnotitie - Als de resulterende set kandidaatmethoden leeg is, worden verdere verwerking van de volgende stappen stopgezet en wordt in plaats daarvan geprobeerd de aanroep te verwerken als een extensiemethodeaanroep (§12.8.10.3). Als dit mislukt, bestaan er geen toepasselijke methoden en treedt er een bindingstijdfout op.
- De beste methode van de set kandidaatmethoden wordt geïdentificeerd met behulp van de overbelastingsoplossingsregels van §12.6.4. Als één beste methode niet kan worden geïdentificeerd, is de aanroep van de methode dubbelzinnig en treedt er een bindingstijdfout op. Bij het uitvoeren van overbelastingsresolutie worden de parameters van een algemene methode overwogen na vervanging van de typeargumenten (opgegeven of afgeleid) voor de bijbehorende parameters van het methodetype.
Zodra een methode is geselecteerd en gevalideerd tijdens bindingstijd door de bovenstaande stappen, wordt de werkelijke aanroep van runtime verwerkt volgens de regels van de aanroep van functieleden die worden beschreven in §12.6.6.
Opmerking: Het intuïtieve effect van de hierboven beschreven oplossingsregels is als volgt: Als u de specifieke methode wilt vinden die wordt aangeroepen door een methodeaanroep, begint u met het type dat wordt aangegeven door de methodeaanroep en gaat u verder met de overnameketen totdat ten minste één toepasselijke, toegankelijke, niet-onderdrukkingsmethodedeclaratie wordt gevonden. Voer vervolgens type-inferentie en overbelasting resolutie uit op de reeks toepasselijke, toegankelijke, niet-override methoden die in dat type zijn gedeclareerd en roep de aldus geselecteerde methode aan. Als er geen methode is gevonden, probeert u in plaats daarvan de aanroep te verwerken als een aanroep van de extensiemethode. eindnotitie
12.8.10.3 Uitbreidingsmethode aanroepen
In een methode-aanroep (§12.6.6.2) van een van de formulieren
«expr» . «identifier» ( )
«expr» . «identifier» ( «args» )
«expr» . «identifier» < «typeargs» > ( )
«expr» . «identifier» < «typeargs» > ( «args» )
als bij de normale verwerking van de aanroep geen toepasselijke methoden worden gevonden, wordt geprobeerd de constructie te verwerken als een aanroep van de extensiemethode. Als «expr» of een van de «args» compileertijdtype dynamic
heeft, zijn extensiemethoden niet van toepassing.
Het doel is om de beste type_nameC
te vinden, zodat de bijbehorende statische methode-aanroep kan plaatsvinden:
C . «identifier» ( «expr» )
C . «identifier» ( «expr» , «args» )
C . «identifier» < «typeargs» > ( «expr» )
C . «identifier» < «typeargs» > ( «expr» , «args» )
Een extensiemethode Cᵢ.Mₑ
is in aanmerking komend als:
-
Cᵢ
is een niet-generieke, niet-geneste klasse - De naam van
Mₑ
is identificator -
Mₑ
is toegankelijk en van toepassing wanneer deze wordt toegepast op de argumenten als een statische methode, zoals hierboven wordt weergegeven - Er bestaat een impliciete identiteit-, verwijzings- of boxing-conversie van expr naar het type van de eerste parameter van
Mₑ
.
De zoekopdracht naar C
gaat als volgt:
- Beginnend met de dichtstbijzijnde naamruimtedeclaratie, doorgaand met elke declaratie van de naamruimte en eindigend met de bijbehorende compilatie-eenheid, worden opeenvolgende pogingen gedaan om een kandidaatset met extensiemethoden te vinden:
- Als de opgegeven naamruimte of compilatie-eenheid rechtstreeks niet-algemene typedeclaraties bevat
Cᵢ
met in aanmerking komende uitbreidingsmethodenMₑ
, is de set van deze uitbreidingsmethoden de kandidaatset. - Als naamruimten die worden geïmporteerd met behulp van naamruimte-instructies in de opgegeven naamruimte of compilatie-eenheid rechtstreeks niet-algemene typedeclaraties
Cᵢ
met in aanmerking komende extensiemethodenMₑ
bevatten, is de set van deze extensiemethoden de kandidaatset.
- Als de opgegeven naamruimte of compilatie-eenheid rechtstreeks niet-algemene typedeclaraties bevat
- Als er geen kandidaatset wordt gevonden in een naamruimtedeclaratie of compilatie-eenheid, treedt er een compilatietijdfout op.
- Anders wordt overbelastingsafhandeling toegepast op de kandidaatset zoals beschreven in §12.6.4. Als er geen enkele beste methode wordt gevonden, treedt er een compilatietijdfout op.
-
C
is het type waarin de beste methode wordt gedeclareerd als een extensiemethode.
Met behulp van C
als doel wordt de methode-aanroep vervolgens verwerkt als een statische methode-aanroep (§12.6.6).
Opmerking: in tegenstelling tot een aanroep van een instantiemethode wordt er geen uitzondering gegenereerd wanneer expr resulteert in een null-verwijzing. In plaats daarvan wordt deze
null
waarde doorgegeven aan de extensiemethode, zoals bij een normale statische methode-aanroep. Het is aan de implementatie van de uitbreidingsmethode om te bepalen hoe moet worden gereageerd op een dergelijke oproep. eindnotitie
De voorgaande regels betekenen dat instantiemethoden voorrang hebben op extensiemethoden, dat extensiemethoden die beschikbaar zijn in declaraties van binnennaamruimten voorrang hebben op extensiemethoden die beschikbaar zijn in declaraties van buitennaamruimten, en dat extensiemethoden die rechtstreeks in een naamruimte zijn gedeclareerd voorrang hebben op extensiemethoden die in dezelfde naamruimte zijn geïmporteerd met een using namespace-instructie.
voorbeeld van:
public static class E { public static void F(this object obj, int i) { } public static void F(this object obj, string s) { } } class A { } class B { public void F(int i) { } } class C { public void F(object obj) { } } class X { static void Test(A a, B b, C c) { a.F(1); // E.F(object, int) a.F("hello"); // E.F(object, string) b.F(1); // B.F(int) b.F("hello"); // E.F(object, string) c.F(1); // C.F(object) c.F("hello"); // C.F(object) } }
In het voorbeeld heeft de methode van
B
voorrang op de eerste extensiemethode en heeftC
methode voorrang op beide uitbreidingsmethoden.public static class C { public static void F(this int i) => Console.WriteLine($"C.F({i})"); public static void G(this int i) => Console.WriteLine($"C.G({i})"); public static void H(this int i) => Console.WriteLine($"C.H({i})"); } namespace N1 { public static class D { public static void F(this int i) => Console.WriteLine($"D.F({i})"); public static void G(this int i) => Console.WriteLine($"D.G({i})"); } } namespace N2 { using N1; public static class E { public static void F(this int i) => Console.WriteLine($"E.F({i})"); } class Test { static void Main(string[] args) { 1.F(); 2.G(); 3.H(); } } }
De uitvoer van dit voorbeeld is:
E.F(1) D.G(2) C.H(3)
D.G
heeft voorrang opC.G
enE.F
heeft voorrang op zowelD.F
alsC.F
.einde voorbeeld
12.8.10.4 Aanroepen delegeren
Voor de aanroep van een delegate moet de primary_expression van de invocation_expression een waarde zijn van een delegate_type. Aangezien de delegate_type bovendien een functielid met dezelfde parameterlijst als de delegate_typeis, is het delegate_type van toepassing (§12.6.4.2) met betrekking tot de argument_list van het invocation_expression.
De runtime-verwerking van een delegate-aanroep van de vorm D(A)
, waarbij D
een primary_expression is van een delegate_type en A
een optionele argument_listis, bestaat uit de volgende stappen:
-
D
wordt geëvalueerd. Als deze evaluatie een uitzondering veroorzaakt, worden er geen verdere stappen uitgevoerd. - De lijst met argumenten
A
wordt geëvalueerd. Als deze evaluatie een uitzondering veroorzaakt, worden er geen verdere stappen uitgevoerd. - De waarde van
D
is gecontroleerd op geldigheid. Als de waarde vanD
isnull
, wordt er eenSystem.NullReferenceException
gegenereerd en worden er geen verdere stappen uitgevoerd. - Anders is
D
een verwijzing naar een gedelegeerde instantie. Aanroepen van functieleden (§12.6.6) worden uitgevoerd op elk van de aanroepbare entiteiten in de aanroeplijst van de gemachtigde. Voor aanroepbare entiteiten die bestaan uit een instantie en een instantiemethode, is de instantie voor de oproep de instantie in de aanroepbare entiteit.
Zie §20.6 voor meer informatie over meerdere aanroeplijsten zonder parameters.
12.8.11 Null-voorwaardelijke aanroepuitdrukking
Een null_conditional_invocation_expression is syntactisch gezien ofwel een null_conditional_member_access (§12.8.8) of een null_conditional_element_access (§12.8.13), waarbij het laatste dependent_access een aanroepuitdrukking is (§12.8.10).
Een null_conditional_invocation_expression vindt plaats in het kader van een statement_expression (§13.7), anonymous_function_body (§12.19.1) of method_body (§15.6.1).
In tegenstelling tot het syntactisch equivalente null_conditional_member_access of null_conditional_element_access, kan een null_conditional_invocation_expression worden geclassificeerd als niets.
null_conditional_invocation_expression
: null_conditional_member_access null_forgiving_operator? '(' argument_list? ')'
| null_conditional_element_access null_forgiving_operator? '(' argument_list? ')'
;
De optionele null_forgiving_operator kan worden opgenomen als en alleen als de null_conditional_member_access of null_conditional_element_access een delegate_typeheeft.
Een null_conditional_invocation_expression expressie E
is van de vorm P?A
; waarbij A
de rest van de syntactisch equivalente null_conditional_member_access of null_conditional_element_accessis, begint A
daarom met .
of [
. Laat PA
de samenvoeging van P
en A
aanduiden.
Wanneer E
optreedt als een statement_expression, is de betekenis van E
hetzelfde als de betekenis van de -instructie.
if ((object)P != null) PA
behalve dat P
slechts één keer wordt geëvalueerd.
Wanneer E
voorkomt als een anonymous_function_body of method_body, is de betekenis van E
afhankelijk van de classificatie:
Als
E
als niets wordt geclassificeerd, is de betekenis ervan hetzelfde als de betekenis van het blok:{ if ((object)P != null) PA; }
behalve dat
P
slechts één keer wordt geëvalueerd.Anders is de betekenis van
E
hetzelfde als de betekenis van het blok:{ return E; }
en op zijn beurt hangt de betekenis van dit blok ervan af of
E
syntactisch gelijk is aan een null_conditional_member_access (§12.8.8) of null_conditional_element_access (§12.8.13).
12.8.12 Elementtoegang
12.8.12.1 Algemeen
Een element_access bestaat uit een primary_no_array_creation_expression, gevolgd door een ‘[
’ token, gevolgd door een argument_list, gevolgd door een ‘]
’ token. De argument_list bestaat uit een of meer arguments, gescheiden door komma's.
element_access
: primary_no_array_creation_expression '[' argument_list ']'
;
De argument_list van een element_access mag geen out
of ref
argumenten bevatten.
Een element_access is dynamisch gebonden (§12.3.3) indien ten minste een van de volgende bewaringen geldt:
- De primary_no_array_creation_expression heeft het type compilatietijd
dynamic
. - Ten minste één van de expressies in de argument_list heeft tijdens compileertijd het type
dynamic
en de primary_no_array_creation_expression heeft geen arraytype.
In dit geval classificeert de compiler de element_access als een waarde van het type dynamic
. De onderstaande regels om de betekenis van de element_access te bepalen, worden vervolgens tijdens runtime toegepast met behulp van het runtimetype in plaats van het type compileertijd van die van de primary_no_array_creation_expression en argument_list expressies met het type compileertijd dynamic
. Als de primary_no_array_creation_expression geen compilatietijdtype heeft dynamic
, ondergaat de toegang tot het element een beperkte controle tijdens compilatietijd, zoals beschreven in §12.6.5.
Als de primary_no_array_creation_expression van een element_access een waarde van een array_typeis, is de element_access een matrixtoegang (§12.8.12.2). Anders is de primary_no_array_creation_expression een variabele of waarde van een klasse, struct of interfacetype met een of meer indexers, in welk geval de element_access een indexertoegang is (§12.8.12.3).
12.8.12.2 Matrixtoegang
Voor een arraytoegang moet de primary_no_array_creation_expression van de element_access een waarde zijn van een array_type. Bovendien mag de argument_list van een matrixtoegang geen benoemde argumenten bevatten. Het aantal expressies in de argument_list moet gelijk zijn aan de rang van de array_typeen elke expressie moet van het type int
, uint
, long
of ulong,
zijn of impliciet worden omgezet in een of meer van deze typen.
Het resultaat van het evalueren van een matrixtoegang is een variabele van het elementtype van de matrix, namelijk het matrixelement dat is geselecteerd door de waarde(s) van de expressies in de argument_list.
De runtimeverwerking van een matrixtoegang van het formulier P[A]
, waarbij P
een primary_no_array_creation_expression is van een array_type en A
een argument_listis, bestaat uit de volgende stappen:
-
P
wordt geëvalueerd. Als deze evaluatie een uitzondering veroorzaakt, worden er geen verdere stappen uitgevoerd. - De indexexpressies van de argument_list worden op volgorde geëvalueerd, van links naar rechts. Na de evaluatie van elke indexexpressie wordt een impliciete conversie (§10,2) uitgevoerd op een van de volgende typen:
int
,uint
,long
,ulong
. Het eerste type in deze lijst waarvoor een impliciete conversie bestaat, wordt gekozen. Als de indexexpressie bijvoorbeeld van het typeshort
is, wordt een impliciete conversie naarint
uitgevoerd, omdat impliciete conversies vanshort
naarint
en vanshort
naarlong
mogelijk zijn. Als de evaluatie van een indexexpressie of de daaropvolgende impliciete conversie een uitzondering veroorzaakt, worden er geen verdere indexexpressies geëvalueerd en worden er geen verdere stappen uitgevoerd. - De waarde van
P
is gecontroleerd op geldigheid. Als de waarde vanP
isnull
, wordt er eenSystem.NullReferenceException
gegenereerd en worden er geen verdere stappen uitgevoerd. - De waarde van elke expressie in de argument_list wordt gecontroleerd aan de hand van de werkelijke limieten van elke dimensie van het matrixexemplaar waarnaar wordt verwezen door
P
. Als een of meer waarden buiten het bereik vallen, wordt er eenSystem.IndexOutOfRangeException
gegenereerd en worden er geen verdere stappen uitgevoerd. - De locatie van het matrixelement dat door de indexexpressies wordt gegeven, wordt berekend en deze locatie wordt het resultaat van de matrixtoegang.
12.8.12.3 Indexeerfunctietoegang
Voor toegang tot een indexeerfunctie moet de primary_no_array_creation_expression van de element_access een variabele of waarde van een klasse, struct of interfacetype zijn en dit type een of meer indexeerfuncties implementeren die van toepassing zijn op de argument_list van de element_access.
De bindingstijdverwerking van een indexeerfunctietoegang van de vorm P[A]
, waarbij P
een primary_no_array_creation_expression is van een klasse, struct of interfacetype T
, en A
een argument_listis, bestaat uit de volgende stappen:
- De set van indexeerfuncties die door
T
wordt geleverd, is samengesteld. De set bestaat uit alle indexeerfuncties die zijn gedeclareerd inT
of een basistype vanT
die geen declaraties overschrijven en die toegankelijk zijn in de huidige context (§7,5). - De set wordt gereduceerd tot de indexeerfuncties die van toepassing zijn en niet worden verborgen door andere indexeerfuncties. De volgende regels worden toegepast op elke indexeerfunctie
S.I
in de set, waarbijS
het type is waarin de indexeerfunctieI
wordt gedeclareerd:- Als
I
niet van toepassing is opA
(§12.6.4.2), wordtI
uit de set verwijderd. - Indien
I
van toepassing is opA
(§12.6.4.2), worden alle indexeerfuncties die zijn gedeclareerd in een basistype vanS
uit de set verwijderd. - Indien
I
van toepassing is opA
(§12.6.4.2) enS
een ander klassetype danobject
is, worden alle indexeerfuncties die in een interface zijn gedeclareerd, uit de set verwijderd.
- Als
- Als de resulterende set kandidaat-indexeerfuncties leeg is, bestaan er geen toepasselijke indexeerfuncties en treedt er een bindingstijdfout op.
- De beste indexeerfunctie van de set kandidaat-indexeerfuncties wordt geïdentificeerd met behulp van de overbelastingsoplossingsregels van §12.6.4. Als één beste indexeerfunctie niet kan worden geïdentificeerd, is de toegang van de indexeerfunctie niet eenduidig en treedt er een bindingstijdfout op.
- De indexexpressies van de argument_list worden op volgorde geëvalueerd, van links naar rechts. Het resultaat van het verwerken van de toegang tot de indexeerder is een expressie die als indexeerdertoegang wordt geclassificeerd. De toegangsexpressie van de indexeerfunctie verwijst naar de indexeerfunctie die is bepaald in de bovenstaande stap en heeft een bijbehorende exemplaarexpressie van
P
en een bijbehorende argumentenlijst vanA
en een gekoppeld type dat het type indexeerfunctie is. AlsT
een klassetype is, wordt het bijbehorende type gekozen uit de eerste declaratie of onderdrukking van de indexeerfunctie die is gevonden bij het starten metT
en het doorzoeken van de basisklassen.
Afhankelijk van de context waarin het wordt gebruikt, zorgt een indexertoegang voor de aanroep van de get-accessor of de set-accessor van de indexer. Als de toegang tot de indexeerfunctie het doel is van een toewijzing, wordt de settoegangsfunctie aangeroepen om een nieuwe waarde toe te wijzen (§12.21.2). In alle andere gevallen wordt de get accessor aangeroepen om de huidige waarde te verkrijgen (§12.2.2).
12.8.13 Voorwaardelijke toegang tot null-elementen
Een null_conditional_element_access bestaat uit een primary_no_array_creation_expression, gevolgd door de twee tokens "?
" en "[
", dan gevolgd door een argument_list, vervolgens een "]
" token, en daarna nul of meer dependent_access, die elk vooraf kunnen worden gegaan door een null_forgiving_operator.
null_conditional_element_access
: primary_no_array_creation_expression '?' '[' argument_list ']'
(null_forgiving_operator? dependent_access)*
;
Een null_conditional_element_access is een voorwaardelijke versie van element_access (§12.8.12) en het is een bindingstijdfout als het resultaattype is void
. Zie voor een null-voorwaardelijke expressie waarin het resultaattype kan worden void
(§12.8.11).
Een null_conditional_element_access expressie E
heeft de vorm P?[A]B
; waarbij B
de dependent_accesszijn, indien aanwezig. De betekenis van E
wordt als volgt bepaald:
Als het type
P
een null-waardetype is:Laat
T
het type expressieP.Value[A]B
zijn.Als
T
een typeparameter is waarvan niet bekend is of het een verwijzingstype of een waarde-type dat niet null is, optreedt er een fout tijdens de compilatietijd.Als
T
een niet-null-waardetype is, wordt het typeE
T?
en is de betekenis vanE
hetzelfde als de betekenis van:((object)P == null) ? (T?)null : P.Value[A]B
Behalve dat
P
slechts één keer wordt geëvalueerd.Anders is het type
E
T
en is de betekenis vanE
hetzelfde als de betekenis van:((object)P == null) ? null : P.Value[A]B
Behalve dat
P
slechts één keer wordt geëvalueerd.
Anders:
Laat
T
het type expressieP[A]B
zijn.Als
T
een typeparameter is waarvan niet bekend is of het een verwijzingstype of een waarde-type dat niet null is, optreedt er een fout tijdens de compilatietijd.Als
T
een niet-null-waardetype is, wordt het typeE
T?
en is de betekenis vanE
hetzelfde als de betekenis van:((object)P == null) ? (T?)null : P[A]B
Behalve dat
P
slechts één keer wordt geëvalueerd.Anders is het type
E
T
en is de betekenis vanE
hetzelfde als de betekenis van:((object)P == null) ? null : P[A]B
Behalve dat
P
slechts één keer wordt geëvalueerd.
Opmerking: In een expressie van het formulier:
P?[A₀]?[A₁]
Als
P
evalueert totnull
, worden nochA₀
, nochA₁
geëvalueerd. Hetzelfde geldt als een expressie een reeks null_conditional_element_access of null_conditional_member_access§12.8.8 bewerkingen is.eindnotitie
12.8.14 Deze toegang
Een this_access bestaat uit het trefwoord this
.
this_access
: 'this'
;
Een this_access is alleen toegestaan in het blok van een instantieconstructor, een instantiemethode, een exemplaartoegangsfunctie (§12.2.1) of een finalizer. Het heeft een van de volgende betekenissen:
- Wanneer
this
wordt gebruikt in een primary_expression binnen een instantieconstructor van een klasse, wordt deze geclassificeerd als een waarde. Het type van de waarde is het exemplaartype (§15.3.2) van de klasse waarin het gebruik plaatsvindt en de waarde is een verwijzing naar het object dat wordt samengesteld. - Wanneer
this
wordt gebruikt in een primary_expression binnen een instantiemethode of instantietoegangsmiddel van een klasse, wordt deze geclassificeerd als een waarde. Het type van de waarde is het exemplaartype (§15.3.2) van de klasse waarin het gebruik plaatsvindt en de waarde is een verwijzing naar het object waarvoor de methode of accessor is aangeroepen. - Wanneer
this
wordt gebruikt in een primary_expression binnen een instantieconstructor van een struct, wordt deze geclassificeerd als een variabele. Het type van de variabele is het exemplaartype (§15.3.2) van de struct waarin het gebruik plaatsvindt en de variabele vertegenwoordigt de struct die wordt samengesteld.- Als de constructordeclaratie geen constructor-initialisatiefunctie heeft, gedraagt de
this
variabele zich precies hetzelfde als een uitvoerparameter van het structtype. Dit betekent met name dat de variabele zeker in elk uitvoeringspad van de instantieconstructor moet worden toegewezen. - Anders gedraagt de
this
-variabele zich precies hetzelfde als eenref
parameter van het structtype. Dit betekent met name dat de variabele in eerste instantie wordt beschouwd als toegewezen.
- Als de constructordeclaratie geen constructor-initialisatiefunctie heeft, gedraagt de
- Wanneer
this
wordt gebruikt in een primary_expression binnen een instantiemethode of instantietoegangsmiddel van een struct, wordt deze geclassificeerd als een variabele. Het type variabele is het exemplaartype (§15.3.2) van de struct waarin het gebruik plaatsvindt.- Als de methode of accessor geen iterator is (§15.14) of asynchrone functie (§15.15), vertegenwoordigt de
this
variabele de struct waarvoor de methode of accessor is aangeroepen.- Als de struct een
readonly struct
is, gedraagt dethis
variabele zich precies hetzelfde als een invoerparameter van het structtype - Anders gedraagt de
this
-variabele zich precies hetzelfde als eenref
parameter van het structtype
- Als de struct een
- Als de methode of accessor een iterator- of asynchrone functie is, vertegenwoordigt de
this
variabele een kopie van de struct waarvoor de methode of accessor is aangeroepen en gedraagt zich precies hetzelfde als een waarde parameter van het structtype.
- Als de methode of accessor geen iterator is (§15.14) of asynchrone functie (§15.15), vertegenwoordigt de
Het gebruik van this
in een primary_expression in een andere context dan de hierboven genoemde is een compilatiefout. Het is met name niet mogelijk om te verwijzen naar this
in een statische methode, een accessor voor statische eigenschappen of in een variable_initializer van een velddeclaratie.
12.8.15 Basistoegang
Een base_access bestaat uit het trefwoord base
gevolgd door een token ".
", een identificator en een optionele type_argument_list, of een argument_list tussen vierkante haken.
base_access
: 'base' '.' identifier type_argument_list?
| 'base' '[' argument_list ']'
;
Een base_access wordt gebruikt voor toegang tot basisklasseleden die zijn verborgen door leden met vergelijkbare namen in de huidige klasse of structuur. Een base_access is alleen toegestaan in de hoofdtekst van een constructor van een instantie, een methode van een instantie, een toegangsfunctie van een instantie (§12.2.1) of een finalizer. Wanneer base.I
plaatsvindt in een klasse of struct, geeft I
een lid van de basisklasse van die klasse of struct aan. Wanneer base[E]
plaatsvindt in een klasse, moet een toepasselijke indexeerfunctie ook aanwezig zijn in de basisklasse.
Tijdens de bindingstijd worden base_access expressies van de vorm base.I
en base[E]
exact geëvalueerd alsof ze zijn geschreven ((B)this).I
en ((B)this)[E]
, waarbij B
de basisklasse is van de klasse of struct waarin de constructie plaatsvindt. Daarom komen base.I
en base[E]
overeen met this.I
en this[E]
, behalve this
wordt weergegeven als een exemplaar van de basisklasse.
Wanneer een base_access verwijst naar een lid van een virtuele functie (een methode, eigenschap of indexeerfunctie), wordt de bepaling van welk functielid tijdens runtime moet worden aangeroepen (§12.6.6) gewijzigd. Het functielid dat wordt aangeroepen, wordt bepaald door de meest afgeleide implementatie (§15.6.4) van het functielid te vinden met betrekking tot B
(in plaats van het uitvoeringstype van this
, zoals gebruikelijk in een niet-basistoegang). Zo kan binnen een overschrijving van een virtuele functielid een base_access worden gebruikt om de overgenomen implementatie van het functielid aan te roepen. Als het functielid waarnaar wordt verwezen door een base_access abstract is, treedt er een bindingstijdfout op.
Opmerking: in tegenstelling tot
this
isbase
geen expressie op zichzelf. Het is een trefwoord dat alleen wordt gebruikt in de context van een base_access of een constructor_initializer (§15.11.2). eindnotitie
12.8.16 Postfix incrementeer- en decrementeeroperators
post_increment_expression
: primary_expression '++'
;
post_decrement_expression
: primary_expression '--'
;
De operand van een bewerking voor het verhogen of verlagen van een voorvoegsel moet een expressie zijn die is geclassificeerd als een variabele, een eigenschapstoegang of een indexeerfunctietoegang. Het resultaat van de bewerking is een waarde van hetzelfde type als de operand.
Als het primary_expression het type compileertijd heeft dynamic
, is de operator dynamisch gebonden (§12.3.3), heeft de post_increment_expression of post_decrement_expression het type compileertijd dynamic
en worden de volgende regels toegepast tijdens runtime met behulp van het uitvoeringstype van de primary_expression.
Als de operand van een bewerking voor het verhogen of verlagen van een postfix een eigenschaps- of indexeerfunctietoegang is, moet de eigenschap of indexeerfunctie zowel een get- als een settoegangsfunctie hebben. Als dit niet het geval is, treedt er een bindingstijdfout op.
De oplossing voor overbelasting van unaire operatoren (§12.4.4) wordt toegepast om een specifieke operator-implementatie te selecteren. Vooraf gedefinieerde ++
- en --
operators bestaan voor de volgende typen: sbyte
, byte
, short
, ushort
, int
, uint
, long
, ulong
, char
, float
, double
, decimal
en een opsommingstype. De vooraf gedefinieerde ++
operators retourneren de waarde die wordt geproduceerd door 1
toe te voegen aan de operand en de vooraf gedefinieerde --
operators retourneren de waarde die wordt geproduceerd door 1
af te trekken van de operand. Als in een gecontroleerde context het resultaat van deze optel- of aftrekking buiten het bereik van het resultaattype valt en het resultaattype een integraal type of opsommingstype is, wordt er een System.OverflowException
gegenereerd.
Er moet een impliciete conversie zijn van het retourtype van de geselecteerde unaire operator naar het type van de primary_expression, anders treedt er een compilatietijdfout op.
De uitvoeringstijdverwerking van een postfix-increment- of -decrementbewerking van de vorm x++
of x--
bestaat uit de volgende stappen:
- Als
x
is geclassificeerd als een variabele:-
x
wordt geëvalueerd om de variabele te produceren. - De waarde van
x
wordt opgeslagen. - De opgeslagen waarde van
x
wordt geconverteerd naar het operandtype van de geselecteerde operator en de operator wordt aangeroepen met deze waarde als argument. - De waarde die door de operator wordt geretourneerd, wordt geconverteerd naar het type
x
en opgeslagen op de locatie die is opgegeven door de eerdere evaluatie vanx
. - De opgeslagen waarde van
x
wordt het resultaat van de bewerking.
-
- Als
x
is geclassificeerd als een eigenschap of indexertoegang:- De exemplaarexpressie (als
x
nietstatic
is) en de lijst met argumenten (alsx
een indexeerfunctietoegang is) die aanx
is gekoppeld, worden geëvalueerd en de resultaten worden gebruikt in de daaropvolgende aanroepen van de get- en set-toegangsmethodes. - De get accessor van
x
wordt aangeroepen en de geretourneerde waarde wordt opgeslagen. - De opgeslagen waarde van
x
wordt geconverteerd naar het operandtype van de geselecteerde operator en de operator wordt aangeroepen met deze waarde als argument. - De waarde die door de operator wordt geretourneerd, wordt geconverteerd naar het type
x
en de set accessor vanx
wordt aangeroepen met deze waarde als waardeargument. - De opgeslagen waarde van
x
wordt het resultaat van de bewerking.
- De exemplaarexpressie (als
De operators ++
en --
ondersteunen ook voorvoegsel notatie (§12.9.6). Het resultaat van x++
of x--
is de waarde van x
vóór de bewerking, terwijl het resultaat van ++x
of --x
de waarde van x
is na de bewerking. In beide gevallen heeft x
zelf dezelfde waarde na de bewerking.
Een operator ++
- of operator --
-implementatie kan worden aangeroepen met behulp van een achtervoegsel- of voorvoegselnotatie. Het is niet mogelijk om afzonderlijke operator-implementaties te hebben voor de twee notaties.
12.8.17 De nieuwe operator
12.8.17.1 Algemeen
De operator new
wordt gebruikt om nieuwe exemplaren van typen te maken.
Er zijn drie vormen van nieuwe expressies:
- Expressies voor het maken van objecten en anonieme expressies voor het maken van objecten worden gebruikt om nieuwe exemplaren van klassetypen en waardetypen te maken.
- Expressies voor het maken van matrices worden gebruikt om nieuwe exemplaren van matrixtypen te maken.
- Expressies voor het maken van delegates worden gebruikt om instanties van delegate-typen te creëren.
De operator new
impliceert het maken van een exemplaar van een type, maar impliceert niet noodzakelijkerwijs toewijzing van geheugen. In het bijzonder vereisen exemplaren van waardetypen geen extra geheugen buiten de variabelen waarin ze zich bevinden en vinden er geen toewijzingen plaats wanneer new
wordt gebruikt om instanties van waardetypen te maken.
Opmerking: expressies voor het maken van gedelegeerden maken niet altijd nieuwe instanties. Wanneer de expressie op dezelfde manier wordt verwerkt als een methodegroepconversie (§10,8) of een anonieme functieconversie (§10,7) kan dit ertoe leiden dat een bestaand gemachtigde exemplaar opnieuw wordt gebruikt. eindnotitie
12.8.17.2 Expressies voor het maken van objecten
Een object_creation_expression wordt gebruikt om een nieuw exemplaar van een class_type of een value_typete maken.
object_creation_expression
: 'new' type '(' argument_list? ')' object_or_collection_initializer?
| 'new' type object_or_collection_initializer
;
object_or_collection_initializer
: object_initializer
| collection_initializer
;
Het type van een object_creation_expression is een class_type, een value_typeof een type_parameter. Het type kan geen tuple_type of een abstracte of statische class_typezijn.
De optionele argument_list (§12.6.2) is alleen toegestaan als het type een class_type of een struct_typeis.
Een expressie voor het maken van objecten kan de lijst met constructorargumenten weglaten en haakjes plaatsen, mits deze een object-initialisatiefunctie of verzamelingsinitiizer bevat. Het weglaten van de lijst met constructorargumenten en het insluiten van haakjes is gelijk aan het opgeven van een lege argumentenlijst.
Verwerking van een expressie voor het maken van objecten die een object initialisatie of verzamelings initialisatie bevat, bestaat uit de eerste verwerking van de instantieconstructor en vervolgens de initialisaties van het lid of element die zijn opgegeven door de object initialisatie (§12.8.17.17.3) of de initialisatie van verzamelingen (§12.8.17.4).
Als een van de argumenten in de optionele argument_list het type compileertijd heeft dynamic
, is de object_creation_expression dynamisch gebonden (§12.3.3) en worden de volgende regels toegepast tijdens runtime met behulp van het runtimetype van deze argumenten van de argument_list met het type compileertijd dynamic
. Het maken van het object ondergaat echter een beperkte controle van de compilatietijd, zoals beschreven in §12.6.5.
De bindingstijdverwerking van een object_creation_expression van het formulier new T(A)
, waarbij T
een class_typeof een value_typeis, en A
een optionele argument_listis, bestaat uit de volgende stappen:
- Als
T
een value_type is enA
niet aanwezig is:- De object_creation_expression is een standaardconstructor-aanroep. Het resultaat van de object_creation_expression is een waarde van het type
T
, namelijk de standaardwaarde voorT
zoals gedefinieerd in §8.3.3.
- De object_creation_expression is een standaardconstructor-aanroep. Het resultaat van de object_creation_expression is een waarde van het type
- Anders, als
T
een type_parameter is enA
niet aanwezig is:- Als er geen waardetypebeperking of constructorbeperking (§15.2.5) is opgegeven voor
T
, treedt er een bindingstijdfout op. - Het resultaat van de object_creation_expression is een waarde van het runtimetype waaraan de typeparameter is gebonden, namelijk het resultaat van het aanroepen van de standaardconstructor van dat type. Het runtimetype kan een verwijzingstype of een waardetype zijn.
- Als er geen waardetypebeperking of constructorbeperking (§15.2.5) is opgegeven voor
- Anders, als
T
een class_type of een struct_typeis:- Als
T
een abstracte of statische class_typeis, treedt er een compilatietijdfout op. - De instantieconstructor die moet worden aangeroepen, wordt bepaald met behulp van de regels voor overbelastingsoplossing van §12.6.4. De set kandidaat-exemplaarconstructors bestaat uit alle toegankelijke exemplaarconstructors die zijn gedeclareerd in
T
, die van toepassing zijn opA
(§12.6.4.2). Als de set kandidaat-exemplaarconstructors leeg is of als één beste exemplaarconstructor niet kan worden geïdentificeerd, treedt er een bindingstijdfout op. - Het resultaat van de object_creation_expression is een waarde van het type
T
, namelijk de waarde die wordt geproduceerd door de instantieconstructor aan te roepen die in de bovenstaande stap is bepaald. - Anders is de object_creation_expression ongeldig en treedt er een bindingstijdfout op.
- Als
Zelfs als de object_creation_expression dynamisch is gebonden, is het type compileertijd nog steeds T
.
De runtimeverwerking van een object_creation_expression van de vorm new T(A)
, waarbij T
een class_type of een struct_type is en A
een optionele argument_listis, bestaat uit de volgende stappen:
- Als
T
een class_typeis:- Er wordt een nieuw exemplaar van klasse
T
toegewezen. Als er onvoldoende geheugen beschikbaar is om het nieuwe exemplaar toe te wijzen, wordt er eenSystem.OutOfMemoryException
gegenereerd en worden er geen verdere stappen uitgevoerd. - Alle velden van het nieuwe exemplaar worden geïnitialiseerd tot de standaardwaarden (§9,3).
- De instantieconstructor wordt aangeroepen volgens de regels van het aanroepen van functieleden (§12.6.6). Een verwijzing naar het zojuist toegewezen exemplaar wordt automatisch doorgegeven aan de instantieconstructor en het exemplaar kan als zodanig worden geopend vanuit die constructor.
- Er wordt een nieuw exemplaar van klasse
- Als
T
een struct_typeis:- Een exemplaar van het type
T
wordt gemaakt door een tijdelijke lokale variabele toe te wijzen. Aangezien een instantieconstructor van een struct_type is vereist om zeker een waarde toe te wijzen aan elk veld van het exemplaar dat wordt gemaakt, is er geen initialisatie van de tijdelijke variabele nodig. - De instantieconstructor wordt aangeroepen volgens de regels van het aanroepen van functieleden (§12.6.6). Een verwijzing naar het zojuist toegewezen exemplaar wordt automatisch doorgegeven aan de instantieconstructor en het exemplaar kan als zodanig worden geopend vanuit die constructor.
- Een exemplaar van het type
12.8.17.3 Object-initialisatoren
Een object initialisatie geeft waarden op voor nul of meer velden, eigenschappen of geïndexeerde elementen van een object.
object_initializer
: '{' member_initializer_list? '}'
| '{' member_initializer_list ',' '}'
;
member_initializer_list
: member_initializer (',' member_initializer)*
;
member_initializer
: initializer_target '=' initializer_value
;
initializer_target
: identifier
| '[' argument_list ']'
;
initializer_value
: expression
| object_or_collection_initializer
;
Een object-initialisator bestaat uit een reeks lid-initialisatoren, tussen {
en }
tokens en gescheiden door komma's. Elke member_initializer wijst een doel aan voor de initialisatie. Een identifier duidt op een toegankelijk veld of een eigenschap van het object dat wordt geïnitialiseerd, terwijl een argument_list tussen vierkante haken de argumenten specificeert voor een toegankelijke indexeerfunctie. Het is een fout dat een object-initialisatiefunctie meer dan één lid initialisatiefunctie voor hetzelfde veld of dezelfde eigenschap bevat.
Opmerking: hoewel een object-initialisatiefunctie niet meer dan één keer hetzelfde veld of dezelfde eigenschap mag instellen, zijn er geen dergelijke beperkingen voor indexeerfuncties. Een object-initialisatiefunctie kan meerdere initialisatiedoelen bevatten die verwijzen naar indexeerfuncties en kunnen zelfs meerdere keren dezelfde indexeerfunctieargumenten gebruiken. eindnotitie
Elke initializer_target wordt gevolgd door een gelijkteken en een expressie, een object-initialisatiefunctie of een verzamelings-initialisatiefunctie. Het is niet mogelijk voor expressies in de object-initialisatiefunctie om te verwijzen naar het zojuist gemaakte object dat het initialiseert.
Een lid-initialisator die een expressie na het gelijkteken specificeert, wordt op dezelfde manier verwerkt als een toewijzing (§12.21.2) op het doel.
Een lid-initialisatie die een object-initialisatie specificeert na het gelijkteken, is een geneste object-initialisatie, d.w.z. een initialisatie van een ingesloten object. In plaats van een nieuwe waarde toe te wijzen aan het veld of de eigenschap, worden de toewijzingen in de geneste object-initialisatiefunctie behandeld als toewijzingen aan leden van het veld of de eigenschap. Geneste object-initialisatiefuncties kunnen niet worden toegepast op eigenschappen met een waardetype of op alleen-lezenvelden met een waardetype.
Een lidinitialisator die na het gelijkteken een verzamelingsinitialisator specificeert, is een initialisatie van een ingesloten verzameling. In plaats van een nieuwe verzameling toe te wijzen aan het doelveld, de eigenschap of de indexeerfunctie, worden de elementen in de initialisatiefunctie toegevoegd aan de verzameling waarnaar wordt verwezen door het doel. Het doelobject moet van een verzamelingstype zijn dat voldoet aan de eisen gespecificeerd in §12.8.17.4.
Wanneer een initialisatiedoel verwijst naar een indexeerfunctie, worden de argumenten voor de indexeerfunctie altijd één keer geëvalueerd. Dus zelfs als de argumenten nooit worden gebruikt (bijvoorbeeld vanwege een lege geneste initialisatiefunctie), worden ze geëvalueerd op hun bijwerkingen.
Voorbeeld: De volgende klasse vertegenwoordigt een punt met twee coördinaten:
public class Point { public int X { get; set; } public int Y { get; set; } }
Een exemplaar van
Point
kan als volgt worden gemaakt en geïnitialiseerd:Point a = new Point { X = 0, Y = 1 };
Dit heeft hetzelfde effect als
Point __a = new Point(); __a.X = 0; __a.Y = 1; Point a = __a;
waarbij
__a
een onzichtbare en ontoegankelijke tijdelijke variabele is.In de volgende klasse ziet u een rechthoek die is gemaakt op basis van twee punten en het maken en initialiseren van een
Rectangle
-exemplaar:public class Rectangle { public Point P1 { get; set; } public Point P2 { get; set; } }
Een exemplaar van
Rectangle
kan als volgt worden gemaakt en geïnitialiseerd:Rectangle r = new Rectangle { P1 = new Point { X = 0, Y = 1 }, P2 = new Point { X = 2, Y = 3 } };
Dit heeft hetzelfde effect als
Rectangle __r = new Rectangle(); Point __p1 = new Point(); __p1.X = 0; __p1.Y = 1; __r.P1 = __p1; Point __p2 = new Point(); __p2.X = 2; __p2.Y = 3; __r.P2 = __p2; Rectangle r = __r;
waarbij
__r
,__p1
en__p2
tijdelijke variabelen zijn die anders onzichtbaar en niet toegankelijk zijn.Als de constructor van
Rectangle
de twee ingeslotenPoint
exemplaren toewijst, kunnen ze worden gebruikt om de ingeslotenPoint
exemplaren te initialiseren, in plaats van nieuwe toe te wijzen.public class Rectangle { public Point P1 { get; } = new Point(); public Point P2 { get; } = new Point(); }
de volgende constructie kan worden gebruikt om de ingesloten
Point
exemplaren te initialiseren in plaats van nieuwe exemplaren toe te wijzen:Rectangle r = new Rectangle { P1 = { X = 0, Y = 1 }, P2 = { X = 2, Y = 3 } };
Dit heeft hetzelfde effect als
Rectangle __r = new Rectangle(); __r.P1.X = 0; __r.P1.Y = 1; __r.P2.X = 2; __r.P2.Y = 3; Rectangle r = __r;
einde voorbeeld
12.8.17.4 Initializers voor verzameling
Met een initialisatiefunctie voor verzamelingen worden de elementen van een verzameling opgegeven.
collection_initializer
: '{' element_initializer_list '}'
| '{' element_initializer_list ',' '}'
;
element_initializer_list
: element_initializer (',' element_initializer)*
;
element_initializer
: non_assignment_expression
| '{' expression_list '}'
;
expression_list
: expression
| expression_list ',' expression
;
Een verzamelingsinitializer bestaat uit een reeks elementinitialisaties, tussen {
en }
tokens en gescheiden door komma's. Met elke element-initialisatiefunctie wordt een element opgegeven dat moet worden toegevoegd aan het verzamelingsobject dat wordt geïnitialiseerd en bestaat uit een lijst met expressies tussen {
en }
tokens en gescheiden door komma's. Een initialisatiefunctie voor één expressie-element kan zonder accolades worden geschreven, maar kan geen toewijzingsexpressie zijn om dubbelzinnigheid met initialisatie van leden te voorkomen. De non_assignment_expression productie wordt gedefinieerd in §12.22.
voorbeeld: hieronder ziet u een voorbeeld van een expressie voor het maken van objecten die een initialisatiefunctie voor verzamelingen bevat:
List<int> digits = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
einde voorbeeld
Het verzamelingsobject waarop een verzamelingsinitializer wordt toegepast, moet van een type zijn dat System.Collections.IEnumerable
implementeert, anders volgt er een compile-time fout. Voor elk opgegeven element in volgorde van links naar rechts wordt normale opzoekactie toegepast om een lid met de naam Add
te vinden. Als het resultaat van het opzoeken van leden geen methodegroep is, treedt er een compilatietijdfout op. Anders wordt overbelastingsresolutie toegepast met de expressielijst van de element-initialisatiefunctie als de argumentenlijst en roept de initialisatiefunctie voor de verzameling de resulterende methode aan. Het verzamelingsobject bevat dus een toepasselijke instantie of extensiemethode met de naam Add
voor elke element-initialisatiefunctie.
Voorbeeld: Hieronder ziet u een klasse die een contactpersoon vertegenwoordigt met een naam en een lijst met telefoonnummers, en het maken en initialiseren van een
List<Contact>
:public class Contact { public string Name { get; set; } public List<string> PhoneNumbers { get; } = new List<string>(); } class A { static void M() { var contacts = new List<Contact> { new Contact { Name = "Chris Smith", PhoneNumbers = { "206-555-0101", "425-882-8080" } }, new Contact { Name = "Bob Harris", PhoneNumbers = { "650-555-0199" } } }; } }
dat hetzelfde effect heeft als
var __clist = new List<Contact>(); Contact __c1 = new Contact(); __c1.Name = "Chris Smith"; __c1.PhoneNumbers.Add("206-555-0101"); __c1.PhoneNumbers.Add("425-882-8080"); __clist.Add(__c1); Contact __c2 = new Contact(); __c2.Name = "Bob Harris"; __c2.PhoneNumbers.Add("650-555-0199"); __clist.Add(__c2); var contacts = __clist;
waarbij
__clist
,__c1
en__c2
tijdelijke variabelen zijn die anders onzichtbaar en niet toegankelijk zijn.einde voorbeeld
12.8.17.5 Expressies voor het maken van matrices
Een array_creation_expression wordt gebruikt om een nieuw exemplaar van een array_typete maken.
array_creation_expression
: 'new' non_array_type '[' expression_list ']' rank_specifier*
array_initializer?
| 'new' array_type array_initializer
| 'new' rank_specifier array_initializer
;
Met een expressie voor het aanmaken van een array in de eerste vorm wordt een arrayinstantie van het type toegewezen die het resultaat is van het verwijderen van elk van de afzonderlijke expressies uit de expressielijst.
voorbeeld: de expressie voor het maken van een array
new int[10,20]
produceert een arrayexemplaar van het typeint[,]
, en de expressie voor het maken van een nieuwe arrayint[10][,]
produceert een arrayexemplaar van het typeint[][,]
. einde voorbeeld
Elke expressie in de expressielijst moet van het type int
, uint
, long
of ulong
zijn of impliciet kunnen worden omgezet in een of meer van deze typen. De waarde van elke uitdrukking bepaalt de lengte van de bijbehorende dimensie in de nieuw toegewezen array-instantie. Omdat de lengte van een matrixdimensie niet-negatief is, is het een compilatiefout om een constante expressie met een negatieve waarde in de expressielijst te hebben.
Behalve in een onveilige context (§23.2), is de indeling van matrices niet opgegeven.
Als een expressie voor het maken van een matrix van het eerste formulier een initialisatiefunctie voor een matrix bevat, moet elke expressie in de expressielijst een constante zijn en moeten de rang- en dimensielengten die door de expressielijst zijn opgegeven, overeenkomen met die van de initialisatiefunctie van de matrix.
In een expressie voor het maken van een matrix van het tweede of derde formulier moet de rangorde van het opgegeven matrixtype of rangaanduiding overeenkomen met die van de initialisatiefunctie van de matrix. De afzonderlijke dimensielengten worden afgeleid van het aantal elementen in elk van de bijbehorende nestniveaus van de matrix-initialisatiefunctie. De initialisatie-expressie in de volgende declaratie
var a = new int[,] {{0, 1}, {2, 3}, {4, 5}};
komt precies overeen met
var a = new int[3, 2] {{0, 1}, {2, 3}, {4, 5}};
Een expressie voor het maken van een matrix van het derde formulier wordt een impliciet getypte expressie voor het maken van matricesgenoemd. Het is vergelijkbaar met het tweede formulier, behalve dat het elementtype van de matrix niet expliciet wordt opgegeven, maar wordt bepaald als het beste gemeenschappelijke type (§12.6.3.15) van de set expressies in de matrix-initialisatiefunctie. Voor een multidimensionale matrix, d.w.z. een matrix waarin de rank_specifier ten minste één komma bevat, bestaat deze set uit alle expressiesdie zijn gevonden in geneste array_initializers.
Matrix initializers worden verder beschreven in §17.7.
Het resultaat van het evalueren van een expressie voor het aanmaken van een array wordt beschouwd als een waarde, namelijk een verwijzing naar het zojuist toegewezen array-exemplaar. De looptijdverwerking van een expressie voor het maken van een matrix bestaat uit de volgende stappen:
- De dimensielengte-expressies van de expression_list worden op volgorde geëvalueerd, van links naar rechts. Na de evaluatie van elke expressie wordt een impliciete conversie (§10,2) uitgevoerd op een van de volgende typen:
int
,uint
,long
,ulong
. Het eerste type in deze lijst waarvoor een impliciete conversie bestaat, wordt gekozen. Als de evaluatie van een expressie of de daaropvolgende impliciete conversie een uitzondering veroorzaakt, worden er geen verdere expressies geëvalueerd en worden er geen verdere stappen uitgevoerd. - De berekende waarden voor de dimensielengten worden als volgt gevalideerd: Als een of meer van de waarden kleiner zijn dan nul, wordt er een
System.OverflowException
gegenereerd en worden er geen verdere stappen uitgevoerd. - Een array-exemplaar met de opgegeven dimensielengten wordt toegewezen. Als er onvoldoende geheugen beschikbaar is om het nieuwe exemplaar toe te wijzen, wordt er een
System.OutOfMemoryException
gegenereerd en worden er geen verdere stappen uitgevoerd. - Alle elementen van de nieuwe arrayinstanties worden geïnitialiseerd met hun standaardwaarden (§9.3).
- Als de expressie voor het maken van de matrix een initialisatiefunctie voor matrices bevat, wordt elke expressie in de matrix-initialisatie geëvalueerd en toegewezen aan het bijbehorende matrixelement. De evaluaties en toewijzingen worden uitgevoerd in de volgorde waarin de expressies worden geschreven in de initialisatiefunctie van de matrix, met andere woorden: elementen worden geïnitialiseerd in toenemende indexvolgorde, waarbij de meest rechtse dimensie eerst toeneemt. Als de evaluatie van een bepaalde expressie of de volgende toewijzing aan het bijbehorende matrixelement een uitzondering veroorzaakt, worden er geen verdere elementen geïnitialiseerd (en de resterende elementen hebben dus hun standaardwaarden).
Met een expressie voor het maken van een matrix kan een matrix worden geïnstantieerd met elementen van een matrixtype, maar de elementen van een dergelijke matrix moeten handmatig worden geïnitialiseerd.
Voorbeeld: De verklaring
int[][] a = new int[100][];
maakt een eendimensionale matrix met 100 elementen van het type
int[]
. De initiële waarde van elk element isnull
. Het is niet mogelijk dat dezelfde expressie voor het maken van een matrix ook de submatrices instantiëren en de instructieint[][] a = new int[100][5]; // Error
resulteert in een compilatietijdfout. Instantiëring van de submatrices kan in plaats daarvan handmatig worden uitgevoerd, zoals in
int[][] a = new int[100][]; for (int i = 0; i < 100; i++) { a[i] = new int[5]; }
einde voorbeeld
Opmerking: Wanneer een array van arrays een "rechthoekige" vorm heeft, dat wil zeggen wanneer de sub-arrays allemaal dezelfde lengte hebben, is het efficiënter om een multidimensionale array te gebruiken. In het bovenstaande voorbeeld maakt instantiëring van de matrix van matrices 101 objecten: één buitenste matrix en 100 submatrices. Daarentegen,
int[,] a = new int[100, 5];
maakt slechts één object, een tweedimensionale matrix en bereikt de toewijzing in één instructie.
eindnotitie
voorbeeld van: Hieronder ziet u voorbeelden van impliciet getypte expressies voor het maken van matrices:
var a = new[] { 1, 10, 100, 1000 }; // int[] var b = new[] { 1, 1.5, 2, 2.5 }; // double[] var c = new[,] { { "hello", null }, { "world", "!" } }; // string[,] var d = new[] { 1, "one", 2, "two" }; // Error
De laatste expressie veroorzaakt een compilatiefout omdat noch
int
nochstring
impliciet converteerbaar is naar de andere, en er is dus geen best gemeenschappelijk type. In dit geval moet een expliciet getypte expressie voor het maken van een array worden gebruikt, bijvoorbeeld door het typeobject[]
op te geven. U kunt ook een van de elementen casten naar een gemeenschappelijk basistype, dat vervolgens het uitgestelde elementtype wordt.einde voorbeeld
Impliciet getypte expressies voor het maken van matrices kunnen worden gecombineerd met anonieme object initialisatiefuncties (§12.8.17.7) om anoniem getypte gegevensstructuren te maken.
voorbeeld van:
var contacts = new[] { new { Name = "Chris Smith", PhoneNumbers = new[] { "206-555-0101", "425-882-8080" } }, new { Name = "Bob Harris", PhoneNumbers = new[] { "650-555-0199" } } };
einde voorbeeld
12.8.17.6 Aanmaakexpressies voor gedelegeerden
Een delegate_creation_expression wordt gebruikt om een exemplaar van een delegate_typete verkrijgen.
delegate_creation_expression
: 'new' delegate_type '(' expression ')'
;
Het argument van een expressie voor het creëren van gedelegeerden moet een methodegroep, een anonieme functie, of een waarde van het compileertijdtype dynamic
of een delegate_typezijn. Als het argument een methodegroep is, identificeert het de methode en, voor een instantiemethode, het object waarvoor een gemachtigde moet worden gemaakt. Als het argument een anonieme functie is, definieert het rechtstreeks de parameters en de hoofdtekst van de methode van het gemachtigde doel. Als het argument een waarde is, wordt een gemachtigde instantie geïdentificeerd waarvan een kopie moet worden gemaakt.
Als de expressie het type compileertijd heeft dynamic
, wordt de delegate_creation_expression dynamisch gebonden (§12.8.17.6) en worden de onderstaande regels toegepast tijdens runtime met behulp van het uitvoeringstype van de -expressie. Anders worden de regels tijdens het compileren toegepast.
De bindingstijdverwerking van een delegate_creation_expression van de vorm nieuw D(E)
, waarbij D
een delegate_type is en E
een expressieis, bestaat uit de volgende stappen:
Als
E
een methodegroep is, wordt de expressie voor het maken van gemachtigden op dezelfde manier verwerkt als een methodegroepconversie (§10,8) vanE
totD
.Als
E
een anonieme functie is, wordt de expressie voor het maken van gemachtigden op dezelfde manier verwerkt als een anonieme functieconversie (§10,7) vanE
totD
.Als
E
een waarde is, moetE
compatibel zijn (§20.2) metD
, en het resultaat is een verwijzing naar een zojuist gemaakte gemachtigde met een aanroeplijst met één vermelding dieE
aanroept.
De runtimeverwerking van een delegate_creation_expression van de vorm new D(E)
, waarbij D
een delegate_type is en E
een expressieis, bestaat uit de volgende stappen:
- Als
E
een methodegroep is, wordt de expressie voor het maken van gemachtigden geëvalueerd als een methodegroepconversie (§10,8) vanE
naarD
. - Als
E
een anonieme functie is, wordt de creatie van de delegeer geëvalueerd als een omzetting van anonieme functie vanE
naarD
(§10.7). - Als
E
een waarde van een delegate_typeis:-
E
wordt geëvalueerd. Als deze evaluatie een uitzondering veroorzaakt, worden er geen verdere stappen uitgevoerd. - Als de waarde van
E
isnull
, wordt er eenSystem.NullReferenceException
gegenereerd en worden er geen verdere stappen uitgevoerd. - Er wordt een nieuw exemplaar van het delegate-type
D
toegewezen. Als er onvoldoende geheugen beschikbaar is om het nieuwe exemplaar toe te wijzen, wordt er eenSystem.OutOfMemoryException
gegenereerd en worden er geen verdere stappen uitgevoerd. - Het nieuwe gemachtigde exemplaar wordt geïnitialiseerd met een aanroeplijst met één vermelding die
E
aanroept.
-
De aanroeplijst van een gemachtigde wordt bepaald wanneer de gemachtigde wordt geïnstantieerd en blijft vervolgens constant gedurende de gehele levensduur van de gemachtigde. Met andere woorden, het is niet mogelijk om de aanroepbare doelobjecten van een delegate te wijzigen zodra deze gemaakt is.
Opmerking: Onthoud dat wanneer twee gedelegeerden worden gecombineerd of een van de andere wordt verwijderd, er een nieuwe gedelegeerde ontstaat; de inhoud van geen bestaande gedelegeerde verandert. eindnotitie
Het is niet mogelijk om een gemachtigde te maken die verwijst naar een eigenschap, indexeerfunctie, door de gebruiker gedefinieerde operator, instantieconstructor, finalizer of statische constructor.
Voorbeeld: Zoals hierboven beschreven, wordt bij het maken van een delegate van een methodegroep de parameterlijst en het retourtype van de delegate bepaald welke van de overbelaste methoden worden geselecteerd. In het voorbeeld
delegate double DoubleFunc(double x); class A { DoubleFunc f = new DoubleFunc(Square); static float Square(float x) => x * x; static double Square(double x) => x * x; }
het
A.f
veld wordt geïnitialiseerd met een gemachtigde die verwijst naar de tweedeSquare
methode, omdat die methode exact overeenkomt met de parameterlijst en het retourtype vanDoubleFunc
. Als de tweedeSquare
methode niet aanwezig was, zou er een compilatietijdfout zijn opgetreden.einde voorbeeld
12.8.17.7 Anonieme expressies voor het maken van objecten
Een anonymous_object_creation_expression wordt gebruikt om een object van een anoniem type te maken.
anonymous_object_creation_expression
: 'new' anonymous_object_initializer
;
anonymous_object_initializer
: '{' member_declarator_list? '}'
| '{' member_declarator_list ',' '}'
;
member_declarator_list
: member_declarator (',' member_declarator)*
;
member_declarator
: simple_name
| member_access
| null_conditional_projection_initializer
| base_access
| identifier '=' expression
;
Een anonieme object-initialisatiefunctie declareert een anoniem type en retourneert een exemplaar van dat type. Een anoniem type is een naamloos klassetype dat rechtstreeks wordt overgenomen van object
. De leden van een anoniem type zijn een reeks alleen-lezen eigenschappen die zijn afgeleid van de anonieme object-initialisatiefunctie die wordt gebruikt om een exemplaar van het type te maken. Een anonieme object-initialisatiefunctie van het formulier
new {
p₁=
e₁,
p₂=
e₂,
...
pv=
ev}
declareert een anoniem type van het formulier
class __Anonymous1
{
private readonly «T1» «f1»;
private readonly «T2» «f2»;
...
private readonly «Tn» «fn»;
public __Anonymous1(«T1» «a1», «T2» «a2»,..., «Tn» «an»)
{
«f1» = «a1»;
«f2» = «a2»;
...
«fn» = «an»;
}
public «T1» «p1» { get { return «f1»; } }
public «T2» «p2» { get { return «f2»; } }
...
public «Tn» «pn» { get { return «fn»; } }
public override bool Equals(object __o) { ... }
public override int GetHashCode() { ... }
}
waarbij elke «Tx» het type is van de bijbehorende expressie «ex». De expressie die in een member_declarator wordt gebruikt, heeft een type. Het is een compilatiefout dat een uitdrukking in een member_declaratornull
of een anonieme functie is.
De namen van een anoniem type en van de parameter aan de Equals
methode worden automatisch gegenereerd door de compiler en kunnen niet worden verwezen in programmatekst.
Binnen hetzelfde programma worden twee anonieme object-initialisatiefuncties die een reeks eigenschappen van dezelfde namen en compileertijdtypen in dezelfde volgorde opgeven, exemplaren van hetzelfde anonieme type produceren.
Voorbeeld: In het voorbeeld
var p1 = new { Name = "Lawnmower", Price = 495.00 }; var p2 = new { Name = "Shovel", Price = 26.95 }; p1 = p2;
de toewijzing op de laatste regel is toegestaan omdat
p1
enp2
van hetzelfde anonieme type zijn.einde voorbeeld
De methoden Equals
en GetHashcode
voor anonieme typen overschrijven de methoden die zijn overgenomen van object
en worden gedefinieerd in termen van de Equals
en GetHashcode
van de eigenschappen, zodat twee exemplaren van hetzelfde anonieme type gelijk zijn als en alleen als alle eigenschappen gelijk zijn.
Een liddeclaratie kan worden afgekort tot een eenvoudige naam (§12.8.4), een lidtoegang (§12.8.7), een null-conditionele projectie-initialisator (§12.8.8) of een basistoegang (§12.8.15). Dit wordt een projectie-initialisatie genoemd en is een afkorting voor een declaratie van en toewijzing aan een eigenschap met dezelfde naam. Met name liddeclaraties van de formulieren
«identifier»
, «expr» . «identifier»
en «expr» ? . «identifier»
zijn exact gelijk aan respectievelijk het volgende:
«identifer» = «identifier»
, «identifier» = «expr» . «identifier»
en «identifier» = «expr» ? . «identifier»
In een projectie-initialisatiefunctie selecteert de id dus zowel de waarde als het veld of de eigenschap waaraan de waarde is toegewezen. Intuïtief projecteert een projectie initializer niet alleen een waarde, maar ook de naam van de waarde.
12.8.18 De typeof-operator
De operator typeof
wordt gebruikt om het System.Type
-object voor een type te verkrijgen.
typeof_expression
: 'typeof' '(' type ')'
| 'typeof' '(' unbound_type_name ')'
| 'typeof' '(' 'void' ')'
;
unbound_type_name
: identifier generic_dimension_specifier?
| identifier '::' identifier generic_dimension_specifier?
| unbound_type_name '.' identifier generic_dimension_specifier?
;
generic_dimension_specifier
: '<' comma* '>'
;
comma
: ','
;
nl-NL: De eerste vorm van typeof_expression bestaat uit een typeof
trefwoord gevolgd door een type tussen haakjes. Het resultaat van een expressie van dit formulier is het System.Type
object voor het aangegeven type. Er is slechts één System.Type
object voor een bepaald type. Dit betekent dat voor een type T
, typeof(T) == typeof(T)
altijd waar is. Het type mag niet dynamic
zijn.
De tweede vorm van typeof_expression bestaat uit een typeof
trefwoord gevolgd door een unbound_type_nametussen haakjes.
Opmerking: een unbound_type_name is vergelijkbaar met een type_name (§7.8), behalve dat een unbound_type_namegeneric_dimension_specifierbevat waarin een type_nametype_argument_listbevat. eindnotitie
Wanneer de operand van een typeof_expression een reeks tokens is die voldoet aan de grammatica van zowel unbound_type_name als type_name, namelijk wanneer deze geen generic_dimension_specifier of een type_argument_listbevat, wordt de reeks tokens beschouwd als een type_name. De betekenis van een unbound_type_name wordt als volgt bepaald:
- Converteer de reeks tokens naar een type_name door elke generic_dimension_specifier te vervangen door een type_argument_list met hetzelfde aantal komma's en het trefwoord
object
als elke type_argument. - Evalueer de resulterende type_name, terwijl alle parameterbeperkingen van het type worden genegeerd.
- De unbound_type_name wordt omgezet in het niet-afhankelijke algemene type dat is gekoppeld aan het resulterende samengestelde type (§8.4).
Het is een fout als de typenaam een nullable referentietype is.
Het resultaat van de typeof_expression is het System.Type
object voor het resulterende ongebonden generieke type.
De derde vorm van typeof_expression bestaat uit een typeof
trefwoord, gevolgd door een void
trefwoord tussen haakjes. Het resultaat van een expressie van dit formulier is het System.Type
object dat de afwezigheid van een type aangeeft. Het typeobject dat wordt geretourneerd door typeof(void)
verschilt van het typeobject dat wordt geretourneerd voor elk type.
Opmerking: dit speciale
System.Type
-object is handig in klassebibliotheken die reflectie op methoden in de taal mogelijk maken, waarbij deze methoden een manier willen hebben om het retourtype van een methode weer te geven, inclusiefvoid
methoden, met een exemplaar vanSystem.Type
. eindnotitie
De operator typeof
kan worden gebruikt voor een typeparameter. Dit is een compilatietijdfout als de naam van het type een null-verwijzingstype is. Het resultaat is het System.Type
-object voor het runtimetype dat is gebonden aan de typeparameter. Als het runtimetype een nullable verwijzingstype is, is het resultaat het overeenkomstige niet-null-referentietype. De operator typeof
kan ook worden gebruikt voor een samengesteld type of een niet-afhankelijk algemeen type (§8.4.4). Het System.Type
-object voor een niet-afhankelijk algemeen type is niet hetzelfde als het System.Type
object van het exemplaartype (§15.3.2). Het exemplaartype is altijd een gesloten geconstrueerd type tijdens runtime, zodat het System.Type
object afhankelijk is van de argumenten van het runtimetype die in gebruik zijn. Het niet-afhankelijke algemene type heeft daarentegen geen typeargumenten en levert hetzelfde System.Type
object op, ongeacht de argumenten van het runtimetype.
voorbeeld: het voorbeeld
class X<T> { public static void PrintTypes() { Type[] t = { typeof(int), typeof(System.Int32), typeof(string), typeof(double[]), typeof(void), typeof(T), typeof(X<T>), typeof(X<X<T>>), typeof(X<>) }; for (int i = 0; i < t.Length; i++) { Console.WriteLine(t[i]); } } } class Test { static void Main() { X<int>.PrintTypes(); } }
produceert de volgende uitvoer:
System.Int32 System.Int32 System.String System.Double[] System.Void System.Int32 X`1[System.Int32] X`1[X`1[System.Int32]] X`1[T]
Houd er rekening mee dat
int
enSystem.Int32
hetzelfde type zijn. Het resultaat vantypeof(X<>)
is niet afhankelijk van het typeargument, maar het resultaat vantypeof(X<T>)
wel.einde voorbeeld
12.8.19 De grootte van de operator
De operator sizeof
retourneert het aantal 8-bits bytes dat wordt bezet door een variabele van een bepaald type. Het type dat is opgegeven als operand voor grootte moet een unmanaged_type zijn (§8,8).
sizeof_expression
: 'sizeof' '(' unmanaged_type ')'
;
Voor bepaalde vooraf gedefinieerde typen levert de operator sizeof
een constante int
waarde op, zoals wordt weergegeven in de onderstaande tabel:
Expressie- | resultaat |
---|---|
sizeof(sbyte) |
1 |
sizeof(byte) |
1 |
sizeof(short) |
2 |
sizeof(ushort) |
2 |
sizeof(int) |
4 |
sizeof(uint) |
4 |
sizeof(long) |
8 |
sizeof(ulong) |
8 |
sizeof(char) |
2 |
sizeof(float) |
4 |
sizeof(double) |
8 |
sizeof(bool) |
1 |
sizeof(decimal) |
16 |
Voor een enumtype T
is het resultaat van de expressie sizeof(T)
een constante waarde is die gelijk is aan de grootte van het onderliggende type, zoals hierboven is aangegeven. Voor alle andere operanden wordt de operator sizeof
opgegeven in §23.6.9.
12.8.20 De gecontroleerde en ongecontroleerde operators
De operators checked
en unchecked
worden gebruikt om de context voor overloopcontrole te beheren voor rekenkundige bewerkingen en conversies van integraal type.
checked_expression
: 'checked' '(' expression ')'
;
unchecked_expression
: 'unchecked' '(' expression ')'
;
De operator checked
evalueert de ingesloten expressie in een gecontroleerde context en de operator unchecked
evalueert de ingesloten expressie in een niet-gecontroleerde context. Een checked_expression of unchecked_expression komt precies overeen met een parenthesized_expression (§12.8.5), behalve dat de ingesloten expressie wordt geëvalueerd binnen de gespecificeerde context van overloopcontrole.
De context voor overloopcontrole kan ook worden beheerd via de checked
- en unchecked
-instructies (§13.12).
De volgende bewerkingen worden beïnvloed door de context van de overloopcontrole die is ingesteld door de gecontroleerde en niet-gecontroleerde operators en instructies:
- De vooraf gedefinieerde operatoren
++
en--
(§12.8.16 en §12.9.6), wanneer de operand een integraal of opsommingstype heeft. - De vooraf gedefinieerde
-
unaire operator (§12.9.3), wanneer de operand van een integraal type is. - De vooraf gedefinieerde
+
,-
,*
en/
binaire operatoren (§12.10), wanneer beide operanden van integrale of opsommingstypen zijn. - Expliciete numerieke conversies (§10.3.2) van het ene integraal- of enumtype naar een ander integraal of opsommingstype, of van
float
ofdouble
naar een integraal of opsommingstype.
Wanneer een van de bovenstaande bewerkingen een resultaat produceert dat te groot is om weer te geven in het doeltype, bepaalt de context waarin de bewerking wordt uitgevoerd het resulterende gedrag:
- Als de bewerking in een
checked
context een constante expressie is (§12.23), treedt er een compilatietijdfout op. Anders, als de bewerking tijdens runtime wordt uitgevoerd, wordt er eenSystem.OverflowException
opgeworpen. - In een
unchecked
context wordt het resultaat afgekapt door alle bits in hoge volgorde te verwijderen die niet in het doeltype passen.
Voor niet-constante expressies (§12.23) (expressies die tijdens runtime worden geëvalueerd) die niet zijn ingesloten door checked
of unchecked
operators of instructies, wordt de standaardoverloopcontrolecontext uitgeschakeld, tenzij externe factoren (zoals compilerswitches en configuratie van uitvoeringsomgevingen) voor gecontroleerde evaluatie worden aangeroepen.
Voor constante expressies (§12.23) (expressies die tijdens het compileren volledig kunnen worden geëvalueerd), wordt de standaardoverloopcontrolecontext altijd gecontroleerd. Tenzij een constante expressie expliciet in een unchecked
context wordt geplaatst, veroorzaakt overloop die optreedt tijdens de compilatietijd-evaluatie van de expressie altijd compilatietijdfouten.
De hoofdtekst van een anonieme functie wordt niet beïnvloed door checked
of unchecked
contexten waarin de anonieme functie plaatsvindt.
Voorbeeld: In de volgende code
class Test { static readonly int x = 1000000; static readonly int y = 1000000; static int F() => checked(x * y); // Throws OverflowException static int G() => unchecked(x * y); // Returns -727379968 static int H() => x * y; // Depends on default }
er worden geen compilatiefouten gerapporteerd omdat geen van de expressies tijdens het compileren kan worden geëvalueerd. Tijdens runtime genereert de methode
F
eenSystem.OverflowException
en retourneert de methodeG
–727379968 (de lagere 32 bits van het buitenbereikresultaat). Het gedrag van de methodeH
is afhankelijk van de standaardcontext voor overloopcontrole voor de compilatie, maar dit is hetzelfde alsF
of hetzelfde alsG
.einde voorbeeld
Voorbeeld: In de volgende code
class Test { const int x = 1000000; const int y = 1000000; static int F() => checked(x * y); // Compile-time error, overflow static int G() => unchecked(x * y); // Returns -727379968 static int H() => x * y; // Compile-time error, overflow }
de overlopen die optreden bij het evalueren van de constante uitdrukkingen in
F
enH
leiden tot compilatietijdfouten omdat de uitdrukkingen worden geëvalueerd in eenchecked
-context. Er treedt ook een overloop op bij het evalueren van de constante expressie inG
, maar omdat de evaluatie plaatsvindt in eenunchecked
context, wordt de overloop niet gerapporteerd.einde voorbeeld
De operators checked
en unchecked
beïnvloeden alleen de context van de overloopcontrole voor die bewerkingen die tekstueel zijn opgenomen binnen de tokens '(
' en ')
'. De operators hebben geen effect op functieleden die worden aangeroepen als gevolg van het evalueren van de ingesloten expressie.
Voorbeeld: In de volgende code
class Test { static int Multiply(int x, int y) => x * y; static int F() => checked(Multiply(1000000, 1000000)); }
het gebruik van
checked
in F heeft geen invloed op de evaluatie vanx * y
inMultiply
, dusx * y
wordt geëvalueerd in de standaardoverloopcontrolecontext.einde voorbeeld
De operator unchecked
is handig bij het schrijven van constanten van de ondertekende integrale typen in hexadecimale notatie.
voorbeeld van:
class Test { public const int AllBits = unchecked((int)0xFFFFFFFF); public const int HighBit = unchecked((int)0x80000000); }
Beide hexadecimale constanten hierboven zijn van het type
uint
. Omdat de constanten buiten hetint
-bereik vallen, zouden de casts naarunchecked
compileertijdfouten produceren zonder deint
-operator.einde voorbeeld
Opmerking: de operators en instructies van de
checked
enunchecked
stellen programmeurs in staat om bepaalde aspecten van sommige numerieke berekeningen te beheren. Het gedrag van sommige numerieke operators is echter afhankelijk van de gegevenstypen van hun operanden. Als u bijvoorbeeld twee decimalen vermenigvuldigt, resulteert dit altijd in een uitzondering op overloop, zelfs binnen een expliciet niet-gecontroleerd constructie. Op dezelfde manier resulteert het vermenigvuldigen van twee floats nooit in een uitzondering op overloop, zelfs binnen een expliciet gecontroleerde constructie. Bovendien worden andere operators nooit beïnvloed door de controlemodus, of dit nu standaard of expliciet is. eindnotitie
12.8.21 Standaardwaardeexpressies
Een standaardwaardeexpressie wordt gebruikt om de standaardwaarde (§9.3) van een type te verkrijgen.
default_value_expression
: explictly_typed_default
| default_literal
;
explictly_typed_default
: 'default' '(' type ')'
;
default_literal
: 'default'
;
Een default_literal vertegenwoordigt een standaardwaarde (§9,3). Het heeft geen type, maar kan worden geconverteerd naar elk type via een standaard letterlijke conversie (§10.2.16).
Het resultaat van een default_value_expression is de standaardwaarde (§9,3) van het expliciete type in een explictly_typed_default, of het doeltype van de conversie voor een default_value_expression.
Een default_value_expression is een constante expressie (§12.23) als het type een van de volgende is:
- een referentietype
- een typeparameter die bekend staat als referentietype (§8.2);
- een van de volgende waardetypen:
sbyte
,byte
,short
,ushort
,int
,uint
,long
,ulong
,char
,float
,double
,decimal
,bool,
; of - elke opsommingstype.
12.8.22 Stacktoewijzing
Met een stackallocatie-expressie wordt een blok geheugen toegewezen vanuit de uitvoeringsstack. De uitvoeringsstack is een geheugengebied waarin lokale variabelen worden opgeslagen. De uitvoeringsstack maakt geen deel uit van de beheerde heap. Het geheugen dat wordt gebruikt voor lokale variabele opslag, wordt automatisch hersteld wanneer de huidige functie wordt geretourneerd.
De veilige contextregels voor een stacktoewijzingsexpressie worden beschreven in §16.4.12.7.
stackalloc_expression
: 'stackalloc' unmanaged_type '[' expression ']'
| 'stackalloc' unmanaged_type? '[' constant_expression? ']' stackalloc_initializer
;
stackalloc_initializer
: '{' stackalloc_initializer_element_list '}'
;
stackalloc_initializer_element_list
: stackalloc_element_initializer (',' stackalloc_element_initializer)* ','?
;
stackalloc_element_initializer
: expression
;
De unmanaged_type (§8.8) geeft het type aan van de items die worden opgeslagen op de zojuist toegewezen locatie en de expressie het aantal van deze items aangeeft. Samen geven deze de vereiste toewijzingsgrootte op. Het type van de expressie kan impliciet worden omgezet naar het type int
.
Omdat de grootte van een stacktoewijzing niet negatief kan zijn, is het een compilatiefout om het aantal items op te geven als een constant_expression die een negatieve waarde oplevert.
Tijdens runtime als het aantal items dat moet worden toegewezen een negatieve waarde is, is het gedrag niet gedefinieerd. Als deze nul is, wordt er geen toewijzing gemaakt en wordt de geretourneerde waarde door de implementatie gedefinieerd. Als er onvoldoende geheugen beschikbaar is om de items toe te wijzen, wordt er een System.StackOverflowException
gegenereerd.
Wanneer er een stackalloc_initializer aanwezig is:
- Als unmanaged_type wordt weggelaten, wordt dit afgeleid volgens de regels voor het beste gemeenschappelijke type (§12.6.3.15) voor de reeks stackalloc_element_initializer-s.
- Als constant_expression wordt weggelaten, wordt aangenomen dat het overeenkomt met het aantal stackalloc_element_initializers.
- Indien de constant_expression aanwezig is, moet deze gelijk zijn aan het aantal stackalloc_element_initializer's.
Elke stackalloc_element_initializer heeft een impliciete conversie naar unmanaged_type (§10,2). De stackalloc_element_initializerelementen in het toegewezen geheugen initialiseren in toenemende volgorde, beginnend met het element bij index nul. Als er geen stackalloc_initializeris, is de inhoud van het zojuist toegewezen geheugen niet gedefinieerd.
Als een stackalloc_expression rechtstreeks optreedt als de initialisatie-expressie van een local_variable_declaration (§13.6.2), waarbij de local_variable_type een type aanwijzer (§23.3) of afgeleid (var
), dan is het resultaat van de stackalloc_expression een aanwijzer van het type T*
(§23,9). In dit geval moet de stackalloc_expression in onveilige code worden weergegeven. Anders is het resultaat van een stackalloc_expression een exemplaar van het type Span<T>
, waarbij T
het unmanaged_typeis:
-
Span<T>
(§C.3) is een verwijzingstype (§16.2.3), dat een geheugenblok weergeeft, hier het blok dat door de stackalloc_expressionis toegewezen, als een indexeerbare verzameling getypte (T
) items. - De eigenschap
Length
van het resultaat retourneert het aantal toegewezen items. - De indexeerder van het resultaat (§15,9) retourneert een variabele_referentie (§9,5) naar een item van het toegewezen blok en controleert het bereik.
Initialisaties voor stacktoewijzing zijn niet toegestaan in catch
of finally
blokken (§13.11).
Opmerking: er is geen manier om expliciet geheugen vrij te maken met behulp van
stackalloc
. eindnotitie
Alle stack-toegewezen geheugenblokken die zijn gemaakt tijdens de uitvoering van een functielid, worden automatisch verwijderd wanneer dat functielid terugkeert.
Behalve de stackalloc
-operator biedt C# geen vooraf gedefinieerde constructies voor het beheren van niet-garbage verzameld geheugen. Dergelijke services worden doorgaans geleverd door ondersteunende klassebibliotheken of rechtstreeks vanuit het onderliggende besturingssysteem geïmporteerd.
voorbeeld van:
// Memory uninitialized Span<int> span1 = stackalloc int[3]; // Memory initialized Span<int> span2 = stackalloc int[3] { -10, -15, -30 }; // Type int is inferred Span<int> span3 = stackalloc[] { 11, 12, 13 }; // Error; result is int*, not allowed in a safe context var span4 = stackalloc[] { 11, 12, 13 }; // Error; no conversion from Span<int> to Span<long> Span<long> span5 = stackalloc[] { 11, 12, 13 }; // Converts 11 and 13, and returns Span<long> Span<long> span6 = stackalloc[] { 11, 12L, 13 }; // Converts all and returns Span<long> Span<long> span7 = stackalloc long[] { 11, 12, 13 }; // Implicit conversion of Span<T> ReadOnlySpan<int> span8 = stackalloc int[] { 10, 22, 30 }; // Implicit conversion of Span<T> Widget<double> span9 = stackalloc double[] { 1.2, 5.6 }; public class Widget<T> { public static implicit operator Widget<T>(Span<double> sp) { return null; } }
In het geval van
span8
resulteertstackalloc
in eenSpan<int>
, die wordt geconverteerd door een impliciete operator naarReadOnlySpan<int>
. Op dezelfde manier wordt voorspan9
het resulterendeSpan<double>
geconverteerd naar het door de gebruiker gedefinieerde typeWidget<double>
met behulp van de conversie, zoals wordt weergegeven. einde voorbeeld
12.8.23 De naam van de operator
Een nameof_expression wordt gebruikt om de naam van een programma-entiteit te verkrijgen als een constante tekenreeks.
nameof_expression
: 'nameof' '(' named_entity ')'
;
named_entity
: named_entity_target ('.' identifier type_argument_list?)*
;
named_entity_target
: simple_name
| 'this'
| 'base'
| predefined_type
| qualified_alias_member
;
Omdat nameof
geen trefwoord is, is een nameof_expression altijd syntactisch dubbelzinnig met een aanroep van de eenvoudige naam nameof
. Omwille van compatibiliteitsredenen, als een naamzoekactie (§12.8.4) van de naam nameof
slaagt, wordt de uitdrukking beschouwd als een aanroep-uitdrukking, ongeacht of de aanroep geldig is. Anders is het een nameof_expression.
Eenvoudige naam- en lidtoegangsopzoekingen worden uitgevoerd op de named_entity tijdens de compilatietijd, volgens de regels beschreven in §12.8.4 en §12.8.7. Wanneer de zoekactie die wordt beschreven in §12.8.4 en §12.8.7 echter een fout oplevert omdat een exemplaarlid in een statische context is gevonden, veroorzaakt een nameof_expression geen dergelijke fout.
Het is een compilatiefout voor een named_entity die een methodegroep aanduidt om een type_argument_listte hebben. Het is een compilatietijdfout als een named_entity_target het type dynamic
heeft.
Een nameof_expression is een constante expressie van het type string
en heeft geen effect tijdens runtime. Het named_entity ervan wordt niet geëvalueerd en wordt genegeerd voor een definitieve toewijzingsanalyse (§9.4.4.22). De waarde is de laatste identificator van de named_entity vóór de optionele laatste type_argument_list, veranderd op de volgende manier:
- Het voorvoegsel "
@
", indien gebruikt, wordt verwijderd. - Elke unicode_escape_sequence wordt omgezet in het bijbehorende Unicode-teken.
- Alle formatting_characters worden verwijderd.
Dit zijn dezelfde transformaties die worden toegepast in §6.4.3 bij het testen van gelijkheid tussen id's.
Voorbeeld: De volgende illustratie toont de resultaten van verschillende
nameof
expressies, ervan uitgaande dat een algemeen typeList<T>
binnen deSystem.Collections.Generic
naamruimte is gedeclareerd.using TestAlias = System.String; class Program { static void Main() { var point = (x: 3, y: 4); string n1 = nameof(System); // "System" string n2 = nameof(System.Collections.Generic); // "Generic" string n3 = nameof(point); // "point" string n4 = nameof(point.x); // "x" string n5 = nameof(Program); // "Program" string n6 = nameof(System.Int32); // "Int32" string n7 = nameof(TestAlias); // "TestAlias" string n8 = nameof(List<int>); // "List" string n9 = nameof(Program.InstanceMethod); // "InstanceMethod" string n10 = nameof(Program.GenericMethod); // "GenericMethod" string n11 = nameof(Program.NestedClass); // "NestedClass" // Invalid // string x1 = nameof(List<>); // Empty type argument list // string x2 = nameof(List<T>); // T is not in scope // string x3 = nameof(GenericMethod<>); // Empty type argument list // string x4 = nameof(GenericMethod<T>); // T is not in scope // string x5 = nameof(int); // Keywords not permitted // Type arguments not permitted for method group // string x6 = nameof(GenericMethod<Program>); } void InstanceMethod() { } void GenericMethod<T>() { string n1 = nameof(List<T>); // "List" string n2 = nameof(T); // "T" } class NestedClass { } }
Mogelijk verrassende delen van dit voorbeeld zijn de resolutie van
nameof(System.Collections.Generic)
naar alleen 'Algemeen' in plaats van de volledige naamruimte, en vannameof(TestAlias)
naar 'TestAlias' in plaats van 'Tekenreeks'. einde voorbeeld
12.8.24 Anonieme methodeexpressies
Een anonymous_method_expression is een van de twee manieren om een anonieme functie te definiëren. Deze worden verder beschreven in §12.19.
12.9 Unaire operatoren
12.9.1 Algemeen
De +
, -
, !
(logische negatie §12.9.4 alleen), ~
, ++
, --
, cast- en await
operatoren worden de unaire operatoren genoemd.
Opmerking: de postfix null-forgiving operator (§12.8.9),
!
, vanwege zijn aard dat het alleen tijdens compilatietijd is en niet-overlaadbaar, wordt uitgesloten van de bovenstaande lijst. eindnotitie
unary_expression
: primary_expression
| '+' unary_expression
| '-' unary_expression
| logical_negation_operator unary_expression
| '~' unary_expression
| pre_increment_expression
| pre_decrement_expression
| cast_expression
| await_expression
| pointer_indirection_expression // unsafe code support
| addressof_expression // unsafe code support
;
pointer_indirection_expression (§23.6.2) en addressof_expression (§23.6.5) zijn alleen beschikbaar in onveilige code (§23).
Als de operand van een unary_expression het type compileertijd heeft dynamic
, is deze dynamisch gebonden (§12.3.3). In dit geval is het compilatietijdtype van de unary_expressiondynamic
, en de onderstaande resolutie zal plaatsvinden tijdens de uitvoeringstijd met behulp van het uitvoeringstijdtype van de operand.
12.9.2 Unary plus operator
Voor een werking van het formulier +x
wordt een unaire operator overbelastingsresolutie (§12.4.4) toegepast om een specifieke operator-implementatie te selecteren. De operand wordt geconverteerd naar het parametertype van de geselecteerde operator en het type van het resultaat is het retourtype van de operator. De vooraf gedefinieerde unaire plusoperators zijn:
int operator +(int x);
uint operator +(uint x);
long operator +(long x);
ulong operator +(ulong x);
float operator +(float x);
double operator +(double x);
decimal operator +(decimal x);
Voor elk van deze operators is het resultaat gewoon de waarde van de operand.
Opgeheven (§12.4.8) vormen van de niet-opgeheven vooraf gedefinieerde unaire plus hierboven gedefinieerde operatoren zijn ook vooraf gedefinieerd.
12.9.3 Unaire mintekenoperator
Voor een werking van het formulier –x
wordt een unaire operator overbelastingsresolutie (§12.4.4) toegepast om een specifieke operator-implementatie te selecteren. De operand wordt geconverteerd naar het parametertype van de geselecteerde operator en het type van het resultaat is het retourtype van de operator. De vooraf gedefinieerde unaire mintekenoperators zijn:
Negatie van gehele getallen:
int operator –(int x); long operator –(long x);
Het resultaat wordt berekend door
X
van nul af te trekken. Als de waarde vanX
de kleinste vertegenwoordigbare waarde is van het operandtype (−2¹ voorint
of −2⁶³ voorlong
), is de wiskundige negatie vanX
niet zichtbaar binnen het operandtype. Als dit gebeurt binnen eenchecked
context, wordt er eenSystem.OverflowException
gegenereerd; als deze zich in eenunchecked
context voordoet, is het resultaat de waarde van de operand en wordt de overloop niet gerapporteerd.Als de operand van de negatieoperator van het type
uint
is, wordt deze geconverteerd naar het typelong
en wordt het type van het resultaatlong
. Een uitzondering is de regel waarmee deint
waarde−2147483648
(−2¹¹) kan worden geschreven als een letterlijk geheel getal voor decimalen (§6.4.5.3).Als de operand van de negatieoperator van het type
ulong
is, treedt er een compilatietijdfout op. Een uitzondering is de regel waarmee delong
waarde−9223372036854775808
(−2⁶³) kan worden geschreven als een letterlijk decimaal geheel getal (§6.4.5.3)Negatie met drijvende komma:
float operator –(float x); double operator –(double x);
Het resultaat is de waarde van
X
waarbij het teken is omgekeerd. Alsx
NaN
is, wordt het resultaat ookNaN
.Decimale negatie
decimal operator –(decimal x);
Het resultaat wordt berekend door
X
van nul af te trekken. Decimaal negatie is gelijk aan het gebruik van de unaire min-operator van het typeSystem.Decimal
.
Opgeheven (§12.4.8) vormen van de onopgeheven, voorgedefinieerde unaire min-operatoren die hierboven zijn gedefinieerd, zijn ook voorgedefinieerd.
12.9.4 Logische negatieoperator
Voor een werking van het formulier !x
wordt een unaire operator overbelastingsresolutie (§12.4.4) toegepast om een specifieke operator-implementatie te selecteren. De operand wordt geconverteerd naar het parametertype van de geselecteerde operator en het type van het resultaat is het retourtype van de operator. Er bestaat slechts één vooraf gedefinieerde logische negatieoperator:
bool operator !(bool x);
Deze operator berekent de logische negatie van de operand: Als de operand is true
, wordt het resultaat false
. Als de operand false
is, wordt het resultaat true
.
Geheven (§12.4.8) vormen van de niet-geheven logische negatieoperator die hierboven is gedefinieerd, zijn eveneens vooraf gedefinieerd.
Opmerking: De voorvoegseloperator voor logische negatie en de postfix null-forgiving operator (§12.8.9) worden weliswaar door hetzelfde lexicale token (!
) uitgebeeld, maar zijn verschillend.
eindnotitie
12.9.5 Bitsgewijze complementoperator
Voor een werking van het formulier ~x
wordt een unaire operator overbelastingsresolutie (§12.4.4) toegepast om een specifieke operator-implementatie te selecteren. De operand wordt geconverteerd naar het parametertype van de geselecteerde operator en het type van het resultaat is het retourtype van de operator. De vooraf gedefinieerde bitsgewijze complementoperators zijn:
int operator ~(int x);
uint operator ~(uint x);
long operator ~(long x);
ulong operator ~(ulong x);
Voor elk van deze operators is het resultaat van de bewerking het bitsgewijze complement van x
.
Elk opsommingstype E
impliciet de volgende bitsgewijze complementoperator biedt:
E operator ~(E x);
Het resultaat van het evalueren van ~x
, waarbij X
een expressie is van een opsommingstype E
met een onderliggend type U
, is precies hetzelfde als het evalueren van (E)(~(U)x)
, behalve dat de conversie naar E
altijd wordt uitgevoerd alsof in een unchecked
context (§12.8.20).
Opgeheven (§12.4.8) vormen van de niet-opgeheven vooraf gedefinieerde bitwise complementoperators die hierboven zijn gedefinieerd, zijn ook vooraf gedefinieerd.
12.9.6 Voorvoegsel incrementeer en decrementeer operatoren
pre_increment_expression
: '++' unary_expression
;
pre_decrement_expression
: '--' unary_expression
;
De operand van een voorvoegselverhoging of -degradatiebewerking moet een expressie zijn die is geclassificeerd als een variabele, een eigenschapstoegang of een indexeerfunctietoegang. Het resultaat van de bewerking is een waarde van hetzelfde type als de operand.
Als de operand van het voorvoegsel van een incrementele of decrementele bewerking een eigenschap of indexertoegang is, moet de eigenschap of indexer zowel een get- als een set-toegangsfunctie hebben. Als dit niet het geval is, treedt er een bindingstijdfout op.
De oplossing voor overbelasting van unaire operatoren (§12.4.4) wordt toegepast om een specifieke operator-implementatie te selecteren. Vooraf gedefinieerde ++
- en --
operators bestaan voor de volgende typen: sbyte
, byte
, short
, ushort
, int
, uint
, long
, ulong
, char
, float
, double
, decimal
en een opsommingstype. De vooraf gedefinieerde ++
operators retourneren de waarde die wordt geproduceerd door 1
toe te voegen aan de operand en de vooraf gedefinieerde --
operators retourneren de waarde die wordt geproduceerd door 1
af te trekken van de operand. Als in een checked
-context het resultaat van deze optelling of aftrekking buiten het bereik van het resultaattype valt en het resultaattype een integraal type of opsommingstype is, wordt er een System.OverflowException
gegooid.
Er moet een impliciete conversie zijn van het retourtype van de geselecteerde unaire operator naar het type van de unary_expression, anders treedt er een compilatietijdfout op.
De runtimeverwerking van een voorvoegselverhoging of -degradatiebewerking van het formulier ++x
of --x
bestaat uit de volgende stappen:
- Als
x
is geclassificeerd als een variabele:-
x
wordt geëvalueerd om de variabele te produceren. - De waarde van
x
wordt geconverteerd naar het operandtype van de geselecteerde operator en de operator wordt aangeroepen met deze waarde als argument. - De waarde die door de operator wordt geretourneerd, wordt geconverteerd naar het type
x
. De resulterende waarde wordt opgeslagen op de locatie die is opgegeven door de evaluatie vanx
en wordt het resultaat van de bewerking.
-
- Als
x
is geclassificeerd als een eigenschap of indexertoegang:- De exemplaarexpressie (als
x
nietstatic
is) en de lijst met argumenten (alsx
een indexeerfunctietoegang is) die aanx
is gekoppeld, worden geëvalueerd en de resultaten worden gebruikt in de daaropvolgende aanroepen van de get- en set-toegangsmethodes. - De get-accessor van
x
wordt aangeroepen. - De waarde die door de get-accessor wordt geretourneerd, wordt geconverteerd naar het operandtype van de geselecteerde operator en de operator wordt aangeroepen met deze waarde als argument.
- De waarde die door de operator wordt geretourneerd, wordt geconverteerd naar het type
x
. De set accessor vanx
wordt aangeroepen met deze waarde als waardeargument. - Deze waarde wordt ook het resultaat van de bewerking.
- De exemplaarexpressie (als
De operators ++
en --
ondersteunen ook postfix-notatie (§12.8.16). Het resultaat van x++
of x--
is de waarde van x
vóór de bewerking, terwijl het resultaat van ++x
of --x
de waarde is van x
na de bewerking. In beide gevallen heeft x
zelf dezelfde waarde na de bewerking.
Een operator ++
- of operator --
-implementatie kan worden aangeroepen met behulp van een achtervoegsel- of voorvoegselnotatie. Het is niet mogelijk om afzonderlijke operator-implementaties te hebben voor de twee notaties.
Opgeheven (§12.4.8) vormen van de niet-opgeheven vooraf gedefinieerde voorvoegsel- en decrementoperatoren die hierboven zijn gedefinieerd, zijn ook vooraf gedefinieerd.
12.9.7 Cast-expressies
Een cast_expression wordt gebruikt om expliciet een expressie te converteren naar een bepaald type.
cast_expression
: '(' type ')' unary_expression
;
Een cast_expression van de vorm (T)E
, waarin T
een type is en E
een unary_expressionis, voert een expliciete conversie uit (§10.3) van de waarde van E
naar type T
. Als er geen expliciete conversie bestaat van E
naar T
, treedt er een bindingstijdfout op. Anders is het resultaat de waarde die wordt geproduceerd door de expliciete conversie. Het resultaat wordt altijd geclassificeerd als een waarde, zelfs als E
een variabele aangeeft.
De grammatica voor een cast_expression leidt tot bepaalde syntactische ambiguïteiten.
voorbeeld: de expressie
(x)–y
kan worden geïnterpreteerd als een cast_expression (een cast van–y
omx
te typen) of als een additive_expression gecombineerd met een parenthesized_expression (waarmee de waardex – y
wordt berekend). einde voorbeeld
Om cast_expression dubbelzinnigheden op te lossen, bestaat de volgende regel: Een reeks tokens (§6.4) tussen haakjes wordt beschouwd als het begin van een cast_expression alleen als ten minste één van de volgende waar is:
- De volgorde van tokens is de juiste grammatica voor een type, maar niet voor een expressie.
- De volgorde van tokens is de juiste grammatica voor een type en het token direct na de sluitende haakjes is het token "
~
", het token "!
", het token "(
", een identifier (§6.4.3), een letterlijk (§6.4.5), of een trefwoord (§6.4.4) met uitzondering vanas
enis
.
De term "juiste grammatica" hierboven betekent alleen dat de reeks tokens voldoet aan de specifieke grammaticale productie. Het houdt in het bijzonder geen rekening met de werkelijke betekenis van eventuele samenstellende id's.
voorbeeld: als
x
eny
id's zijn, isx.y
de juiste grammatica voor een type, zelfs alsx.y
geen type aangeeft. einde voorbeeld
Opmerking: Uit de ondubbelzinnigheidsregel volgt dat, als
x
eny
id's zijn,(x)y
,(x)(y)
en(x)(-y)
cast_expressionzijn, maar(x)-y
niet, zelfs niet alsx
een type identificeert. Alsx
echter een trefwoord is waarmee een vooraf gedefinieerd type (zoalsint
) wordt geïdentificeerd, worden alle vier de formulieren cast_expressions (omdat een dergelijk trefwoord geen expressie kan zijn). eindnotitie
12.9.8 Awaits-expressies
12.9.8.1 Algemeen
De operator await
wordt gebruikt om de evaluatie van de omringende asynchrone functie op te schorten totdat de asynchrone bewerking die wordt vertegenwoordigd door de operand is voltooid.
await_expression
: 'await' unary_expression
;
Een await_expression is alleen toegestaan in de hoofdtekst van een asynchrone functie (§15.15). Binnen de dichtstbijzijnde insluitende asynchrone functie vindt een await_expression niet plaats op deze plaatsen:
- Binnen een geneste (niet-asynchrone) anonieme functie
- Binnen het blok van een lock_statement
- In een anonieme functieconversie naar een expressiestructuurtype (§10.7.3)
- In een onveilige context
Opmerking: een await_expression kan niet voorkomen op de meeste plaatsen binnen een query_expression, omdat deze syntactisch zijn getransformeerd om niet-asynchrone lambda-expressies te gebruiken. eindnotitie
Binnen een asynchrone functie wordt await
niet gebruikt als een available_identifier, hoewel de exacte aanduiding @await
kan worden gebruikt. Er is dus geen syntactische dubbelzinnigheid tussen await_expressions en verschillende expressies met id's. Buiten asynchrone functies fungeert await
als een normale id.
De operand van een await_expression wordt de taakgenoemd. Het vertegenwoordigt een asynchrone bewerking die al dan niet voltooid is op het moment dat de await_expression wordt geëvalueerd. Het doel van de operator await
is het onderbreken van de uitvoering van de omhullende asynchrone functie totdat de afgewachte taak is voltooid en vervolgens het resultaat ervan verkrijgen.
12.9.8.2 Wachtbare uitdrukkingen
De taak van een await_expression moet afwachting mogelijk maken. Een expressie t
kan worden verwacht als een van de volgende bewaringen geldt:
-
t
is van het compilatietijdstypedynamic
-
t
heeft een toegankelijke instantie of extensiemethode met de naamGetAwaiter
zonder parameters en zonder typeparameters, en een retourtypeA
waarvoor al het volgende van toepassing is:-
A
implementeert de interfaceSystem.Runtime.CompilerServices.INotifyCompletion
(hierna bekend alsINotifyCompletion
voor beknoptheid) -
A
heeft een toegankelijke, leesbare instantieeigenschapIsCompleted
van het typebool
-
A
heeft een toegankelijke exemplaarmethodeGetResult
zonder parameters en geen typeparameters
-
Het doel van de GetAwaiter
-methode is om een afwachter voor een taak te verkrijgen. Het type A
wordt het afwachtertype genoemd voor de await-expressie.
Het doel van de eigenschap IsCompleted
is om te bepalen of de taak al is voltooid. Zo ja, dan hoeft u de evaluatie niet op te schorten.
Het doel van de INotifyCompletion.OnCompleted
methode is het registreren van een "vervolg" voor de taak; Een gemachtigde (van het type System.Action
) die wordt aangeroepen zodra de taak is voltooid.
Het doel van de GetResult
methode is het verkrijgen van het resultaat van de taak zodra deze is voltooid. Dit resultaat kan een succesvolle voltooiing zijn, mogelijk met een resultaatwaarde, of het kan een uitzondering zijn die wordt gegooid door de methode GetResult
.
12.9.8.3 Classificatie van await-expressies
De expressie await t
wordt op dezelfde manier geclassificeerd als de expressie (t).GetAwaiter().GetResult()
. Als het retourtype van GetResult
dus void
is, wordt de await_expression geclassificeerd als niets. Als het een niet-void
retourtype T
heeft, wordt de await_expression geclassificeerd als een waarde van het type T
.
12.9.8.4 Runtime-evaluatie van await-expressies
Tijdens runtime wordt de expressie await t
als volgt geëvalueerd:
- Een awaiter
a
wordt verkregen door de expressie(t).GetAwaiter()
te evalueren. - Een
bool
b
wordt verkregen door de uitdrukking(a).IsCompleted
te evalueren. - Als
b
isfalse
, hangt de evaluatie ervan af ofa
de interface-System.Runtime.CompilerServices.ICriticalNotifyCompletion
implementeert (hierna bekend alsICriticalNotifyCompletion
voor beknoptheid). Deze controle wordt uitgevoerd op het moment van binden; d.w.z., tijdens runtime alsa
het compileertijd typedynamic
heeft, en anders tijdens compileertijd. Geefr
de hervattingsdelegatie aan (§15.15):- Als
a
geenICriticalNotifyCompletion
implementeert, wordt de expressie((a) as INotifyCompletion).OnCompleted(r)
geëvalueerd. - Als
a
ICriticalNotifyCompletion
implementeert, wordt de expressie((a) as ICriticalNotifyCompletion).UnsafeOnCompleted(r)
geëvalueerd. - De evaluatie wordt vervolgens onderbroken en het besturingselement wordt teruggezet naar de huidige aanroeper van de asynchrone functie.
- Als
- Hetzij direct na (indien
b
istrue
) of bij latere aanroep van de hervattingsdelegatie (indienb
isfalse
), wordt de expressie(a).GetResult()
geëvalueerd. Als er een waarde wordt geretourneerd, is die waarde het resultaat van de await_expression. Anders is het resultaat niets.
De implementatie van de interfacemethoden INotifyCompletion.OnCompleted
en ICriticalNotifyCompletion.UnsafeOnCompleted
moet ervoor zorgen dat de gedelegeerde r
maximaal één keer wordt aangeroepen. Anders is het gedrag van de ingesloten asynchrone functie niet gedefinieerd.
12.10 Rekenkundige operatoren
12.10.1 Algemeen
De operatoren *
, /
, %
, +
en -
worden de rekenkundige operatoren genoemd.
multiplicative_expression
: unary_expression
| multiplicative_expression '*' unary_expression
| multiplicative_expression '/' unary_expression
| multiplicative_expression '%' unary_expression
;
additive_expression
: multiplicative_expression
| additive_expression '+' multiplicative_expression
| additive_expression '-' multiplicative_expression
;
Als een operand van een rekenkundige operator het type compileertijd heeft dynamic
, is de expressie dynamisch gebonden (§12.3.3). In dit geval is het type compilatietijd van de expressie dynamic
en vindt de onderstaande resolutie plaats tijdens runtime met behulp van het runtimetype van die operanden met het type compileertijd dynamic
.
12.10.2 Vermenigvuldigingsoperator
Voor een werking van het formulier x * y
wordt overbelastingsresolutie van binaire operatoren (§12.4.5) toegepast om een specifieke operator-implementatie te selecteren. De operanden worden geconverteerd naar de parametertypen van de geselecteerde operator en het type van het resultaat is het retourtype van de operator.
De vooraf gedefinieerde vermenigvuldigingsoperatoren worden hieronder vermeld. De operators berekenen allemaal het product van x
en y
.
Vermenigvuldiging van gehele getallen:
int operator *(int x, int y); uint operator *(uint x, uint y); long operator *(long x, long y); ulong operator *(ulong x, ulong y);
In een
checked
context, als het product buiten het bereik van het resultaattype valt, wordt er eenSystem.OverflowException
opgeworpen. In eenunchecked
context worden overloop niet gerapporteerd en worden belangrijke bits met hoge volgorde buiten het bereik van het resultaattype verwijderd.Vermenigvuldiging van drijvende komma:
float operator *(float x, float y); double operator *(double x, double y);
Het product wordt berekend volgens de regels van IEC 60559 rekenkundige berekeningen. De volgende tabel bevat de resultaten van alle mogelijke combinaties van niet-nulwaarden, nullen, infiniteiten en NaN's. In de tabel zijn
x
eny
positieve eindige waarden.z
is het resultaat vanx * y
, afgerond op de dichtstbijzijnde vertegenwoordigbare waarde. Als de grootte van het resultaat te groot is voor het doeltype,z
oneindig is. Vanwege afronding kanz
nul zijn, ook al isx
nochy
nul.+y
-y
+0
-0
+∞
-∞
NaN
+x
+z
-z
+0
-0
+∞
-∞
NaN
-x
-z
+z
-0
+0
-∞
+∞
NaN
+0
+0
-0
+0
-0
NaN
NaN
NaN
-0
-0
+0
-0
+0
NaN
NaN
NaN
+∞
+∞
-∞
NaN
NaN
+∞
-∞
NaN
-∞
-∞
+∞
NaN
NaN
-∞
+∞
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
(Tenzij anders vermeld, betekent in de tabellen met drijvende komma in §12.10.2–§12.10.6 het gebruik van "
+
" de waarde positief is; het gebruik van "-
" betekent dat de waarde negatief is; en het ontbreken van een teken betekent dat de waarde positief of negatief kan zijn of geen teken heeft (NaN).)Decimale vermenigvuldiging:
decimal operator *(decimal x, decimal y);
Als de grootte van de resulterende waarde te groot is om weer te geven in de decimale notatie, wordt er een
System.OverflowException
gegenereerd. Vanwege afronding kan het resultaat nul zijn, ook al is geen operand nul. De schaal van het resultaat, vóór afronding, is de som van de schalen van de twee operanden. Decimale vermenigvuldiging is gelijk aan het gebruik van de vermenigvuldigingsoperator van het typeSystem.Decimal
.
Opgeheven (§12.4.8) vormen van de niet-opgeheven vooraf gedefinieerde vermenigvuldigingsoperatoren die hierboven zijn gedefinieerd, zijn ook vooraf gedefinieerd.
12.10.3 Divisieoperator
Voor een werking van het formulier x / y
wordt overbelastingsresolutie van binaire operatoren (§12.4.5) toegepast om een specifieke operator-implementatie te selecteren. De operanden worden geconverteerd naar de parametertypen van de geselecteerde operator en het type van het resultaat is het retourtype van de operator.
Hieronder vindt u de vooraf gedefinieerde divisieoperators. De operators berekenen allemaal het quotiënt van x
en y
.
Deling van gehele getallen:
int operator /(int x, int y); uint operator /(uint x, uint y); long operator /(long x, long y); ulong operator /(ulong x, ulong y);
Als de waarde van de rechteroperand nul is, wordt er een
System.DivideByZeroException
gegooid.De divisie rondt het resultaat af op nul. De absolute waarde van het resultaat is dus het grootst mogelijke gehele getal dat kleiner is dan of gelijk is aan de absolute waarde van het quotiënt van de twee operanden. Het resultaat is nul of positief wanneer de twee operanden hetzelfde teken en nul of negatief hebben wanneer de twee operanden tegengestelde tekens hebben.
Als de linkeroperand de kleinste vertegenwoordigbare
int
oflong
waarde is en de rechteroperand–1
is, treedt er een overloop op. In eenchecked
context wordt hierdoor eenSystem.ArithmeticException
(of een subklasse daarvan) opgeworpen. In eenunchecked
-context ligt het aan de implementatie of eenSystem.ArithmeticException
(of een subklasse daarvan) wordt geworpen of dat de overloop niet gerapporteerd wordt, met als resulterende waarde die van de linkeroperand.Deling met drijvende komma
float operator /(float x, float y); double operator /(double x, double y);
Het quotiënt wordt berekend volgens de regels van IEC 60559-rekenkundige bewerkingen. De volgende tabel bevat de resultaten van alle mogelijke combinaties van niet-nulwaarden, nullen, infiniteiten en NaN's. In de tabel zijn
x
eny
positieve eindige waarden.z
is het resultaat vanx / y
, afgerond op de dichtstbijzijnde vertegenwoordigbare waarde.+y
-y
+0
-0
+∞
-∞
NaN
+x
+z
-z
+∞
-∞
+0
-0
NaN
-x
-z
+z
-∞
+∞
-0
+0
NaN
+0
+0
-0
NaN
NaN
+0
-0
NaN
-0
-0
+0
NaN
NaN
-0
+0
NaN
+∞
+∞
-∞
+∞
-∞
NaN
NaN
NaN
-∞
-∞
+∞
-∞
+∞
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
Decimaal deling:
decimal operator /(decimal x, decimal y);
Als de waarde van de rechteroperand nul is, wordt er een
System.DivideByZeroException
gegooid. Als de grootte van de resulterende waarde te groot is om weer te geven in de decimale notatie, wordt er eenSystem.OverflowException
gegenereerd. Vanwege afronding kan het resultaat nul zijn, ook al is de eerste operand niet nul. De schaal van het resultaat, vóór afronding, is de dichtstbijzijnde schaal naar de voorkeursschaal die een resultaat behoudt dat gelijk is aan het exacte resultaat. De voorkeursschaal is de schaal vanx
minder de schaal vany
.Decimaal deling is gelijk aan het gebruik van de delingsoperator van het type
System.Decimal
.
Opgeheven (§12.4.8) vormen van de niet-opgeheven vooraf gedefinieerde divisieoperators die hierboven zijn gedefinieerd, zijn ook vooraf gedefinieerd.
12.10.4 Restoperator
Voor een werking van het formulier x % y
wordt overbelastingsresolutie van binaire operatoren (§12.4.5) toegepast om een specifieke operator-implementatie te selecteren. De operanden worden geconverteerd naar de parametertypen van de geselecteerde operator en het type van het resultaat is het retourtype van de operator.
De vooraf gedefinieerde restoperators worden hieronder weergegeven. De operators berekenen allemaal de rest van de deling tussen x
en y
.
Rest van een geheel getal
int operator %(int x, int y); uint operator %(uint x, uint y); long operator %(long x, long y); ulong operator %(ulong x, ulong y);
Het resultaat van
x % y
is de waarde die wordt geproduceerd doorx – (x / y) * y
. Alsy
nul is, wordt er eenSystem.DivideByZeroException
opgeworpen.Als de linkeroperand de kleinste
int
oflong
waarde is en de rechteroperand–1
is, wordt er eenSystem.OverflowException
gegenereerd als en alleen alsx / y
een uitzondering zou genereren.Rest van drijvende komma:
float operator %(float x, float y); double operator %(double x, double y);
De volgende tabel bevat de resultaten van alle mogelijke combinaties van niet-nulwaarden, nullen, infiniteiten en NaN's. In de tabel zijn
x
eny
positieve eindige waarden.z
is het resultaat vanx % y
en wordt berekend alsx – n * y
, waarbij n het grootst mogelijke gehele getal is dat kleiner is dan of gelijk is aanx / y
. Deze methode voor het berekenen van de rest is vergelijkbaar met de methode die wordt gebruikt voor gehele getallen, maar verschilt van de IEC 60559-definitie (waarbijn
het gehele getal is dat het dichtst bijx / y
ligt).+y
-y
+0
-0
+∞
-∞
NaN
+x
+z
+z
NaN
NaN
+x
+x
NaN
-x
-z
-z
NaN
NaN
-x
-x
NaN
+0
+0
+0
NaN
NaN
+0
+0
NaN
-0
-0
-0
NaN
NaN
-0
-0
NaN
+∞
NaN
NaN
NaN
NaN
NaN
NaN
NaN
-∞
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
Decimale restwaarde
decimal operator %(decimal x, decimal y);
Als de waarde van de rechteroperand nul is, wordt er een
System.DivideByZeroException
gegooid. Het is implementatie-afhankelijk wanneer eenSystem.ArithmeticException
(of een subklasse daarvan) wordt opgeworpen. Een conforme implementatie genereert geen uitzondering voorx % y
in elk geval wanneerx / y
geen uitzondering genereert. De schaal van het resultaat, vóór afronding, is de grotere schaal van de twee operanden en het teken van het resultaat is, indien niet-nul, gelijk aan dat vanx
.Decimale rest is gelijk aan het gebruik van de restoperator van type
System.Decimal
.Opmerking: deze regels zorgen ervoor dat voor alle typen het resultaat nooit het tegenovergestelde teken van de linkeroperand heeft. eindnotitie
Opgeheven (§12.4.8) vormen van de niet-opgeheven vooraf gedefinieerde restoperators die hierboven zijn gedefinieerd, zijn ook vooraf gedefinieerd.
12.10.5 Plusoperator
Voor een werking van het formulier x + y
wordt overbelastingsresolutie van binaire operatoren (§12.4.5) toegepast om een specifieke operator-implementatie te selecteren. De operanden worden geconverteerd naar de parametertypen van de geselecteerde operator en het type van het resultaat is het retourtype van de operator.
De vooraf gedefinieerde toevoegingsoperatoren worden hieronder vermeld. Voor numerieke en opsommingstypen berekenen de vooraf gedefinieerde opteloperators de som van de twee operanden. Wanneer een of beide operanden van het type string
zijn, voegen de vooraf gedefinieerde opteloperators de tekenreeksweergave van de operanden samen.
Optellen van gehele getallen:
int operator +(int x, int y); uint operator +(uint x, uint y); long operator +(long x, long y); ulong operator +(ulong x, ulong y
In een
checked
-context, als de som buiten het bereik van het resultaattype valt, wordt er eenSystem.OverflowException
geworpen. In eenunchecked
context worden overloop niet gerapporteerd en worden belangrijke bits met hoge volgorde buiten het bereik van het resultaattype verwijderd.Toevoeging van drijvende komma:
float operator +(float x, float y); double operator +(double x, double y);
De som wordt berekend volgens de regels van IEC 60559-rekenkundige berekeningen. De volgende tabel bevat de resultaten van alle mogelijke combinaties van niet-nulwaarden, nullen, infiniteiten en NaN's. In de tabel zijn
x
eny
niet-nul eindige waarden en isz
het resultaat vanx + y
. Alsx
eny
dezelfde grootte maar tegengestelde tekens hebben, isz
positief nul. Alsx + y
te groot is om aan te geven in het doeltype, isz
een oneindigheid met hetzelfde teken alsx + y
.y
+0
-0
+∞
-∞
NaN
x
z
x
x
+∞
-∞
NaN
+0
y
+0
+0
+∞
–∞
NaN
-0
y
+0
-0
+∞
-∞
NaN
+∞
+∞
+∞
+∞
+∞
NaN
NaN
-∞
-∞
-∞
-∞
NaN
-∞
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
Decimaal optellen:
decimal operator +(decimal x, decimal y);
Als de grootte van de resulterende waarde te groot is om weer te geven in de decimale notatie, wordt er een
System.OverflowException
gegenereerd. De schaal van het resultaat, voordat het wordt afgerond, is de grotere schaal van de twee operanden.Decimaal optellen komt overeen met het gebruik van de optellingsoperator van het type
System.Decimal
.Opsommingstoevoeging. Elk opsommingstype biedt impliciet de volgende vooraf gedefinieerde operators, waarbij
E
het enumtype is enU
het onderliggende typeE
is:E operator +(E x, U y); E operator +(U x, E y);
Tijdens runtime worden deze operators exact geëvalueerd als
(E)((U)x + (U)y
).Tekenreekssamenvoeging:
string operator +(string x, string y); string operator +(string x, object y); string operator +(object x, string y);
Deze overbelastingen van de binaire
+
-operator voeren tekenreekssamenvoeging uit. Als een operand van tekenreekssamenvoegingnull
is, wordt een lege tekenreeks vervangen. Anders wordt elke niet-string
operand geconverteerd naar de tekenreeksweergave door de virtueleToString
methode aan te roepen die is overgenomen van het typeobject
. AlsToString
null
retourneert, wordt een lege tekenreeks vervangen.voorbeeld van:
class Test { static void Main() { string s = null; Console.WriteLine("s = >" + s + "<"); // Displays s = >< int i = 1; Console.WriteLine("i = " + i); // Displays i = 1 float f = 1.2300E+15F; Console.WriteLine("f = " + f); // Displays f = 1.23E+15 decimal d = 2.900m; Console.WriteLine("d = " + d); // Displays d = 2.900 } }
De uitvoer die in de opmerkingen wordt weergegeven, is het typische resultaat van een US-English systeem. De exacte uitvoer kan afhankelijk zijn van de regionale instellingen van de uitvoeringsomgeving. De operator voor tekenreekssamenvoeging gedraagt zich in elk geval op dezelfde manier, maar de
ToString
methoden die impliciet worden aangeroepen tijdens de uitvoering, kunnen worden beïnvloed door regionale instellingen.einde voorbeeld
Het resultaat van de tekenreekssamenvoegingsoperator is een
string
die bestaat uit de tekens van de linkeroperand, gevolgd door de tekens van de rechteroperand. De operator voor tekenreekssamenvoeging retourneert nooit eennull
waarde. Er kan eenSystem.OutOfMemoryException
worden gegenereerd als er onvoldoende geheugen beschikbaar is om de resulterende tekenreeks toe te wijzen.Gedelegeerde combinatie. Elk type gemachtigde biedt impliciet de volgende vooraf gedefinieerde operator, waarbij
D
het type gedelegeerde is:D operator +(D x, D y);
Als de eerste operand
null
is, is het resultaat van de bewerking de waarde van de tweede operand (zelfs als dat ooknull
). Als de tweede operandnull
is, is het resultaat van de bewerking de waarde van de eerste operand. Anders is het resultaat van de bewerking een nieuw gemachtigde exemplaar waarvan de aanroeplijst bestaat uit de elementen in de aanroeplijst van de eerste operand, gevolgd door de elementen in de aanroeplijst van de tweede operand. Dit betekent dat de aanroepenlijst van de resulterende delegate de concatenatie is van de aanroepenlijsten van de twee operanden.Opmerking: zie §12.10.6 en §20.6voor voorbeelden van combinatie van gemachtigden. Omdat
System.Delegate
geen gemachtigdentype is, is operator + niet gedefinieerd. eindnotitie
Opgeheven (§12.4.8) vormen van de niet-opgeheven vooraf gedefinieerde toevoegingsoperators zijn ook vooraf gedefinieerd.
12.10.6 Aftrekkingsoperator
Voor een werking van het formulier x – y
wordt overbelastingsresolutie van binaire operatoren (§12.4.5) toegepast om een specifieke operator-implementatie te selecteren. De operanden worden geconverteerd naar de parametertypen van de geselecteerde operator en het type van het resultaat is het retourtype van de operator.
Hieronder vindt u de vooraf gedefinieerde aftrekkingsoperators. De operators trekken allemaal y
van x
af.
Geheel getal aftrekken:
int operator –(int x, int y); uint operator –(uint x, uint y); long operator –(long x, long y); ulong operator –(ulong x, ulong y
In een
checked
context, als het verschil buiten het bereik van het resultaattype valt, wordt eenSystem.OverflowException
geworpen. In eenunchecked
context worden overloop niet gerapporteerd en worden belangrijke bits met hoge volgorde buiten het bereik van het resultaattype verwijderd.Aftrekken van drijvende komma:
float operator –(float x, float y); double operator –(double x, double y);
Het verschil wordt berekend volgens de regels van IEC 60559 rekenkundige berekeningen. De volgende tabel bevat de resultaten van alle mogelijke combinaties van niet-nulwaarden, nullen, infiniteiten en NaN's. In de tabel zijn
x
eny
niet-nul eindige waarden en isz
het resultaat vanx – y
. Alsx
eny
gelijk zijn, isz
positief nul. Alsx – y
te groot is om aan te geven in het doeltype, isz
een oneindigheid met hetzelfde teken alsx – y
.y
+0
-0
+∞
-∞
NaN
x
z
x
x
-∞
+∞
NaN
+0
-y
+0
+0
-∞
+∞
NaN
-0
-y
-0
+0
-∞
+∞
NaN
+∞
+∞
+∞
+∞
NaN
+∞
NaN
-∞
-∞
-∞
-∞
-∞
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
(In de bovenstaande tabel geven de
-y
vermeldingen de negatie vany
aan, niet dat de waarde negatief is.)Decimaal aftrekken:
decimal operator –(decimal x, decimal y);
Als de grootte van de resulterende waarde te groot is om weer te geven in de decimale notatie, wordt er een
System.OverflowException
gegenereerd. De schaal van het resultaat, voordat het wordt afgerond, is de grotere schaal van de twee operanden.Decimaal aftrekken is gelijk aan het gebruik van de aftrekkingsoperator van het type
System.Decimal
.Aftrekken van een opsomming Elk opsommingstype biedt impliciet de volgende vooraf gedefinieerde operator, waarbij
E
het enumtype is enU
het onderliggende typeE
is:U operator –(E x, E y);
Deze operator wordt op dezelfde wijze geëvalueerd als
(U)((U)x – (U)y)
. Met andere woorden, de operator berekent het verschil tussen de rangtelwaarden vanx
eny
, en het type van het resultaat is het onderliggende type van de opsomming.E operator –(E x, U y);
Deze operator wordt op dezelfde wijze geëvalueerd als
(E)((U)x – y)
. Met andere woorden, de operator trekt een waarde af van het onderliggende type van de opsomming, wat resulteert in een waarde van de opsomming.Verwijdering van de afgevaardigde. Elk type gemachtigde biedt impliciet de volgende vooraf gedefinieerde operator, waarbij
D
het type gedelegeerde is:D operator –(D x, D y);
De semantiek is als volgt:
- Als de eerste operand
null
is, wordt het resultaat van de bewerkingnull
. - Als de tweede operand
null
is, is het resultaat van de bewerking de waarde van de eerste operand. - Anders vertegenwoordigen beide operanden niet-lege aanroeplijsten (§20.2).
- Als de lijsten gelijk zijn, zoals bepaald door de gemachtigde gelijkheidsoperator (§12.12.9), wordt het resultaat van de bewerking
null
. - Anders is het resultaat van de bewerking een nieuwe aanroeplijst die bestaat uit de lijst van de eerste operand met de vermeldingen van de tweede operand die uit de lijst zijn verwijderd, mits de lijst van de tweede operand een sublijst van de eerste operand is. (Om gelijkheid in sublijsten te bepalen, worden overeenkomende vermeldingen vergeleken met de operator voor gedelegeerde gelijkheid.) Als de lijst van de tweede operand overeenkomt met meerdere sublijsten met aaneengesloten vermeldingen in de lijst van de eerste operand, wordt de laatste overeenkomende sublijst met aaneengesloten items verwijderd.
- Anders is het resultaat van de bewerking de waarde van de linkeroperand.
- Als de lijsten gelijk zijn, zoals bepaald door de gemachtigde gelijkheidsoperator (§12.12.9), wordt het resultaat van de bewerking
Geen van de lijsten van operanden (indien aanwezig) wordt in het proces gewijzigd.
voorbeeld van:
delegate void D(int x); class C { public static void M1(int i) { ... } public static void M2(int i) { ... } } class Test { static void Main() { D cd1 = new D(C.M1); D cd2 = new D(C.M2); D list = null; list = null - cd1; // null list = (cd1 + cd2 + cd2 + cd1) - null; // M1 + M2 + M2 + M1 list = (cd1 + cd2 + cd2 + cd1) - cd1; // M1 + M2 + M2 list = (cd1 + cd2 + cd2 + cd1) - (cd1 + cd2); // M2 + M1 list = (cd1 + cd2 + cd2 + cd1) - (cd2 + cd2); // M1 + M1 list = (cd1 + cd2 + cd2 + cd1) - (cd2 + cd1); // M1 + M2 list = (cd1 + cd2 + cd2 + cd1) - (cd1 + cd1); // M1 + M2 + M2 + M1 list = (cd1 + cd2 + cd2 + cd1) - (cd1 + cd2 + cd2 + cd1); // null } }
einde voorbeeld
- Als de eerste operand
Opgeheven (§12.4.8) vormen van de vooraf gedefinieerde niet-verheven aftrekkingsoperators die hierboven zijn gedefinieerd, zijn eveneens vooraf gedefinieerd.
12.11 Shift-operatoren
De operators <<
en >>
worden gebruikt om bitverschuifbewerkingen uit te voeren.
shift_expression
: additive_expression
| shift_expression '<<' additive_expression
| shift_expression right_shift additive_expression
;
Als een operand van een shift_expression het type compileertijd heeft dynamic
, is de expressie dynamisch gebonden (§12.3.3). In dit geval is het type compilatietijd van de expressie dynamic
en vindt de onderstaande resolutie plaats tijdens runtime met behulp van het runtimetype van die operanden met het type compileertijd dynamic
.
Voor een werking van het formulier x << count
of x >> count
wordt de overbelastingsresolutie van binaire operatoren (§12.4.5) toegepast om een specifieke operator-implementatie te selecteren. De operanden worden geconverteerd naar de parametertypen van de geselecteerde operator en het type van het resultaat is het retourtype van de operator.
Bij het declareren van een overbelaste ploegendienstoperator moet het type van de eerste operand altijd de klasse of struct zijn die de declaratie van de operator bevat en het type van de tweede operand altijd int
.
De vooraf gedefinieerde shiftoperators worden hieronder weergegeven.
Naar links verschuiven:
int operator <<(int x, int count); uint operator <<(uint x, int count); long operator <<(long x, int count); ulong operator <<(ulong x, int count);
De operator
<<
verschuiftx
naar links met een aantal bits, zoals beschreven in de onderstaande uitleg.De bits met hoge volgorde buiten het bereik van het resultaattype van
x
worden verwijderd, de resterende bits worden naar links verplaatst en de lege bitposities met lage volgorde worden ingesteld op nul.Naar rechts gaan:
int operator >>(int x, int count); uint operator >>(uint x, int count); long operator >>(long x, int count); ulong operator >>(ulong x, int count);
De operator
>>
verschuiftx
naar rechts door een aantal bits dat is berekend, zoals hieronder wordt beschreven.Wanneer
x
van het typeint
oflong
is, worden de bits met lage volgorde vanx
verwijderd, worden de resterende bits naar rechts verplaatst en worden de lege bitposities in hoge volgorde ingesteld op nul alsx
niet-negatief is en ingesteld op een alsx
negatief is.Wanneer
x
van het typeuint
ofulong
is, worden de bits met lage volgorde vanx
verwijderd, worden de resterende bits naar rechts verplaatst en worden de lege bitposities in hoge volgorde ingesteld op nul.
Voor de vooraf gedefinieerde operators wordt het aantal bits dat moet worden verplaatst als volgt berekend:
- Wanneer het type
x
int
ofuint
is, wordt het schuifgetal gegeven door de laagste vijf bits vancount
. Met andere woorden, het aantal diensten wordt berekend op basis vancount & 0x1F
. - Wanneer het type van
x
long
ofulong
is, wordt het aantal verschuivingen gegeven door de lage order zes bits vancount
. Met andere woorden, het aantal diensten wordt berekend op basis vancount & 0x3F
.
Als het resulterende aantal diensten nul is, retourneren de shiftoperators gewoon de waarde van x
.
Shift-bewerkingen veroorzaken nooit overflow en geven dezelfde resultaten in gecontroleerde en zonder controle contexten.
Wanneer de linkeroperand van de operator >>
van een getekend integraal type is, voert de operator een rekenkundige verschuiving naar rechts uit, waarbij de waarde van de meest significante bit (de tekenbit) van de operand wordt doorgegeven aan de hoogste lege bitposities. Wanneer de linkeroperand van de operator >>
van een niet-ondertekend integraal type is, voert de operator een logische verschuiving naar rechts uit, waarbij bitposities van de hoogste orde altijd op nul worden ingesteld. Om de omgekeerde bewerking uit te voeren die wordt afgeleid uit het operandtype, kunnen expliciete casts worden gebruikt.
voorbeeld: als
x
een variabele van het typeint
is, wordt met de bewerkingunchecked ((int)((uint)x >> y))
een logische verschuiving vanx
uitgevoerd. einde voorbeeld
Opgeheven (§12.4.8) vormen van de niet-opgeheven, vooraf gedefinieerde shift-operatoren die hierboven zijn gedefinieerd, zijn ook vooraf gedefinieerd.
12.12 Relationele en typetestoperators
12.12.1 Algemeen
De operators ==
, !=
, <
, >
, <=
, >=
, is
en as
worden de operatoren voor relationele en typetests genoemd.
relational_expression
: shift_expression
| relational_expression '<' shift_expression
| relational_expression '>' shift_expression
| relational_expression '<=' shift_expression
| relational_expression '>=' shift_expression
| relational_expression 'is' type
| relational_expression 'is' pattern
| relational_expression 'as' type
;
equality_expression
: relational_expression
| equality_expression '==' relational_expression
| equality_expression '!=' relational_expression
;
Opmerking: Het zoeken naar de juiste operand van de operator
is
moet eerst worden getest als een typeen vervolgens als een expressie die meerdere tokens kan omvatten. In het geval dat de operand een expressieis, moet de patroonexpressie ten minste evenveel prioriteit hebben als verschuivingsuitdrukking. eindnotitie
De is
operator wordt beschreven in §12.12.12 en de operator as
wordt beschreven in §12.12.13.
De operatoren ==
, !=
, <
, >
, <=
en >=
zijn vergelijkingsoperatoren.
Als een default_literal (§12.8.21) wordt gebruikt als operand van een <
, >
, <=
of >=
operator, treedt er een compilatietijdfout op.
Als een default_literal wordt gebruikt als beide operanden van een ==
- of !=
-operator, treedt er een compilatietijdfout op. Als een default_literal wordt gebruikt als de linkeroperand van de operator is
of as
, treedt er een compilatietijdfout op.
Als een operand van een vergelijkingsoperator het type compileertijd heeft dynamic
, is de expressie dynamisch gebonden (§12.3.3). In dit geval is het compile-tijdtype van de expressie dynamic
, en zal de hieronder beschreven resolutie plaatsvinden tijdens de uitvoertijd met behulp van het uitvoertijdtype van die operanden die het compile-tijdtype dynamic
hebben.
Voor een werking van het formulier x «op» y
, waarbij «op» een vergelijkingsoperator is, wordt overbelastingsresolutie (§12.4.5) toegepast om een specifieke operator-implementatie te selecteren. De operanden worden geconverteerd naar de parametertypen van de geselecteerde operator en het type van het resultaat is het retourtype van de operator. Als beide operanden van een equality_expression de letterlijke null
zijn, wordt de overbelastingsresolutie niet uitgevoerd en wordt de expressie geëvalueerd tot een constante waarde van true
of false
, afhankelijk van of de operator ==
of !=
is.
De vooraf gedefinieerde vergelijkingsoperatoren worden beschreven in de volgende subclauses. Alle vooraf gedefinieerde vergelijkingsoperatoren retourneren een resultaat van het type bool, zoals beschreven in de volgende tabel.
Operatie | resultaat |
---|---|
x == y |
true als x gelijk is aan y , false anders |
x != y |
true als x niet gelijk is aan y , false anders |
x < y |
true als x kleiner is dan y , anders false |
x > y |
true als x groter is dan y , false anders |
x <= y |
true als x kleiner is dan of gelijk is aan y , false anders |
x >= y |
true als x groter is dan of gelijk is aan y , false anders |
12.12.2 Vergelijkingsoperatoren voor gehele getallen
De vooraf gedefinieerde vergelijkingsoperatoren voor gehele getallen zijn:
bool operator ==(int x, int y);
bool operator ==(uint x, uint y);
bool operator ==(long x, long y);
bool operator ==(ulong x, ulong y);
bool operator !=(int x, int y);
bool operator !=(uint x, uint y);
bool operator !=(long x, long y);
bool operator !=(ulong x, ulong y);
bool operator <(int x, int y);
bool operator <(uint x, uint y);
bool operator <(long x, long y);
bool operator <(ulong x, ulong y);
bool operator >(int x, int y);
bool operator >(uint x, uint y);
bool operator >(long x, long y);
bool operator >(ulong x, ulong y);
bool operator <=(int x, int y);
bool operator <=(uint x, uint y);
bool operator <=(long x, long y);
bool operator <=(ulong x, ulong y);
bool operator >=(int x, int y);
bool operator >=(uint x, uint y);
bool operator >=(long x, long y);
bool operator >=(ulong x, ulong y);
Elk van deze operatoren vergelijkt de numerieke waarden van de twee gehele getallen en retourneert een bool
waarde die aangeeft of de specifieke relatie true
of false
is.
Opgeheven (§12.4.8) vormen van de niet-opgeheven vooraf gedefinieerde vergelijkingsoperatoren voor gehele getallen die hierboven zijn gedefinieerd, zijn ook vooraf gedefinieerd.
12.12.3 Vergelijkingsoperators voor zwevende komma
De vooraf gedefinieerd kommagetalvergelijkingsoperatoren zijn:
bool operator ==(float x, float y);
bool operator ==(double x, double y);
bool operator !=(float x, float y);
bool operator !=(double x, double y);
bool operator <(float x, float y);
bool operator <(double x, double y);
bool operator >(float x, float y);
bool operator >(double x, double y);
bool operator <=(float x, float y);
bool operator <=(double x, double y);
bool operator >=(float x, float y);
bool operator >=(double x, double y);
De operators vergelijken de operanden volgens de regels van de IEC 60559-standaard:
Als een van beide operanden NaN is, wordt het resultaat false
voor alle operators, met uitzondering van !=
, waarvoor het resultaat true
is. Voor elke twee operanden produceert x != y
altijd hetzelfde resultaat als !(x == y)
. Wanneer een of beide operanden Echter NaN zijn, produceren de <
, >
, <=
en >=
operators niet niet dezelfde resultaten als de logische negatie van de omgekeerde operator.
voorbeeld: als een van
x
eny
NaN is, wordtx < y
false
, maar!(x >= y)
istrue
. einde voorbeeld
Wanneer geen van beide operanden NaN is, vergelijken de operators de waarden van de twee drijvendekommaoperanden met betrekking tot de volgorde
–∞ < –max < ... < –min < –0.0 == +0.0 < +min < ... < +max < +∞
waarbij min
en max
de kleinste en grootste positieve eindige waarden zijn die in de opgegeven drijvende-kommaformaat kunnen worden weergegeven. Belangrijke effecten van deze volgorde zijn:
- Negatieve en positieve nullen worden als gelijk beschouwd.
- Een negatieve oneindigheid wordt beschouwd als kleiner dan alle andere waarden, maar gelijk aan een andere negatieve oneindigheid.
- Een positief oneindigheid wordt beschouwd als groter dan alle andere waarden, maar gelijk aan een ander positief oneindigheid.
Gelifte (§12.4.8) vormen van de niet-gelifte vooraf gedefinieerde drijvende-punt vergelijkingsoperatoren die hierboven zijn gedefinieerd, zijn ook vooraf gedefinieerd.
12.12.4 Decimale vergelijkingsoperatoren
De vooraf gedefinieerde operatoren voor decimaalvergelijking zijn:
bool operator ==(decimal x, decimal y);
bool operator !=(decimal x, decimal y);
bool operator <(decimal x, decimal y);
bool operator >(decimal x, decimal y);
bool operator <=(decimal x, decimal y);
bool operator >=(decimal x, decimal y);
Elk van deze operatoren vergelijkt de numerieke waarden van de twee decimale operanden en retourneert een bool
waarde die aangeeft of de specifieke relatie true
of false
is. Elke decimale vergelijking is gelijk aan het gebruik van de overeenkomstige relationele of gelijkheidsoperator van het type System.Decimal
.
Opgeheven (§12.4.8) vormen van de niet-opgeheven vooraf gedefinieerde decimale vergelijkingsoperatoren die hierboven zijn gedefinieerd, zijn ook vooraf gedefinieerd.
12.12.5 Booleaanse gelijkheidsoperators
De vooraf gedefinieerde Booleaanse gelijkheidsoperators zijn:
bool operator ==(bool x, bool y);
bool operator !=(bool x, bool y);
Het resultaat van ==
is true
als zowel x
als y
true
zijn of als zowel x
als y
false
zijn. Anders wordt het resultaat false
.
Het resultaat van !=
is false
als zowel x
als y
true
zijn of als zowel x
als y
false
zijn. Anders wordt het resultaat true
. Wanneer de operanden van het type bool
zijn, produceert de operator !=
hetzelfde resultaat als de operator ^
.
Opgeheven (§12.4.8) vormen van de niet-gelifte vooraf gedefinieerde booleaanse gelijkheidsoperators die hierboven zijn gedefinieerd, zijn eveneens vooraf gedefinieerd.
12.12.6 Operatoren voor vergelijking van opsommingen
Elk opsommingstype biedt impliciet de volgende vooraf gedefinieerde vergelijkingsoperatoren
bool operator ==(E x, E y);
bool operator !=(E x, E y);
bool operator <(E x, E y);
bool operator >(E x, E y);
bool operator <=(E x, E y);
bool operator >=(E x, E y);
Het resultaat van het evalueren van x «op» y
, waarbij x en y expressies zijn van een opsommingstype E
met een onderliggend type U
, en «op» een van de vergelijkingsoperatoren is precies hetzelfde als het evalueren van ((U)x) «op» ((U)y)
. Met andere woorden, de vergelijkingsoperatoren voor opsommingstypen vergelijken de onderliggende integrale waarden van de twee operanden.
Opgeheven (§12.4.8) vormen van de niet-opgeheven vooraf gedefinieerde opsommingsoperators die hierboven zijn gedefinieerd, zijn ook vooraf gedefinieerd.
12.12.7 Verwijzingstype gelijkheidsoperators
Elk klassetype C
impliciet de volgende vooraf gedefinieerde referentietype gelijkheidsoperators:
bool operator ==(C x, C y);
bool operator !=(C x, C y);
tenzij vooraf gedefinieerde gelijkheidsoperators anders bestaan voor C
(bijvoorbeeld wanneer C
is string
of System.Delegate
).
De operators retourneren het resultaat van het vergelijken van de twee verwijzingen op gelijkheid of ongelijkheid.
operator ==
retourneert true
als en alleen als x
en y
verwijzen naar hetzelfde exemplaar of beide null
zijn, terwijl operator !=
true
retourneert als en alleen als operator ==
met dezelfde operanden false
zou retourneren.
Naast de normale toepasbaarheidsregels (§12.6.4.2), vereisen de vooraf gedefinieerde referentietype gelijkheidsoperatoren een van de volgende voorwaarden om van toepassing te zijn:
- Beide operanden zijn een waarde van een type dat bekend staat als een reference_type of de letterlijke
null
. Bovendien bestaat er een identiteits- of expliciete verwijzingsconversie (§10.3.5) van de ene operand naar het type van de andere operand. - De ene operand is de letterlijke
null
en de andere operand is een waarde van een typeT
waarbijT
een type_parameter is waarvan het niet bekend is of het een waardetype is, en geen waardetypebeperking heeft.- Als tijdens runtime
T
een niet-null-waardetype is, dan zijn de resultaten van==
enfalse
respectievelijk!=
entrue
. - Als tijdens runtime
T
een type null-waarde is, wordt het resultaat berekend op basis van de eigenschapHasValue
van de operand, zoals beschreven in (§12.12.10). - Als tijdens runtime
T
een verwijzingstype is, wordt het resultaattrue
als de operand isnull
en andersfalse
.
- Als tijdens runtime
Tenzij aan een van deze voorwaarden wordt voldaan, treedt er een bindingstijdfout op.
Opmerking: Belangrijke gevolgen van deze regels zijn:
- Het is een bindingstijdfout om de vooraf gedefinieerde referentietype gelijkheidsoperators te gebruiken om twee verwijzingen te vergelijken die bekend zijn om verschillend te zijn tijdens bindingstijd. Als de bindingstijdtypen van de operanden bijvoorbeeld twee klassetypen zijn en geen van beide zijn afgeleid van de andere, is het onmogelijk voor de twee operanden om naar hetzelfde object te verwijzen. De bewerking wordt dus beschouwd als een bindingstijdfout.
- De vooraf gedefinieerde operatoren voor verwijzingstype gelijkheid staan niet toe dat de operanden van het waardetype worden vergeleken (behalve wanneer typeparameters worden vergeleken met
null
, die speciaal worden verwerkt).- Operanden van vooraf gedefinieerde referentietype-gelijkheidsoperators worden nooit geboxed. Het zou zinloos zijn om dergelijke boxing-operaties uit te voeren, aangezien verwijzingen naar de nieuw toegewezen boxed instanties noodzakelijkerwijs afwijken van alle andere verwijzingen.
Voor een bewerking van de vorm
x == y
ofx != y
, indien een toepasselijke door de gebruiker gedefinieerdeoperator ==
ofoperator !=
bestaat, selecteren de regels voor het oplossen van operatoroverbelasting (§12.4.5) die operator in plaats van de vooraf gedefinieerde operator voor gelijkheid van referentietypen. Het is altijd mogelijk om de voorgeconfigureerde gelijkheidsoperator voor referentietypen te selecteren door expliciet een of beide operanden naar typeobject
te casten.eindnotitie
Voorbeeld: In het volgende voorbeeld wordt gecontroleerd of een argument van een onbeperkt typeparameter
null
is.class C<T> { void F(T x) { if (x == null) { throw new ArgumentNullException(); } ... } }
De
x == null
constructie is toegestaan, ook al kanT
een niet-null-waardetype vertegenwoordigen en wordt het resultaat eenvoudig gedefinieerd om te wordenfalse
wanneerT
een niet-null-waardetype is.einde voorbeeld
Voor een bewerking van de vorm x == y
of x != y
, als er een toepasselijke operator ==
of operator !=
bestaat, selecteert de operator-overbelastingsresolutie (§12.4.5) die operator in plaats van de vooraf gedefinieerde operator voor gelijkheid van referentietypen.
Opmerking: Het is altijd mogelijk om de vooraf gedefinieerde operator voor gelijkheid van verwijzingstypen te selecteren door beide operanden expliciet naar type
object
te casten. eindnotitie
voorbeeld: het voorbeeld
class Test { static void Main() { string s = "Test"; string t = string.Copy(s); Console.WriteLine(s == t); Console.WriteLine((object)s == t); Console.WriteLine(s == (object)t); Console.WriteLine((object)s == (object)t); } }
produceert de uitvoer
True False False False
De variabelen
s
ent
verwijzen naar twee afzonderlijke tekenreeksexemplaren met dezelfde tekens. De eerste vergelijking levertTrue
op omdat de vooraf gedefinieerde operator voor tekenreeks gelijkheid (§12.12.8) is geselecteerd wanneer beide operanden van het typestring
zijn. De resterende vergelijkingen geven allemaalFalse
als uitvoer omdat de overbelasting vanoperator ==
in hetstring
-type niet van toepassing is wanneer een van beide operanden een bindingstijdtype vanobject
heeft.Houd er rekening mee dat de bovenstaande techniek niet zinvol is voor waardetypen. Het voorbeeld
class Test { static void Main() { int i = 123; int j = 123; Console.WriteLine((object)i == (object)j); } }
geeft
False
omdat de casts verwijzingen maken naar twee afzonderlijke exemplaren van geboxteint
waarden.einde voorbeeld
12.12.8 Operatoren voor gelijkheid van tekenreeksen
De vooraf gedefinieerde operatoren voor gelijkheid van tekenreeksen zijn:
bool operator ==(string x, string y);
bool operator !=(string x, string y);
Twee string
waarden worden als gelijk beschouwd wanneer een van de volgende waarden waar is:
- Beide waarden zijn
null
. - Beide waarden zijn niet-
null
verwijzingen naar tekenreeksexemplaren met identieke lengten en identieke tekens in elke tekenpositie.
De operatoren voor gelijkheid van tekenreeksen vergelijken tekenreekswaarden in plaats van tekenreeksverwijzingen. Wanneer twee afzonderlijke tekenreeksexemplaren exact dezelfde reeks tekens bevatten, zijn de waarden van de tekenreeksen gelijk, maar de verwijzingen verschillen.
Opmerking: zoals beschreven in §12.12.7, kunnen de operatoren voor gelijkheid van referentietypen worden gebruikt om tekenreeksverwijzingen te vergelijken in plaats van tekenreekswaarden. eindnotitie
12.12.9 Gelijkheidsoperators delegeren
De vooraf gedefinieerde operatoren voor gelijkheid van gemachtigden zijn:
bool operator ==(System.Delegate x, System.Delegate y);
bool operator !=(System.Delegate x, System.Delegate y);
Twee gedelegeerde instanties worden als gelijk beschouwd:
- Als een van de gedelegeerde instanties
null
is, zijn ze gelijk als en alleen als beidenull
zijn. - Als de delegaten verschillende runtime-typen hebben, zijn ze nooit gelijk.
- Als beide gemachtigden een aanroeplijst hebben (§20.2), zijn deze instanties gelijk als en alleen als hun aanroeplijsten dezelfde lengte hebben en elke vermelding in de aanroeplijst gelijk is (zoals hieronder gedefinieerd) aan de overeenkomstige vermelding in de lijst met aanroepen van de andere.
De volgende regels bepalen de gelijkheid van vermeldingen in de aanroeplijst:
- Als twee vermeldingen in de aanroeplijst beide verwijzen naar dezelfde statische methode, zijn de vermeldingen gelijk.
- Als twee vermeldingen in de aanroeplijst beide verwijzen naar dezelfde niet-statische methode op hetzelfde doelobject (zoals gedefinieerd door de operatoren voor verwijzings gelijkheid), zijn de vermeldingen gelijk.
- Vermeldingen in de aanroeplijst die zijn geproduceerd uit de evaluatie van semantisch identieke anonieme functies (§12.19) met dezelfde (mogelijk lege) set vastgelegde buitenste variabelenexemplaren zijn toegestaan (maar niet vereist) gelijk te zijn.
Als de overbelastingresolutie van operatoren resulteert in een gedelegeerde gelijkheidsoperator, en de bindingstijdtypen van beide operanden gedelegeerdentypen zijn, zoals beschreven is in §20 in plaats van in System.Delegate
, en er geen identiteitsconversie is tussen de bindingstype operandtypen, ontstaat er een bindingstijdfout.
Opmerking: deze regel voorkomt vergelijkingen die nooit rekening kunnen houden met niet-
null
-waarden omdat ze verwijzingen zijn naar exemplaren van verschillende typen gedelegeerden. eindnotitie
12.12.10 Gelijkheidsoperatoren tussen nullable waardetypes en de null-literal
Met de operatoren ==
en !=
kan één operand een waarde zijn van een type null-waarde en de andere de letterlijke null
zijn, zelfs als er geen vooraf gedefinieerde of door de gebruiker gedefinieerde operator (in niet-opgehaalde of opgeheven vorm) bestaat voor de bewerking.
Voor een bewerking van een van de formulieren
x == null null == x x != null null != x
als x
een expressie is van een nullbaar waardetype en de operator overbelastingsresolutie (§12.4.5) geen toepasselijke operator kan vinden, wordt het resultaat in plaats daarvan berekend op basis van de eigenschap HasValue
van x
. Met name worden de eerste twee formulieren omgezet in !x.HasValue
en de laatste twee formulieren worden omgezet in x.HasValue
.
12.12.11 Tuple-gelijkheidsoperators
De tuple gelijkheidsoperatoren worden paarsgewijs toegepast op de elementen van de tuple operanden in lexicale volgorde.
Als elke operand x
en y
van een operator ==
of !=
wordt geclassificeerd als tupel of als een waarde met een tupletype (§8.3.11), is de operator een tuple-gelijkheidsoperator.
Als een operand e
als tuple wordt geclassificeerd, zijn de elementen e1...en
de resultaten van het evalueren van de elementexpressies van de tuple-expressie. Anders, als e
een waarde van een tupeltype is, zullen de elementen t.Item1...t.Itemn
zijn, waarbij t
het resultaat is van het evalueren van e
.
De operanden x
en y
van een tuple-gelijkheidsoperator moeten dezelfde arity hebben, of er treedt een compilatietijdfout op. Voor elk paar elementen xi
en yi
is dezelfde gelijkheidsoperator van toepassing en wordt een resultaat van het type bool
, dynamic
, een type dat een impliciete conversie naar bool
heeft, of een type dat de operatoren true
en false
definieert.
De tuple-gelijkheidsoperator x == y
wordt als volgt geëvalueerd:
- De linkeroperand
x
wordt geëvalueerd. - De rechteroperand
y
wordt geëvalueerd. - Voor elk paar elementen
xi
enyi
in lexicale volgorde:- De operator
xi == yi
wordt geëvalueerd en het resultaat van het typebool
wordt op de volgende manier verkregen:- Als de vergelijking een
bool
oplevert, is dat het resultaat. - Anders, als de vergelijking een
dynamic
oplevert, wordt de operatorfalse
er dynamisch op aangeroepen en wordt de resulterendebool
-waarde met de logische negatieoperator (!
) ontkend. - Anders wordt die conversie toegepast als het type van de vergelijking een impliciete conversie naar
bool
heeft. - Anders, als het type van de vergelijking een operator
false
heeft, dan wordt die operator aangeroepen en wordt de resulterendebool
-waarde geïnverteerd met de logische negatieoperator (!
).
- Als de vergelijking een
- Als de resulterende
bool
false
is, wordt er geen verdere evaluatie uitgevoerd en wordt het resultaat van de tuple-gelijkheidsoperatorfalse
.
- De operator
- Als alle elementvergelijkingen
true
opleveren, wordt het resultaat van de tupel gelijkheidsoperatortrue
.
De tuple-gelijkheidsoperator x != y
wordt als volgt geëvalueerd:
- De linkeroperand
x
wordt geëvalueerd. - De rechteroperand
y
wordt geëvalueerd. - Voor elk paar elementen
xi
enyi
in lexicale volgorde:- De operator
xi != yi
wordt geëvalueerd en het resultaat van het typebool
wordt op de volgende manier verkregen:- Als de vergelijking een
bool
oplevert, is dat het resultaat. - Anders, als de vergelijking een
dynamic
oplevert, wordt de operatortrue
er dynamisch op aangeroepen en is de resulterende waardebool
het resultaat. - Anders wordt die conversie toegepast als het type van de vergelijking een impliciete conversie naar
bool
heeft. - Als het type van de vergelijking een operator
true
heeft, wordt deze operator aangeroepen en is de resulterende waardebool
het resultaat.
- Als de vergelijking een
- Als de resulterende
bool
true
is, wordt er geen verdere evaluatie uitgevoerd en wordt het resultaat van de tuple-gelijkheidsoperatortrue
.
- De operator
- Als alle elementvergelijkingen
false
opleveren, wordt het resultaat van de tupel gelijkheidsoperatorfalse
.
12.12.12 De operator is
Er zijn twee vormen van de operator is
. Een hiervan is de is-type operator, die een type aan de rechterzijde heeft. De andere is de is-pattern operator, die een patroon aan de rechterkant heeft.
De is-type operator
De is-type operator wordt gebruikt om te controleren of het runtimetype van een object compatibel is met een bepaald type. De controle wordt uitgevoerd tijdens runtime. Het resultaat van de bewerking E is T
, waarbij E
een expressie is en T
een ander type is dan dynamic
, is een Booleaanse waarde die aangeeft of E
niet-null is en kan worden geconverteerd naar type T
door een verwijzingsconversie, een boksconversie, een conversie voor uitpakken, een terugloopconversie of een uitgepakte conversie.
De bewerking wordt als volgt geëvalueerd:
- Als
E
een anonieme functie of methodegroep is, treedt er een compilatietijdfout op. - Als
E
de letterlijkenull
is of als de waarde vanE
null
is, wordt het resultaatfalse
. - Anders:
- Laat
R
het runtimetype vanE
zijn. - Laat
D
als volgt worden afgeleid vanR
: - Als
R
een null-waardetype is, isD
het onderliggende typeR
. - Anders wordt
D
R
. - Het resultaat is afhankelijk van
D
enT
als volgt: - Als
T
een verwijzingstype is, wordt het resultaattrue
als:- er bestaat een identiteitsconversie tussen
D
enT
, -
D
is een verwijzingstype en een impliciete verwijzingsconversie vanD
totT
bestaat, of - Ofwel:
D
is een waardetype en een boksconversie vanD
naarT
bestaat.
Of:D
is een waardetype enT
is een interfacetype dat doorD
wordt geïmplementeerd.
- er bestaat een identiteitsconversie tussen
- Als
T
een null-waarde type is, is het resultaattrue
alsD
het onderliggende type vanT
is. - Als
T
een niet-null-waardetype is, wordt het resultaattrue
alsD
enT
hetzelfde type zijn. - Anders wordt het resultaat
false
.
Door de gebruiker gedefinieerde conversies worden niet meegenomen door de operator is
.
Opmerking: als de operator
is
tijdens runtime wordt geëvalueerd, zijn alle typeargumenten vervangen en zijn er geen open typen (§8.4.3) om rekening mee te houden. eindnotitie
Opmerking: de operator
is
kan als volgt worden begrepen in termen van compileertijdtypen en conversies, waarbijC
het type compilatietijd vanE
is:
- Als het compileertijdtype van
e
hetzelfde is alsT
of als een impliciete verwijzingsconversie (§10.2.8), boksconversie (§10.2.9), wrapconversie (§10.6), of een expliciete uitgepakte conversie (§10.6) bestaat vanuit het compileertijdtype vanE
naarT
:
- Als
C
van een niet-null-waardetype is, wordt het resultaat van de bewerkingtrue
.- Anders is het resultaat van de bewerking gelijk aan het evalueren van
E != null
.- Anders, als een expliciete verwijzingsconversie (§10.3.5) of een unboxing conversie (§10.3.7) bestaat van
C
naarT
, of alsC
ofT
een open type is (§8.4.3), dan worden runtimecontroles zoals hierboven uitgevoerd.- Anders is er geen referentie-, boks-, inpak- of uitpakconversie van
E
naar typeT
mogelijk, en het resultaat van de bewerking isfalse
. Een compiler kan optimalisaties implementeren op basis van het type compileertijd.eindnotitie
12.12.12.2 De is-patroon-operator
De is-patroonoperator wordt gebruikt om te controleren of de waarde die wordt berekend door een expressie overeenkomt met een bepaald patroon (§11). De controle wordt uitgevoerd tijdens runtime. Het resultaat van de is-patroon operator is waar als de waarde overeenkomt met het patroon, anders is het onwaar.
Voor een expressie van het formulier E is P
, waarbij E
een relationele expressie van het type T
is en P
een patroon is, is het een compilatiefout als een van de volgende bewaringen geldt:
-
E
wijst geen waarde aan of heeft geen type. - Het patroon
P
is niet van toepassing (§11.2) op het typeT
.
12.12.13 De operator
De operator as
wordt gebruikt om expliciet een waarde te converteren naar een bepaald verwijzingstype of null-waardetype. In tegenstelling tot een cast-expressie (§12.9.7), veroorzaakt de operator as
nooit een uitzondering. Als de aangegeven conversie niet mogelijk is, wordt de resulterende waarde null
.
In een operatie van de vorm E as T
moet E
een expressie zijn en T
een verwijzingstype zijn, een typeparameter die bekend staat als een verwijzingstype, of een null-waarde type. Bovendien is ten minste één van de volgende waar, of op andere wijze treedt er een compilatietijdfout op:
- Een identiteit (§10.2.2), impliciet nullable (§10.2.6), impliciete verwijzing (§10.2.8), boksen (§10.2.9), expliciet nullable (§10.3.4), expliciete verwijzing (§10.3.5), of wrapping (§8.3.12) conversie bestaat van
E
totT
. - Het type
E
ofT
is een open type. -
E
is de letterlijke waarde vannull
.
Als het type compilatietijd van E
niet is dynamic
, produceert de bewerking E as T
hetzelfde resultaat als
E is T ? (T)(E) : (T)null
behalve dat E
slechts eenmaal wordt geëvalueerd. Er kan worden verwacht dat een compiler E as T
optimaliseert om maximaal één runtimetypecontrole uit te voeren in plaats van de twee runtimetypecontroles die worden geïmpliceerd door de bovenstaande uitbreiding.
Als het compilatietijdtype van E
dynamic
is, dan is, in tegenstelling tot de cast-operator, de operator as
niet dynamisch gebonden (§12.3.3). Daarom is de uitbreiding in dit geval:
E is T ? (T)(object)(E) : (T)null
Houd er rekening mee dat sommige conversies, zoals door de gebruiker gedefinieerde conversies, niet mogelijk zijn met de operator as
en in plaats daarvan moeten worden uitgevoerd met cast-expressies.
Voorbeeld: In het voorbeeld
class X { public string F(object o) { return o as string; // OK, string is a reference type } public T G<T>(object o) where T : Attribute { return o as T; // Ok, T has a class constraint } public U H<U>(object o) { return o as U; // Error, U is unconstrained } }
de typeparameter
T
vanG
is een referentietype, omdat het de klassebeperking heeft. Het typeparameterU
vanH
is echter niet; vandaar dat het gebruik van de operatoras
inH
niet is toegestaan.einde voorbeeld
12.13 Logische operators
12.13.1 Algemeen
De operators &
, ^
en |
worden de logische operatoren genoemd.
and_expression
: equality_expression
| and_expression '&' equality_expression
;
exclusive_or_expression
: and_expression
| exclusive_or_expression '^' and_expression
;
inclusive_or_expression
: exclusive_or_expression
| inclusive_or_expression '|' exclusive_or_expression
;
Als een operand van een logische operator het type compileertijd heeft dynamic
, is de expressie dynamisch gebonden (§12.3.3). In dit geval is het compile-tijdtype van de expressie dynamic
, en zal de hieronder beschreven resolutie plaatsvinden tijdens de uitvoertijd met behulp van het uitvoertijdtype van die operanden die het compile-tijdtype dynamic
hebben.
Voor een werking van het formulier x «op» y
, waarbij «op» een van de logische operatoren is, wordt overbelastingsresolutie (§12.4.5) toegepast om een specifieke operator-implementatie te selecteren. De operanden worden geconverteerd naar de parametertypen van de geselecteerde operator en het type van het resultaat is het retourtype van de operator.
De vooraf gedefinieerde logische operators worden beschreven in de volgende subclauses.
12.13.2 Logische operatoren voor gehele getallen
De vooraf gedefinieerde logische operatoren voor gehele getallen zijn:
int operator &(int x, int y);
uint operator &(uint x, uint y);
long operator &(long x, long y);
ulong operator &(ulong x, ulong y);
int operator |(int x, int y);
uint operator |(uint x, uint y);
long operator |(long x, long y);
ulong operator |(ulong x, ulong y);
int operator ^(int x, int y);
uint operator ^(uint x, uint y);
long operator ^(long x, long y);
ulong operator ^(ulong x, ulong y);
De operator &
berekent de bitsgewijze logische EN van de twee operanden, de operator |
berekent de bitsgewijze logische OR van de twee operanden, en de operator ^
berekent de bitsgewijze logische exclusieve OR van de twee operanden. Er zijn geen overloopmogelijkheden bij deze bewerkingen.
Opgeheven (§12.4.8) vormen van de niet-opgeheven, vooraf gedefinieerde logische operatoren voor gehele getallen die hierboven zijn gedefinieerd, zijn ook vooraf gedefinieerd.
12.13.3 Logische operatoren voor enumeraties
Elk opsommingstype E
impliciet de volgende vooraf gedefinieerde logische operators biedt:
E operator &(E x, E y);
E operator |(E x, E y);
E operator ^(E x, E y);
Het resultaat van het evalueren van x «op» y
, waarbij x
en y
expressies zijn van een opsommingstype E
met een onderliggend type U
en «op» een van de logische operatoren is, is precies hetzelfde als het evalueren van (E)((U)x «op» (U)y)
. Met andere woorden, de logische operatoren van het opsommingstype voeren de logische bewerking uit op het onderliggende type van de twee operanden.
Opgeheven (§12.4.8) vormen van de niet-opgeheven logische inventarisatieoperators die hierboven zijn gedefinieerd, zijn ook vooraf gedefinieerd.
12.13.4 Booleaanse logische operatoren
De vooraf gedefinieerde logische booleaanse operators zijn:
bool operator &(bool x, bool y);
bool operator |(bool x, bool y);
bool operator ^(bool x, bool y);
Het resultaat van x & y
is true
als zowel x
als y
true
zijn. Anders wordt het resultaat false
.
Het resultaat van x | y
is true
als x
of y
true
is. Anders wordt het resultaat false
.
Het resultaat van x ^ y
is true
als x
true
is en y
false
is, of x
false
is en y
is true
. Anders wordt het resultaat false
. Wanneer de operanden van het type bool
zijn, berekent de operator ^
hetzelfde resultaat als de operator !=
.
12.13.5 Nullable Booleaanse waarden & en | operatoren
Het nulbare booleaanse type bool?
kan drie waarden, true
, false
en null
, vertegenwoordigen.
Net als bij de andere binaire operatoren zijn opgeheven vormen van de logische operatoren &
en |
(§12.13.4) ook vooraf gedefinieerd:
bool? operator &(bool? x, bool? y);
bool? operator |(bool? x, bool? y);
De semantiek van de opgeheven &
en |
operatoren worden gedefinieerd door de volgende tabel:
x |
y |
x & y |
x \| y |
---|---|---|---|
true |
true |
true |
true |
true |
false |
false |
true |
true |
null |
null |
true |
false |
true |
false |
true |
false |
false |
false |
false |
false |
null |
false |
null |
null |
true |
null |
true |
null |
false |
false |
null |
null |
null |
null |
null |
Opmerking: het
bool?
type is conceptueel vergelijkbaar met het type met drie waarden dat wordt gebruikt voor Boole-expressies in SQL. De bovenstaande tabel volgt dezelfde semantiek als SQL, terwijl het toepassen van de regels van §12.4.8 op de&
en|
operators dat niet zou doen. De regels van §12.4.8 bieden al SQL-achtige semantiek voor de opgetilde^
operator. eindnotitie
12.14 Voorwaardelijke logische operators
12.14.1 Algemeen
De operators &&
en ||
worden de voorwaardelijke logische operators genoemd. Ze worden ook wel de kortsluitende logische operatoren genoemd.
conditional_and_expression
: inclusive_or_expression
| conditional_and_expression '&&' inclusive_or_expression
;
conditional_or_expression
: conditional_and_expression
| conditional_or_expression '||' conditional_and_expression
;
De operators &&
en ||
zijn voorwaardelijke versies van de operators &
en |
:
- De bewerking
x && y
komt overeen met de bewerkingx & y
, behalve daty
alleen wordt geëvalueerd alsx
niet isfalse
. - De bewerking
x || y
komt overeen met de bewerkingx | y
, behalve daty
alleen wordt geëvalueerd alsx
niet istrue
.
Opmerking: de reden dat kortsluiting gebruikmaakt van de voorwaarden 'niet waar' en 'niet onwaar' is om door de gebruiker gedefinieerde voorwaardelijke operators in staat te stellen te definiëren wanneer kortsluiting van toepassing is. Door de gebruiker gedefinieerde typen kunnen een status hebben waarin
operator true
false
retourneert enoperator false
false
retourneert. In die gevallen zouden noch&&
noch||
kortsluiting hebben. eindnotitie
Als een operand van een voorwaardelijke logische operator het type compileertijd heeft dynamic
, is de expressie dynamisch gebonden (§12.3.3). In dit geval is het compile-tijdtype van de expressie dynamic
, en zal de hieronder beschreven resolutie plaatsvinden tijdens de uitvoertijd met behulp van het uitvoertijdtype van die operanden die het compile-tijdtype dynamic
hebben.
Een bewerking van de vorm x && y
of x || y
wordt verwerkt door overbelastingsresolutie toe te passen (§12.4.5) alsof de bewerking als x & y
of x | y
is geschreven. Dan
- Als overbelastingresolutie geen enkele beste operator vindt, of als overbelastingresolutie een van de vooraf gedefinieerde logische operatoren voor gehele getallen of logische Boole-operatoren (§12.13.5) selecteert, treedt er een bindingstijdfout op.
- Als de geselecteerde operator een van de vooraf gedefinieerde booleaanse logische operatoren (§12.13.4) is, wordt de bewerking verwerkt zoals beschreven in §12.14.2.
- Anders is de geselecteerde operator een door de gebruiker gedefinieerde operator en wordt de bewerking verwerkt zoals beschreven in §12.14.3.
Het is niet mogelijk om de voorwaardelijke logische operators rechtstreeks te overbelasten. Omdat de voorwaardelijke logische operators echter worden geëvalueerd in termen van de reguliere logische operators, worden overbelastingen van de reguliere logische operators, met bepaalde beperkingen, ook beschouwd als overbelasting van de voorwaardelijke logische operators. Dit wordt verder beschreven in §12.14.3.
12.14.2 Booleaanse voorwaardelijke logische operators
Wanneer de operanden van &&
of ||
van het type bool
zijn, of wanneer de operanden van typen zijn die geen toepasselijke operator &
of operator |
definiëren, maar impliciete conversies naar bool
definiëren, wordt de bewerking als volgt verwerkt:
- De bewerking
x && y
wordt geëvalueerd alsx ? y : false
. Met andere woorden,x
wordt eerst geëvalueerd en geconverteerd naar typebool
. Alsx
vervolgens istrue
, wordty
geëvalueerd en geconverteerd naar het typebool
en wordt dit het resultaat van de bewerking. Anders wordt het resultaat van de bewerkingfalse
. - De bewerking
x || y
wordt geëvalueerd alsx ? true : y
. Met andere woorden,x
wordt eerst geëvalueerd en geconverteerd naar typebool
. Alsx
vervolgens istrue
, wordt het resultaat van de bewerkingtrue
. Anders wordty
geëvalueerd en geconverteerd naar het typebool
en wordt dit het resultaat van de bewerking.
12.14.3 Door de gebruiker gedefinieerde conditionele logische operators
Wanneer de operanden van &&
of ||
van typen zijn die een door de gebruiker gedefinieerde operator &
of operator |
declareren, gelden beide van de volgende waarden, wanneer T
het type is waarin de geselecteerde operator wordt gedeclareerd:
- Het retourtype en het type van elke parameter van de geselecteerde operator worden
T
. Met andere woorden, de operator berekent de logische AND of de logische OR van twee operanden van het typeT
en retourneert een resultaat van het typeT
. -
T
bevat verklaringen vanoperator true
enoperator false
.
Er treedt een bindingstijdfout op als niet aan een van deze vereisten wordt voldaan. Anders wordt de &&
- of ||
-bewerking geëvalueerd door de door de gebruiker gedefinieerde operator true
of operator false
te combineren met de geselecteerde door de gebruiker gedefinieerde operator:
- De bewerking
x && y
wordt geëvalueerd alsT.false(x) ? x : T.&(x, y)
, waarbijT.false(x)
een aanroep is van deoperator false
die inT
is gedeclareerd enT.&(x, y)
een aanroep van de geselecteerdeoperator &
is. Met andere woorden,x
wordt eerst geëvalueerd enoperator false
wordt aangeroepen op het resultaat om te bepalen ofx
zeker onwaar is. Alsx
dan zeker onwaar is, is het resultaat van de bewerking de waarde die eerder is berekend voorx
. Anders wordty
geëvalueerd en wordt de geselecteerdeoperator &
aangeroepen op de waarde die eerder is berekend voorx
en de waarde die is berekend voory
om het resultaat van de bewerking te produceren. - De bewerking
x || y
wordt geëvalueerd alsT.true(x) ? x : T.|(x, y)
, waarbijT.true(x)
een aanroep is van deoperator true
die inT
is gedeclareerd enT.|(x, y)
een aanroep van de geselecteerdeoperator |
is. Met andere woorden,x
wordt eerst geëvalueerd enoperator true
wordt aangeroepen op het resultaat om te bepalen ofx
zeker waar is. Alsx
dan zeker waar is, is het resultaat van de bewerking de waarde die eerder is berekend voorx
. Anders wordty
geëvalueerd en wordt de geselecteerdeoperator |
aangeroepen op de waarde die eerder is berekend voorx
en de waarde die is berekend voory
om het resultaat van de bewerking te produceren.
In een van deze bewerkingen wordt de expressie die door x
wordt gegeven slechts eenmaal geëvalueerd, en wordt de expressie die door y
wordt gegeven, ofwel niet geëvalueerd of precies één keer geëvalueerd.
12.15 De null-coalescing-operator
De ??
-operator wordt de null-coalescing-operator genoemd.
null_coalescing_expression
: conditional_or_expression
| conditional_or_expression '??' null_coalescing_expression
| throw_expression
;
In een null-coalitie-expressie van de vorm a ?? b
, als a
nietnull
is, wordt het resultaat a
; anders wordt het resultaat b
. De bewerking evalueert b
alleen wanneer a
null
is.
De operator null-samenvoegen is rechts-associatief, wat betekent dat bewerkingen van rechts naar links worden gegroepeerd.
Voorbeeld: een expressie van het formulier
a ?? b ?? c
wordt geëvalueerd alsa ?? (b ?? c)
. In het algemeen retourneert een expressie met de vormE1 ?? E2 ?? ... ?? EN
de eerste van de operanden die niet-null
is, ofnull
als alle operandennull
zijn. einde voorbeeld
Het type expressie a ?? b
is afhankelijk van welke impliciete conversies beschikbaar zijn voor de operanden. In volgorde van voorkeur is het type a ?? b
A₀
, A
of B
, waarbij A
het type a
is (mits a
een type heeft), is B
het type b
(mits b
een type heeft) en is A₀
het onderliggende type A
als A
een type null-waarde is of anders A
. In het bijzonder wordt a ?? b
als volgt verwerkt:
- Als
A
bestaat en geen niet-nullbaar waarde- of referentietype is, treedt er een compilatietijdfout op. - Als
A
bestaat enb
een dynamische expressie is, wordt het resultaattypedynamic
. Tijdens runtime wordta
eerst geëvalueerd. Alsa
niet isnull
, wordta
geconverteerd naardynamic
en wordt dit het resultaat. Anders wordtb
geëvalueerd en dit wordt het resultaat. - Anders, als
A
bestaat en een null-waardetype is en er een impliciete conversie bestaat vanb
naarA₀
, is het resultaattypeA₀
. Tijdens runtime wordta
eerst geëvalueerd. Alsa
nietnull
is, wordta
naar typeA₀
uitgepakt, en wordt dit het resultaat. Anders wordtb
geëvalueerd en geconverteerd naar het typeA₀
, en wordt dit het resultaat. - Als
A
bestaat en er een impliciete conversie bestaat vanb
naarA
, wordt het resultaattypeA
. Tijdens runtime wordta
eerst geëvalueerd. Alsa
niet isnull
, wordta
het resultaat. Anders wordtb
geëvalueerd en geconverteerd naar het typeA
, en wordt dit het resultaat. - Anders, als
A
bestaat en het een nullbare waarde is,b
een typeB
heeft en er een impliciete conversie bestaat vanA₀
naarB
, is het resultaattypeB
. Tijdens runtime wordta
eerst geëvalueerd. Alsa
nietnull
is, wordta
uitgepakt naar typeA₀
, geconverteerd naar typeB
, en dit wordt vervolgens het resultaat. Anders wordtb
geëvalueerd en wordt het resultaat. - Als
b
een typeB
heeft en er een impliciete conversie bestaat vana
totB
, wordt het resultaattypeB
. Tijdens runtime wordta
eerst geëvalueerd. Alsa
niet isnull
, wordta
geconverteerd naar het typeB
en wordt dit het resultaat. Anders wordtb
geëvalueerd en wordt het resultaat.
Anders zijn a
en b
niet compatibel en treedt er een compilatiefout op.
12.16 De operator voor het opwerpen van expressies
throw_expression
: 'throw' null_coalescing_expression
;
Een throw_expression genereert de waarde die wordt geproduceerd door de null_coalescing_expressionte evalueren. De expressie moet impliciet omgezet kunnen worden naar System.Exception
en het resultaat van de evaluatie van de expressie wordt geconverteerd naar System.Exception
voordat deze wordt gegooid. Het gedrag tijdens de evaluatie van een throw-expressie is hetzelfde als opgegeven voor een throw-instructie (§13.10.6).
Een throw_expression heeft geen type. Een throw_expression is om te zetten naar elk type via een impliciete throw-conversie.
Een expressie die genereert, vindt alleen plaats in de volgende syntactische contexten:
- Als tweede of derde operand van een ternaire voorwaardelijke operator (
?:
). - Als de tweede operand van een null coalescing-operator (
??
). - Als de hoofdtekst van een expressie-bodied lambda of lid.
12.17 Declaratie-expressies
Een declaratie-expressie declareert een lokale variabele.
declaration_expression
: local_variable_type identifier
;
local_variable_type
: type
| 'var'
;
De simple_name_
wordt ook beschouwd als een declaratie-expressie als een eenvoudige naamzoekactie geen bijbehorende declaratie heeft gevonden (§12.8.4). Wanneer deze wordt gebruikt als een declaratie-expressie, wordt _
een eenvoudige verwijderinggenoemd. Het is semantisch equivalent aan var _
, maar is toegestaan op meer plaatsen.
Een declaratie-expressie vindt alleen plaats in de volgende syntactische contexten:
- Als
out
argument_value in een argumentenlijst. - Als een eenvoudige wegwerpwaarde
_
die deel uitmaakt van de linkerkant van een eenvoudige toewijzing (§12.21.2). - Als een tuple_element in een of meer recursief geneste tuple_expressions, waarbij de buitenste van deze de linkerzijde vormt van een deconstructerende toewijzing. Een deconstruction_expression geeft aanleiding tot declaratie-expressies in deze positie, ook al zijn de declaratie-expressies niet syntactisch aanwezig.
Opmerking: dit betekent dat een declaratie-expressie niet tussen haakjes kan worden geplaatst. eindnotitie
Het is een fout als er naar een impliciet getypte variabele wordt gerefereerd binnen de argument_list waarin deze met een declaration_expression is gedeclareerd.
Het is een fout voor een variabele die is gedeclareerd met een declaration_expression waarnaar moet worden verwezen binnen de deconstructing-toewijzing waar deze zich voordoet.
Een declaratie-expressie die een simpele weglating is of waarbij de local_variable_type de identifier var
is, wordt geclassificeerd als een impliciet getypte variabele. De expressie heeft geen type en het type van de lokale variabele wordt als volgt afgeleid op basis van de syntactische context:
- In een argument_list is het afgeleide type van de variabele het gedeclareerde type van de bijbehorende parameter.
- Als de linkerzijde van een eenvoudige toewijzing is het afgeleide type van de variabele het type van de rechterkant van de toewijzing.
- In een tuple_expression aan de linkerkant van een eenvoudige toewijzing is het afgeleide type van de variabele het type van het overeenkomstige tuple-element aan de rechterkant (na deconstructie) van de toewijzing.
Anders wordt de declaratie-expressie geclassificeerd als een expliciet getypt variabel, en het type van de expressie en de gedeclareerde variabele moeten worden aangegeven door de local_variable_type.
Een declaratie-expressie met de id _
is een verwijdering (§9.2.9.2) en introduceert geen naam voor de variabele. Een declaratie-expressie met een andere identifier dan _
introduceert die naam in de dichtstbijzijnde omgeving voor lokale variabeleverklaringen (§7.3).
voorbeeld van:
string M(out int i, string s, out bool b) { ... } var s1 = M(out int i1, "One", out var b1); Console.WriteLine($"{i1}, {b1}, {s1}"); // Error: i2 referenced within declaring argument list var s2 = M(out var i2, M(out i2, "Two", out bool b2), out b2); var s3 = M(out int _, "Three", out var _);
De declaratie van
s1
toont zowel expliciete als impliciet getypte declaratie-expressies. Het afgeleide type vanb1
isbool
omdat het het type van de bijbehorende uitvoerparameter inM1
is. De daaropvolgendeWriteLine
heeft toegang toti1
enb1
, die zijn geïntroduceerd in het ingesloten bereik.De declaratie van
s2
toont een poging omi2
te gebruiken in de geneste aanroep naarM
, wat niet is toegestaan, omdat de verwijzing optreedt in de lijst met argumenten waarini2
is gedeclareerd. Aan de andere kant is de verwijzing naarb2
in het laatste argument toegestaan, omdat deze plaatsvindt na het einde van de geneste lijst met argumenten waarb2
is gedeclareerd.De declaratie van
s3
toont het gebruik van zowel impliciet als expliciet getypte declaratie-expressies die worden weggegooid. Omdat discards geen benoemde variabele declareren, zijn meerdere verschijningen van de identificator_
toegestaan.(int i1, int _, (var i2, var _), _) = (1, 2, (3, 4), 5);
In dit voorbeeld ziet u het gebruik van impliciete en expliciet-getype declaratie-expressies voor zowel variabelen als wegwerpwaarden in een deconstruerende toewijzing. De simple_name
_
is gelijk aanvar _
wanneer er geen declaratie van_
wordt gevonden.void M1(out int i) { ... } void M2(string _) { M1(out _); // Error: `_` is a string M1(out var _); }
In deze voorbeelden ziet u het gebruik van
var _
om impliciet getypte verwijdering te bieden wanneer_
niet beschikbaar is, omdat er een variabele in het bereik insluiten wordt toegewezen.einde voorbeeld
12.18 Voorwaardelijke operator
De operator ?:
wordt de voorwaardelijke operator genoemd. Het wordt soms ook wel de ternaire operator genoemd.
conditional_expression
: null_coalescing_expression
| null_coalescing_expression '?' expression ':' expression
| null_coalescing_expression '?' 'ref' variable_reference ':'
'ref' variable_reference
;
Een throw-expressie (§12.16) is niet toegestaan in een voorwaardelijke operator als ref
aanwezig is.
Een voorwaardelijke expressie van het formulier b ? x : y
evalueert eerst de voorwaarde b
. Als b
vervolgens is true
, wordt x
geëvalueerd en wordt het resultaat van de bewerking. Anders wordt y
geëvalueerd en is dit het resultaat van de bewerking. Een voorwaardelijke expressie evalueert nooit zowel x
als y
.
De voorwaardelijke operator is rechts-associatief, wat betekent dat bewerkingen van rechts naar links worden gegroepeerd.
Voorbeeld: een expressie van het formulier
a ? b : c ? d : e
wordt geëvalueerd alsa ? b : (c ? d : e)
. einde voorbeeld
De eerste operand van de operator ?:
moet een expressie zijn die impliciet kan worden geconverteerd naar bool
of een expressie van een type dat operator true
implementeert. Als aan geen van deze vereisten wordt voldaan, treedt er een compilatietijdfout op.
Als ref
aanwezig is:
- Er bestaat een identiteitsconversie tussen de typen van de twee variable_references en het type van het resultaat kan een van beide typen zijn. Als een van de typen
dynamic
is, geeft type deductie de voorkeur aandynamic
(§8,7). Als een van beide typen een tupeltype is (§8.3.11), bevat de deductie de elementnamen wanneer de elementnamen in dezelfde rangorde overeenkomen met beide tuples. - Het resultaat is een variabele verwijzing, die kan worden geschreven als beide variable_references schrijfbaar zijn.
Opmerking: wanneer
ref
aanwezig is, retourneert de conditional_expression een variabelereferentie die kan worden toegewezen aan een verwijzingsvariabele met behulp van de= ref
-operator of doorgegeven als referentie-/invoer-/uitvoerparameter. eindnotitie
Als ref
niet aanwezig is, bepalen de tweede en derde operanden, x
en y
, van de operator ?:
het type voorwaardelijke expressie:
- Als
x
typeX
heeft eny
danY
heeft,- Als er een identiteitsconversie bestaat tussen
X
enY
, is het resultaat het beste gemeenschappelijke type van een set expressies (§12.6.3.15). Als een van de typendynamic
is, geeft type deductie de voorkeur aandynamic
(§8,7). Als een van beide typen een tupeltype is (§8.3.11), bevat de deductie de elementnamen wanneer de elementnamen in dezelfde rangorde overeenkomen met beide tuples. - Als er anders een impliciete conversie (§10,2) bestaat van
X
totY
, maar niet vanY
totX
, isY
het type van de voorwaardelijke expressie. - Als er anders een impliciete opsommingsconversie (§10.2.4) bestaat van
X
totY
, isY
het type voorwaardelijke expressie. - Als er anders een impliciete opsommingsconversie (§10.2.4) bestaat van
Y
totX
, isX
het type voorwaardelijke expressie. - Als er anders een impliciete conversie (§10,2) bestaat van
Y
totX
, maar niet vanX
totY
, isX
het type van de voorwaardelijke expressie. - Anders kan geen expressietype worden bepaald en treedt er een compilatietijdfout op.
- Als er een identiteitsconversie bestaat tussen
- Als slechts één van
x
eny
een type heeft, en zowelx
alsy
impliciet kunnen worden omgezet in dat type, is dat het type van de voorwaardelijke expressie. - Anders kan geen expressietype worden bepaald en treedt er een compilatietijdfout op.
De runtimeverwerking van een voorwaardelijke ref-expressie van het formulier b ? ref x : ref y
bestaat uit de volgende stappen:
- Eerst wordt
b
geëvalueerd en wordt debool
waarde vanb
bepaald:- Als er een impliciete conversie van het type
b
naarbool
bestaat, wordt deze impliciete conversie uitgevoerd om eenbool
waarde te produceren. - Anders wordt de
operator true
die door het typeb
wordt bepaald aangeroepen om eenbool
-waarde te produceren.
- Als er een impliciete conversie van het type
- Als de
bool
waarde die in de bovenstaande stap wordt geproduceerd,true
is, wordtx
geëvalueerd en wordt de resulterende variabelereferentie het resultaat van de voorwaardelijke expressie. - Anders wordt
y
geëvalueerd en wordt de resulterende variabelereferentie het resultaat van de voorwaardelijke expressie.
De runtimeverwerking van een voorwaardelijke expressie van het formulier b ? x : y
bestaat uit de volgende stappen:
- Eerst wordt
b
geëvalueerd en wordt debool
waarde vanb
bepaald:- Als er een impliciete conversie van het type
b
naarbool
bestaat, wordt deze impliciete conversie uitgevoerd om eenbool
waarde te produceren. - Anders wordt de
operator true
die door het typeb
wordt bepaald aangeroepen om eenbool
-waarde te produceren.
- Als er een impliciete conversie van het type
- Als de
bool
waarde die in de bovenstaande stap wordt geproduceerd,true
is, wordtx
geëvalueerd en geconverteerd naar het type voorwaardelijke expressie. Dit wordt het resultaat van de voorwaardelijke expressie. - Anders wordt
y
geëvalueerd en geconverteerd naar het type voorwaardelijke expressie. Dit wordt het resultaat van de voorwaardelijke expressie.
12.19 Anonieme functie-expressies
12.19.1 Algemeen
Een anonieme functie is een expressie die een 'in-line'-methodedefinitie vertegenwoordigt. Een anonieme functie heeft op zichzelf geen waarde of type, maar kan worden omgezet naar een compatibel delegate- of expressieboomtype. De evaluatie van een anonieme-functieconversie is afhankelijk van het doeltype van de conversie: als het een gedelegeerdetype is, wordt de conversie geëvalueerd naar een gedelegeerde waarde die verwijst naar de methode die door de anonieme functie wordt gedefinieerd. Als het een expressiestructuurtype is, evalueert de conversie naar een expressiestructuur die de structuur van de methode voorstelt als objectstructuur.
Opmerking: Om historische redenen zijn er twee syntactische varianten van anonieme functies, namelijk lambda_expressions en anonymous_method_expressions. Voor vrijwel alle doeleinden zijn lambda_expressions beknopter en expressief dan anonymous_method_expressions, die in de taal blijven voor achterwaartse compatibiliteit. eindnotitie
lambda_expression
: 'async'? anonymous_function_signature '=>' anonymous_function_body
;
anonymous_method_expression
: 'async'? 'delegate' explicit_anonymous_function_signature? block
;
anonymous_function_signature
: explicit_anonymous_function_signature
| implicit_anonymous_function_signature
;
explicit_anonymous_function_signature
: '(' explicit_anonymous_function_parameter_list? ')'
;
explicit_anonymous_function_parameter_list
: explicit_anonymous_function_parameter
(',' explicit_anonymous_function_parameter)*
;
explicit_anonymous_function_parameter
: anonymous_function_parameter_modifier? type identifier
;
anonymous_function_parameter_modifier
: 'ref'
| 'out'
| 'in'
;
implicit_anonymous_function_signature
: '(' implicit_anonymous_function_parameter_list? ')'
| implicit_anonymous_function_parameter
;
implicit_anonymous_function_parameter_list
: implicit_anonymous_function_parameter
(',' implicit_anonymous_function_parameter)*
;
implicit_anonymous_function_parameter
: identifier
;
anonymous_function_body
: null_conditional_invocation_expression
| expression
| 'ref' variable_reference
| block
;
Bij de erkenning van een anonymous_function_body indien zowel de null_conditional_invocation_expression als de expressie alternatieven van toepassing zijn, wordt de vroegere gekozen.
Opmerking: de overlappende en prioriteit tussen deze alternatieven is uitsluitend bedoeld voor beschrijvend gemak; de grammaticaregels kunnen worden uitgewerkt om de overlapping te verwijderen. ANTLR en andere grammaticasystemen gebruiken hetzelfde gemak, zodat anonymous_function_body de opgegeven semantiek automatisch heeft. eindnotitie
Opmerking: bij behandeling als een expressie, zou een syntactische vorm zoals
x?.M()
een fout zijn als het resultaattype vanM
void
is (§12.8.13). Maar wanneer het wordt behandeld als een null_conditional_invocation_expression, mag het resultaattypevoid
zijn. eindnotitie
Voorbeeld: het resultaattype van
List<T>.Reverse
isvoid
. In de volgende code is de hoofdtekst van de anonieme expressie een null_conditional_invocation_expression, dus het is geen fout.Action<List<int>> a = x => x?.Reverse();
einde voorbeeld
De operator =>
heeft dezelfde prioriteit als toewijzing (=
) en is rechts-associatief.
Een anonieme functie met de async
modifier is een asynchrone functie en volgt de regels die worden beschreven in §15.15.
De parameters van een anonieme functie in de vorm van een lambda_expression kunnen expliciet of impliciet worden getypt. In een expliciet getypte parameterlijst wordt het type van elke parameter expliciet vermeld. In een impliciet getypte parameterlijst worden de typen van de parameters afgeleid van de context waarin de anonieme functie zich voordoet, met name wanneer de anonieme functie wordt geconverteerd naar een compatibel gedelegeerd type of expressiestructuurtype, dat type de parametertypen levert (§10.7).
In een lambda_expression met één impliciet getypte parameter kunnen de haakjes worden weggelaten uit de parameterlijst. Met andere woorden, een anonieme functie van het formulier
( «param» ) => «expr»
kan worden afgekort tot
«param» => «expr»
De parameterlijst van een anonieme functie in de vorm van een anonymous_method_expression is optioneel. Indien opgegeven, worden de parameters expliciet getypt. Als dat niet het geval is, wordt de anonieme functie omgezet naar een gemachtigde met een parameterlijst die geen uitvoerparameters bevat.
Een blok hoofdtekst van een anonieme functie is altijd bereikbaar (§13.2).
voorbeeld van: hieronder volgen enkele voorbeelden van anonieme functies:
x => x + 1 // Implicitly typed, expression body x => { return x + 1; } // Implicitly typed, block body (int x) => x + 1 // Explicitly typed, expression body (int x) => { return x + 1; } // Explicitly typed, block body (x, y) => x * y // Multiple parameters () => Console.WriteLine() // No parameters async (t1,t2) => await t1 + await t2 // Async delegate (int x) { return x + 1; } // Anonymous method expression delegate { return 1 + 1; } // Parameter list omitted
einde voorbeeld
Het gedrag van lambda_expressionen anonymous_method_expressions is hetzelfde, met uitzondering van de volgende punten:
- anonymous_method_expressionstaan toe dat de parameterlijst volledig wordt weggelaten, waardoor conversie naar delegeringstypen van een willekeurige lijst met waardeparameters mogelijk wordt.
- lambda_expressiontoestaan dat parametertypen worden weggelaten en afgeleid, terwijl anonymous_method_expressions vereisen dat parametertypen expliciet worden vermeld.
- De hoofdtekst van een lambda_expression kan een expressie of een blok zijn, terwijl de hoofdtekst van een anonymous_method_expression een blok moet zijn.
- Alleen lambda_expressionhebben conversies naar compatibele expressiestructuurtypen (§8,6).
12.19.2 Anonieme functiehandtekeningen
De anonymous_function_signature van een anonieme functie definieert de namen en eventueel de typen parameters voor de anonieme functie. Het bereik van de parameters van de anonieme functie is de anonymous_function_body (§7,7). Samen met de parameterlijst (indien gegeven) vormt de anonieme methode-body een declaratieruimte (§7.3). Het is dus een compilatiefout voor de naam van een parameter van de anonieme functie die overeenkomt met de naam van een lokale variabele, lokale constante of parameter waarvan het bereik de anonymous_method_expression of lambda_expressionbevat.
Als een anonieme functie een explicit_anonymous_function_signatureheeft, is de set van compatibele gedelegeerde typen en expressiestructuurtypen beperkt tot die welke dezelfde parametertypen en modifiers in dezelfde volgorde hebben (§10,7). In tegenstelling tot methodegroepconversies (§10,8), wordt contravariantie van anonieme functieparametertypen niet ondersteund. Als een anonieme functie geen anonymous_function_signatureheeft, wordt de set compatibele typen gemachtigden en expressiestructuurtypen beperkt tot de typen met geen uitvoerparameters.
Houd er rekening mee dat een anonymous_function_signature geen kenmerken of een parametermatrix kan bevatten. Niettemin kan een anonymous_function_signature compatibel zijn met een gemachtigdentype waarvan de parameterlijst een parametermatrix bevat.
Houd er ook rekening mee dat de conversie naar een expressiestructuurtype, zelfs als dit compatibel is, tijdens het compileren nog steeds kan mislukken (§8.6).
12.19.3 Anonieme functielichamen
De hoofdtekst (expressie of blok) van een anonieme functie is onderworpen aan de volgende regels:
- Als de anonieme functie een handtekening bevat, zijn de parameters die zijn opgegeven in de handtekening beschikbaar in de hoofdtekst. Als de anonieme functie geen handtekening heeft, kan deze worden geconverteerd naar een gemachtigde of expressietype met parameters (§10.7), maar de parameters kunnen niet worden geopend in de hoofdtekst.
- Met uitzondering van by-reference-parameters die zijn opgegeven in de signatuur (indien aanwezig) van de dichtstbijzijnde anonieme functie, is het een compilatiefout als de functiebody toegang probeert te krijgen tot een by-reference-parameter.
- Behalve voor parameters die zijn opgegeven in de signatuur (indien aanwezig) van de dichtstbijzijnde anonieme functie, is het een compilatiefout als de body probeert toegang te krijgen tot een parameter van een type
ref struct
. - Wanneer het type
this
een structtype is, is het een compilatiefout voor de code om toegang te krijgen totthis
. Dit is waar of de toegang expliciet is (zoals inthis.x
) of impliciet (zoals inx
waarx
een exemplaarlid van de struct is). Deze regel verbiedt alleen dergelijke toegang en heeft geen invloed op het feit of het opzoeken van leden resulteert in een lid van de struct. - Het lichaam heeft toegang tot de buitenste variabelen (§12.19.6) van de anonieme functie. Toegang tot een buitenste variabele verwijst naar het exemplaar van de variabele die actief is op het moment dat de lambda_expression of anonymous_method_expression wordt geëvalueerd (§12.19.7).
- Het is een compilatiefout als de hoofdtekst een
goto
-instructie, eenbreak
-instructie of eencontinue
-instructie bevat, waarvan het doel zich buiten de hoofdtekst bevindt of binnen de hoofdtekst van een ingesloten anonieme functie. - Een
return
-instructie in de hoofdtekst retourneert de controle van een aanroep van de dichtstbijzijnde omsluitende anonieme functie, niet van de omsluitende functielid.
Het is expliciet niet gespecificeerd of er een andere manier is om het blok van een anonieme functie uit te voeren dan via evaluatie en aanroep van de lambda_expression of anonymous_method_expression. Een compiler kan er met name voor kiezen om een anonieme functie te implementeren door een of meer benoemde methoden of typen te synthetiseren. De namen van dergelijke gesynthetiseerde elementen moeten van een vorm zijn die gereserveerd is voor compilergebruik (§6.4.3).
12.19.4 Overbelastingsresolutie
Anonieme functies in een lijst met argumenten nemen deel aan typedeductie en overbelastingsresolutie. Raadpleeg §12.6.3 en §12.6.4 voor de exacte regels.
voorbeeld: in het volgende voorbeeld ziet u het effect van anonieme functies op overbelastingsresolutie.
class ItemList<T> : List<T> { public int Sum(Func<T, int> selector) { int sum = 0; foreach (T item in this) { sum += selector(item); } return sum; } public double Sum(Func<T, double> selector) { double sum = 0; foreach (T item in this) { sum += selector(item); } return sum; } }
De klasse
ItemList<T>
heeft tweeSum
methoden. Elk heeft eenselector
argument, waarmee de waarde wordt geëxtraheerd die moet worden opgeteld uit een lijstitem. De geëxtraheerde waarde kan eenint
of eendouble
zijn en de resulterende som is eveneens eenint
of eendouble
.De
Sum
methoden kunnen bijvoorbeeld worden gebruikt voor het berekenen van sommen uit een lijst met detailregels in een volgorde.class Detail { public int UnitCount; public double UnitPrice; ... } class A { void ComputeSums() { ItemList<Detail> orderDetails = GetOrderDetails( ... ); int totalUnits = orderDetails.Sum(d => d.UnitCount); double orderTotal = orderDetails.Sum(d => d.UnitPrice * d.UnitCount); ... } ItemList<Detail> GetOrderDetails( ... ) { ... } }
In de eerste aanroep van
orderDetails.Sum
zijn beideSum
methoden van toepassing omdat de anonieme functied => d.UnitCount
compatibel is met zowelFunc<Detail,int>
alsFunc<Detail,double>
. Overbelastingsresolutie kiest echter de eersteSum
methode omdat de conversie naarFunc<Detail,int>
beter is dan de conversie naarFunc<Detail,double>
.In de tweede aanroep van
orderDetails.Sum
is alleen de tweedeSum
methode van toepassing omdat de anonieme functied => d.UnitPrice * d.UnitCount
een waarde van het typedouble
produceert. Overbelastingsresolutie kiest dus de tweedeSum
methode voor die aanroep.einde voorbeeld
12.19.5 Anonieme functies en dynamische binding
Een anonieme functie kan geen ontvanger, argument of operand van een dynamisch gebonden bewerking zijn.
12.19.6 Buitenste variabelen
12.19.6.1 Algemeen
Een lokale variabele, waardeparameter of parametermatrix waarvan het bereik de lambda_expression of anonymous_method_expression bevat, wordt een buitenste variabele van de anonieme functie genoemd. In een exemplaarfunctielid van een klasse wordt de waarde beschouwd als een waardeparameter en is dit een buitenste variabele van een anonieme functie die zich in het functielid bevindt.
12.19.6.2 Vastgelegde buitenste variabelen
Wanneer naar een buitenste variabele wordt verwezen door een anonieme functie, wordt gezegd dat de buitenste variabele is vastgelegd door de anonieme functie. Normaal gesproken is de levensduur van een lokale variabele beperkt tot de uitvoering van het blok of de instructie waaraan deze is gekoppeld (§9.2.9.1). De levensduur van een vastgelegde buitenste variabele wordt echter ten minste verlengd totdat de gedelegeerde of expressiestructuur die is gemaakt op basis van de anonieme functie in aanmerking komt voor garbagecollection.
Voorbeeld: In het voorbeeld
delegate int D(); class Test { static D F() { int x = 0; D result = () => ++x; return result; } static void Main() { D d = F(); Console.WriteLine(d()); Console.WriteLine(d()); Console.WriteLine(d()); } }
de lokale variabele
x
wordt vastgelegd door de anonieme functie, en de levensduur vanx
wordt ten minste verlengd totdat de gedelegeerde die is geretourneerd uitF
in aanmerking komt voor afvalinzameling. Aangezien elke aanroep van de anonieme functie op hetzelfde exemplaar vanx
werkt, is de uitvoer van het voorbeeld:1 2 3
einde voorbeeld
Wanneer een lokale variabele of een waardeparameter wordt vastgelegd door een anonieme functie, wordt de lokale variabele of parameter niet langer beschouwd als een vaste variabele (§23.4), maar wordt in plaats daarvan beschouwd als een verplaatsbare variabele. Vastgelegde buitenste variabelen kunnen echter niet worden gebruikt in een fixed
instructie (§23.7), zodat het adres van een vastgelegde buitenste variabele niet kan worden genomen.
Opmerking: in tegenstelling tot een niet-captured variabele kan een vastgelegde lokale variabele tegelijkertijd worden blootgesteld aan meerdere threads van uitvoering. eindnotitie
12.19.6.3 Instantiering van lokale variabelen
Een lokale variabele wordt beschouwd als geïnstantieerd wanneer de uitvoering het bereik van de variabele invoert.
voorbeeld: wanneer de volgende methode bijvoorbeeld wordt aangeroepen, wordt de lokale variabele
x
drie keer geïnstantieerd en geïnitialiseerd, eenmaal voor elke herhaling van de lus.static void F() { for (int i = 0; i < 3; i++) { int x = i * 2 + 1; ... } }
Het verplaatsen van de declaratie van
x
buiten de lus resulteert echter in één instantie vanx
:static void F() { int x; for (int i = 0; i < 3; i++) { x = i * 2 + 1; ... } }
einde voorbeeld
Wanneer deze niet wordt vastgelegd, is er geen manier om precies te zien hoe vaak een lokale variabele wordt geïnstantieerd, omdat de levensduur van de instantiëringen niet aaneengesloten zijn, is het voor elke instantatie mogelijk om gewoon dezelfde opslaglocatie te gebruiken. Wanneer een anonieme functie echter een lokale variabele vastlegt, worden de gevolgen van instantiëring duidelijk.
voorbeeld: het voorbeeld
delegate void D(); class Test { static D[] F() { D[] result = new D[3]; for (int i = 0; i < 3; i++) { int x = i * 2 + 1; result[i] = () => Console.WriteLine(x); } return result; } static void Main() { foreach (D d in F()) { d(); } } }
produceert de uitvoer:
1 3 5
Wanneer de declaratie van
x
echter buiten de lus wordt verplaatst:delegate void D(); class Test { static D[] F() { D[] result = new D[3]; int x; for (int i = 0; i < 3; i++) { x = i * 2 + 1; result[i] = () => Console.WriteLine(x); } return result; } static void Main() { foreach (D d in F()) { d(); } } }
de uitvoer is:
5 5 5
Houd er rekening mee dat een compiler mag (maar niet verplicht is) de drie instanties optimaliseren tot één gedelegeerde instantie (§10.7.2).
einde voorbeeld
Als een for-lus een iteratievariabele declareert, wordt die variabele zelf beschouwd als buiten de lus.
voorbeeld: dus als het voorbeeld wordt gewijzigd om de iteratievariabele zelf vast te leggen:
delegate void D(); class Test { static D[] F() { D[] result = new D[3]; for (int i = 0; i < 3; i++) { result[i] = () => Console.WriteLine(i); } return result; } static void Main() { foreach (D d in F()) { d(); } } }
er wordt slechts één exemplaar van de iteratievariabele vastgelegd, waardoor de uitvoer wordt geproduceerd:
3 3 3
einde voorbeeld
Het is mogelijk dat anonieme functiedelegen bepaalde vastgelegde variabelen delen, maar nog afzonderlijke exemplaren van anderen hebben.
Voorbeeld: bijvoorbeeld als
F
wordt gewijzigd instatic D[] F() { D[] result = new D[3]; int x = 0; for (int i = 0; i < 3; i++) { int y = 0; result[i] = () => Console.WriteLine($"{++x} {++y}"); } return result; }
de drie afgevaardigden leggen hetzelfde exemplaar van
x
vast, maar afzonderlijke exemplaren vany
, en het resultaat is:1 1 2 1 3 1
einde voorbeeld
Afzonderlijke anonieme functies kunnen hetzelfde exemplaar van een buitenste variabele vastleggen.
voorbeeld: In het voorbeeld:
delegate void Setter(int value); delegate int Getter(); class Test { static void Main() { int x = 0; Setter s = (int value) => x = value; Getter g = () => x; s(5); Console.WriteLine(g()); s(10); Console.WriteLine(g()); } }
de twee anonieme functies leggen hetzelfde exemplaar van de lokale variabele vast
x
, en ze kunnen dus 'communiceren' via die variabele. De uitvoer van het voorbeeld is:5 10
einde voorbeeld
12.19.7 Evaluatie van anonieme functie-expressies
Een anonieme functie F
wordt altijd geconverteerd naar een gemachtigde D
of een expressiestructuurtype E
, hetzij rechtstreeks of via de uitvoering van een expressie voor het maken van een gemachtigde new D(F)
. Deze conversie bepaalt het resultaat van de anonieme functie, zoals beschreven in §10,7.
Implementatievoorbeeld 12.19.8
Deze subclause is informatief.
In dit subclause wordt een mogelijke implementatie van anonieme functieconversies beschreven in termen van andere C#-constructies. De implementatie die hier wordt beschreven, is gebaseerd op dezelfde principes die door een commerciële C#-compiler worden gebruikt, maar het is niet de enige mogelijke implementatie. Er wordt slechts kort melding gemaakt van conversies naar expressiebomen, omdat hun exacte semantiek buiten het bereik van deze specificatie valt.
De rest van deze subclause geeft verschillende voorbeelden van code die anonieme functies met verschillende kenmerken bevat. Voor elk voorbeeld wordt een bijbehorende vertaling naar code opgegeven die alleen andere C#-constructies gebruikt. In de voorbeelden wordt aangenomen dat de identifier D
het volgende gemachtigdentype vertegenwoordigt.
public delegate void D();
De eenvoudigste vorm van een anonieme functie is een functie die geen buitenste variabelen vastlegt:
delegate void D();
class Test
{
static void F()
{
D d = () => Console.WriteLine("test");
}
}
Dit kan worden vertaald naar een gemachtigde instantie die verwijst naar een door een compiler gegenereerde statische methode waarin de code van de anonieme functie wordt geplaatst:
delegate void D();
class Test
{
static void F()
{
D d = new D(__Method1);
}
static void __Method1()
{
Console.WriteLine("test");
}
}
In het volgende voorbeeld verwijst de anonieme functie naar de instantieleden van this
:
delegate void D();
class Test
{
int x;
void F()
{
D d = () => Console.WriteLine(x);
}
}
Dit kan worden vertaald naar een door compiler gegenereerde exemplaarmethode die de code van de anonieme functie bevat:
delegate void D();
class Test
{
int x;
void F()
{
D d = new D(__Method1);
}
void __Method1()
{
Console.WriteLine(x);
}
}
In dit voorbeeld legt de anonieme functie een lokale variabele vast:
delegate void D();
class Test
{
void F()
{
int y = 123;
D d = () => Console.WriteLine(y);
}
}
De levensduur van de lokale variabele moet nu worden verlengd tot ten minste de levensduur van de gemachtigde voor anonieme functies. Dit kan worden bereikt door de lokale variabele te 'hoisen' in een veld van een door compiler gegenereerde klasse. Instantiëring van de lokale variabele (§12.19.6.3) komt vervolgens overeen met het maken van een exemplaar van de door compiler gegenereerde klasse en het openen van de lokale variabele komt overeen met het openen van een veld in het exemplaar van de door compiler gegenereerde klasse. Bovendien wordt de anonieme functie een instantiemethode van de door de compiler gegenereerde klasse:
delegate void D();
class Test
{
void F()
{
__Locals1 __locals1 = new __Locals1();
__locals1.y = 123;
D d = new D(__locals1.__Method1);
}
class __Locals1
{
public int y;
public void __Method1()
{
Console.WriteLine(y);
}
}
}
Ten slotte worden met de volgende anonieme functie this
en twee lokale variabelen met verschillende levensduur vastgelegd:
delegate void D();
class Test
{
int x;
void F()
{
int y = 123;
for (int i = 0; i < 10; i++)
{
int z = i * 2;
D d = () => Console.WriteLine(x + y + z);
}
}
}
Hier wordt een door compiler gegenereerde klasse gemaakt voor elk blok waarin de lokale bevolking wordt vastgelegd, zodat de lokale bevolking in de verschillende blokken onafhankelijke levensduur kan hebben. Een exemplaar van __Locals2
, de door de compiler gegenereerde klasse voor het binnenste blok, bevat de lokale variabele z
en een veld dat verwijst naar een exemplaar van __Locals1
. Een exemplaar van __Locals1
, de door compiler gegenereerde klasse voor het buitenste blok, bevat de lokale variabele y
en een veld dat verwijst naar this
van het omsluitende functielid. Met deze gegevensstructuren is het mogelijk om alle vastgelegde buitenste variabelen te bereiken via een exemplaar van __Local2
, en de code van de anonieme functie kan dus worden geïmplementeerd als een instantiemethode van die klasse.
delegate void D();
class Test
{
int x;
void F()
{
__Locals1 __locals1 = new __Locals1();
__locals1.__this = this;
__locals1.y = 123;
for (int i = 0; i < 10; i++)
{
__Locals2 __locals2 = new __Locals2();
__locals2.__locals1 = __locals1;
__locals2.z = i * 2;
D d = new D(__locals2.__Method1);
}
}
class __Locals1
{
public Test __this;
public int y;
}
class __Locals2
{
public __Locals1 __locals1;
public int z;
public void __Method1()
{
Console.WriteLine(__locals1.__this.x + __locals1.y + z);
}
}
}
Dezelfde techniek die hier wordt toegepast om lokale variabelen vast te leggen, kan ook worden gebruikt bij het converteren van anonieme functies naar expressiestructuren: verwijzingen naar door de compiler gegenereerde objecten kunnen worden opgeslagen in de expressiestructuur en toegang tot de lokale variabelen kunnen worden weergegeven als veldtoegang op deze objecten. Het voordeel van deze aanpak is dat de "gelifte" lokale variabelen kunnen worden gedeeld tussen delegates en expressiebomen.
einde van informatieve tekst.
12.20 Query-expressies
12.20.1 Algemeen
Query-expressies een taalgebaseerde syntaxis bieden voor query's die vergelijkbaar zijn met relationele en hiërarchische querytalen zoals SQL en XQuery.
query_expression
: from_clause query_body
;
from_clause
: 'from' type? identifier 'in' expression
;
query_body
: query_body_clauses? select_or_group_clause query_continuation?
;
query_body_clauses
: query_body_clause
| query_body_clauses query_body_clause
;
query_body_clause
: from_clause
| let_clause
| where_clause
| join_clause
| join_into_clause
| orderby_clause
;
let_clause
: 'let' identifier '=' expression
;
where_clause
: 'where' boolean_expression
;
join_clause
: 'join' type? identifier 'in' expression 'on' expression
'equals' expression
;
join_into_clause
: 'join' type? identifier 'in' expression 'on' expression
'equals' expression 'into' identifier
;
orderby_clause
: 'orderby' orderings
;
orderings
: ordering (',' ordering)*
;
ordering
: expression ordering_direction?
;
ordering_direction
: 'ascending'
| 'descending'
;
select_or_group_clause
: select_clause
| group_clause
;
select_clause
: 'select' expression
;
group_clause
: 'group' expression 'by' expression
;
query_continuation
: 'into' identifier query_body
;
Een query-expressie begint met een from
-component en eindigt met een select
- of group
-component. De eerste from
component kan worden gevolgd door nul of meer from
, let
, where
, join
of orderby
componenten. Elke from
clausule is een generator die een bereikvariabele introduceert die varieert over de elementen van een reeks. Elke let
component introduceert een bereikvariabele die een waarde vertegenwoordigt die wordt berekend met behulp van eerdere bereikvariabelen. Elke where
component is een filter dat items uitsluit van het resultaat. Elke join
component vergelijkt de opgegeven sleutels van de bronreeks met sleutels van een andere reeks, wat overeenkomende paren oplevert. Elke orderby
component herschikt items volgens de opgegeven criteria. De laatste select
of group
component geeft de vorm van het resultaat op in termen van de bereikvariabelen. Ten slotte kan een into
component worden gebruikt om query's te 'spliceren' door de resultaten van één query te behandelen als een generator in een volgende query.
12.20.2 Dubbelzinnigheden in query-expressies
Query-expressies gebruiken een aantal contextuele trefwoorden (§6.4.4): ascending
, by
, descending
, equals
, from
, group
, into
, join
, let
, on
, orderby
, select
en where
.
Om dubbelzinnigheden te voorkomen die kunnen ontstaan door het gebruik van deze identificatoren, zowel als trefwoorden als eenvoudige namen, worden deze identificatoren overal in een query-expressie als trefwoorden beschouwd, tenzij ze worden voorafgegaan door “@
” (§6.4.4), in welk geval ze als identificatoren worden beschouwd. Voor dit doel is een query-expressie een expressie die begint met "from
id" gevolgd door een token behalve ";
", "=
" of ",
".
12.20.3 Vertaling van query-expressie
12.20.3.1 Algemeen
De C#-taal geeft de uitvoeringssemantiek van query-expressies niet op. In plaats daarvan worden queryexpressies omgezet in aanroepen van methoden die voldoen aan het queryexpressiepatroon (§12.20.4). Queryexpressies worden met name omgezet in aanroepen van methoden met de naam Where
, Select
, SelectMany
, Join
, GroupJoin
, OrderBy
, OrderByDescending
, ThenBy
, ThenByDescending
, GroupBy
en Cast
. Deze methoden zijn naar verwachting voorzien van bepaalde handtekeningen en retourtypen, zoals beschreven in §12.20.4. Deze methoden kunnen exemplaarmethoden zijn van het object waarop query's worden uitgevoerd of uitbreidingsmethoden die extern zijn voor het object. Met deze methoden wordt de werkelijke uitvoering van de query geïmplementeerd.
De vertaling van query-expressies naar methode-aanroepen is een syntactische toewijzing die plaatsvindt voordat een typebinding of overbelastingsresolutie wordt uitgevoerd. Na de vertaling van query-expressies worden de resulterende methode-aanroepen verwerkt als reguliere methode-aanroepen. Hierdoor kunnen op zijn beurt compilatiefouten worden gedetecteerd. Deze foutvoorwaarden omvatten, maar zijn niet beperkt tot methoden die niet bestaan, argumenten van de verkeerde typen en algemene methoden waarbij typedeductie mislukt.
Een query-expressie wordt verwerkt door herhaaldelijk de volgende vertalingen toe te passen totdat er geen verdere reducties mogelijk zijn. De vertalingen worden weergegeven in volgorde van toepassing: in elke sectie wordt ervan uitgegaan dat de vertalingen in de voorgaande secties volledig zijn uitgevoerd en zodra deze zijn uitgeput, wordt een sectie later niet meer bekeken in de verwerking van dezelfde query-expressie.
Het is een compileertijdfout voor een query-expressie om een toewijzing op te nemen aan een bereikvariabele of het gebruik van een bereikvariabele als argument voor een verwijzings- of uitvoerparameter.
Bepaalde vertalingen injecteren bereikvariabelen met transparante identificaties aangeduid met *. Deze worden verder beschreven in §12.20.3.8.
12.20.3.2 Query-expressies met doorvoeringen
Een query-expressie met een vervolg na de hoofdtekst van de query
from «x1» in «e1» «b1» into «x2» «b2»
wordt omgezet in
from «x2» in ( from «x1» in «e1» «b1» ) «b2»
Bij de vertalingen in de volgende secties wordt ervan uitgegaan dat query's geen vervolgen hebben.
Voorbeeld: Het voorbeeld:
from c in customers group c by c.Country into g select new { Country = g.Key, CustCount = g.Count() }
wordt omgezet in:
from g in (from c in customers group c by c.Country) select new { Country = g.Key, CustCount = g.Count() }
de definitieve vertaling is:
customers. GroupBy(c => c.Country). Select(g => new { Country = g.Key, CustCount = g.Count() })
einde voorbeeld
12.20.3.3 Expliciete typen voor bereikvariabelen
Een from
-component waarmee expliciet een bereikvariabeletype wordt opgegeven
from «T» «x» in «e»
wordt omgezet in
from «x» in ( «e» ) . Cast < «T» > ( )
Een join
-component waarmee expliciet een bereikvariabeletype wordt opgegeven
join «T» «x» in «e» on «k1» equals «k2»
wordt omgezet in
join «x» in ( «e» ) . Cast < «T» > ( ) on «k1» equals «k2»
Bij de vertalingen in de volgende secties wordt ervan uitgegaan dat query's geen expliciete bereikvariabeltypen hebben.
voorbeeld: het voorbeeld
from Customer c in customers where c.City == "London" select c
wordt omgezet in
from c in (customers).Cast<Customer>() where c.City == "London" select c
de definitieve vertaling daarvan is
customers. Cast<Customer>(). Where(c => c.City == "London")
einde voorbeeld
Opmerking: expliciete bereikvariabeletypen zijn handig voor het uitvoeren van query's op verzamelingen die de niet-algemene
IEnumerable
-interface implementeren, maar niet de algemeneIEnumerable<T>
interface. In het bovenstaande voorbeeld zou dit het geval zijn als klanten van het typeArrayList
zijn. eindnotitie
12.20.3.4 Degeneratieve zoekuitdrukkingen
Een query-expressie van het formulier
from «x» in «e» select «x»
wordt omgezet in
( «e» ) . Select ( «x» => «x» )
voorbeeld: het voorbeeld
from c in customers select c
wordt omgezet in
(customers).Select(c => c)
einde voorbeeld
Een degenereerde query-expressie is een expressie die de elementen van de bron triviaal selecteert.
Opmerking: latere fasen van de vertaling (§12.20.3.6 en §12.20.3.7) verwijderen degenererende query's die zijn geïntroduceerd door andere vertaalstappen door ze te vervangen door hun bron. Het is echter belangrijk om ervoor te zorgen dat het resultaat van een query-expressie nooit het bronobject zelf is. Anders kan het retourneren van het resultaat van een dergelijke query per ongeluk persoonlijke gegevens (bijvoorbeeld een elementmatrix) blootstellen aan een aanroeper. Daarom beveiligt deze stap degenereerde query's die rechtstreeks in de broncode zijn geschreven door expliciet
Select
aan te roepen op de bron. Het is vervolgens aan de implementeerfuncties vanSelect
en andere queryoperators om ervoor te zorgen dat deze methoden nooit het bronobject zelf retourneren. eindnotitie
12.20.3.5 Vanuit, let, waar, voeg toe en sorteer op clausules
Een query-expressie met een tweede from
-component gevolgd door een select
-component
from «x1» in «e1»
from «x2» in «e2»
select «v»
wordt omgezet in
( «e1» ) . SelectMany( «x1» => «e2» , ( «x1» , «x2» ) => «v» )
voorbeeld: het voorbeeld
from c in customers from o in c.Orders select new { c.Name, o.OrderID, o.Total }
wordt omgezet in
(customers). SelectMany(c => c.Orders, (c,o) => new { c.Name, o.OrderID, o.Total } )
einde voorbeeld
Een query-expressie met een tweede from
component gevolgd door een querytekst Q
met een niet-lege set querytekstcomponenten:
from «x1» in «e1»
from «x2» in «e2»
Q
wordt omgezet in
from * in («e1») . SelectMany( «x1» => «e2» ,
( «x1» , «x2» ) => new { «x1» , «x2» } )
Q
voorbeeld: het voorbeeld
from c in customers from o in c.Orders orderby o.Total descending select new { c.Name, o.OrderID, o.Total }
wordt omgezet in
from * in (customers). SelectMany(c => c.Orders, (c,o) => new { c, o }) orderby o.Total descending select new { c.Name, o.OrderID, o.Total }
de definitieve vertaling daarvan is
customers. SelectMany(c => c.Orders, (c,o) => new { c, o }). OrderByDescending(x => x.o.Total). Select(x => new { x.c.Name, x.o.OrderID, x.o.Total })
waarbij
x
een door compiler gegenereerde id is die anders onzichtbaar en niet toegankelijk is.einde voorbeeld
Een let
-uitdrukking samen met de voorgaande from
-clausule:
from «x» in «e»
let «y» = «f»
...
wordt omgezet in
from * in ( «e» ) . Select ( «x» => new { «x» , «y» = «f» } )
...
voorbeeld: het voorbeeld
from o in orders let t = o.Details.Sum(d => d.UnitPrice * d.Quantity) where t >= 1000 select new { o.OrderID, Total = t }
wordt omgezet in
from * in (orders).Select( o => new { o, t = o.Details.Sum(d => d.UnitPrice * d.Quantity) }) where t >= 1000 select new { o.OrderID, Total = t }
de definitieve vertaling daarvan is
orders .Select(o => new { o, t = o.Details.Sum(d => d.UnitPrice * d.Quantity) }) .Where(x => x.t >= 1000) .Select(x => new { x.o.OrderID, Total = x.t })
waarbij
x
een door compiler gegenereerde id is die anders onzichtbaar en niet toegankelijk is.einde voorbeeld
Een where
-uitdrukking samen met de voorgaande from
-clausule:
from «x» in «e»
where «f»
...
wordt omgezet in
from «x» in ( «e» ) . Where ( «x» => «f» )
...
Een join
-component onmiddellijk gevolgd door een select
-component
from «x1» in «e1»
join «x2» in «e2» on «k1» equals «k2»
select «v»
wordt omgezet in
( «e1» ) . Join( «e2» , «x1» => «k1» , «x2» => «k2» , ( «x1» , «x2» ) => «v» )
voorbeeld: het voorbeeld
from c in customers join o in orders on c.CustomerID equals o.CustomerID select new { c.Name, o.OrderDate, o.Total }
wordt omgezet in
(customers).Join( orders, c => c.CustomerID, o => o.CustomerID, (c, o) => new { c.Name, o.OrderDate, o.Total })
einde voorbeeld
Een join
-component gevolgd door een querybody-component:
from «x1» in «e1»
join «x2» in «e2» on «k1» equals «k2»
...
wordt omgezet in
from * in ( «e1» ) . Join(
«e2» , «x1» => «k1» , «x2» => «k2» ,
( «x1» , «x2» ) => new { «x1» , «x2» })
...
Een join
-into
-component onmiddellijk gevolgd door een select
-component
from «x1» in «e1»
join «x2» in «e2» on «k1» equals «k2» into «g»
select «v»
wordt omgezet in
( «e1» ) . GroupJoin( «e2» , «x1» => «k1» , «x2» => «k2» ,
( «x1» , «g» ) => «v» )
Een join into
-component gevolgd door een querybody-component
from «x1» in «e1»
join «x2» in «e2» on «k1» equals «k2» into *g»
...
wordt omgezet in
from * in ( «e1» ) . GroupJoin(
«e2» , «x1» => «k1» , «x2» => «k2» , ( «x1» , «g» ) => new { «x1» , «g» })
...
voorbeeld: het voorbeeld
from c in customers join o in orders on c.CustomerID equals o.CustomerID into co let n = co.Count() where n >= 10 select new { c.Name, OrderCount = n }
wordt omgezet in
from * in (customers).GroupJoin( orders, c => c.CustomerID, o => o.CustomerID, (c, co) => new { c, co }) let n = co.Count() where n >= 10 select new { c.Name, OrderCount = n }
de definitieve vertaling daarvan is
customers .GroupJoin( orders, c => c.CustomerID, o => o.CustomerID, (c, co) => new { c, co }) .Select(x => new { x, n = x.co.Count() }) .Where(y => y.n >= 10) .Select(y => new { y.x.c.Name, OrderCount = y.n })
waarbij
x
eny
door compiler gegenereerde id's zijn die anders onzichtbaar en niet toegankelijk zijn.einde voorbeeld
Een orderby
-clausule en de voorgaande from
-clausule:
from «x» in «e»
orderby «k1» , «k2» , ... , «kn»
...
wordt omgezet in
from «x» in ( «e» ) .
OrderBy ( «x» => «k1» ) .
ThenBy ( «x» => «k2» ) .
... .
ThenBy ( «x» => «kn» )
...
Als een ordering
-component een indicator voor aflopende richting opgeeft, wordt in plaats daarvan een oproep van OrderByDescending
of ThenByDescending
uitgevoerd.
voorbeeld: het voorbeeld
from o in orders orderby o.Customer.Name, o.Total descending select o
heeft de definitieve vertaling
(orders) .OrderBy(o => o.Customer.Name) .ThenByDescending(o => o.Total)
einde voorbeeld
In de volgende vertalingen wordt ervan uitgegaan dat er geen let
, where
, join
- of orderby
-componenten zijn, en niet meer dan de eerste from
component in elke query-expressie.
12.20.3.6 Clausules selecteren
Een query-expressie van het formulier
from «x» in «e» select «v»
wordt omgezet in
( «e» ) . Select ( «x» => «v» )
behalve wanneer «v»
de identifier «x»
is, is de vertaling gewoon
( «e» )
voorbeeld: het voorbeeld
from c in customers.Where(c => c.City == "London") select c
wordt simpelweg vertaald in
(customers).Where(c => c.City == "London")
einde voorbeeld
12.20.3.7 Groepsclausules
Een group
-clausule
from «x» in «e» group «v» by «k»
wordt omgezet in
( «e» ) . GroupBy ( «x» => «k» , «x» => «v» )
behalve wanneer «v»
de id «x»
is, is de vertaling
( «e» ) . GroupBy ( «x» => «k» )
voorbeeld: het voorbeeld
from c in customers group c.Name by c.Country
wordt omgezet in
(customers).GroupBy(c => c.Country, c => c.Name)
einde voorbeeld
12.20.3.8 Transparante id's
Bepaalde vertalingen injecteren bereikvariabelen met transparante id's aangeduid door *
. Transparante id's bestaan alleen als een tussenliggende stap in het vertaalproces voor query-expressies.
Wanneer een queryvertaling een transparante id injecteert, worden verdere vertaalstappen de transparante id doorgestuurd naar anonieme functies en anonieme object-initialisaties. In deze contexten hebben transparante id's het volgende gedrag:
- Wanneer een transparante id als een parameter in een anonieme functie voorkomt, zijn de leden van het bijbehorende anonieme type automatisch beschikbaar binnen het lichaam van de anonieme functie.
- Wanneer een lid met een zichtbare identificatie binnen het bereik valt, bevinden de leden van dat lid zich ook binnen het bereik.
- Wanneer een transparante id voorkomt als liddeclaratie in een anonieme object-initialisatiefunctie, introduceert deze een lid met een transparante id.
In de hierboven beschreven vertaalstappen worden transparante id's altijd samen met anonieme typen geïntroduceerd, met de bedoeling om meerdere bereikvariabelen vast te leggen als leden van één object. Een implementatie van C# is toegestaan om een ander mechanisme te gebruiken dan anonieme typen om meerdere bereikvariabelen te groeperen. In de volgende vertaalvoorbeelden wordt ervan uitgegaan dat anonieme typen worden gebruikt en dat er één mogelijke vertaling van transparante id's wordt weergegeven.
voorbeeld: het voorbeeld
from c in customers from o in c.Orders orderby o.Total descending select new { c.Name, o.Total }
wordt omgezet in
from * in (customers).SelectMany(c => c.Orders, (c,o) => new { c, o }) orderby o.Total descending select new { c.Name, o.Total }
die verder wordt vertaald in
customers .SelectMany(c => c.Orders, (c,o) => new { c, o }) .OrderByDescending(* => o.Total) .Select(\* => new { c.Name, o.Total })
die, wanneer transparante identificatoren worden gewist, gelijk is aan
customers .SelectMany(c => c.Orders, (c,o) => new { c, o }) .OrderByDescending(x => x.o.Total) .Select(x => new { x.c.Name, x.o.Total })
waarbij
x
een door compiler gegenereerde id is die anders onzichtbaar en niet toegankelijk is.Het voorbeeld
from c in customers join o in orders on c.CustomerID equals o.CustomerID join d in details on o.OrderID equals d.OrderID join p in products on d.ProductID equals p.ProductID select new { c.Name, o.OrderDate, p.ProductName }
wordt omgezet in
from * in (customers).Join( orders, c => c.CustomerID, o => o.CustomerID, (c, o) => new { c, o }) join d in details on o.OrderID equals d.OrderID join p in products on d.ProductID equals p.ProductID select new { c.Name, o.OrderDate, p.ProductName }
die verder wordt gereduceerd tot
customers .Join(orders, c => c.CustomerID, o => o.CustomerID, (c, o) => new { c, o }) .Join(details, * => o.OrderID, d => d.OrderID, (*, d) => new { *, d }) .Join(products, * => d.ProductID, p => p.ProductID, (*, p) => new { c.Name, o.OrderDate, p.ProductName })
de definitieve vertaling daarvan is
customers .Join(orders, c => c.CustomerID, o => o.CustomerID, (c, o) => new { c, o }) .Join(details, x => x.o.OrderID, d => d.OrderID, (x, d) => new { x, d }) .Join(products, y => y.d.ProductID, p => p.ProductID, (y, p) => new { y.x.c.Name, y.x.o.OrderDate, p.ProductName })
waarbij
x
eny
door compiler gegenereerde id's zijn die anders onzichtbaar en niet toegankelijk zijn. einde voorbeeld
12.20.4 Het queryexpressiepatroon
Met het queryexpressiepatroon wordt een patroon vastgesteld van methoden die typen kunnen implementeren ter ondersteuning van query-expressies.
Een algemeen type C<T>
ondersteunt het query-expressiepatroon als de methoden voor openbare leden en de openbaar toegankelijke extensiemethoden kunnen worden vervangen door de volgende klassedefinitie. De leden en toegankelijke uitbreidingsmethoden worden aangeduid als de 'structuur' van een algemeen type C<T>
. Een algemeen type wordt gebruikt om de juiste relaties tussen parameter- en retourtypen te illustreren, maar het is ook mogelijk om het patroon voor niet-algemene typen te implementeren.
delegate R Func<T1,R>(T1 arg1);
delegate R Func<T1,T2,R>(T1 arg1, T2 arg2);
class C
{
public C<T> Cast<T>() { ... }
}
class C<T> : C
{
public C<T> Where(Func<T,bool> predicate) { ... }
public C<U> Select<U>(Func<T,U> selector) { ... }
public C<V> SelectMany<U,V>(Func<T,C<U>> selector,
Func<T,U,V> resultSelector) { ... }
public C<V> Join<U,K,V>(C<U> inner, Func<T,K> outerKeySelector,
Func<U,K> innerKeySelector, Func<T,U,V> resultSelector) { ... }
public C<V> GroupJoin<U,K,V>(C<U> inner, Func<T,K> outerKeySelector,
Func<U,K> innerKeySelector, Func<T,C<U>,V> resultSelector) { ... }
public O<T> OrderBy<K>(Func<T,K> keySelector) { ... }
public O<T> OrderByDescending<K>(Func<T,K> keySelector) { ... }
public C<G<K,T>> GroupBy<K>(Func<T,K> keySelector) { ... }
public C<G<K,E>> GroupBy<K,E>(Func<T,K> keySelector,
Func<T,E> elementSelector) { ... }
}
class O<T> : C<T>
{
public O<T> ThenBy<K>(Func<T,K> keySelector) { ... }
public O<T> ThenByDescending<K>(Func<T,K> keySelector) { ... }
}
class G<K,T> : C<T>
{
public K Key { get; }
}
De bovenstaande methoden gebruiken de generieke gedelegeerde typen Func<T1, R>
en Func<T1, T2, R>
, maar ze hadden net zo goed andere gedelegeerde typen of expressie-boomtypen met dezelfde relaties in parameter- en retourtypen kunnen gebruiken.
Opmerking: de aanbevolen relatie tussen
C<T>
enO<T>
die ervoor zorgt dat de methodenThenBy
enThenByDescending
alleen beschikbaar zijn op het resultaat van eenOrderBy
ofOrderByDescending
. eindnotitie
Opmerking: de aanbevolen vorm van het resultaat van
GroupBy
: een reeks reeksen, waarbij elke binnenste reeks een extraKey
eigenschap heeft. eindnotitie
Opmerking: Omdat query-expressies middels een syntactische toewijzing worden vertaald naar methodeaanroepen, hebben typen aanzienlijke flexibiliteit in hoe zij enig of alle onderdelen van het query-expressiepatroon implementeren. De methoden van het patroon kunnen bijvoorbeeld worden geïmplementeerd als exemplaarmethoden of als uitbreidingsmethoden omdat de twee dezelfde aanroepsyntaxis hebben en de methoden gemachtigden of expressiestructuren kunnen aanvragen omdat anonieme functies kunnen worden geconverteerd naar beide. Typen die slechts een deel van het queryexpressiepatroon implementeren, ondersteunen alleen vertalingen van query-expressies die worden toegewezen aan de methoden die door het type worden ondersteund. eindnotitie
Opmerking: de
System.Linq
-naamruimte biedt een implementatie van het queryexpressiepatroon voor elk type dat deSystem.Collections.Generic.IEnumerable<T>
-interface implementeert. eindnotitie
12.21 Toewijzingsoperator
12.21.1 Algemeen
Alle toewijzingsoperatoren wijzen een nieuwe waarde toe aan een variabele, een eigenschap, een gebeurtenis of een indexeerelement. De uitzondering, = ref
, wijst een variabele verwijzing (§9,5) toe aan een verwijzingsvariabele (§9,7).
assignment
: unary_expression assignment_operator expression
;
assignment_operator
: '=' 'ref'? | '+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' | '<<='
| right_shift_assignment
;
De linkeroperand van een toewijzing moet een expressie zijn die is geclassificeerd als een variabele, of, met uitzondering van = ref
, een eigenschapstoegang, een indexeerfunctietoegang, een gebeurtenistoegang of een tuple. Een declaratie-expressie is niet rechtstreeks toegestaan als een linkeroperand, maar kan optreden als een stap in de evaluatie van een deconstructing-toewijzing.
De operator =
wordt de eenvoudige toewijzingsoperatorgenoemd. Hiermee wordt de waarde of waarden van de rechteroperand toegewezen aan de variabele, eigenschap, indexeerelement of tuple-elementen die door de linkeroperand worden gegeven. De linkeroperand van de eenvoudige toewijzingsoperator mag geen toegang tot gebeurtenissen zijn (behalve zoals beschreven in §15.8.2). De eenvoudige toewijzingsoperator wordt beschreven in §12.21.2.
De operator = ref
wordt de verwijzingstoewijzing genoemd. Het zorgt ervoor dat de rechteroperand, die een variable_reference (§9.5) is, het referent van de verwijzingsvariabele wordt die door de linkeroperand wordt aangewezen. De "ref"-toewijzingsoperator wordt beschreven in §12.21.3.
De andere toewijzingsoperatoren dan de =
- en = ref
-operators worden de operatoren voor samengestelde toewijzingen genoemd. Deze operators voeren de aangegeven bewerking uit op de twee operanden en wijzen vervolgens de resulterende waarde toe aan het element variabele, eigenschap of indexeerfunctie dat is opgegeven door de linkeroperand. De samengestelde toewijzingsoperatoren worden beschreven in §12.21.4.
De operators +=
en -=
met een expressie voor gebeurtenistoegang als de linkeroperand worden de operatoren gebeurtenistoewijzinggenoemd. Er is geen andere toewijzingsoperator geldig met gebeurtenistoegang als de linkeroperand. De gebeurtenistoewijzingsoperatoren worden beschreven in §12.21.5.
De toewijzingsoperatoren zijn rechts-associatief, wat betekent dat bewerkingen van rechts naar links worden gegroepeerd.
Voorbeeld: een expressie van het formulier
a = b = c
wordt geëvalueerd alsa = (b = c)
. einde voorbeeld
12.21.2 Eenvoudige opdracht
De operator =
wordt de eenvoudige toewijzingsoperator genoemd.
Als de linkeroperand van een eenvoudige toewijzing van de vorm E.P
of E[Ei]
is, waarbij E
het compilatietijdtype dynamic
heeft, is de toewijzing dynamisch gebonden (§12.3.3). In dit geval is het compile-tijdtype van de toewijzingsexpressie dynamic
, en de hieronder beschreven oplossing zal plaatsvinden op run-time op basis van het run-time type van E
. Als de linkeroperand in de vorm van E[Ei]
is waarbij ten minste één element van Ei
het type tijdens de compilatie heeft dynamic
en het type tijdens de compilatie van E
geen array is, is de resulterende indextoegang dynamisch gebonden, maar met beperkte controle tijdens de compilatie (§12.6.5).
Een eenvoudige toewijzing waarbij de linkeroperand wordt geclassificeerd als een tuple, wordt ook wel een deconstructietoewijzinggenoemd. Als een van de tuple-elementen van de linkeroperand een elementnaam heeft, treedt er een compilatiefout op. Als een van de tuple-elementen van de linkeroperand een declaration_expression is en een ander element geen declaration_expression of een eenvoudige verwijdering is, treedt er een compilatietijdfout op.
Het type eenvoudige toewijzing x = y
is het type toewijzing aan x
van y
, dat recursief wordt bepaald als volgt:
- Als
x
een tuple-expressie(x1, ..., xn)
is eny
kan worden gedeconstrueerd tot een tuple-expressie(y1, ..., yn)
metn
elementen (§12.7), en elke toewijzing aanxi
vanyi
het typeTi
heeft, dan heeft de toewijzing het type(T1, ..., Tn)
. - Anders, als
x
als variabele wordt geclassificeerd, de variabele nietreadonly
is,x
een typeT
heeft eny
een impliciete conversie naarT
heeft, dan heeft de toewijzing het typeT
. - Als
x
is geclassificeerd als een impliciet getypte variabele (bijvoorbeeld een impliciet getypeerde declaratie-expressie) eny
een typeT
heeft, wordt het afgeleid type van de variabeleT
en heeft de toewijzing het typeT
. - Als
x
is geclassificeerd als een eigenschap of indexeerfunctie, en deze een toegankelijke setmutator heeft,x
een typeT
heeft, eny
een impliciete conversie naarT
heeft, dan heeft de toewijzing het typeT
. - Anders is de toewijzing niet geldig en treedt er een bindingstijdfout op.
De runtimeverwerking van een eenvoudige toewijzing van het formulier x = y
met het type T
wordt uitgevoerd als een toewijzing aan x
van y
met het type T
, dat bestaat uit de volgende recursieve stappen:
-
x
wordt geëvalueerd als het nog niet eerder is geëvalueerd. - Als
x
als variabele wordt geclassificeerd, wordty
geëvalueerd en indien nodig geconverteerd naarT
via een impliciete conversie (§10.2).- Als de variabele van
x
een matrixelement van een reference_typeis, wordt er een runtimecontrole uitgevoerd om ervoor te zorgen dat de waarde die wordt berekend voory
compatibel is met het matrixexemplaren waarvanx
een element is. De controle slaagt alsy
isnull
of als er een impliciete verwijzingsconversie (§10.2.8) bestaat van het type exemplaar waarnaar wordt verwezen doory
naar het werkelijke elementtype van het matrixexemplaren metx
. Anders wordt er eenSystem.ArrayTypeMismatchException
gegooid. - De waarde die het resultaat is van de evaluatie en conversie van
y
wordt opgeslagen op de locatie die is opgegeven door de evaluatie vanx
en wordt als resultaat van de toewijzing opgeleverd.
- Als de variabele van
- Als
x
is geclassificeerd als een eigenschap of indexertoegang:-
y
wordt geëvalueerd en, indien nodig, geconverteerd naarT
via een impliciete conversie (§10,2). - De set accessor van
x
wordt aangeroepen met de waarde die het resultaat is van de evaluatie en conversie vany
als waardeargument. - De waarde die voortkomt uit de evaluatie en conversie van
y
wordt als het resultaat van de toewijzing geleverd.
-
- Als
x
is geclassificeerd als een tuple(x1, ..., xn)
met ariteitn
:-
y
wordt gedeconstrueerd metn
elementen tot een tuple-expressiee
. - een resultaat-tuple-
t
wordt gemaakt doore
te converteren naarT
met behulp van een impliciete tupleconversie. - voor elke
xi
in volgorde van links naar rechts wordt een toewijzing aanxi
vant.Itemi
uitgevoerd, behalve dat dexi
niet opnieuw worden geëvalueerd. -
t
wordt verkregen als gevolg van de toewijzing.
-
Opmerking: als het type compilatietijd van
x
isdynamic
en er een impliciete conversie is van het type compilatietijd vany
naardynamic
, is er geen runtime-oplossing vereist. eindnotitie
Opmerking: De regels voor covariantie van matrix (§17,6) staan toe dat een waarde van een matrixtype
A[]
een verwijzing naar een exemplaar van een matrixtypeB[]
is, mits er een impliciete verwijzingsconversie bestaat vanB
totA
. Vanwege deze regels is voor toewijzing aan een matrixelement van een reference_type een runtimecontrole vereist om ervoor te zorgen dat de waarde die wordt toegewezen compatibel is met het matrixexemplaren. In het voorbeeldstring[] sa = new string[10]; object[] oa = sa; oa[0] = null; // OK oa[1] = "Hello"; // OK oa[2] = new ArrayList(); // ArrayTypeMismatchException
de laatste toewijzing zorgt ervoor dat een
System.ArrayTypeMismatchException
wordt geworpen omdat een verwijzing naar eenArrayList
niet kan worden opgeslagen in een element van eenstring[]
.eindnotitie
Wanneer een eigenschap of indexeerfunctie die is gedeclareerd in een struct_type het doel is van een toewijzing, wordt de exemplaarexpressie die is gekoppeld aan de toegang tot de eigenschap of indexeerfunctie geclassificeerd als een variabele. Als de exemplaarexpressie wordt geclassificeerd als een waarde, treedt er een bindingstijdfout op.
Opmerking: Vanwege §12.8.7geldt dezelfde regel ook voor velden. eindnotitie
Voorbeeld: Gegeven de declaraties:
struct Point { int x, y; public Point(int x, int y) { this.x = x; this.y = y; } public int X { get { return x; } set { x = value; } } public int Y { get { return y; } set { y = value; } } } struct Rectangle { Point a, b; public Rectangle(Point a, Point b) { this.a = a; this.b = b; } public Point A { get { return a; } set { a = value; } } public Point B { get { return b; } set { b = value; } } }
in het voorbeeld
Point p = new Point(); p.X = 100; p.Y = 100; Rectangle r = new Rectangle(); r.A = new Point(10, 10); r.B = p;
de toewijzingen aan
p.X
,p.Y
,r.A
enr.B
zijn toegestaan omdatp
enr
variabelen zijn. Echter, in het voorbeeldRectangle r = new Rectangle(); r.A.X = 10; r.A.Y = 10; r.B.X = 100; r.B.Y = 100;
de toewijzingen zijn allemaal ongeldig, omdat
r.A
enr.B
geen variabelen zijn.einde voorbeeld
12.21.3 Ref-toewijzing
De = ref
operator staat bekend als de toewijzingsoperator .
De linkeroperand moet een expressie zijn waarmee een verwijzingsvariabele (§9,7), een verwijzingsparameter (anders dan this
), een uitvoerparameter of een invoerparameter wordt gekoppeld. De rechteroperand moet een uitdrukking zijn die resulteert in een variable_reference die een waarde aanwijst van hetzelfde type als de linkeroperand.
Het is een compilatietijdfout als de ref-safe-context (§9.7.2) van de linkeroperand breder is dan de ref-safe-context van de rechteroperand.
De juiste operand wordt zeker toegewezen op het punt van de verw-toewijzing.
Wanneer de linkeroperand wordt gebonden aan een uitvoerparameter, is het een fout als deze uitvoerparameter niet definitief is toegewezen aan het begin van de ref-toewijzingsoperator.
Als de linkeroperand een beschrijfbare ref is (d.w.w.v. een andere aanduiding dan een ref readonly
lokale of invoerparameter), is de rechteroperand een schrijfbare variable_reference. Als de variabele van de rechteroperand beschrijfbaar is, kan de linkeroperand een beschrijfbare of alleen-lezen referentie zijn.
De bewerking maakt de linkeroperand een alias van de rechteroperandvariabele. De alias kan alleen-lezen worden gemaakt, zelfs als de juiste operandvariabele schrijfbaar is.
De ref-toewijzingsoperator levert een variable_reference van het toegewezen type op. Deze kan worden geschreven als de linkeroperand schrijfbaar is.
De operator voor het toewijzen van verwijzingen leest de opslaglocatie waar de rechteroperand naar verwijst niet.
voorbeeld: hier volgen enkele voorbeelden van het gebruik van
= ref
:public static int M1() { ... } public static ref int M2() { ... } public static ref uint M2u() { ... } public static ref readonly int M3() { ... } public static void Test() { int v = 42; ref int r1 = ref v; // OK, r1 refers to v, which has value 42 r1 = ref M1(); // Error; M1 returns a value, not a reference r1 = ref M2(); // OK; makes an alias r1 = ref M2u(); // Error; lhs and rhs have different types r1 = ref M3(); // error; M3 returns a ref readonly, which r1 cannot honor ref readonly int r2 = ref v; // OK; make readonly alias to ref r2 = ref M2(); // OK; makes an alias, adding read-only protection r2 = ref M3(); // OK; makes an alias and honors the read-only r2 = ref (r1 = ref M2()); // OK; r1 is an alias to a writable variable, // r2 is an alias (with read-only access) to the same variable }
einde voorbeeld
Opmerking: wanneer u code leest met behulp van een operator voor
= ref
, kan het verleidelijk zijn om hetref
deel te lezen als onderdeel van de operand. Dit is met name verwarrend wanneer de operand een voorwaardelijke?:
expressie is. Als u bijvoorbeeldref int a = ref b ? ref x : ref y;
leest, is het belangrijk om dit te lezen als= ref
de operator enb ? ref x : ref y
de juiste operand zijn:ref int a = ref (b ? ref x : ref y);
. Belangrijk is dat de expressieref b
niet deel uitmaakt van die verklaring, ook al lijkt dat op het eerste gezicht zo. eindnotitie
12.21.4 Samengestelde toewijzing
Als de linkeroperand van een samengestelde toewijzing de vorm heeft E.P
of E[Ei]
waarbij E
het type compilatietijd heeft dynamic
, is de toewijzing dynamisch gebonden (§12.3.3). In dit geval is het compile-tijdtype van de toewijzingsexpressie dynamic
, en de hieronder beschreven oplossing zal plaatsvinden op run-time op basis van het run-time type van E
. Als de linkeroperand in de vorm van E[Ei]
is waarbij ten minste één element van Ei
het type tijdens de compilatie heeft dynamic
en het type tijdens de compilatie van E
geen array is, is de resulterende indextoegang dynamisch gebonden, maar met beperkte controle tijdens de compilatie (§12.6.5).
Een werking van het formulier x «op»= y
wordt verwerkt door een overbelastingsresolutie van binaire operatoren toe te passen (§12.4.5) alsof de bewerking is geschreven x «op» y
. Dan
- Als het retourtype van de geselecteerde operator impliciet wordt omgezet in het type
x
, wordt de bewerking geëvalueerd alsx = x «op» y
, behalve datx
slechts eenmaal wordt geëvalueerd. - Als de geselecteerde operator anders een vooraf gedefinieerde operator is, als het retourtype van de geselecteerde operator expliciet wordt omgezet in het type
x
en alsy
impliciet wordt omgezet in het typex
of als de operator een shift-operator is, wordt de bewerking geëvalueerd alsx = (T)(x «op» y)
, waarbijT
het typex
is, behalve datx
slechts één keer wordt geëvalueerd. - Anders is de samengestelde toewijzing ongeldig en treedt er een bindingstijdfout op.
De term 'slechts eenmaal geëvalueerd' betekent dat in de evaluatie van x «op» y
de resultaten van alle samenstellende expressies van x
tijdelijk worden opgeslagen en vervolgens opnieuw worden gebruikt bij het uitvoeren van de toewijzing aan x
.
Voorbeeld: in de toewijzings-
A()[B()] += C()
, waarbijA
een methode is dieint[]
retourneert enB
enC
methoden zijn dieint
retourneren, worden de methoden slechts eenmaal aangeroepen, in de volgordeA
,B
,C
. einde voorbeeld
Wanneer de linkeroperand van een samengestelde toewijzing een eigenschapstoegang of indexeerfunctietoegang is, moet de eigenschap of indexeerfunctie zowel een get-accessor als een set accessor hebben. Als dit niet het geval is, treedt er een bindingstijdfout op.
Met de tweede regel hierboven kan x «op»= y
worden geëvalueerd als x = (T)(x «op» y)
in bepaalde contexten. De regel bestaat zodanig dat de vooraf gedefinieerde operators kunnen worden gebruikt als samengestelde operatoren wanneer de linkeroperand van het type sbyte
, byte
, short
, ushort
of char
is. Zelfs wanneer beide argumenten van een van deze typen zijn, produceren de vooraf gedefinieerde operatoren een resultaat van het type int
, zoals beschreven in §12.4.7.3. Zonder cast zou het dus niet mogelijk zijn om het resultaat toe te wijzen aan de linkeroperand.
Het intuïtieve effect van de regel voor vooraf gedefinieerde operators is simpelweg dat x «op»= y
is toegestaan als zowel van x «op» y
als x = y
zijn toegestaan.
Voorbeeld: In de volgende code
byte b = 0; char ch = '\0'; int i = 0; b += 1; // OK b += 1000; // Error, b = 1000 not permitted b += i; // Error, b = i not permitted b += (byte)i; // OK ch += 1; // Error, ch = 1 not permitted ch += (char)1; // OK
de intuïtieve reden voor elke fout is dat een bijbehorende eenvoudige toewijzing ook een fout zou zijn geweest.
einde voorbeeld
Opmerking: dit betekent ook dat samengestelde toewijzingsbewerkingen lifted operators ondersteunen. Aangezien een samengestelde toewijzing
x «op»= y
wordt geëvalueerd alsx = x «op» y
ofx = (T)(x «op» y)
, omvatten de evaluatieregels impliciet verheven operatoren. eindnotitie
12.21.5 Gebeurtenistoewijzing
Als de linkeroperand van a += or -=
operator is geclassificeerd als een gebeurtenistoegang, wordt de expressie als volgt geëvalueerd:
- De eventuele exemplaarexpressie van de toegang tot een gebeurtenis wordt geëvalueerd.
- De rechteroperand van de operator
+=
of-=
wordt geëvalueerd en indien nodig geconverteerd naar het type linkeroperand via een impliciete conversie (§10,2). - Er wordt een gebeurtenistoegangspunt van de gebeurtenis aangeroepen, met een lijst met argumenten die bestaat uit de waarde die in de vorige stap is berekend. Als de operator
+=
is, wordt de toevoegaccessor aangeroepen; als de operator-=
is, wordt de verwijderaccessor aangeroepen.
Een expressie voor gebeurtenistoewijzing levert geen waarde op. Een expressie voor gebeurtenistoewijzing is dus alleen geldig in de context van een statement_expression (§13.7).
12.22 Expressie
Een expressie is een non_assignment_expression of een toewijzing.
expression
: non_assignment_expression
| assignment
;
non_assignment_expression
: declaration_expression
| conditional_expression
| lambda_expression
| query_expression
;
12.23 Constante uitdrukkingen
Een constante expressie is een expressie die tijdens het compileren volledig wordt geëvalueerd.
constant_expression
: expression
;
Een constante expressie heeft de waarde null
of een van de volgende typen:
-
sbyte
,byte
,short
,ushort
,int
,uint
,long
,ulong
,char
,float
,double
,decimal
,bool
,string
; - een opsommingstype; of
- een standaardwaardeexpressie (§12.8.21) voor een referentietype.
Alleen de volgende constructies zijn toegestaan in constante expressies:
- Letterlijke gegevens (inclusief de letterlijke
null
). - Verwijzingen naar
const
-leden van klasse- en structuurtypen. - Verwijzingen naar leden van opsommingstypen.
- Verwijzingen naar lokale constanten.
- Subexpressies tussen haakjes, die zelf constante expressies zijn.
- Cast-expressies.
-
checked
enunchecked
expressies. -
nameof
expressies. - De vooraf gedefinieerde
+
,-
,!
(logische negatie) en~
unaire operatoren. - De vooraf gedefinieerde
+
,-
,*
,/
,%
,<<
,>>
,&
,|
,^
,&&
,||
,==
,!=
,<
,>
,<=
en>=
binaire operatoren. - De
?:
voorwaardelijke operator. - De null-forgiving operator
!
(§12.8.9). -
sizeof
expressies, mits het niet-beheerde type een van de typen is die zijn opgegeven in §23.6.9 waarvoorsizeof
een constante waarde retourneert. - Standaardwaarde-expressies, mits het type een van de bovenstaande typen is.
De volgende conversies zijn toegestaan in constante expressies:
- Identiteitsconversies
- Numerieke conversies
- Enumeratieconversies
- Omzettingen van constante expressies
- Impliciete en expliciete verwijzingsconversies, mits de bron van de conversies een constante expressie is die resulteert in de
null
waarde.
Opmerking: andere conversies, waaronder boksen, uitpakken en impliciete verwijzingsconversies van niet-
null
waarden, zijn niet toegestaan in constante expressies. eindnotitie
Voorbeeld: In de volgende code
class C { const object i = 5; // error: boxing conversion not permitted const object str = "hello"; // error: implicit reference conversion }
de initialisatie van
i
is een fout omdat een boksconversie is vereist. De initialisatie vanstr
is een fout omdat een impliciete verwijzingsconversie van een niet-null
waarde is vereist.einde voorbeeld
Wanneer een expressie voldoet aan de bovenstaande vereisten, wordt de expressie geëvalueerd tijdens het compileren. Dit geldt zelfs als de expressie een subexpressie is van een grotere expressie die niet-constante constructies bevat.
De evaluatie van de compilatietijd van constante expressies maakt gebruik van dezelfde regels als de runtime-evaluatie van niet-constante expressies, behalve dat wanneer de runtime-evaluatie een uitzondering zou hebben veroorzaakt, zorgt de compileertijd-evaluatie ervoor dat er een compilatiefout optreedt.
Tenzij een constante expressie expliciet in een unchecked
context wordt geplaatst, veroorzaakt overloop die optreden in rekenkundige bewerkingen en conversies van integraal type tijdens de evaluatie van de compilatietijd van de expressie altijd compilatiefouten (§12.8.20).
Constante expressies zijn vereist in de onderstaande contexten en dit wordt aangegeven in de grammatica met behulp van constant_expression. In deze contexten treedt een compilatietijdfout op als een expressie niet volledig kan worden geëvalueerd tijdens het compileren.
- Constante definities (§15.4)
- Opsommingsliddeclaraties (§19.4)
- Standaardargumenten van parameterlijsten (§15.6.2)
-
case
labels van eenswitch
verklaring (§13.8.3). -
goto case
verklaringen (§13.10.4) - Dimensielengten in een expressie voor het maken van een matrix (§12.8.17.5) met een initialisatiefunctie.
- Kenmerken (§22)
- In een constant_pattern (§11.2.3)
Met een impliciete expressieconversie (§10.2.11) kan een constante expressie van het type int
worden geconverteerd naar sbyte
, byte
, short
, ushort
, uint
of ulong
, mits de waarde van de constante expressie binnen het bereik van het doeltype valt.
12.24 Booleaanse expressies
Een boolean_expression is een expressie die een resultaat oplevert van het type bool
; hetzij rechtstreeks of via de toepassing van operator true
in bepaalde contexten, zoals opgegeven in de volgende:
boolean_expression
: expression
;
De voorwaardelijke expressie van een if_statement (§13.8.2), while_statement (§13.9.2), do_statement (§13.9.3), of for_statement (§13.9.4) bestaat uit een boolean_expression. De besturingsvoorwaardelijke expressie van de operator ?:
(§12.18) volgt dezelfde regels als een boolean_expression, maar om redenen van prioriteit van de operator wordt als een null_coalescing_expressiongeclassificeerd.
Een boolean_expressionE
is vereist om als volgt een waarde van het type bool
te kunnen produceren:
- Als E impliciet converteerbaar is naar
bool
, wordt tijdens runtime de impliciete conversie toegepast. - Anders wordt een unaire overbelastingsresolutie (§12.4.4) gebruikt om een unieke beste implementatie van
operator true
opE
te vinden en die implementatie wordt toegepast tijdens runtime. - Als er geen dergelijke operator wordt gevonden, treedt er een bindingstijdfout op.
ECMA C# draft specification