Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
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 eigendom. Elke eigenschapstoegang heeft een bijbehorend type, namelijk het type van de 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.21).
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.5), 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
+=en-=operators (§12.23.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 settoegangsfunctie aangeroepen om een nieuwe waarde toe te wijzen (§12.23.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 aanroep van een functielid (§12.6.6) uitgevoerd en wordt het resultaat van de aanroep de waarde van de expressie voor eigenschapstoegang.
- 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. In plaats daarvan, als de uitvoeringstijdbinding mislukt, worden fouten tijdens de uitvoeringstijd als uitzonderingen gerapporteerd.
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.WriteLinewordt 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.WriteLinewordt 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 hierop niet altijd 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 compile-time type dynamisch wordt geacht het type te hebben van de werkelijke waarde waarnaar de expressie tijdens de uitvoertijd 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 methodeFaangeroepen met behulp van de oude waarde vani, wordt de methodeGaangeroepen met de oude waarde vanien tot slot wordt de methodeHaangeroepen 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 * zwordt 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 Operatoren §12.8 Primair x.yx?.yf(x)a[x]a?[x]x++x--x!newtypeofdefaultcheckeduncheckeddelegatestackalloc§12.9 Unaire +-!~^++x--x(T)xawait x§12.10 Bereik ..§12.11 Schakelaar switch { … }§12.12 Multiplicatieve */%§12.12 Toevoeging +-§12.13 Verschuiving <<>>§12.14 Relationele en type-testen <><=>=isas§12.14 Gelijkheid ==!=§12.15 Logische AND &§12.15 Logische XOR ^§12.15 Logische OR \|§12.16 Voorwaardelijk en &&§12.16 Voorwaardelijk OF \|\|§12.17 en §12.18 Null-coalescing en gooi-expressie ??throw x§12.20 Voorwaardelijk ?:§12.23 en §12.21 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, de bereikoperator en de operator null-samensamenvulling, zijn alle binaire operatoren links-associatief, wat betekent dat bewerkingen van links naar rechts worden uitgevoerd.
voorbeeld van:
x + y + zwordt geëvalueerd als(x + y) + z. einde voorbeeld - De toewijzingsoperatoren, de null-coalescerende operator en de voorwaardelijke operator (
?:) zijn rechts-associatieve, wat betekent dat bewerkingen van rechts naar links worden uitgevoerd.voorbeeld van:
x = y = zwordt geëvalueerd alsx = (y = z). einde voorbeeld - De bereikoperator is niet-associatief, wat betekent dat de linker- of rechteroperand van een bereikoperator geen range_expression kan zijn.
Voorbeeld: Beide
x..y..zenx..(y..z)zijn ongeldig, net als..niet-associatief. einde voorbeeld
Prioriteit en associativiteit kunnen worden beheerd met haakjes.
voorbeeld:
x + y * zvermenigvuldigt eerstymetzen voegt vervolgens het resultaat toe aanx, maar(x + y) * zvoegt eerstxenytoe 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 operatorenzijn:
+ - !(alleen logische ontkenning)~ ++ -- true false
Alleen de hierboven genoemde operators kunnen overbelast worden. Het is met name niet mogelijk om de operator null-vergeefs te overbelasten (postfix, !) of de unaire index van de eindoperator (voorvoegsel ^, §12.9.6)).
Opmerking: Hoewel
trueenfalseniet expliciet worden gebruikt in expressies (en daarom niet zijn opgenomen in de prioriteitstabel in §12.4.2), worden ze beschouwd als operators omdat ze worden aangeroepen in verschillende expressiecontexten: Boole-expressies (§12.26) en expressies met betrekking tot de voorwaardelijke operatoren (§12.20) en voorwaardelijke logische operators (§12.16). eindnotitie
De overbelastingsbare binaire operatorenzijn:
+ - * / % & | ^ << >> == != > < <= >=
Alleen de hierboven genoemde operators kunnen overbelast worden. In het bijzonder is het niet mogelijk om de toegang van leden, methode-aanroepen of de .., =, &&||???:=>checkedunchecked, new, , typeof, , , defaulten asis operators 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.23. einde voorbeeld
De toewijzingsoperator zelf (=) kan niet worden overbelast. Een toewijzing voert altijd een eenvoudig archief van een waarde uit in een variabele (§12.23.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
isofas. 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 geschiktboolresultaat retourneren. eindnotitie
De beschrijvingen van individuele operators in §12.9 tot en met §12.23 geven de vooraf gedefinieerde implementaties van de operators en eventuele aanvullende regels op 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 Xis, wordt als volgt verwerkt:
- De set door de gebruiker gedefinieerde kandidaatoperators die door
Xworden 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 Overbelasting van binaire operatoren
Een werking van het formulier x «op» y, waarbij «op» een overbelastingbare binaire operator is, x een expressie van het type Xis en y een expressie van het type Yis, wordt als volgt verwerkt:
- De set door de gebruiker gedefinieerde kandidaatoperators die worden geleverd door
XenYvoor de bewerkingoperator «op»(x, y)wordt bepaald. De set bestaat uit de vereniging van de doorXenYverstrekte kandidaat-operators, die elk worden bepaald volgens de regels van §12.4.6. Voor de gecombineerde set worden kandidaten als volgt samengevoegd:- Als
XenYidentiteit converteerbaar zijn of alsXenYzijn afgeleid van een gemeenschappelijk basistype, worden gedeelde kandidaatoperators slechts eenmaal in de gecombineerde set uitgevoerd. - Als er een identiteitsconversie is tussen
XenY, heeft een operator«op»YvanYhetzelfde retourtype als een«op»Xgeleverd doorXen de operandtypen van«op»Yeen identiteitsconversie hebben naar de bijbehorende operandtypen van«op»Xdan vindt er slechts«op»Xplaats 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. AlsTeen 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₀andersobjectis, 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.12), relationele operatoren (§12.14) en integrale logische operatoren (§12.15.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, waarbijbeenbyteis enseenshortis, selecteert overbelastingsresolutieoperator *(int, int)als de beste operator. Het effect is dus datbensworden geconverteerd naarint, en het type van het resultaat isint. Voor de bewerkingi * d, waarbijieenintis endeendoubleis, selecteertoverloadresolutieoperator *(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, ushortof char om intte typen . Daarnaast converteert de unaire numerieke promotie operanden van het type uint naar type longvoor 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
decimalis, wordt de andere operand geconverteerd naar het typedecimalof treedt er een bindingstijdfout op als de andere operand van het typefloatofdoubleis. - Als een van beide operanden van het type
doubleis, wordt de andere operand geconverteerd naar het typedouble. - Als een van beide operanden van het type
floatis, wordt de andere operand geconverteerd naar het typefloat. - Als een operand van het type
ulongis, wordt de andere operand geconverteerd naar het typeulongof treedt er een bindingstijdfout op als de andere operand vantype sbyte,short,intoflongis. - Als een van beide operanden van het type
longis, wordt de andere operand geconverteerd naar het typelong. - Als een operand van het type
uintis en de andere operand van het typesbyte,shortofint, worden beide operanden geconverteerd naar het typelong. - Als een van beide operanden van het type
uintis, 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 typendoubleenfloat. De regel volgt uit het feit dat er geen impliciete conversies zijn tussen hetdecimaltype en dedoubleenfloattypen. eindnotitie
Opmerking: houd er ook rekening mee dat het niet mogelijk is dat een operand van het type
ulongis wanneer de andere operand van een ondertekend integraal type is. De reden hiervoor is dat er geen integraal type bestaat dat het volledige bereik vanulongen 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
decimalniet 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
Met een lifted operator kunnen vooraf gedefinieerde en door de gebruiker gedefinieerde operatoren die op een niet-nullbaar waardetype werken, ook worden gebruikt met de null-vorm van dat type. 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-null-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 operandnullis. Anders pakt de lifted operator de operand uit, past de onderliggende operator toe en omhult het resultaat. - Voor de binaire operatoren
+,-,*,/,%,&,|, ,^,..en<<>>, bestaat er een opgeheven vorm van een operator als de operand en resultaattypen alle niet-null-waardetypen zijn. De opgetilde vorm wordt samengesteld door een enkele?modifier toe te voegen aan elke operand en het resultaattype. De lifted operator produceert eennullwaarde als een of beide operanden zijnnull(een uitzondering: de&operatoren|van hetbool?type, zoals beschreven in §12.15.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 geennullis. Als beide operanden niet-nullzijn, haalt de verhoogde operator de operanden uit en past de basisoperator toe om hetboolresultaat 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 waardefalseals een of beide operandennullzijn. Anders pakt de lifted operator de operanden uit en past de onderliggende bewerking toe om het resultaatboolte 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 gemachtigde (§21) of het type dynamic (§8.2.4), wordt het lid geacht aan te roepen.
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
Nbepaald:- Als
Teen typeparameter is, is de set de samenvoeging van de sets toegankelijke leden met de naamNin 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 naamNinobject. - Anders bestaat de set uit alle toegankelijke leden (§7,5) met de naam
NinT, inclusief overgenomen leden en de toegankelijke leden met de naamNinobject. AlsTeen samengesteld type is, wordt de set leden verkregen door typeargumenten te vervangen zoals beschreven in §15.3.3. Leden met eenoverridemodifier worden uitgesloten van de set.
- Als
- Als
Knul is, worden alle geneste typen waarvan de declaraties typeparameters bevatten, verwijderd. AlsKniet nul is, worden alle leden met een ander aantal typeparameters verwijderd. WanneerKnul 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.Min de set, waarbijShet type is waarin het lidMwordt gedeclareerd, worden de volgende regels toegepast:- Als
Meen constant, veld, eigenschap, gebeurtenis of opsommingslid is, worden alle leden die zijn gedeclareerd in een basistypeSuit de set verwijderd. - Als
Meen typedeclaratie is, worden alle niet-typen die zijn gedeclareerd in een basistype vanSuit de set verwijderd en worden alle typedeclaraties met hetzelfde aantal typeparameters verwijderd alsMgedeclareerd in een basistype vanSuit de set worden verwijderd. - Als
Meen methode is, worden alle niet-methodeleden die zijn gedeclareerd in een basistype vanSuit de set verwijderd.
- Als
- Vervolgens worden interfaceleden die door klasseleden worden verborgen, uit de set verwijderd. Deze stap heeft alleen een effect als
Teen typeparameter is enTzowel een effectieve basisklasse heeft danobjectals een niet-lege effectieve interfaceset (§15.2.5). Voor elk lidS.Min de set, waarbijShet type is waarin het lidMwordt gedeclareerd, worden de volgende regels toegepast alsSeen andere klassedeclaratie is danobject:- Als
Meen constante, veld, eigenschap, gebeurtenis, opsommingslid of typedeclaratie is, worden alle leden die in een interfacedeclaratie zijn gedeclareerd, verwijderd uit de set. - Als
Meen methode is, worden alle niet-methodeleden die zijn gedeclareerd in een interfacedeclaratie uit de set verwijderd en worden alle methoden met dezelfde handtekening alsMdie 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 kunnen ontstaan door lidzoekacties in interfaces met meerdere overnames, worden beschreven in §19.4.11.
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
Tisobjectofdynamic, heeftTgeen basistype. - Als
Teen enum_typeis, zijn de basistypen vanTde klassetypenSystem.Enum,System.ValueTypeenobject. - Als
Teen struct_typeis, zijn de basistypen vanTde klassetypenSystem.ValueTypeenobject.Opmerking: een nullable_value_type is een struct_type (§8.3.1). eindnotitie
- Als
Teen class_typeis, zijn de basistypen vanTde basisklassen vanT, inclusief het klassetypeobject. - Als
Teen interface_typeis, zijn de basistypen vanTde basisinterfaces vanTen het klassetypeobject. - Als
Teen array_typeis, zijn de basistypen vanTde klassetypenSystem.Arrayenobject. - Als
Teen delegate_typeis, zijn de basistypen vanTde klassetypenSystem.Delegateenobject.
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
- Instantieconstructors
- 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,yenvalueexpressies aan die zijn geclassificeerd als variabelen of waarden,Teen expressie aangeeft die is geclassificeerd als een type,Fde eenvoudige naam van een methode is enPde eenvoudige naam van een eigenschap is.
Bouwen Voorbeeld Beschrijving Methode-aanroep F(x, y)Overbelastingsresolutie wordt toegepast om de beste methode Fin de bevatte klasse of structuur te selecteren. De methode wordt aangeroepen met de argumentenlijst(x, y). Als de methode nietstaticis, is de instantie-expressiethis.T.F(x, y)Overbelastingsresolutie wordt toegepast om de beste methode te selecteren Fin de klasse of structT. Er treedt een fout op tijdens de bindtijd als de methode nietstaticis. De methode wordt aangeroepen met de argumentenlijst(x, y).e.F(x, y)Overbelastingresolutie wordt toegepast om de beste methode Fin 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 exemplaarexpressieeen de lijst met argumenten(x, y).Toegang tot eigenschappen PDe 'get' accessor van de eigenschap Pin de omvattende klasse of struct wordt aangeroepen. Er treedt een compilatiefout op alsPschrijf-alleen is. AlsPnietstaticis, is de exemplaarexpressiethis.P = valueDe set accessor van de eigenschap Pin de bevatde klasse of struct wordt aangeroepen met de lijst met argumenten(value). Er treedt een compilatietijdfout op alsPalleen-lezen is. AlsPnietstaticis, is de exemplaarexpressiethis.T.PDe toegangsfunctie 'get' van de eigenschap Pin de klasse of structuurTwordt aangeroepen. Er treedt een compilatiefout op alsPnietstaticis of alsPalleen-voor-schrijven is.T.P = valueDe set accessor van de eigenschap Pin de klasse of structTwordt aangeroepen met de argumentenlijst(value). Er treedt een compilatiefout op alsPnietstaticis of alsPalleen lezen is.e.PDe get-accessor van de eigenschap Pin de klasse, structuur of interface zoals opgegeven door het typeE, wordt aangeroepen met de instantietie-expressiee. Er treedt een bindingstijdfout op alsPstaticis of alsPalleen voor schrijven is.e.P = valueDe settoegangsfunctie van de eigenschap Pin de klasse, struct of interface die wordt opgegeven door het typeEwordt aangeroepen met de exemplaarexpressieeen de lijst met argumenten(value). Er treedt een bindtijdfout op alsPstaticis of alsPalleen-lezen is.Toegang tot gebeurtenissen E += valueDe add accessor van de gebeurtenis Ein de bevatde klasse of struct wordt aangeroepen. AlsEnietstaticis, is de exemplaarexpressiethis.E -= valueDe verwijdertoegangsfunctie van de gebeurtenis Ein de bevattende Klasse of struct wordt aangeroepen. AlsEnietstaticis, is de exemplaarexpressiethis.T.E += valueDe add accessor van de gebeurtenis Ein de klasse of structTwordt aangeroepen. Er treedt een bindingstijdfout op alsEniet isstatic.T.E -= valueDe remove-toegangsfunctie van de gebeurtenis Ein de klasse of structTwordt aangeroepen. Er treedt een bindingstijdfout op alsEniet isstatic.e.E += valueDe add accessor van de gebeurtenis Ein de klasse, struct of interface die wordt opgegeven door het typeE, wordt aangeroepen met de expressie van de instantiee. Er treedt een bindingstijdfout op alsEisstatic.e.E -= valueDe verwijdertoegangsmethode van de gebeurtenis Ein de klasse, struct of interface, zoals gegeven door het typeE, wordt aangeroepen met de exemplaarexpressiee. Er treedt een bindingstijdfout op alsEisstatic.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 indexer wordt aangeroepen met de instantiefunctieeen de argumentenlijst(x, y). Er treedt een bindingstijdfout op als de indexeerfunctie alleen-schrijven is.e[x, y] = valueOverbelasting 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 exemplaarexpressieeen de argumentenlijst(x, y, value). Er treedt een bindingstijdfout op als de indexeerfunctie alleen-lezen is.Operator aanroepen -xOverbelastingsresolutie 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 + yOverbelastingsresolutie wordt toegepast om de beste binaire operator te selecteren in de klassen of structs die worden gegeven door de typen xeny. 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 Tte 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
ingevolgd 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
refgevolgd 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
outgevolgd 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 referentieparameter veroorzaakt een waarschuwing, omdat het veld niet kan worden behandeld als vluchtig 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 set-accessor komt de expressie die is opgegeven als de rechteroperand van de toewijzingsoperator overeen met de impliciete
valueparameter van de set-accessor-declaratie.
- 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 naamczich 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 argumentIn de
M1(i)methodeaanroep wordtizelf doorgegeven als invoerargument, omdat deze is geclassificeerd als een variabele en hetzelfde type heeftintals de invoerparameter. In deM1(i + 5)methodeaanroep wordt een niet-benoemdeintvariabele 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.ArrayTypeMismatchExceptiongegenereerd.
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
Fzorgt ervoor dat eenSystem.ArrayTypeMismatchExceptionwordt gegenereerd omdat het werkelijke elementtype vanbstringis 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 = 3einde 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.4) 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 methodenvorm
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
intenstringbepaald 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: Typedeductie wordt ook gebruikt in andere contexten, waaronder voor de conversie van methodegroepen (§12.6.3.15) en het vinden van het beste algemene type van een set expressies (§12.6.3.16). eindnotitie
12.6.3.2 De eerste fase
Voor elk van de methodeargumenten Eᵢwordt een invoertypedeductie (§12.6.3.7) gemaakt van Eᵢ het overeenkomstige parametertype Tⱼ.
12.6.3.3 De tweede fase
De tweede fase gaat als volgt:
- Alle niet-opgeloste typevariabelen
Xᵢdie niet afhankelijk zijn van (§12.6.3.6) zijnXₑvast (§12.6.3.13). - 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-vastgemaakte typevariabelenXₑbevatten, maar de invoertypen (§12.6.3.4) niet, wordt een deductie van het uitvoertype (§12.6.3.8) gemaakt vanEᵢnaarTⱼ. 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 Tinvoertypen vanEmet 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 vanEmet 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 Invoertypededucties
Een invoertypedeductie wordt afgeleidE een expressie naar een type T op de volgende manier:
- Als
Eeen tuple-expressie (§12.8.6) is met arityNen elementenEᵢ, enTeen tupletype is met arity met bijbehorende elementtypenNofTₑeen null-waardetypeTis enT0?een tupletype is met arityT0dat een corresponderend elementtypeNheeft, wordt voor elkTₑtype invoertype een invoertypedeductieEᵢgemaakt vanEᵢtotTₑ. - Als
Ehet een anonieme functie is, wordt een expliciete parametertypedeductie (§12.6.3.9) gemaakt vanEnaarT -
EAls er anders een typeUis en de bijbehorende parameter een waardeparameter (§15.6.2.2) is, wordt een ondergrensdeductie (§12.6.3.11) gemaakt vanUnaarT. -
EAls er anders een typeUis en de bijbehorende parameter een referentieparameter is (§15.6.2.3.3) of uitvoerparameter (§15.6.2.3.4), wordt een exacte deductie (§12.6.3.10) gemaakt vanUnaarT. -
EAls u een typeUhebt en de bijbehorende parameter een invoerparameter is (§15.6.2.3.2) enEeen invoerargument is, wordt een exacte deductie (§12.6.3.10) gemaakt vanUnaarT. -
EAls er anders een typeUis en de bijbehorende parameter een invoerparameter (§15.6.2.3.2) is, wordt een ondergrensdeductie (§12.6.3.11) gemaakt vanUT - Anders wordt er geen deductie gemaakt voor dit argument.
12.6.3.8 Uitvoertypededucties
Een uitvoertype-afleiding wordt gemaakt van een expressie Enaar een type T op de volgende manier:
- Als
Eeen tuple-expressie met ariteitNen elementenEᵢis enTeen tupletype is met arpleNmet bijbehorende elementtypenTₑofTeen null-waardetypeT0?is enT0een tupletype is met arityNdat een corresponderend elementtypeTₑheeft, wordt voor elkEᵢtype uitvoertype deductie gemaakt vanEᵢnaarTₑ. - Als
Edit een anonieme functie is met afgeleid retourtypeU(§12.6.3.14) enTeen gemachtigde type of expressiestructuur met retourtypeTₓis, wordt een ondergrensdeductie (§12.6.3.11) gemaakt vanUnaarTₓ. - Als
Eeen methodegroep is enTeen gedelegeerd type of expressiebombomitetype is met parametertypenT₁...Tᵥen retourtypeTₓ, en overbelastingsresolutie vanEmet de typenT₁...Tᵥeen enkele methode oplevert met retourtypeU, dan wordt een inferentie van de ondergrens gemaakt vanUtotTₓ. - Als
Eeen expressie is met het typeU, dan wordt er een ondergrens deductie gemaakt vanUnaarT. - Anders worden er geen deducties gemaakt.
12.6.3.9 Expliciete parametertypededucties
Een expliciete parametertypedeductie wordt afgeleid van een expressie Enaar een type T op de volgende manier:
- Als
Eeen expliciet getypte anonieme functie met parametertypenU₁...Uᵥis enTeen gemachtigde type of expressiestructuur is met parametertypenV₁...Vᵥ, wordt voor elkeUᵢexacte deductie (§12.6.3.10) gemaakt vanUᵢnaar het bijbehorendeVᵢ.
12.6.3.10 Exacte deducties
Een exacte afleidingvan een type Utot een type V wordt als volgt gemaakt:
- Als
Veen van de niet-vastgesteldeXᵢis, wordtUtoegevoegd 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:-
Vis een matrixtypeV₁[...]enUis een matrixtypeU₁[...]van dezelfde rang -
Vis het typeV₁?enUhet typeU₁ -
Vis een samengesteld typeC<V₁...Vₑ>enUeen 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.11 Ondergrensdeducties
Een ondergrensdeductie van een type U naar een type V wordt als volgt gemaakt:
- Als
Veen van de niet-vastgesteldeXᵢis, wordtUtoegevoegd aan de set van ondergrenzen voorXᵢ. - Als
Vhet typeV₁?is enUis 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:-
Vis een matrixtypeV₁[...]enUis een matrixtypeU₁[...]van dezelfde rang -
Vis een vanIEnumerable<V₁>,ICollection<V₁>,IReadOnlyList<V₁>>,IReadOnlyCollection<V₁>ofIList<V₁>enUeen enkeldimensionaal matrixtypeU₁[] -
Vis een samengesteldclass,struct,interfaceofdelegatetypeC<V₁...Vₑ>en er is een uniek typeC<U₁...Uₑ>zodanig datU(of, alsUeen typeparameteris, de effectieve basisklasse of een lid van de effectieve interfaceset) identiek is aan,inheritsvan (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 vanUtotC<T>, omdatU₁XofYkan 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
Ueen arraytype is, wordt er een ondergrensinferentie gemaakt - Als
VC<V₁...Vₑ>is, dan is deductie afhankelijk van dei-thtypeparameter 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.12 Bovengrensdeducties
Een bovengrensdeductie van een type Unaar een type V wordt als volgt gemaakt:
- Als
Veen van de niet-vastgesteldeXᵢis, wordtUtoegevoegd 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:-
Uis een matrixtypeU₁[...]enVis een matrixtypeV₁[...]van dezelfde rang -
Uis een vanIEnumerable<Uₑ>,ICollection<Uₑ>,IReadOnlyList<Uₑ>,IReadOnlyCollection<Uₑ>ofIList<Uₑ>enVeen enkeldimensionaal matrixtypeVₑ[] -
Uis het typeU1?enVhet typeV1? -
Uis een geconstrueerd klasse-, struct-, interface- of gedelegeerd typeC<U₁...Uₑ>enVis eenclass, struct, interface- ofdelegate-type dat (direct of indirect) verwant is aan, afkomstig is van, of implementeert (direct of indirect) een uniek typeidentical - (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
Veen matrixtype is, wordt er anders een bovengrensafleiding gemaakt. - Als
UC<U₁...Uₑ>is, dan is deductie afhankelijk van dei-thtypeparameter 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.13 Bevestiging
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 aanUuit de kandidaatset worden verwijderd. Voor elke ondergrensUvanXᵢworden alle typenUₑwaarvoor er geen impliciete conversie vanUis, uit de kandidaatset verwijderd. Voor elke bovengrens U vanXᵢworden alle typenUₑwaaruit er geen impliciete conversie naarUis, uit de kandidaatset verwijderd. - Als er onder de overgebleven kandidaattypen
Uₑeen uniek typeVis waarop een impliciete conversie van alle andere kandidaattypen mogelijk is, wordtXᵢvastgezet opV. - Anders faalt de typeafleiding.
12.6.3.14 Uitgestelde 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
Feen uitdrukking is die een type heeft, dan is het afgeleide effectieve retourtype vanFhet type van die uitdrukking. - Als de hoofdtekst
Fvan een blok is en de set expressies in de instructies van het blokreturneen best gemeenschappelijk typeTheeft (§12.6.3.16), isFhet uitgestelde effectieve retourtypeT. - Anders kan een effectief retourtype niet worden afgeleid voor
F.
Het afgeleide retourtype wordt als volgt bepaald:
- Als
Fasynchroon is en het lijf vanFofwel een expressie is geclassificeerd als niets (§12.2), ofwel een blok waarin geenreturninstructies expressies bevatten, is het afgeleide retourtype«TaskType»(§15.14.1). - Als
Fasynchroon is en een afgeleid effectief retourtypeTheeft, dan is het afgeleide retourtype«TaskType»<T>»(§15.14.1). - Als
Fniet-asynchroon is en een afgeleid effectief retourtypeTheeft, 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
Selectextensiemethode 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.Linqnaamruimte is geïmporteerd met eenusing namespaceinstructie, en gegeven een klasseCustomermet 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
Selectwordt 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
TSourcemoet wordenCustomer. Vervolgens wordtcmet behulp van het hierboven beschreven deductieproces van het anonieme functietype het typeCustomergegeven, en wordt de expressiec.Namegerelateerd aan het retourtype van de selectieparameter, waarbijTResultwordt 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
Xmoet wordenstring. Vervolgens krijgt de parameter van de eerste anonieme functie,s, het afgeleide typestringen is de expressieTimeSpan.Parse(s)gerelateerd aan het retourtype vanf1, waardoorYwordt geïnterpreteerd alsSystem.TimeSpan. Ten slotte krijgt de parameter van de tweede anonieme functie,t, het uitgestelde typeSystem.TimeSpanen de expressiet.TotalHoursis gerelateerd aan het retourtype vanf2, waardoorZmoet wordendouble. Het resultaat van de aanroep is dus van het typedouble.einde voorbeeld
12.6.3.15 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 (§21.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-vastgemaakte, en wordt een ondergrensinferentie gemaakt van elk argumenttype Uₑ van D naar het overeenkomstige parametertype van Tₑ. Als er voor geen van de Xᵢ limieten werden gevonden, mislukt type-inferentie. Anders worden alle Xᵢvastgezet aan overeenkomende Sᵢ, die het resultaat zijn van type-inferentie.
12.6.3.16 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
Eiwordt een deductie van het uitvoertype (§12.6.3.8) uitgevoerd naarX. -
Xis opgelost (§12.6.3.13), indien mogelijk, 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 bindingstijdmechanisme voor het selecteren van het beste functielid om aan te roepen op basis van een lijst met argumenten en een reeks kandidaat-functieleden. 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
Akomt 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
Ais 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
inheeft, 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
inmodifier 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
Aovereenkomt met het totale aantal parameters. AlsAminder 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
Aeen 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
thistoegang 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 parameterRxniet minder specifiek is danSx, en voor ten minste één parameter isRxspecifieker 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 parameterdoorgiftelmodus op basis van waarden de betere keuze voor de doorgiftemodus voor parameters.
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:
-
Eexact overeenkomt metT₁enEkomt niet exact overeen metT₂(§12.6.4.6) -
Ekomt exact overeen met zowelT₁alsT₂, enT₁is een beter conversiedoel danT₂(§12.6.4.7) -
Eis een methodegroep (§12.2),T₁is compatibel (§21.4) met de beste methode uit de methodegroep voor conversieC₁enT₂is niet compatibel met de beste methode uit de methodegroep voor conversieC₂
12.6.4.6 Exact overeenkomende expressie
Gezien een expressie E en een type T, komt Eexact overeen metT als een van de volgende waarden geldt:
-
Eheeft een typeSen er bestaat een identiteitsconversie vanStotT -
Eis een anonieme functie,Tis een delegaat typeDof een expressieboomtypeExpression<D>en geldt een van de volgende:- Er bestaat een afgeleid retourtype
XvoorEin de context van de parameterlijst vanD(§12.6.3.13) en er bestaat een identiteitsconversie vanXhet retourtypeD -
Eis eenasynclambda zonder retourwaarde, enDheeft een retourtype dat een niet-generieke«TaskType»is -
Eis niet asynchroon enDheeft een retourtypeY, ofEis asynchroon enDheeft een retourtype«TaskType»<Y>(§15.14.1), en een van de volgende is van toepassing:- De hoofdtekst van
Eis een expressie die exact overeenkomt metY - De hoofdtekst van
Eis 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.14.1),T₂is«TaskType»<S₂>, en isS₁een beter conversiedoel danS₂ -
T₁is«TaskType»<S₁>(§15.14.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₁issbyteenS₂isbyte,ushort,uint, ofulong -
S₁isshortenS₂isushort,uint, ofulong -
S₁isintenS₂isuintofwelulong -
S₁islongenS₂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.4) 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
Feen 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 argumenten.
Voor F om de controle te doorstaan, moeten alle volgende voorwaarden gelden:
- De gewijzigde parameterlijst voor
Fis 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
Fin de bovenstaande stap zijn vervangen, worden aan hun beperkingen voldaan. - Als
Feen 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
Feen 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.
- Instantiefunctieleden. 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
Meen statisch functielid is:- De lijst met argumenten wordt geëvalueerd zoals beschreven in §12.6.2.
-
Mwordt aangeroepen.
- Als het type
Eeen waardetypeVis enMwordt gedeclareerd of overschreven inV:-
Ewordt 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 wordtEgeclassificeerd als een variabele. - Als
Eniet wordt geclassificeerd als een variabele, of alsVhet geen alleen-lezen structtype (§16.2.2) is enMgeen alleen-lezen functielid is (§16.4.12), enEeen van:- een invoerparameter (§15.6.2.3.2) of
- een
readonlyveld (§15.5.3), of - een
readonlyreferentievariabele of retournering (§9.7), wordt vervolgens een tijdelijke lokale variabele vanEhet type gemaakt en wordt de waarde toegewezenEaan die variabele.Ewordt vervolgens opnieuw geclassificeerd als een verwijzing naar die tijdelijke lokale variabele. De tijdelijke variabele is toegankelijk alsthisbinnenM, maar niet op een andere manier. Dus alleen wanneerEkan worden geschreven, kan de beller de wijzigingen observeren dieMaanthisaanbrengt.
- De lijst met argumenten wordt geëvalueerd zoals beschreven in §12.6.2.
-
Mwordt aangeroepen. De variabele waarnaar wordt verwezen doorEwordt de variabele waarnaar wordt verwezen doorthis.
-
- Anders:
-
Ewordt 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
Eeen value_typeis, wordt een boxing-conversie (§10.2.9) uitgevoerd omEte converteren naar een class_type, en wordtEin de volgende stappen als die class_type beschouwd. Als het value_type een enum_type is, is de class_typeSystem.Enum;; anders is dezeSystem.ValueType. - De waarde van
Eis gecontroleerd op geldigheid. Als de waarde vanEnull is, wordt er eenSystem.NullReferenceExceptiongegenereerd en worden er geen verdere stappen uitgevoerd. - De implementatie van het functielid dat moet worden aangeroepen, wordt bepaald:
- Als het binding-time type van
Eeen interface is, is het functielid dat moet worden aangeroepen de implementatie vanMgeleverd door het runtime-type van de instantie waarEnaar verwijst. Dit functielid wordt bepaald door de interfacetoewijzingsregels (§19.6.5) toe te passen om de implementatie te bepalen vanMhet uitvoeringstype van het exemplaar waarnaar wordt verwezen doorE. - Anders, als
Meen virtueel functielid is, dan is het functielid dat aangeroepen moet worden de implementatie vanM, geleverd door het uitvoeringstype van het exemplaar waarnaarEverwijst. Dit functielid wordt bepaald door de regels toe te passen voor het bepalen van de meest afgeleide implementatie (§15.6.4) vanMmet betrekking tot het uitvoeringstype van het exemplaar waarnaar wordt verwezen doorE. - Anders is
Mhet niet-virtuele functielid en is het functielid dat moet worden aangeroepenMzelf.
- 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
Ewordt het object waarnaar door dit wordt verwezen.
-
Opmerking:§12.2 classificeert de toegang tot eigenschappen als het aanroepen van het bijbehorende functielid, ofwel de
getaccessor of desettoegangsfunctie. Het bovenstaande proces wordt gevolgd voor het aanroepen van die accessor. eindnotitie
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 door middel van een omgezet 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.ValueTypeofSystem.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
Eeen tuple-expressie is metnelementen, is het resultaat van de deconstructie de expressieEzichzelf. - Anders, als
Eeen tupletype(T1, ..., Tn)metnelementen heeft, dan wordtEgeëvalueerd in een tijdelijke variabele__ven 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
Eniet worden gedeconstrueerd.
Hier verwijzen __v en __v1, ..., __vn naar onzichtbare en ontoegankelijke tijdelijke variabelen.
Opmerking: een expressie van het type
dynamickan niet worden gedeconstrueerd. eindnotitie
12.8 Primaire expressies
12.8.1 Algemeen
Primaire expressies bevatten de eenvoudigste vormen van expressies.
primary_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
| array_creation_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 grammaticaregel is niet gereed voor ANTLR omdat deze deel uitmaakt van een reeks wederzijds recursieve regels (
primary_expression,member_access,invocation_expressionelement_access, ,post_increment_expression, ,post_decrement_expression, ennull_forgiving_expressionpointer_member_accesspointer_element_access) die ANTLR niet verwerkt. Standaardtechnieken kunnen worden gebruikt om de grammatica te transformeren en de wederzijdse linker recursie te verwijderen. Deze standaard doet dit niet omdat niet alle parseringsstrategieën dit vereisen (bijvoorbeeld een LALR-parser niet) en dit zou de structuur en beschrijving verdoezelen. eindnotitie
pointer_member_access (§24.6.3) en pointer_element_access (§24.6.4) zijn alleen beschikbaar in onveilige code (§24).
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_Format Interpolated_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 het links uitgelijnd door 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.FormattableStringretourneert ; anders wordt het type string en wordt de methode string.Format (§C.2) die een waarde van het type stringretourneert .
In beide gevallen bestaat de argumentenlijst van de aanroep uit een opmaakreeksletterlijke met opmaakspecificaties voor elke interpolatie en een argument voor elke expressie die overeenkomt met de opmaakspecificaties.
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 ≥ 1voor elk getalIvan0totN-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 het 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 zijn dit 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
stringwaarde is de waarde zelf, - positieve uitlijningswaarden die rechts uitlijnen binnen de opgegeven minimale veldbreedte,
- negatieve uitlijningswaarden die links uitlijnen 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
enul is en de simple_name wordt weergegeven in een lokale ruimte voor variabeledeclaratie (§7.3) die rechtstreeks een lokale variabele, parameter of constante met naamIbevat, verwijst de simple_name naar die lokale variabele, parameter of constante en wordt geclassificeerd als een variabele of waarde. - Als
enul 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 naamIbevat, 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
enul is en de declaratie vanTeen typeparameter met de naamIbevat, verwijst de simple_name naar die typeparameter. - Anders, als een ledenopzoeking (§12,5) van
IinTmetetypeargumenten een overeenkomst oplevert:- Als
Thet 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
Thet 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 wanneerenul is. - Anders is het resultaat hetzelfde als een toegang voor leden (§12.8.7) van de vorm
T.IofT.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
enul is enIde naam van een naamruimte inNis, dan:- Als de locatie waar de simple_name plaatsvindt, wordt ingesloten door een naamruimtedeclaratie voor
Nen de naamruimtedeclaratie een extern_alias_directive of using_alias_directive bevat die de naamIkoppelt 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
IinN.
- Als de locatie waar de simple_name plaatsvindt, wordt ingesloten door een naamruimtedeclaratie voor
- Anders, als
Neen toegankelijk type bevat met de naamIen typeparameters voore, dan:- Als
enul is en de locatie waar de simple_name plaatsvindt, wordt ingesloten door een naamruimtedeclaratie voorNen bevat de naamruimtedeclaratie een extern_alias_directive of using_alias_directive die de naamIkoppelt 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
enul is en de naamruimtedeclaratie een extern_alias_directive of using_alias_directive bevat die de naamIkoppelt 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
Ienetype 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
Ienetypeparameters, 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 dit
eniet het getal is enIde id_is, is de simple_name een eenvoudige verwijdering, een vorm van declaratie-expressie (§12.19). - 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 id die in een deconstruction_expression genest , introduceert dus een declaratie-expressie (§12.19). Als gevolg hiervan kan een deconstruction_expression zich alleen aan de linkerkant van een eenvoudige opdracht voordoen.
Voorbeeld: De volgende code declareert drie variabelen: a, b en c. Elk van hen is een geheel getal en krijgt zijn waarde van de tuple aan de rechterkant van de toewijzing.
var (a, b, c) = (1, 2, 3); // a is 1, b is 2, and c is 3. var sum = a + b + c; // sum is 6.Elk van de afzonderlijke elementen van de opdracht kan zelf een deconstructie-uitdrukking zijn. Met de volgende deconstructie-expressie worden bijvoorbeeld zes variabelen toegewezen,
aviaf.var (a, b, (c, d, (e, f))) = (1, 2, (3, 4, (5, 6)));In dit voorbeeld ziet u dat de structuur van geneste tuples aan beide zijden van de toewijzing moet overeenkomen.
Als de variabelen aan de linkerkant impliciet worden getypt, moet de bijbehorende expressie een type hebben:
(int a, string? b) = (42, null); //OK var (c, d) = (42, null); // Invalid as type of d cannot be inferred (int e, var f) = (42, null); // Invalid as type of f cannot be inferredeinde voorbeeld
Een tuple-expressie heeft een type als en alleen als elk van de elementexpressies Ei een type Tiheeft. 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
Eiin de vorm vanNiofE.NiofE?.Niis, dan wordt het tuplet-type-elementTi Ni, tenzij een van de volgende voorwaarden geldt:.- Een ander element van de tuple-expressie heeft de naam
Niof - Een ander tuple-element zonder naam heeft een tuple-elementexpressie van de vorm
NiofE.NiofE?.Ni, of -
Niis van de vormItemX, waarbijXeen reeks niet-0geïnitieerde decimalen is die de positie van een tuple-element kunnen vertegenwoordigen enXniet de positie van het element vertegenwoordigt.
- Een ander element van de tuple-expressie heeft de naam
- Anders moet het tupel-type-element
Tizijn.
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.13), door deze opnieuw te classificeren als een waarde (§12.2.2)) of door deze het doel te maken van een deconstructingtoewijzing (§12.23.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 typeIn dit voorbeeld zijn alle vier de tuple-expressies geldig. De eerste twee,
t1ent2, gebruiken niet het type tuple-expressie, maar passen in plaats daarvan een impliciete tupleconversie toe. In het geval vant2is de impliciete tuple-conversie afhankelijk van de impliciete conversies van2naarlongen vannullnaarstring. De derde tuple-expressie heeft een type(int i, string)en kan daarom opnieuw worden geclassificeerd als een waarde van dat type. De declaratie vant4daarentegen 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 wordt de toegang tot leden geclassificeerd 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
enul is enEeen naamruimte is enEeen geneste naamruimte met naamIbevat, is het resultaat die naamruimte. - Als
Eeen naamruimte is enEeen toegankelijk type met een naamIenKtypeparameters bevat, is het resultaat dat het type is samengesteld met de opgegeven typeargumenten. - Als
Eis geclassificeerd als een type, alsEgeen typeparameter is en als een lidzoekactie (§12,5) vanIinEmetKtypeparameters een overeenkomst produceert, wordtE.Ials volgt geëvalueerd en geclassificeerd:Opmerking: wanneer het resultaat van een dergelijke lidzoekactie een methodegroep is en
Knul is, kan de methodegroep methoden met typeparameters bevatten. Hierdoor kunnen dergelijke methoden worden overwogen voor het afleiden van het typeargument. eindnotitie- Als
Ieen type identificeert, is het resultaat dat type samengesteld met gegeven typeargumenten. - Als
Ieen of meer methoden identificeert, is het resultaat een methodegroep zonder bijbehorende exemplaarexpressie. - Als
Ieen statische eigenschap identificeert, is het resultaat een eigenschapstoegang zonder bijbehorende exemplaarexpressie. - Als
Ieen 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
IinE. - Anders is het resultaat een variabele, namelijk het statische veld
IinE.
- 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
Ieen 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.Iprecies verwerkt alsofIeen 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
Ieen constante identificeert, is het resultaat een waarde, namelijk de waarde van die constante. - Als
Ieen opsommingslid identificeert, is het resultaat een waarde, namelijk de waarde van dat opsommingslid. - Anders is
E.Ieen ongeldige lidverwijzing en treedt er een compilatiefout op.
- Als
- Als
Eeen eigenschapstoegang, indexeerfunctietoegang, variabele of waarde is, waarvan het typeTis en een lidzoekactie (§12,5) vanIinTmetKtypeargumenten een overeenkomst oplevert, wordtE.Ials volgt geëvalueerd en geclassificeerd:- Als
Eeen 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
Ieen of meer methoden identificeert, is het resultaat een methodegroep met een bijbehorende exemplaarexpressie vanE. - Als
Ieen exemplaareigenschap identificeert, is het resultaat een eigenschapstoegang met een bijbehorende exemplaarexpressie vanEen een gekoppeld type dat het type van de eigenschap is. AlsTeen klassetype is, wordt het bijbehorende type gekozen uit de eerste declaratie of override van de eigenschap die wordt gevonden door te beginnen metTen vervolgens door de basisklassen te zoeken. - Als
Teen class_type is enIeen exemplaarveld van die class_typeidentificeert:- Als de waarde van
Enullis, wordt er eenSystem.NullReferenceExceptiongegenereerd. - 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
Iin het object die doorEwordt gerefereerd. - Anders is het resultaat een variabele, namelijk het veld
Iin het object waarnaar wordt verwezen doorE.
- Als de waarde van
- Als
Teen struct_type is enIeen exemplaarveld van die struct_typeidentificeert:- Als
Eeen 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 veldIbinnen het doorEgegeven structuurexemplaar. - Anders is het resultaat een variabele, namelijk het veld
Iin het struct-exemplaar dat is gegeven doorE.
- Als
- Als
Ieen 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.Iprecies verwerkt alsofIeen exemplaarveld is. - Anders is het resultaat een gebeurtenistoegang met een bijbehorende instantie-uitdrukking 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.Ite verwerken als een aanroep van een uitbreidingsmethode (§12.8.10.3). Als dit mislukt, isE.Ieen 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.IE 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_accesses die door een null_forgiving_operator kunnen worden voorafgegaan.
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
Peen null-waardetype is:Laat
Thet typeP.Value.Azijn.Als
Teen 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
Teen niet-null-waardetype is, wordt het typeET?en is de betekenis vanEhetzelfde als de betekenis van:((object)P == null) ? (T?)null : P.Value.ABehalve dat
Pslechts één keer wordt geëvalueerd.Anders is het type
ETen is de betekenis vanEhetzelfde als de betekenis van:((object)P == null) ? (T)null : P.Value.ABehalve dat
Pslechts één keer wordt geëvalueerd.
Anders:
Laat
Thet type expressieP.Azijn.Als
Teen 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
Teen niet-null-waardetype is, wordt het typeET?en is de betekenis vanEhetzelfde als de betekenis van:((object)P == null) ? (T?)null : P.ABehalve dat
Pslechts één keer wordt geëvalueerd.Anders is het type
ETen is de betekenis vanEhetzelfde als de betekenis van:((object)P == null) ? (T)null : P.ABehalve dat
Pslechts één keer wordt geëvalueerd.
Opmerking: In een expressie van het formulier:
P?.A₀?.A₁Als
Pnaarnullevalueert, 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. Dit gebeurt alleen als een projectie-initialisatiefunctie in een anonieme expressie voor het maken van objecten (§12.8.17.3).
12.8.9 Null-forgiving expressies
12.8.9.1 Algemeen
De waarde, het type, de classificatie (§12.2) en de veilige context (§16.4.15) van een null-vergeefse expressie zijn 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 laatste kan overbelast zijn (§15.10), de definitie van de operator null-forgiving is opgelost.
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 beslissing van een compiler te negeren dat een expressie misschien null is; en om een waarschuwing van een compiler met betrekking tot nullwaarde te onderdrukken.
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
IsValidtrueretourneert, kanpveilig worden gedereferentieerd om toegang te krijgen tot de eigenschapNameen 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. Alsxtijdens runtime echternullis, treedt er een uitzondering op omdatnullniet naarintkan worden gecast.einde voorbeeld
12.8.9.3 Overschaduwen 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?, waarbijlveen uitvoerparameter is en het een eenvoudige toewijzing uitvoert.Methode
Mgeeft de variabelesvan het typestringdoor als uitvoerparameter vanAssign. De gebruikte compiler geeft een waarschuwing omdatsgeen nullable variabele is. Aangezien het tweede argument vanAssignniet 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 wordt de invocation_expression geclassificeerd 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 wordt geclassificeerd, is alleen toegestaan in de context van een statement_expression (§13.7) of als de hoofdtekst van een lambda_expression (§12.21). 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
Tis, wordt het bijbehorende type gekozen uit de eerste declaratie of overschrijving van de methode die wordt gevonden door te beginnen metTen 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
Tis, wordt het bijbehorende type gekozen uit de eerste declaratie of overschrijving van de methode die wordt gevonden door te beginnen metTen 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 verzameling van kandidaatmethoden voor de methode-aanroep wordt samengesteld. Voor elke methode
Fgekoppeld aan de methodegroepM:- Als
Fniet-algemeen is, isFeen kandidaat wanneer:-
Mheeft geen lijst met typeargumenten en -
Fgeldt voorA(§12.6.4.2).
-
- Als
Falgemeen is enMgeen lijst met typeargumenten heeft, isFeen 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
Faan hun beperkingen (§8.4.5) en is de parameterlijst vanFvan toepassing opA(§12.6.4.2)
- Als
Falgemeen is enMeen lijst met typeargumenten bevat, isFeen kandidaat wanneer:-
Fhetzelfde 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
Faan hun beperkingen (§8.4.5), en is de parameterlijst vanFvan 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.Fin de set, waarbijChet type is waarin de methodeFwordt gedeclareerd, worden alle methoden die zijn gedeclareerd in een basistype vanCuit de set verwijderd. AlsCbovendien 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
objecten 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 dynamicheeft, zijn extensiemethoden niet van toepassing.
Het doel is om de beste type_nameCte 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 in geen enkele omringende naamruimteverklaring of compilatie-eenheid een kandidaatset wordt gevonden, 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.
-
Cis 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
nullwaarde 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
Bvoorrang op de eerste extensiemethode en heeftCmethode 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.Gheeft voorrang opC.G, enE.Fheeft voorrang op zowelD.FalsC.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:
-
Dwordt geëvalueerd. Als deze evaluatie een uitzondering veroorzaakt, worden er geen verdere stappen uitgevoerd. - De lijst met argumenten
Awordt geëvalueerd. Als deze evaluatie een uitzondering veroorzaakt, worden er geen verdere stappen uitgevoerd. - De waarde van
Dis gecontroleerd op geldigheid. Als de waarde vanDisnull, wordt er eenSystem.NullReferenceExceptiongegenereerd en worden er geen verdere stappen uitgevoerd. - Anders is
Deen 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 §21.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.21.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 slechts als de null_conditional_member_access of null_conditional_element_access een delegate_type heeft.
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 [. Laten we PA de samenvoeging van P en A.
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
Eals niets wordt geclassificeerd, is de betekenis ervan hetzelfde als de betekenis van het blok:{ if ((object)P != null) PA; }behalve dat
Pslechts één keer wordt geëvalueerd.Anders is de betekenis van
Ehetzelfde als de betekenis van het blok:{ return E; }en op zijn beurt hangt de betekenis van dit blok ervan af of
Esyntactisch 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_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_expression '[' argument_list ']'
;
Bij de erkenning van een primary_expression indien zowel de element_access als de pointer_element_access (§24.6.4) alternatieven van toepassing zijn, wordt de laatste gekozen indien de ingesloten primary_expression van het type aanwijzer is (§24.3).
De primary_expression van een element_access mag geen array_creation_expression zijn, tenzij deze een array_initializer bevat, en mag geen stackalloc_expression zijn, tenzij deze een stackalloc_initializer bevat.
Opmerking: Deze beperking is ingesteld om te voorkomen dat er mogelijk verwarrende code ontstaat, zoals:
var a = new int[3][1];die anders als volgt zouden worden geïnterpreteerd:
var a = (new int[3])[1];Een vergelijkbare beperking geldt voor null_conditional_element_access (§12.8.13). eindnotitie
Een element_access is dynamisch gebonden (§12.3.3) indien ten minste een van de volgende bewaringen geldt:
- De primary_expression heeft het compilatietijdstype
dynamic. - Ten minste één expressie van het argument_list heeft het type compileertijd
dynamic.
In dit geval is het type compilatietijd van de element_access afhankelijk van het type compileertijd van de primary_expression: als het een matrixtype heeft, is het type compileertijd het elementtype van dat matrixtype; anders is dynamic het type compileertijd en wordt de element_access geclassificeerd als een waarde van het type dynamic. De onderstaande regels om de betekenis van de element_access te bepalen, worden vervolgens toegepast tijdens runtime, met behulp van het runtimetype in plaats van het type compileertijd van die van de primary_expression en argument_list expressies met het type compileertijd dynamic. Als de primary_expression geen type compilatietijd dynamicheeft, ondergaat de toegang tot het element een beperkte controle van de compilatietijd, zoals beschreven in §12.6.5.
voorbeeld van:
var index = (dynamic)1; // index has compile-time type dynamic int[] a = {0, 1, 2}; var a_elem = a[index]; // dynamically bound, a_elem has compile-time type int string s = "012"; var s_elem = s[index]; // dynamcially bound, s_elem has compile-time type dynamiceinde voorbeeld
Als de primary_expression van een element_access is:
- een waarde van een matrixtype, de element_access is een matrixtoegang (§12.8.12.2);
- een waarde van het
stringtype, de element_access is een tekenreekstoegang (§12.8.12.3); - anders is de primary_expression een variabele of waarde van een klasse, struct of interfacetype met een of meer indexeerleden, in welk geval de element_access een indexeerfunctietoegang is (§12.8.12.4).
12.8.12.2 Matrixtoegang
Voor een matrixtoegang mag de argument_list geen benoemde argumenten of by-reference-argumenten bevatten (§15.6.2.3).
Het aantal expressies in het argument_list moet gelijk zijn aan de rang van de array_type en elke expressie moet:
- van het type
int,uint, oflong; ofulong - alleen voor toegang tot één rangmatrix, van het type
Indexof ; ofRange - impliciet converteerbaar zijn naar een of meer van de bovenstaande typen.
De runtimeverwerking van een matrixtoegang van het formulier P[A], waarbij P een primary_expression van een array_type is en A een argument_list van indexexpressies is, bestaat uit de volgende stappen:
-
Pwordt geëvalueerd. Als deze evaluatie een uitzondering veroorzaakt, worden er geen verdere stappen uitgevoerd. - Voor elke indexexpressie in de argument_list in volgorde, van links naar rechts:
- De indexexpressie wordt geëvalueerd, laat het type van de resulterende waarde T zijn;
- Deze waarde wordt vervolgens geconverteerd naar de eerste van de typen:
int,uint,long, ,ulong, of alleen voor toegang tot één rangmatrix ofIndexRange; waarvoor een impliciete conversie (§10.2) van T bestaat. - 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
Pis gecontroleerd op geldigheid. Als de waarde vanPisnull, wordt er eenSystem.NullReferenceExceptiongegenereerd en worden er geen verdere stappen uitgevoerd. - Als de voorgaande stappen één indexwaarde van het type
Rangehebben geproduceerd, gaat u als volgt te werk:- Laat L de lengte zijn van de matrix waarnaar wordt verwezen door
P. -
Ais gecontroleerd of het geldig is met betrekking tot L (§18.3). Als dit niet het probleem is, wordt er eenSystem.ArgumentOutOfRangeExceptiongegenereerd en worden er geen verdere stappen uitgevoerd. - De begin offset, S en het aantal artikelen, N, voor
Awat betreft L , worden bepaald zoals beschreven voorGetOffsetAndLength(§18.3). - Het resultaat van de matrixtoegang is een matrix met een ondiepe kopie van de N-elementen die
Pbeginnen bij index S. Als N nul is, heeft de matrix nulelementen.
- Laat L de lengte zijn van de matrix waarnaar wordt verwezen door
Notitie: Zowel S als N kunnen nul zijn ($ 24,3). Het indexeren van een lege matrix is meestal ongeldig, maar indexeren met een leeg bereik vanaf nul is geldig en retourneert een lege matrix. Met de definitie kan S ook L zijn, de afgelopen-eindindex (§18.1), in welk geval N nul is en een lege matrix die wordt geretourneerd. eindnotitie
Notitie: Een bereik van elementen van een matrix kan niet worden toegewezen aan het gebruik van een matrixtoegang. Dit verschilt van de indexeerfunctie (§12.8.12.4) die mogelijk, maar niet, ondersteuning biedt voor een reeks indexen die zijn opgegeven door een
Rangewaarde. eindnotitie
- Anders:
- Het resultaat van het evalueren van de matrixtoegang is een variabele verwijzing (§9.5) van het elementtype van de matrix.
- 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.IndexOutOfRangeExceptiongegenereerd en worden er geen verdere stappen uitgevoerd. - De variabele verwijzing van het matrixelement dat door de indexexpressies wordt gegeven, wordt berekend en dit wordt het resultaat van de matrixtoegang.
12.8.12.3 Tekenreekstoegang
Voor een tekenreeks moet de argument_list van het element_access één argument zonder naam bevatten (§15.6.2.2) dat:
- van het type
int,Indexof ; ofRange - impliciet converteerbaar naar een of meer van de bovenstaande typen.
De runtimeverwerking van een tekenreekstoegang van het formulier P[A], waarbij P een primary_expression van het string type is en A één expressie is, bestaat uit de volgende stappen:
-
Pwordt geëvalueerd. Als deze evaluatie een uitzondering veroorzaakt, worden er geen verdere stappen uitgevoerd. - De indexexpressie wordt geëvalueerd, laat het type van de resulterende waarde T zijn;
- Deze waarde wordt vervolgens geconverteerd naar de eerste van de typen:
int,IndexofRange; waarvoor een impliciete conversie (§10.2) van T bestaat. - 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
Pis gecontroleerd op geldigheid. Als de waarde vanPisnull, wordt er eenSystem.NullReferenceExceptiongegenereerd en worden er geen verdere stappen uitgevoerd. - Als de voorgaande stappen een indexwaarde van het type
Rangehebben geproduceerd, gaat u als volgt te werk:- Het resultaat van het evalueren van de tekenreekstoegang is een waarde van het
stringtype. - Laat L de lengte zijn van de tekenreeks waarnaar wordt verwezen.
P -
Ais gecontroleerd of het geldig is met betrekking tot L (§18.3), als het niet is, wordt er eenSystem.ArgumentOutOfRangeExceptiongegooid en worden er geen verdere stappen uitgevoerd. - De begin offset, S en het aantal artikelen, N, voor
Awat betreft L , worden bepaald zoals beschreven voorGetOffsetAndLength(§18.3). - Het resultaat van de tekenreekstoegang is een tekenreeks die wordt gevormd door het kopiëren van de N-tekens die
Pbeginnen met S, als N nul is, is de tekenreeks leeg.
- Het resultaat van het evalueren van de tekenreekstoegang is een waarde van het
Notitie: Zowel S als N mogen nul zijn (§18.3). Het indexeren van een lege tekenreeks is meestal ongeldig, maar indexeren met een leeg bereik vanaf nul is geldig en retourneert een lege tekenreeks. Met de definitie kan S ook L zijn, de laatste-eindindex (§18.1), in welk geval N nul is en een lege tekenreeks wordt geretourneerd. eindnotitie
- Anders:
- Het resultaat van het evalueren van de tekenreekstoegang is een waarde van het
chartype. - De waarde van de geconverteerde indexexpressie wordt gecontroleerd op basis van de werkelijke grenzen van het tekenreeksexemplaren waarnaar wordt
Pverwezen. Als de waarde buiten het bereik valt, wordt er eenSystem.IndexOutOfRangeExceptiongegenereerd en worden er geen verdere stappen uitgevoerd. - De waarde van het teken op de verschuiving van de geconverteerde indexexpressie met de tekenreeks
Pwordt het resultaat van de tekenreekstoegang.
- Het resultaat van het evalueren van de tekenreekstoegang is een waarde van het
12.8.12.4 Toegang tot indexeerfunctie
Voor toegang tot een indexeerfunctie moet de primary_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 argument_list mag geen argumenten bevatten of out bevattenref.
De bindingstijdverwerking van een indexeerfunctietoegang van het formulier P[A], waarbij P een primary_expression van een klasse, struct of interfacetype Tis en A een argument_list is, bestaat uit de volgende stappen:
- De set van indexeerfuncties die door
Twordt geleverd, is samengesteld. De set bestaat uit alle indexeerfuncties die zijn gedeclareerd inTof een basistype vanTdie 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.Iin de set, waarbijShet type is waarin de indexeerfunctieIwordt gedeclareerd:- Als
Iniet van toepassing is opA(§12.6.4.2), wordtIuit de set verwijderd. - Indien
Ivan toepassing is opA(§12.6.4.2), worden alle indexeerfuncties die zijn gedeclareerd in een basistype vanSuit de set verwijderd. - Indien
Ivan toepassing is opA(§12.6.4.2) enSeen ander klassetype danobjectis, 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 accessors van de beste indexeerfunctie worden gecontroleerd:
- Als de toegang tot de indexeerfunctie het doel is van een toewijzing, heeft de indexeerfunctie een set of ref-get accessor, anders treedt er een fout op in de bindingstijd;
- Anders heeft de indexeerfunctie een get- of ref get-accessor, anders treedt er een bindingstijdfout op.
De runtimeverwerking van de toegang tot de indexeerfunctie bestaat uit de volgende stappen:
- Het doel primary_expression
Pwordt geëvalueerd. - De indexexpressies van de argument_list
Aworden op volgorde geëvalueerd, van links naar rechts. - Gebruik de beste indexeerfunctie die tijdens bindingstijd is bepaald:
- Als de toegang tot de indexeerfunctie het doel is van een toewijzing, wordt de ingestelde toegangs- of verw-toegangsfunctie aangeroepen om een nieuwe waarde toe te wijzen (§12.23.2).
- In alle andere gevallen wordt de get accessor of ref 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_expression gevolgd door de twee tokens '?' en '[', gevolgd door een argument_list, gevolgd door een ']'-token, gevolgd door nul of meer dependent_accesswaarvan een van deze kan worden voorafgegaan door een null_forgiving_operator.
null_conditional_element_access
: primary_expression '?' '[' argument_list ']'
(null_forgiving_operator? dependent_access)*
;
De argument_list van een null_conditional_element_access mag geen out of ref argumenten bevatten.
De primary_expression van een null_conditional_element_access mag geen array_creation_expression zijn, tenzij het een array_initializer of een stackalloc_expression bevat, tenzij het een stackalloc_initializer bevat.
Opmerking: Deze beperking bestaat om mogelijk verwarrende code niet toe te laten. Een vergelijkbare beperking is van toepassing op element_access (§12.8.12) waarbij een voorbeeld van wat wordt uitgesloten, kan worden gevonden. eindnotitie
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
Peen null-waardetype is:Laat
Thet type expressieP.Value[A]Bzijn.Als
Teen 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
Teen niet-null-waardetype is, wordt het typeET?en is de betekenis vanEhetzelfde als de betekenis van:((object)P == null) ? (T?)null : P.Value[A]BBehalve dat
Pslechts één keer wordt geëvalueerd.Anders is het type
ETen is de betekenis vanEhetzelfde als de betekenis van:((object)P == null) ? null : P.Value[A]BBehalve dat
Pslechts één keer wordt geëvalueerd.
Anders:
Laat
Thet type expressieP[A]Bzijn.Als
Teen 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
Teen niet-null-waardetype is, wordt het typeET?en is de betekenis vanEhetzelfde als de betekenis van:((object)P == null) ? (T?)null : P[A]BBehalve dat
Pslechts één keer wordt geëvalueerd.Anders is het type
ETen is de betekenis vanEhetzelfde als de betekenis van:((object)P == null) ? null : P[A]BBehalve dat
Pslechts één keer wordt geëvalueerd.
Opmerking: In een expressie van het formulier:
P?[A₀]?[A₁]Als
Pevalueert 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
thiswordt 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
thiswordt 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
thiswordt 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
thisvariabele 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 eenrefparameter 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
thiswordt 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.15) of asynchrone functie (§15.14), vertegenwoordigt de
thisvariabele de struct waarvoor de methode of accessor is aangeroepen.- Als de struct een
readonly structis, gedraagt dethisvariabele zich precies hetzelfde als een invoerparameter van het structtype - Anders gedraagt de
this-variabele zich precies hetzelfde als eenrefparameter van het structtype
- Als de struct een
- Als de methode of accessor een iterator- of asynchrone functie is, vertegenwoordigt de
thisvariabele 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.15) of asynchrone functie (§15.14), 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
thisisbasegeen 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, decimalen 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
xis geclassificeerd als een variabele:-
xwordt geëvalueerd om de variabele te produceren. - De waarde van
xwordt opgeslagen. - De opgeslagen waarde van
xwordt 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
xen opgeslagen op de locatie die is opgegeven door de eerdere evaluatie vanx. - De opgeslagen waarde van
xwordt het resultaat van de bewerking.
-
- Als
xis geclassificeerd als een eigenschap of indexertoegang:- De exemplaarexpressie (als
xnietstaticis) en de lijst met argumenten (alsxeen indexeerfunctietoegang is) die aanxis gekoppeld, worden geëvalueerd en de resultaten worden gebruikt in de daaropvolgende aanroepen van de get- en set-toegangsmethodes. - De get accessor van
xwordt aangeroepen en de geretourneerde waarde wordt opgeslagen. - De opgeslagen waarde van
xwordt 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
xen de set accessor vanxwordt aangeroepen met deze waarde als waardeargument. - De opgeslagen waarde van
xwordt het resultaat van de bewerking.
- De exemplaarexpressie (als
De ++ en -- operators ondersteunen ook voorvoegsel notatie (§12.9.7). Het resultaat van x++ of x-- is de waarde van xvóór de bewerking, terwijl het resultaat van ++x of --x de waarde van xis 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 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
12.8.17.2.1 Algemeen
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 initializer bevat, bestaat uit de eerste verwerking van de instantieconstructor en vervolgens het verwerken van de initialisaties van het lid of element die zijn opgegeven door de object-initialisatie (§12.8.17.17.2.2.2.2.2) of de initialisatiefunctie voor verzamelingen (§12.8.17.2.3).
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
Teen value_type is enAniet 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 voorTzoals 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
Teen type_parameter is enAniet 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
Teen class_type of een struct_typeis:- Als
Teen 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
Teen class_typeis:- Er wordt een nieuw exemplaar van klasse
Ttoegewezen. Als er onvoldoende geheugen beschikbaar is om het nieuwe exemplaar toe te wijzen, wordt er eenSystem.OutOfMemoryExceptiongegenereerd 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
Teen struct_typeis:- Een exemplaar van het type
Twordt 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.2.2 Object-initialisators
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.
In het argument_list van een initializer_target is er geen impliciete ondersteuning voor argumenten van het type Index (§18.4.2) of Range (§18.4.3).
Een lid-initialisatiefunctie die een expressie opgeeft nadat het gelijkteken is verwerkt, wordt op dezelfde manier verwerkt als een toewijzing (§12.23.2) aan 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. De target moet van een verzamelingstype zijn dat voldoet aan de specificaties in §12.8.17.2.3.
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
Pointkan 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
__aeen 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
Rectanglekan 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,__p1en__p2tijdelijke variabelen zijn die anders onzichtbaar en niet toegankelijk zijn.Als de constructor van
Rectanglede twee ingeslotenPointexemplaren toewijst, kunnen ze worden gebruikt om de ingeslotenPointexemplaren 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
Pointexemplaren 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.2.3 Initializers voor verzamelingen
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)*
;
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 initialisator voor een element met een enkele expressie kan zonder accolades worden geschreven, maar kan dan geen toewijzingsexpressie zijn om verwarring met lidinitialisatoren te voorkomen. De non_assignment_expression productie wordt gedefinieerd in §12.24.
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 Addte 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,__c1en__c2tijdelijke variabelen zijn die anders onzichtbaar en niet toegankelijk zijn.einde voorbeeld
12.8.17.3 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
p1enp2van hetzelfde anonieme type zijn.einde voorbeeld
De methoden Equals en GetHashcode voor anonieme typen overschrijven de methoden die zijn overgenomen van objecten 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 lid-declaraties van de vormen
«identifier», «expr» . «identifier» en «expr» ? . «identifier»
zijn exact gelijk aan respectievelijk het volgende:
«identifier» = «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.17.4 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, longof ulongzijn 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 (§24.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.16) van de set expressies in de matrixinitiator. 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.OverflowExceptiongegenereerd 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.OutOfMemoryExceptiongegenereerd 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 uitdrukking voor het maken van een matrix ook de submatrices instantieert, en de instructieint[][] a = new int[100][5]; // Errorresulteert 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" }; // ErrorDe laatste expressie veroorzaakt een compilatiefout omdat noch
intnochstringimpliciet 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.3) 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.5 Expressies voor het maken van 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 delegate-instantie geïdentificeerd waarvan een kopie moet worden gemaakt.
Als de expressie het compilatietijdtype dynamicheeft, is de delegate_creation_expression dynamisch gebonden (§12.8.17.5) en worden de onderstaande regels tijdens runtime toegepast met behulp van het runtimetype 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
Eeen methodegroep is, wordt de expressie voor het maken van gemachtigden op dezelfde manier verwerkt als een methodegroepconversie (§10,8) vanEtotD.Als
Eeen anonieme functie is, wordt de expressie voor het maken van gemachtigden op dezelfde manier verwerkt als een anonieme functieconversie (§10,7) vanEtotD.Indien
Eeen waarde is,Emoet deze compatibel zijn (§21.2) metD, en het resultaat is een verwijzing naar een zojuist gemaakte gemachtigde met een aanroeplijst met één vermelding die wordt aangeroepenE.
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
Eeen methodegroep is, wordt de expressie voor het maken van gemachtigden geëvalueerd als een methodegroepconversie (§10,8) vanEnaarD. - Als
Eeen anonieme functie is, wordt de creatie van de delegeer geëvalueerd als een omzetting van anonieme functie vanEnaarD(§10.7). - Als
Eeen waarde van een delegate_typeis:-
Ewordt geëvalueerd. Als deze evaluatie een uitzondering veroorzaakt, worden er geen verdere stappen uitgevoerd. - Als de waarde van
Eisnull, wordt er eenSystem.NullReferenceExceptiongegenereerd en worden er geen verdere stappen uitgevoerd. - Er wordt een nieuw exemplaar van het delegate-type
Dtoegewezen. Als er onvoldoende geheugen beschikbaar is om het nieuwe exemplaar toe te wijzen, wordt er eenSystem.OutOfMemoryExceptiongegenereerd en worden er geen verdere stappen uitgevoerd. - Het nieuwe gedelegeerde object wordt geïnitialiseerd met een aanroepingslijst met één item die
Eaanroept.
-
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.fveld wordt geïnitialiseerd met een gemachtigde die verwijst naar de tweedeSquaremethode, omdat die methode exact overeenkomt met de parameterlijst en het retourtype vanDoubleFunc. Als de tweedeSquaremethode niet aanwezig was, zou er een compilatietijdfout zijn opgetreden.einde voorbeeld
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 generic_dimension_specifier?)*
| unbound_qualified_alias_member
('.' identifier generic_dimension_specifier?)*
;
unbound_qualified_alias_member
: identifier '::' 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 dynamiczijn.
De tweede vorm van typeof_expression bestaat uit een typeof trefwoord gevolgd door een unbound_type_nametussen haakjes.
Opmerking: de grammatica van unbound_type_name en unbound_qualified_alias_member volgen die van type_name (§7.8) en qualified_alias_member (§14.8.1), behalve dat generic_dimension_specifiers worden vervangen door type_argument_lists. eindnotitie
Bij de erkenning van de operand van een typeof_expression indien zowel unbound_type_name als type_name van toepassing zijn, namelijk wanneer deze geen generic_dimension_specifier noch een type_argument_list bevat, wordt type_name gekozen.
Opmerking: ANTLR maakt de opgegeven keuze automatisch vanwege de volgorde van de alternatieven van typeof_expression. eindnotitie
De betekenis van een unbound_type_name wordt bepaald alsof:
- De reeks tokens wordt geconverteerd naar een type_name door elke generic_dimension_specifier te vervangen door een type_argument_list met hetzelfde aantal komma's en het trefwoord
objectals elke type_argument. - Het resulterende type_name wordt omgezet in een samengesteld type (§7.8).
- De unbound_type_name is vervolgens het niet-afhankelijke algemene type dat is gekoppeld aan het opgeloste samengestelde type (§8.4).
Opmerking: er is geen verplichting voor een implementatie om de volgorde van tokens te transformeren of om het intermediaire samengestelde type te produceren, alleen dat het ongebonden generieke type dat wordt bepaald, 'alsof' dit proces is gevolgd. eindnotitie
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, inclusiefvoidmethoden, met een exemplaar vanSystem.Type. eindnotitie
De operator typeof kan worden gebruikt voor een typeparameter. Het is een compilatietijdfout als de typenaam een nullable-referentietype is. Het resultaat is het System.Type-object voor het runtimetype dat is gebonden aan de typeparameter. Als het runtimetype een opzichzelfstaande verwijzingstype is, is het resultaat het overeenkomstige niet-opteerbare 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
intenSystem.Int32hetzelfde 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 sizeof 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 Tis 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 operandtypen wordt de sizeof operator opgegeven in §24.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--(§12.8.16 en §12.9.7), wanneer de operand van een integraal of opsommingstype is. - De vooraf gedefinieerde
-unaire operator (§12.9.3), wanneer de operand van een integraal type is. - De vooraf gedefinieerde
+,-en*/binaire operatoren (§12.12), 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
floatofdoublenaar 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
checkedcontext een constante expressie (§12.25) is, treedt er een compilatietijdfout op. Anders, als de bewerking tijdens runtime wordt uitgevoerd, wordt er eenSystem.OverflowExceptionopgeworpen. - In een
uncheckedcontext wordt het resultaat afgekapt door alle bits in hoge volgorde te verwijderen die niet in het doeltype passen.
Voor niet-constante expressies (§12.25) (expressies die tijdens runtime worden geëvalueerd) die niet zijn ingesloten door een checked of unchecked meer operators of instructies, wordt de standaardoverloopcontrolecontext uitgeschakeld, tenzij externe factoren (zoals compilerswitches en configuratie van de uitvoeringsomgeving) aanroepen voor gecontroleerde evaluatie.
Voor constante expressies (§12.25) (expressies die volledig kunnen worden geëvalueerd tijdens het compileren), 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
FeenSystem.OverflowExceptionen retourneert de methodeG–727379968 (de lagere 32 bits van het buitenbereikresultaat). Het gedrag van de methodeHis afhankelijk van de standaardcontext voor overloopcontrole voor de compilatie, maar dit is hetzelfde alsFof 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
FenHleiden 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 eenuncheckedcontext, 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
checkedin F heeft geen invloed op de evaluatie vanx * yinMultiply, dusx * ywordt 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 naaruncheckedcompileertijdfouten produceren zonder deint-operator.einde voorbeeld
Opmerking: de operators en instructies van de
checkedenuncheckedstellen 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
: explicitly_typed_default
| default_literal
;
explicitly_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 explicitly_typed_default of het doeltype van de conversie voor een default_value_expression.
Een default_value_expression is een constante expressie (§12.25) 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 is geen onderdeel 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.15.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 deze afgeleid uit de regels voor het beste gemeenschappelijke type (§12.6.3.16) voor de set stackalloc_element_initializers.
- 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 zich rechtstreeks voordoet als de initialisatie-expressie van een local_variable_declaration (§13.6.2), waarbij de local_variable_type een type aanwijzer (§24.3) of afgeleid (var) is, is het resultaat van de stackalloc_expression een type T* aanwijzer (§24.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
Lengthvan 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
span8resulteertstackallocin eenSpan<int>, die wordt geconverteerd door een impliciete operator naarReadOnlySpan<int>. Op dezelfde manier wordt voorspan9het 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 dynamicheeft.
Een nameof_expression is een constante expressie van het type stringen 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
nameofexpressies, ervan uitgaande dat een algemeen typeList<T>binnen deSystem.Collections.Genericnaamruimte 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.21.
12.9 Unaire operatoren
12.9.1 Algemeen
De +, -( ! logische negatie alleen §12.9.4 ), ~, , ^, ++, --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
| '^' 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 (§24.6.2) en addressof_expression (§24.6.5) zijn alleen beschikbaar in onveilige code (§24).
Als de operand van een unary_expression het type compileertijd heeft dynamic, is deze dynamisch gebonden (§12.3.3). In dit geval:
- het type compilatietijd van de unary_expression is:
-
Indexvoor de index van de^eindoperator (§12.9.6) -
dynamicvoor alle andere unaire exploitanten; en
-
- de onderstaande oplossing vindt plaats tijdens runtime met behulp van het uitvoeringstype van de operand.
12.9.2 Unary plus operator
Voor een werking van het formulier +xwordt 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 vooraf gedefinieerde niet-opgeheven unaire plusoperatoren, die hierboven zijn gedefinieerd, zijn ook vooraf gedefinieerd.
12.9.3 Unaire mintekenoperator
Voor een werking van het formulier –xwordt 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
Xvan nul af te trekken. Als de waarde vanXde kleinste vertegenwoordigbare waarde is van het operandtype (−2¹ voorintof −2⁶³ voorlong), is de wiskundige negatie vanXniet zichtbaar binnen het operandtype. Als dit gebeurt binnen eencheckedcontext, wordt er eenSystem.OverflowExceptiongegenereerd; als deze zich in eenuncheckedcontext voordoet, is het resultaat de waarde van de operand en wordt de overloop niet gerapporteerd.Als de operand van de negatieoperator van het type
uintis, wordt deze geconverteerd naar het typelongen wordt het type van het resultaatlong. Een uitzondering is de regel waarmee deintwaarde−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
ulongis, treedt er een compilatietijdfout op. Een uitzondering is de regel waarmee delongwaarde−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
Xwaarbij het teken is omgekeerd. AlsxNaNis, wordt het resultaat ookNaN.Decimale negatie
decimal operator –(decimal x);Het resultaat wordt berekend door
Xvan 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 !xwordt 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 falseis, 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 ~xwordt 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).
Geheven (§12.4.8) vormen van de niet-geheven vooraf gedefinieerde bitwise complementoperators die hierboven zijn gedefinieerd, zijn eveneens vooraf gedefinieerd.
12.9.6 Index van eindoperator
De unaire ^ operator wordt de index van de eindoperator genoemd (dit wordt ook wel de hat-operator genoemd). Deze operator is niet overbelast (§12.4.3) en er is één vooraf gedefinieerde operator:
Index operator ^(int x);
Het resultaat van een bewerking van het formulier ^x is een from-endwaarde Index (§18.2) die gelijk is aan het resultaat van de expressie:
new Index(x, true)
Net als bij de andere unary_expression kande operand een type compileertijd (dynamic§12.9.1) hebben en dynamisch gebonden zijn (§12.3.3). Het type compileertijd van het resultaat is altijd Index.
Een opgeheven (§12.4.8) vorm van de index vanaf de eindoperator is ook vooraf gedefinieerd.
12.9.7 Voorvoegsel verhogen en verlagen operators
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, decimalen 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 runtime-verwerking van een voorvoegsel-increment- of decrementoperatie van de vorm ++x of --x bestaat uit de volgende stappen:
- Als
xis geclassificeerd als een variabele:-
xwordt geëvalueerd om de variabele te produceren. - De waarde van
xwordt 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 vanxen wordt het resultaat van de bewerking.
-
- Als
xis geclassificeerd als een eigenschap of indexertoegang:- De exemplaarexpressie (als
xnietstaticis) en de lijst met argumenten (alsxeen indexeerfunctietoegang is) die aanxis gekoppeld, worden geëvalueerd en de resultaten worden gebruikt in de daaropvolgende aanroepen van de get- en set-toegangsmethodes. - De get-accessor van
xwordt 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 vanxwordt 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.8 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)–ykan worden geïnterpreteerd als een cast_expression (een cast van–yomxte typen) of als een additive_expression gecombineerd met een parenthesized_expression (waarmee de waardex – ywordt 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 vanasenis.
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
xenyid's zijn, isx.yde juiste grammatica voor een type, zelfs alsx.ygeen type aangeeft. einde voorbeeld
Opmerking: Uit de ondubbelzinnigheidsregel volgt dat, als
xenyid's zijn,(x)y,(x)(y)en(x)(-y)cast_expressionzijn, maar(x)-yniet, zelfs niet alsxeen type identificeert. Alsxechter 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.9 Await-expressies
12.9.9.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.14). 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.9.2 Wachtbare expressies
De taak van een await_expression moet afwachting mogelijk maken. Een expressie t kan worden verwacht als een van de volgende bewaringen geldt:
-
tis van het compilatietijdstypedynamic -
theeft een toegankelijke instantie of extensiemethode met de naamGetAwaiterzonder parameters en zonder typeparameters, en een retourtypeAwaarvoor al het volgende van toepassing is:-
Aimplementeert de interfaceSystem.Runtime.CompilerServices.INotifyCompletion(hierna bekend alsINotifyCompletionvoor beknoptheid) -
Aheeft een toegankelijke, leesbare instantieeigenschapIsCompletedvan het typebool -
Aheeft een toegankelijke exemplaarmethodeGetResultzonder 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.9.3 Classificatie van wachtexpressies
De expressie await t wordt op dezelfde manier geclassificeerd als de expressie (t).GetAwaiter().GetResult(). Als het retourtype van GetResult dus voidis, wordt de await_expression geclassificeerd als niets. Als het een niet-void retourtype Theeft, wordt de await_expression geclassificeerd als een waarde van het type T.
12.9.9.4 Runtime-evaluatie van wachtexpressies
Tijdens runtime wordt de expressie await t als volgt geëvalueerd:
- Een awaiter
awordt verkregen door de expressie(t).GetAwaiter()te evalueren. - Een
boolbwordt verkregen door de uitdrukking(a).IsCompletedte evalueren. - Als
bisfalse, hangt de evaluatie ervan af ofade interface-System.Runtime.CompilerServices.ICriticalNotifyCompletionimplementeert (hierna bekend alsICriticalNotifyCompletionvoor beknoptheid). Deze controle wordt uitgevoerd op het moment van binden; d.w.z., tijdens runtime alsahet compileertijd typedynamicheeft, en anders tijdens compileertijd. Geefrde hervattingsdelegatie aan (§15.14):- Als
ageenICriticalNotifyCompletionimplementeert, wordt de expressie((a) as INotifyCompletion).OnCompleted(r)geëvalueerd. - Als
aICriticalNotifyCompletionimplementeert, 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
bistrue) of bij latere aanroep van de hervattingsdelegatie (indienbisfalse), 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 Bereikoperator
De .. operator wordt de bereikoperator genoemd.
range_expression
: unary_expression
| unary_expression? '..' unary_expression?
;
De vooraf gedefinieerde bereikoperator is:
Range operator ..(Index x, Index y);
De bereikoperator is niet overbelast (§12.4.3).
Alle bereikexpressies worden behandeld als het formulier x..y, waarbij:
-
xis de linkeroperand indien aanwezig, anders de expressie0; en -
yis de juiste operand indien aanwezig, anders de expressie^0.
Het resultaat van de bewerking is een Range waarde (§18.3) die gelijk is aan het resultaat van de expressie:
new Range(x, y)
Als een van beide operanden in een bereikexpressie het type compileertijd dynamicheeft, is de expressie dynamisch gebonden (§12.3.3).3. Het type compileertijd van het resultaat is altijd Range.
Een opgeheven (§12.4.8) vorm van de bereikoperator is ook vooraf gedefinieerd.
De bereikoperator is niet-associatief (§12.4.2).
12.11 Switch-expressie
Een switch_expression biedt switch-achtige semantiek in een expressiecontext.
switch_expression
: range_expression
| switch_expression 'switch' '{' switch_expression_arms? '}'
;
switch_expression_arms
: switch_expression_arm (',' switch_expression_arm)* ','?
;
switch_expression_arm
: pattern case_guard? '=>' switch_expression_arm_expression
;
switch_expression_arm_expression
: expression
;
Er is een conversie van een switchexpressie (§10.2.18) van een switchexpressie naar een type T als er een impliciete conversie is van elke switch_expression_arm_expression van elk van de switch_expression_armvan elke switchexpressie naar T.
Als een switchexpressie niet onderhevig is aan een conversie van een switchexpressie,
- Het type van de switch_expression is het meest voorkomende type §12.6.3.16) van de switch_expression_arm_expressionvan de switch_expression_arms, als een dergelijk type bestaat en elke switch_expression_arm_expression impliciet kan worden geconverteerd naar dat type.
- Het is een fout als er geen dergelijk type bestaat.
Het is een fout als het patroon van een switch_expression_arm het resultaat niet kan beïnvloeden, omdat een eerder patroon en een bewaker altijd overeenkomen.
Een switchexpressie wordt als volledig beschouwd als elke waarde van de invoer wordt verwerkt door ten minste één arm van de switchexpressie. De compiler levert een waarschuwing op als een switchexpressie niet volledig is.
Tijdens runtime is het resultaat van de switch_expression de waarde van de expressie van de eerste switch_expression_arm waarvoor de expressie aan de linkerkant van de switch_expression overeenkomt met het patroon van de switch_expression_arm, en waarvoor de case_guard van de switch_expression_arm, indien aanwezig, resulteert in true. Als er geen dergelijke switch_expression_arm is, genereert de switch_expression een exemplaar van de uitzondering System.Runtime.CompilerServices.SwitchExpressionException.
Voorbeeld: Met het volgende worden waarden van een enum geconverteerd die visuele aanwijzingen op een onlinekaart vertegenwoordigen naar de bijbehorende kardinaliteitsrichtingen:
static Orientation ToOrientation(Direction direction) => direction switch { Direction.Up => Orientation.North, Direction.Right => Orientation.East, Direction.Down => Orientation.South, Direction.Left => Orientation.West, _ => throw new ArgumentOutOfRangeException(direction.ToString()), }; public enum Direction { Up, Down, Right, Left } public enum Orientation { North, South, East, West }einde voorbeeld
12.12 Rekenkundige operatoren
12.12.1 Algemeen
De operatoren *, /, %, +en - worden de rekenkundige operatoren genoemd.
multiplicative_expression
: switch_expression
| multiplicative_expression '*' switch_expression
| multiplicative_expression '/' switch_expression
| multiplicative_expression '%' switch_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 dynamicen vindt de onderstaande resolutie plaats tijdens runtime met behulp van het runtimetype van die operanden met het type compileertijd dynamic.
12.12.2 Vermenigvuldigingsoperator
Voor een werking van het formulier x * ywordt 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
checkedcontext, als het product buiten het bereik van het resultaattype valt, wordt er eenSystem.OverflowExceptionopgeworpen. In eenuncheckedcontext worden overlopen niet gerapporteerd en worden belangrijke bovenste bits buiten het bereik van het resultaattype genegeerd.Vermenigvuldiging van zwevende 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
xenypositieve eindige waarden.zis het resultaat vanx * y, afgerond op de dichtstbijzijnde vertegenwoordigbare waarde. Als de grootte van het resultaat te groot is voor het doeltype,zoneindig is. Vanwege afronding kanznul zijn, ook al isxnochynul.+y-y+0-0+∞-∞NaN+x+z-z+0-0+∞-∞NaN-x-z+z-0+0-∞+∞NaN+0+0-0+0-0NaNNaNNaN-0-0+0-0+0NaNNaNNaN+∞+∞-∞NaNNaN+∞-∞NaN-∞-∞+∞NaNNaN-∞+∞NaNNaNNaNNaNNaNNaNNaNNaNNaN(Tenzij anders vermeld, betekent het gebruik van "" in de tabellen met drijvende komma in §12.12.12.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.OverflowExceptiongegenereerd. Vanwege afronding kan het resultaat nul zijn, ook al is geen operand nul. De schaal van het resultaat, vóór afronding, is gelijk aan 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 onverheven vooraf gedefinieerde vermenigvuldigingsoperatoren die hierboven zijn gedefinieerd, zijn ook vooraf gedefinieerd.
12.12.3 Operator Divisie
Voor een werking van het formulier x / ywordt 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.DivideByZeroExceptiongegooid.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
intoflongwaarde is en de rechteroperand–1is, treedt er een overloop op. In eencheckedcontext 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
xenypositieve eindige waarden.zis het resultaat vanx / y, afgerond op de dichtstbijzijnde vertegenwoordigbare waarde.+y-y+0-0+∞-∞NaN+x+z-z+∞-∞+0-0NaN-x-z+z-∞+∞-0+0NaN+0+0-0NaNNaN+0-0NaN-0-0+0NaNNaN-0+0NaN+∞+∞-∞+∞-∞NaNNaNNaN-∞-∞+∞-∞+∞NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNDecimaal deling:
decimal operator /(decimal x, decimal y);Als de waarde van de rechteroperand nul is, wordt er een
System.DivideByZeroExceptiongegooid. Als de grootte van de resulterende waarde te groot is om weer te geven in de decimale notatie, wordt er eenSystem.OverflowExceptiongegenereerd. 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 vanxminder 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.
Operator 12.12.4 Rest
Voor een werking van het formulier x % ywordt 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 % yis de waarde die wordt geproduceerd doorx – (x / y) * y. Alsynul is, wordt er eenSystem.DivideByZeroExceptionopgeworpen.Als de linkeroperand de kleinste
intoflongwaarde is en de rechteroperand–1is, wordt er eenSystem.OverflowExceptiongegenereerd als en alleen alsx / yeen 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
xenypositieve eindige waarden.zis het resultaat vanx % yen 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 (waarbijnhet gehele getal is dat het dichtst bijx / yligt).+y-y+0-0+∞-∞NaN+x+z+zNaNNaN+x+xNaN-x-z-zNaNNaN-x-xNaN+0+0+0NaNNaN+0+0NaN-0-0-0NaNNaN-0-0NaN+∞NaNNaNNaNNaNNaNNaNNaN-∞NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNDecimale restwaarde
decimal operator %(decimal x, decimal y);Als de waarde van de rechteroperand nul is, wordt er een
System.DivideByZeroExceptiongegooid. Het is implementatie-afhankelijk wanneer eenSystem.ArithmeticException(of een subklasse daarvan) wordt opgeworpen. Een conforme implementatie genereert geen uitzondering voorx % yin elk geval wanneerx / ygeen 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
Opgetilde (§12.4.8) vormen van de niet-opgetilde vooraf gedefinieerde restoperators die hierboven zijn gedefinieerd, zijn ook vooraf gedefinieerd.
12.12.5 Optellen operator
Voor een werking van het formulier x + ywordt 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 stringzijn, 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 yIn een
checked-context, als de som buiten het bereik van het resultaattype valt, wordt er eenSystem.OverflowExceptiongeworpen. In eenuncheckedcontext worden overlopen niet gerapporteerd en worden belangrijke bovenste bits buiten het bereik van het resultaattype genegeerd.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
xenyniet-nul eindige waarden en iszhet resultaat vanx + y. Alsxenydezelfde grootte maar tegengestelde tekens hebben, iszpositief nul. Alsx + yte groot is om aan te geven in het doeltype, iszeen oneindigheid met hetzelfde teken alsx + y.y+0-0+∞-∞NaNxzxx+∞-∞NaN+0y+0+0+∞–∞NaN-0y+0-0+∞-∞NaN+∞+∞+∞+∞+∞NaNNaN-∞-∞-∞-∞NaN-∞NaNNaNNaNNaNNaNNaNNaNNaNDecimaal 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.OverflowExceptiongegenereerd. 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
Ehet enumtype is enUhet onderliggende typeEis: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 tekenreekssamenvoegingnullis, wordt een lege tekenreeks vervangen. Anders wordt elke niet-stringoperand geconverteerd naar de tekenreeksweergave door de virtueleToStringmethode aan te roepen die is overgenomen van het typeobject. AlsToStringnullretourneert, 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
ToStringmethoden die impliciet worden aangeroepen tijdens de uitvoering, kunnen worden beïnvloed door regionale instellingen.einde voorbeeld
Het resultaat van de tekenreekssamenvoegingsoperator is een
stringdie bestaat uit de tekens van de linkeroperand, gevolgd door de tekens van de rechteroperand. De operator voor tekenreekssamenvoeging retourneert nooit eennullwaarde. Er kan eenSystem.OutOfMemoryExceptionworden geworpen 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
Dhet type gedelegeerde is:D operator +(D x, D y);Als de eerste operand
nullis, is het resultaat van de bewerking de waarde van de tweede operand (zelfs als dat ooknull). Als de tweede operandnullis, 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.12.6 en §21.6 voor voorbeelden van combinatie van gemachtigden. Omdat
System.Delegategeen gemachtigdentype is, is operator + niet gedefinieerd. eindnotitie
Opgeheven (§12.4.8) vormen van de niet-opgeheven vooraf gedefinieerde toevoegingsoperators zijn ook vooraf gedefinieerd.
12.12.6 Aftrekkingsoperator
Voor een werking van het formulier x – ywordt 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 xaf.
Aftrekken 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 yIn een
checkedcontext, als het verschil buiten het bereik van het resultaattype valt, wordt eenSystem.OverflowExceptiongeworpen. In eenuncheckedcontext worden overlopen niet gerapporteerd en worden belangrijke bovenste bits buiten het bereik van het resultaattype genegeerd.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
xenyniet-nul eindige waarden en iszhet resultaat vanx – y. Alsxenygelijk zijn, iszpositief nul. Alsx – yte groot is om aan te geven in het doeltype, iszeen oneindigheid met hetzelfde teken alsx – y.y+0-0+∞-∞NaNxzxx-∞+∞NaN+0-y+0+0-∞+∞NaN-0-y-0+0-∞+∞NaN+∞+∞+∞+∞NaN+∞NaN-∞-∞-∞-∞-∞NaNNaNNaNNaNNaNNaNNaNNaNNaN(In de bovenstaande tabel geven de
-yvermeldingen de negatie vanyaan, 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.OverflowExceptiongegenereerd. 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
Ehet enumtype is enUhet onderliggende typeEis: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 vanxeny, 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
Dhet type gedelegeerde is:D operator –(D x, D y);De semantiek is als volgt:
- Als de eerste operand
nullis, wordt het resultaat van de bewerkingnull. - Als de tweede operand
nullis, is het resultaat van de bewerking de waarde van de eerste operand. - Anders vertegenwoordigen beide operanden niet-lege aanroeplijsten (§21.2).
- Als de lijsten gelijk zijn, zoals bepaald door de gemachtigde gelijkheidsoperator (§12.14.9), is
nullhet resultaat van de bewerking . - 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.14.9), is
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.13 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 dynamicen 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 >> countwordt 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
<<verschuiftxnaar links met een aantal bits, zoals beschreven in de onderstaande uitleg.De bits met hoge volgorde buiten het bereik van het resultaattype van
xworden 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
>>verschuiftxnaar rechts door een aantal bits dat is berekend, zoals hieronder wordt beschreven.Wanneer
xvan het typeintoflongis, worden de bits met lage volgorde vanxverwijderd, worden de resterende bits naar rechts verplaatst en worden de lege bitposities in hoge volgorde ingesteld op nul alsxniet-negatief is en ingesteld op een alsxnegatief is.Wanneer
xvan het typeuintofulongis, worden de bits met lage volgorde vanxverwijderd, 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
xintofuintis, 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
xlongofulongis, 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
xeen variabele van het typeintis, wordt met de bewerkingunchecked ((int)((uint)x >> y))een logische verschuiving vanxuitgevoerd. einde voorbeeld
Opgeheven (§12.4.8) vormen van de niet-opgeheven, vooraf gedefinieerde shift-operatoren die hierboven zijn gedefinieerd, zijn ook vooraf gedefinieerd.
12.14 Relationele en typetestoperators
12.14.1 Algemeen
De operators ==, !=, <, >, <=, >=, isen 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
ismoet eerst worden getest als een typeen vervolgens als een expressie die meerdere tokens kan omvatten. In het geval dat de operand een expressie is, moet de patroonexpressie ten minste zo hoog zijn als shift_expression. eindnotitie
Opmerking: er is een ambiguïteit tussen het type en constant_pattern in een
relational_expressionaan de rechterkant vanis; een van beide kan een geldige parsering van een gekwalificeerde id zijn. In dat geval is het alleen opgelost als het niet kan worden verbonden als een type (voor compatibiliteit met eerdere versies van de taal), wordt het omgezet in het eerste gevonden (wat een constante of een type moet zijn). Deze dubbelzinnigheid is alleen aan de rechterkant van een dergelijke expressie aanwezig.
De is operator wordt beschreven in §12.14.12 en de as operator wordt beschreven in §12.14.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 dynamichebben.
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.14.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 falseis.
Opgeheven (§12.4.8) vormen van de niet-opgeheven vooraf gedefinieerde vergelijkingsoperatoren voor gehele getallen die hierboven zijn gedefinieerd, zijn ook vooraf gedefinieerd.
12.14.3 Vergelijkingsoperatoren voor drijvende 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 trueis. Voor elke twee operanden produceert x != y altijd hetzelfde resultaat als !(x == y). Wanneer een of beide operanden NaN zijn, produceren de <, >, <= en >= operators echter niet dezelfde resultaten als de logische negatie van de omgekeerde operator.
voorbeeld: als een van
xenyNaN is, wordtx < yfalse, 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.14.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 falseis. 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.14.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 ytrue zijn of als zowel x als yfalsezijn. Anders wordt het resultaat false.
Het resultaat van != is false als zowel x als ytrue zijn of als zowel x als yfalsezijn. Anders wordt het resultaat true. Wanneer de operanden van het type boolzijn, 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.14.6 Vergelijkingsoperatoren voor opsomming
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.14.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 nullzijn, terwijl operator !=true retourneert als en alleen als operator == met dezelfde operanden falsezou 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
nullen de andere operand is een waarde van een typeTwaarbijTeen type_parameter is waarvan het niet bekend is of het een waardetype is, en geen waardetypebeperking heeft.- Als tijdens runtime
Teen niet-null-waardetype is, dan zijn de resultaten van==enfalserespectievelijk!=entrue. - Als tijdens runtime
Teen null-waardetype is, wordt het resultaat berekend op basis van deHasValueeigenschap van de operand, zoals beschreven in (§12.14.10). - Als tijdens runtime
Teen verwijzingstype is, wordt het resultaattrueals de operand isnullen 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 == yofx != 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 typeobjectte casten.eindnotitie
Voorbeeld: In het volgende voorbeeld wordt gecontroleerd of een argument van een onbeperkt typeparameter
nullis.class C<T> { void F(T x) { if (x == null) { throw new ArgumentNullException(); } ... } }De
x == nullconstructie is toegestaan, ook al kanTeen niet-null-waardetype vertegenwoordigen en wordt het resultaat eenvoudig gedefinieerd om te wordenfalsewanneerTeen 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
objectte 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 FalseDe variabelen
sentverwijzen naar twee afzonderlijke tekenreeksexemplaren met dezelfde tekens. De eerste vergelijkingsuitvoer omdat de vooraf gedefinieerde operator voor gelijkheid van tekenreeksenTrue(§12.14.8) is geselecteerd wanneer beide operanden van het typestringzijn. De resterende vergelijkingen geven allemaalFalseals uitvoer omdat de overbelasting vanoperator ==in hetstring-type niet van toepassing is wanneer een van beide operanden een bindingstijdtype vanobjectheeft.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
Falseomdat de casts verwijzingen maken naar twee afzonderlijke exemplaren van geboxteintwaarden.einde voorbeeld
12.14.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-
nullverwijzingen 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.14.7, kunnen de operatoren voor gelijkheid van referentietypen worden gebruikt om tekenreeksverwijzingen te vergelijken in plaats van tekenreekswaarden. eindnotitie
12.14.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
nullis, zijn ze gelijk als en alleen als beidenullzijn. - Als de delegaten verschillende runtime-typen hebben, zijn ze nooit gelijk.
- Als beide gemachtigde instanties een aanroeplijst hebben (§21.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 bijbehorende vermelding in de aanroeplijst 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.21) met dezelfde (mogelijk lege) set vastgelegde buitenste variabelenexemplaren zijn toegestaan (maar niet vereist) gelijk te zijn.
Als de oplossing voor overbelasting van operatoren wordt omgezet in een operator voor gedelegeerde gelijkheid en de bindingstijdtypen van beide operanden delegeren zijn, zoals beschreven in §21 in plaats System.Delegatevan , en er geen identiteitsconversie is tussen de operandtypen van het bindingstype, treedt er een bindingstijdfout op.
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.14.10 Gelijkheidsoperatoren tussen typen null-waarden en de letterlijke waarde null
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.HasValueen de laatste twee formulieren worden omgezet in x.HasValue.
12.14.11 Tuple-gelijkheidsoperators
De tupel-gelijkheidsoperatoren worden paarsgewijs toegepast op de elementen van de tupel-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 yiis dezelfde gelijkheidsoperator van toepassing en wordt een resultaat van het type bool, dynamic, een type dat een impliciete conversie naar boolheeft, of een type dat de operatoren true en false definieert.
De tuple-gelijkheidsoperator x == y wordt als volgt geëvalueerd:
- De linkeroperand
xwordt geëvalueerd. - De rechteroperand
ywordt geëvalueerd. - Voor elk paar elementen
xienyiin lexicale volgorde:- De operator
xi == yiwordt geëvalueerd en het resultaat van het typeboolwordt op de volgende manier verkregen:- Als de vergelijking een
booloplevert, is dat het resultaat. - Anders, als de vergelijking een
dynamicoplevert, wordt de operatorfalseer 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
boolheeft. - Anders, als het type van de vergelijking een operator
falseheeft, dan wordt die operator aangeroepen en wordt de resulterendebool-waarde geïnverteerd met de logische negatieoperator (!).
- Als de vergelijking een
- Als de resulterende
boolfalseis, wordt er geen verdere evaluatie uitgevoerd en wordt het resultaat van de tuple-gelijkheidsoperatorfalse.
- De operator
- Als alle elementvergelijkingen
trueopleveren, wordt het resultaat van de tupel gelijkheidsoperatortrue.
De tuple-gelijkheidsoperator x != y wordt als volgt geëvalueerd:
- De linkeroperand
xwordt geëvalueerd. - De rechteroperand
ywordt geëvalueerd. - Voor elk paar elementen
xienyiin lexicale volgorde:- De operator
xi != yiwordt geëvalueerd en het resultaat van het typeboolwordt op de volgende manier verkregen:- Als de vergelijking een
booloplevert, is dat het resultaat. - Anders, als de vergelijking een
dynamicoplevert, wordt de operatortrueer dynamisch op aangeroepen en is de resulterende waardeboolhet resultaat. - Anders wordt die conversie toegepast als het type van de vergelijking een impliciete conversie naar
boolheeft. - Als het type van de vergelijking een operator
trueheeft, wordt deze operator aangeroepen en is de resulterende waardeboolhet resultaat.
- Als de vergelijking een
- Als de resulterende
booltrueis, wordt er geen verdere evaluatie uitgevoerd en wordt het resultaat van de tuple-gelijkheidsoperatortrue.
- De operator
- Als alle elementvergelijkingen
falseopleveren, wordt het resultaat van de tupel gelijkheidsoperatorfalse.
12.14.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.
12.14.12.1 De operator is-type
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 E is T wordt als volgt geëvalueerd:
- Als
Eeen anonieme functie of methodegroep is, treedt er een compilatietijdfout op. - Als
Teen null-referentietype (§8.9.3) is, treedt er een compilatietijdfout op. - Als
Ede letterlijkenullis of als de waarde vanEnullis, wordt het resultaatfalse. - Anders:
- Laat
Rhet runtimetype vanEzijn. - Laten we
Dals volgt worden afgeleid:R- Als
Reen null-waardetype is, isDhet onderliggende typeR. - Anders wordt
DR.
- Als
- Het resultaat is afhankelijk
Dvan enTals volgt:- Als
Teen verwijzingstype is, wordt het resultaattrueals:- er een identiteitsconversie bestaat tussen
DenT, of -
Dis een verwijzingstype en een impliciete verwijzingsconversie vanDtotTbestaat, of -
Dis een waardetype en een boksconversie vanDnaarTbestaat.
- er een identiteitsconversie bestaat tussen
- Als
Teen null-waarde type is, is het resultaattruealsDhet onderliggende type vanTis. - Als
Teen niet-null-waardetype is, wordt het resultaattruealsDenThetzelfde type zijn. - Anders wordt het resultaat
false.
- Als
- Laat
Door de gebruiker gedefinieerde conversies worden niet meegenomen door de operator is.
Opmerking: als de operator
istijdens 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
iskan als volgt worden begrepen in termen van compileertijdtypen en conversies, waarbijChet type compilatietijd vanEis:
- Als het compileertijdtype van
ehetzelfde is alsTof 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 vanEnaarT:
- Als
Cvan 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.- Als er anders een expliciete verwijzingsconversie (§10.3.5) of het uitpakken van conversie (§10.3.7) bestaat van
CofTalsCofTeen open type is (§8.4.3), worden runtimecontroles zoals hierboven uitgevoerd.- Anders is er geen referentie-, boks-, inpak- of uitpakconversie van
Enaar typeTmogelijk, en het resultaat van de bewerking isfalse. Een compiler kan optimalisaties implementeren op basis van het type compileertijd.eindnotitie
12.14.12.2 De operator is-patroon
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:
-
Ewijst geen waarde aan of heeft geen type. - Het patroon
Pis niet van toepassing (§11.2) op het typeT.
Elke single_variable_designation van het patroon introduceert een nieuwe lokale variabele die zeker is toegewezen (§9.4) wanneer de bijbehorende relational_expression test true.
12.14.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.8) genereert de as operator nooit een uitzondering. Als de aangegeven conversie niet mogelijk is, wordt de resulterende waarde null.
In een operatie van de vorm E as Tmoet 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 identiteitsconversie (§10.2.2), impliciete nullable-conversie (§10.2.6), impliciete verwijzingsconversie (§10.2.8), boxing-conversie (§10.2.9), expliciete nullable-conversie (§10.3.4), expliciete verwijzingsconversie (§10.3.5), of wrapping-conversie (§8.3.12) bestaat van
EtotT. - Het type
EofTis een open type. -
Eis 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 Edynamicis, 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
TvanGis een referentietype, omdat het de klassebeperking heeft. Het typeparameterUvanHis echter niet; vandaar dat het gebruik van de operatorasinHniet is toegestaan.einde voorbeeld
12.15 Logische operators
12.15.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 dynamichebben.
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.15.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.
Logische operatoren 12.15.3 Opsomming
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 Uen «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, vooraf gedefinieerde logische enumeratieoperators die hierboven zijn gedefinieerd, zijn ook vooraf gedefinieerd.
12.15.4 Booleaanse logische operators
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 ytruezijn. Anders wordt het resultaat false.
Het resultaat van x | y is true als x of ytrueis. Anders wordt het resultaat false.
Het resultaat van x ^ y is true als xtrue is en yfalseis, of xfalse is en y is true. Anders wordt het resultaat false. Wanneer de operanden van het type boolzijn, berekent de operator ^ hetzelfde resultaat als de operator !=.
12.15.5 Nullable Boolean & and | Exploitanten
Het nulbare booleaanse type bool? kan drie waarden, true, falseen null, vertegenwoordigen.
Net als bij de andere binaire operatoren zijn opgeheven vormen van de logische operatoren & en | (§12.15.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.16 Voorwaardelijke logische operators
12.16.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 && ykomt overeen met de bewerkingx & y, behalve datyalleen wordt geëvalueerd alsxniet isfalse. - De bewerking
x || ykomt overeen met de bewerkingx | y, behalve datyalleen wordt geëvalueerd alsxniet 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 truefalseretourneert enoperator falsefalseretourneert. 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 dynamichebben.
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 | yis geschreven. Dan
- Als overbelastingsresolutie geen enkele beste operator kan vinden, of als overbelastingsresolutie een van de vooraf gedefinieerde logische operatoren voor gehele getallen of logische booleaanse operatoren (§12.15.5) selecteert, treedt er een bindingstijdfout op.
- Als de geselecteerde operator een van de vooraf gedefinieerde logische booleaanse operatoren (§12.15.4) is, wordt de bewerking verwerkt zoals beschreven in §12.16.2.
- Anders is de geselecteerde operator een door de gebruiker gedefinieerde operator en wordt de bewerking verwerkt zoals beschreven in §12.16.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.16.3.
12.16.2 Booleaanse voorwaardelijke logische operators
Wanneer de operanden van && of || van het type boolzijn, of wanneer de operanden van typen zijn die geen toepasselijke operator & of operator |definiëren, maar impliciete conversies naar booldefiniëren, wordt de bewerking als volgt verwerkt:
- De bewerking
x && ywordt geëvalueerd alsx ? y : false. Met andere woorden,xwordt eerst geëvalueerd en geconverteerd naar typebool. Alsxvervolgens istrue, wordtygeëvalueerd en geconverteerd naar het typeboolen wordt dit het resultaat van de bewerking. Anders wordt het resultaat van de bewerkingfalse. - De bewerking
x || ywordt geëvalueerd alsx ? true : y. Met andere woorden,xwordt eerst geëvalueerd en geconverteerd naar typebool. Alsxvervolgens istrue, wordt het resultaat van de bewerkingtrue. Anders wordtygeëvalueerd en geconverteerd naar het typeboolen wordt dit het resultaat van de bewerking.
12.16.3 Door de gebruiker gedefinieerde 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 typeTen retourneert een resultaat van het typeT. -
Tbevat verklaringen vanoperator trueenoperator 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 && ywordt geëvalueerd alsT.false(x) ? x : T.&(x, y), waarbijT.false(x)een aanroep is van deoperator falsedie inTis gedeclareerd enT.&(x, y)een aanroep van de geselecteerdeoperator &is. Met andere woorden,xwordt eerst geëvalueerd enoperator falsewordt aangeroepen op het resultaat om te bepalen ofxzeker onwaar is. Alsxdan zeker onwaar is, is het resultaat van de bewerking de waarde die eerder is berekend voorx. Anders wordtygeëvalueerd en wordt de geselecteerdeoperator &aangeroepen op de waarde die eerder is berekend voorxen de waarde die is berekend vooryom het resultaat van de bewerking te produceren. - De bewerking
x || ywordt geëvalueerd alsT.true(x) ? x : T.|(x, y), waarbijT.true(x)een aanroep is van deoperator truedie inTis gedeclareerd enT.|(x, y)een aanroep van de geselecteerdeoperator |is. Met andere woorden,xwordt eerst geëvalueerd enoperator truewordt aangeroepen op het resultaat om te bepalen ofxzeker waar is. Alsxdan zeker waar is, is het resultaat van de bewerking de waarde die eerder is berekend voorx. Anders wordtygeëvalueerd en wordt de geselecteerdeoperator |aangeroepen op de waarde die eerder is berekend voorxen de waarde die is berekend vooryom 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.17 De operator null-samensnook
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 nietnullis, wordt het resultaat a; anders wordt het resultaat b. De bewerking evalueert b alleen wanneer anullis.
De null-coalescing-operator is rechts-associatief, wat betekent dat bewerkingen van rechts naar links worden gegroepeerd.
Voorbeeld: een expressie van het formulier
a ?? b ?? cwordt geëvalueerd alsa ?? (b ?? c). In het algemeen retourneert een expressie met de vormE1 ?? E2 ?? ... ?? ENde eerste van de operanden die niet-nullis, ofnullals alle operandennullzijn. 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 ?? bA₀, Aof 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:
- Indien
Aaanwezig en is een onbeheerd type (§8.8) of bekend dat het een niet-null-waardetype is, treedt er een compilatietijdfout op. - Als
Abestaat enbeen dynamische expressie is, wordt het resultaattypedynamic. Tijdens runtime wordtaeerst geëvalueerd. Alsaniet isnull, wordtageconverteerd naardynamicen wordt dit het resultaat. Anders wordtbgeëvalueerd en dit wordt het resultaat. - Anders, als
Abestaat en een null-waardetype is en er een impliciete conversie bestaat vanbnaarA₀, is het resultaattypeA₀. Tijdens runtime wordtaeerst geëvalueerd. Alsanietnullis, wordtanaar typeA₀uitgepakt, en wordt dit het resultaat. Anders wordtbgeëvalueerd en geconverteerd naar het typeA₀, en wordt dit het resultaat. - Als
Abestaat en er een impliciete conversie bestaat vanbnaarA, wordt het resultaattypeA. Tijdens runtime wordtaeerst geëvalueerd. Alsaniet isnull, wordtahet resultaat. Anders wordtbgeëvalueerd en geconverteerd naar het typeA, en wordt dit het resultaat. - Anders, als
Abestaat en het een nullbare waarde is,been typeBheeft en er een impliciete conversie bestaat vanA₀naarB, is het resultaattypeB. Tijdens runtime wordtaeerst geëvalueerd. Alsanietnullis, wordtauitgepakt naar typeA₀, geconverteerd naar typeB, en dit wordt vervolgens het resultaat. Anders wordtbgeëvalueerd en wordt het resultaat. - Als
been typeBheeft en er een impliciete conversie bestaat vanatotB, wordt het resultaattypeB. Tijdens runtime wordtaeerst geëvalueerd. Alsaniet isnull, wordtageconverteerd naar het typeBen wordt dit het resultaat. Anders wordtbgeëvalueerd en wordt het resultaat. - Anders zijn
aenbniet compatibel en treedt er een compilatiefout op.
voorbeeld van:
T M<T>(T a, T b) => a ?? b; string s = M(null, "text"); int i = M(19, 23);De typeparameter
Tvoor methodeMis onbegrensd. Daarom kan het typeargument een verwijzingstype of null-waardetype zijn, zoals wordt weergegeven in de eerste aanroep naarM. Het typeargument kan ook een niet-null-waardetype zijn, zoals wordt weergegeven in de tweede aanroep naarM. Wanneer het typeargument een niet-null-waardetype is, is de waarde van de expressiea ?? ba.einde voorbeeld
12.18 De operator voor het werpen 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.Exceptionen 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 het lichaam van een expressielichaam lambda of lid.
12.19 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
outargument_value in een argumentenlijst. - Als een eenvoudige verwijdering
_bestaande uit de linkerkant van een eenvoudige toewijzing (§12.23.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 als een variabele die gedeclareerd is met een declaration_expression wordt verwezen binnen de deconstructietoewijzing 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
s1toont zowel expliciete als impliciet getypte declaratie-expressies. Het afgeleide type vanb1isboolomdat het het type van de bijbehorende uitvoerparameter inM1is. De daaropvolgendeWriteLineheeft toegang toti1enb1, die zijn geïntroduceerd in het ingesloten bereik.De declaratie van
s2toont een poging omi2te gebruiken in de geneste aanroep naarM, wat niet is toegestaan, omdat de verwijzing optreedt in de lijst met argumenten waarini2is gedeclareerd. Aan de andere kant is de verwijzing naarb2in het laatste argument toegestaan, omdat deze plaatsvindt na het einde van de geneste lijst met argumenten waarb2is gedeclareerd.De declaratie van
s3toont 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 _); }Deze voorbeelden laten het gebruik van
var _zien om een impliciet getypte discard te bieden wanneer_niet beschikbaar is, omdat het een variabele in de buitenliggende scope aanwijst.einde voorbeeld
12.20 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.18) is niet toegestaan in een voorwaardelijke operator als ref deze 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 : ewordt 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 boolof een expressie van een type dat operator trueimplementeert. 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
dynamicis, 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
refaanwezig 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
xtypeXheeft enydanYheeft,- Als er een identiteitsconversie bestaat tussen
XenY, is het resultaat het meest voorkomende type expressies (§12.6.3.16). Als een van de typendynamicis, 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
XtotY, maar niet vanYtotX, isYhet type van de voorwaardelijke expressie. - Als er anders een impliciete opsommingsconversie (§10.2.4) bestaat van
XtotY, isYhet type voorwaardelijke expressie. - Als er anders een impliciete opsommingsconversie (§10.2.4) bestaat van
YtotX, isXhet type voorwaardelijke expressie. - Als er anders een impliciete conversie (§10,2) bestaat van
YtotX, maar niet vanXtotY, isXhet 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
xenyeen type heeft, en zowelxalsyimpliciet 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
bgeëvalueerd en wordt deboolwaarde vanbbepaald:- Als er een impliciete conversie van het type
bnaarboolbestaat, wordt deze impliciete conversie uitgevoerd om eenboolwaarde te produceren. - Anders wordt de
operator truedie door het typebwordt bepaald aangeroepen om eenbool-waarde te produceren.
- Als er een impliciete conversie van het type
- Als de
boolwaarde die in de bovenstaande stap wordt geproduceerd,trueis, wordtxgeëvalueerd en wordt de resulterende variabelereferentie het resultaat van de voorwaardelijke expressie. - Anders wordt
ygeë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
bgeëvalueerd en wordt deboolwaarde vanbbepaald:- Als er een impliciete conversie van het type
bnaarboolbestaat, wordt deze impliciete conversie uitgevoerd om eenboolwaarde te produceren. - Anders wordt de
operator truedie door het typebwordt bepaald aangeroepen om eenbool-waarde te produceren.
- Als er een impliciete conversie van het type
- Als de
boolwaarde die in de bovenstaande stap wordt geproduceerd,trueis, wordtxgeëvalueerd en geconverteerd naar het type voorwaardelijke expressie. Dit wordt het resultaat van de voorwaardelijke expressie. - Anders wordt
ygeëvalueerd en geconverteerd naar het type voorwaardelijke expressie. Dit wordt het resultaat van de voorwaardelijke expressie.
12.21 Anonieme functie-expressies
12.21.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 vanMvoidis (§12.8.13). Maar wanneer het wordt behandeld als een null_conditional_invocation_expression, mag het resultaattypevoidzijn. eindnotitie
Voorbeeld: het resultaattype van
List<T>.Reverseisvoid. 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.14.
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 omittedeinde 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.21.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.21.3 Anonieme functieteksten
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
thiseen 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 inxwaarxeen 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. - De hoofdtekst heeft toegang tot de buitenste variabelen (§12.21.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.21.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.21.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 tweeSummethoden. Elk heeft eenselectorargument, waarmee de waarde wordt geëxtraheerd die moet worden opgeteld uit een lijstitem. De geëxtraheerde waarde kan eenintof eendoublezijn en de resulterende som is eveneens eenintof eendouble.De
Summethoden 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.Sumzijn beideSummethoden van toepassing omdat de anonieme functied => d.UnitCountcompatibel is met zowelFunc<Detail,int>alsFunc<Detail,double>. Overbelastingsresolutie kiest echter de eersteSummethode omdat de conversie naarFunc<Detail,int>beter is dan de conversie naarFunc<Detail,double>.In de tweede aanroep van
orderDetails.Sumis alleen de tweedeSummethode van toepassing omdat de anonieme functied => d.UnitPrice * d.UnitCounteen waarde van het typedoubleproduceert. Overbelastingsresolutie kiest dus de tweedeSummethode voor die aanroep.einde voorbeeld
12.21.5 Anonieme functies en dynamische binding
Een anonieme functie kan geen ontvanger, argument of operand van een dynamisch gebonden bewerking zijn.
12.21.6 Buitenste variabelen
12.21.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.21.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 expressiebomen die zijn gemaakt op basis van de anonieme functie in aanmerking komen voor geheugenopruiming.
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
xwordt vastgelegd door de anonieme functie, en de levensduur vanxwordt ten minste verlengd totdat de gedelegeerde die is geretourneerd uitFin aanmerking komt voor afvalinzameling. Aangezien elke aanroep van de anonieme functie op hetzelfde exemplaar vanxwerkt, is de uitvoer van het voorbeeld:1 2 3einde 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 (§24.4), maar wordt in plaats daarvan beschouwd als een verplaatsbare variabele. Vastgelegde buitenste variabelen kunnen echter niet worden gebruikt in een fixed instructie (§24.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.21.6.3 Instantiatie 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
xdrie 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
xbuiten 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 aan elkaar zijn gekoppeld, is het mogelijk dat elke instantie dezelfde opslaglocatie gebruikt. 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 5Wanneer de declaratie van
xechter 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 5Houd 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 3einde voorbeeld
Het is mogelijk dat anonieme functiedelegen bepaalde vastgelegde variabelen delen, maar nog afzonderlijke exemplaren van anderen hebben.
Voorbeeld: bijvoorbeeld als
Fwordt 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
xvast, maar afzonderlijke exemplaren vany, en het resultaat is:1 1 2 1 3 1einde 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 10einde voorbeeld
12.21.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.
12.21.8 Implementatievoorbeeld
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.21.6.3) komt vervolgens overeen met het maken van een exemplaar van de door de 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.22 Query-expressies
12.22.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_clause* select_or_group_clause query_continuation?
;
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.22.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 "fromid" gevolgd door een token behalve ";", "=" of ",".
12.22.3 Vertaling van query-expressies
12.22.3.1 Algemeen
De C#-taal geeft de uitvoeringssemantiek van query-expressies niet op. In plaats daarvan worden query-expressies omgezet in aanroepen van methoden die voldoen aan het queryexpressiepatroon (§12.22.4). Queryexpressies worden met name omgezet in aanroepen van methoden met de naam Where, Select, SelectMany, Join, GroupJoin, OrderBy, OrderByDescending, ThenBy, ThenByDescending, GroupByen Cast. Deze methoden zijn naar verwachting voorzien van bepaalde handtekeningen en retourtypen, zoals beschreven in §12.22.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.22.3.8.
12.22.3.2 Query-expressies met vervolgen
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.22.3.3 Expliciete bereikvariabeletypen
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 cwordt omgezet in
from c in (customers).Cast<Customer>() where c.City == "London" select cde 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 typeArrayListzijn. eindnotitie
12.22.3.4 Degenerate query expressions
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 cwordt 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.22.3.6 en §12.22.3.7) verwijderen degenerate 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
Selectaan te roepen op de bron. Het is vervolgens aan de implementeerfuncties vanSelecten andere queryoperators om ervoor te zorgen dat deze methoden nooit het bronobject zelf retourneren. eindnotitie
12.22.3.5 Van, let, where, join and orderby clauses
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
xeen 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
xeen 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
xenydoor 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 oheeft 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.22.3.6 Componenten 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 cwordt simpelweg vertaald in
(customers).Where(c => c.City == "London")einde voorbeeld
12.22.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.Countrywordt omgezet in
(customers).GroupBy(c => c.Country, c => c.Name)einde voorbeeld
12.22.3.8 Transparante id's
Bepaalde vertalingen injecteren bereikvariabelen met transparante id'saangeduid 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 naar
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
xeen 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
xenydoor compiler gegenereerde id's zijn die anders onzichtbaar en niet toegankelijk zijn. einde voorbeeld
12.22.4 Het query-expressiepatroon
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 'shape' 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 methodenThenByenThenByDescendingalleen beschikbaar zijn op het resultaat van eenOrderByofOrderByDescending. eindnotitie
Opmerking: de aanbevolen vorm van het resultaat van
GroupBy: een reeks reeksen, waarbij elke binnenste reeks een extraKeyeigenschap 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.23 Toewijzingsoperatoren
12.23.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-uitdrukking is niet direct toegestaan als linkeroperand, maar kan wel plaatsvinden als een stap in de evaluatie van een deconstructerende 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.23.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 verw-toewijzingsoperator wordt beschreven in §12.23.3.
De toewijzingsoperatoren anders dan de = en = ref operator worden de operator voor samengestelde toewijzingengenoemd. Deze operators worden als volgt verwerkt:
- Voor de
??=-operator wordt alleen de rechteroperand geëvalueerd en het resultaat toegewezen aan de variabele, eigenschap of indexeerelement dat overeenkomt met de linkeroperand, als de waarde van de linkeroperandnullis. - Anders wordt de aangegeven bewerking uitgevoerd op de twee operanden en wordt de resulterende waarde toegewezen aan het element variabele, eigenschap of indexeerfunctie dat is opgegeven door de linkeroperand. De samengestelde toewijzingsoperatoren worden beschreven in §12.23.4.
De += operatoren met -= een expressie voor gebeurtenistoegang als de linkeroperand worden de operator voor gebeurtenistoewijzinggenoemd. Er is geen andere toewijzingsoperator geldig met gebeurtenistoegang als de linkeroperand. De gebeurtenistoewijzingsoperatoren worden beschreven in §12.23.5.
De toewijzingsoperatoren zijn rechts-associatief, wat betekent dat bewerkingen van rechts naar links worden gegroepeerd.
Voorbeeld: een expressie van het formulier
a = b = cwordt geëvalueerd alsa = (b = c). einde voorbeeld
12.23.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 dynamicheeft, 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 dynamicen 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
xeen tuple-expressie(x1, ..., xn)is enykan worden gedeconstrueerd tot een tuple-expressie(y1, ..., yn)metnelementen (§12.7), en elke toewijzing aanxivanyihet typeTiheeft, dan heeft de toewijzing het type(T1, ..., Tn). - Anders, als
xals variabele wordt geclassificeerd, de variabele nietreadonlyis,xeen typeTheeft enyeen impliciete conversie naarTheeft, dan heeft de toewijzing het typeT. - Als
xis geclassificeerd als een impliciet getypte variabele (bijvoorbeeld een impliciet getypeerde declaratie-expressie) enyeen typeTheeft, wordt het afgeleid type van de variabeleTen heeft de toewijzing het typeT. - Als
xis geclassificeerd als een eigenschap of indexeerfunctie, en deze een toegankelijke setmutator heeft,xeen typeTheeft, enyeen impliciete conversie naarTheeft, dan heeft de toewijzing het typeT. - Anders is de toewijzing niet geldig en treedt er een bindingstijdfout op.
De runtime-verwerking van een eenvoudige toewijzing van de vorm x = y met type T wordt uitgevoerd als een toewijzing aan x van y met type T, die bestaat uit de volgende recursieve stappen:
-
xwordt geëvalueerd als het nog niet eerder is geëvalueerd. - Als
xals variabele wordt geclassificeerd, wordtygeëvalueerd en indien nodig geconverteerd naarTvia een impliciete conversie (§10.2).- Als de variabele van
xeen matrixelement van een reference_typeis, wordt er een runtimecontrole uitgevoerd om ervoor te zorgen dat de waarde die wordt berekend voorycompatibel is met het matrixexemplaren waarvanxeen element is. De controle slaagt alsyisnullof als er een impliciete verwijzingsconversie (§10.2.8) bestaat van het type exemplaar waarnaar wordt verwezen doorynaar het werkelijke elementtype van het matrixexemplaren metx. Anders wordt er eenSystem.ArrayTypeMismatchExceptiongegooid. - De waarde die het resultaat is van de evaluatie en conversie van
ywordt opgeslagen op de locatie die is opgegeven door de evaluatie vanxen wordt als resultaat van de toewijzing opgeleverd.
- Als de variabele van
- Als
xis geclassificeerd als een eigenschap of indexertoegang:-
ywordt geëvalueerd en, indien nodig, geconverteerd naarTvia een impliciete conversie (§10,2). - De set accessor van
xwordt aangeroepen met de waarde die het resultaat is van de evaluatie en conversie vanyals waardeargument. - De waarde die voortkomt uit de evaluatie en conversie van
ywordt als het resultaat van de toewijzing geleverd.
-
- Als
xis geclassificeerd als een tuple(x1, ..., xn)met ariteitn:-
ywordt gedeconstrueerd metnelementen tot een tuple-expressiee. - een resultaat-tuple-
twordt gemaakt doorete converteren naarTmet behulp van een impliciete tupleconversie. - voor elke
xiin volgorde van links naar rechts wordt een toewijzing aanxivant.Itemiuitgevoerd, behalve dat dexiniet opnieuw worden geëvalueerd. -
twordt verkregen als gevolg van de toewijzing.
-
Opmerking: als het type compilatietijd van
xisdynamicen er een impliciete conversie is van het type compilatietijd vanynaardynamic, 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 vanBtotA. 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(); // ArrayTypeMismatchExceptionde laatste toewijzing zorgt ervoor dat een
System.ArrayTypeMismatchExceptionwordt geworpen omdat een verwijzing naar eenArrayListniet 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 verklaringen:
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.Aenr.Bzijn toegestaan omdatpenrvariabelen 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.Aenr.Bgeen variabelen zijn.einde voorbeeld
12.23.3 Verwijzingstoewijzing
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 rechter operand moet definitief zijn toegewezen op het punt van de ref-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 hetrefdeel 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= refde operator enb ? ref x : ref yde juiste operand zijn:ref int a = ref (b ? ref x : ref y);. Belangrijk is dat de expressieref bniet deel uitmaakt van die verklaring, ook al lijkt dat op het eerste gezicht zo. eindnotitie
12.23.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 dynamicen 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).
a ??= b is gelijk aan (T) (a ?? (a = b)), behalve dat a slechts eenmaal wordt geëvalueerd, waar T het type is wanneer het type van a dynamisch is, en anderszins is b het type van T.
Anders wordt een bewerking van het formulier x «op»= y verwerkt door overbelastingsresolutie van binaire operatoren (§12.4.5) toe te passen 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 datxslechts 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
xen alsyimpliciet wordt omgezet in het typexof als de operator een shift-operator is, wordt de bewerking geëvalueerd alsx = (T)(x «op» y), waarbijThet typexis, behalve datxslechts éé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» yde 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(), waarbijAeen methode is dieint[]retourneert enBenCmethoden zijn dieintretourneren, worden de methoden slechts eenmaal aangeroepen, in de volgordeA,B,C. einde voorbeeld
Wanneer de linkeroperand van een samengestelde toewijzing een eigenschapstoegang of indexertoegang is, moet de eigenschap of indexer 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, ushortof charis. 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; // OKde 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»= ywordt geëvalueerd alsx = x «op» yofx = (T)(x «op» y), omvatten de evaluatieregels impliciet verheven operatoren. eindnotitie
12.23.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.24 Expressie
Een expressie is een niet-toewijzingsuitdrukking of een toewijzing.
expression
: non_assignment_expression
| assignment
;
non_assignment_expression
: declaration_expression
| conditional_expression
| lambda_expression
| query_expression
;
12.25 Constante expressies
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
constleden van klasse-, struct- en interfacetypen. - Verwijzingen naar leden van opsommingstypen.
- Verwijzingen naar lokale constanten.
- Subexpressies tussen haakjes, die zelf constante expressies zijn.
- Cast-expressies.
-
checkedenuncheckedexpressies. -
nameofuitdrukkingen. - De vooraf gedefinieerde
+,-,!(logische negatie) en~unaire operatoren. - De vooraf gedefinieerde
+,-,*,/,%,<<,>>,&,|,^,&&,||,==,!=,<,>,<=en>=binaire operatoren. - De
?:voorwaardelijke operator. - De null-forgiving operator
!(§12.8.9). -
sizeofexpressies, mits het niet-beheerde type een van de typen is die zijn opgegeven in §24.6.9 waarvoorsizeofeen constante waarde wordt geretourneerd. - 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
nullwaarde.
Opmerking: andere conversies, waaronder boksen, uitpakken en impliciete verwijzingsconversies van niet-
nullwaarden, 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
iis een fout omdat een boksconversie is vereist. De initialisatie vanstris een fout omdat een impliciete verwijzingsconversie van een niet-nullwaarde 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 (§20.4)
- Standaardargumenten van parameterlijsten (§15.6.2)
-
caselabels van eenswitchverklaring (§13.8.3). -
goto caseverklaringen (§13.10.4) - Dimensielengten in een expressie voor het maken van een matrix (§12.8.17.4) met een initialisatiefunctie.
- Kenmerken (§23)
- 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, uintof ulong, mits de waarde van de constante expressie binnen het bereik van het doeltype valt.
12.26 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.20) volgt dezelfde regels als een boolean_expression, maar om redenen van operatorprioriteit wordt als een null_coalescing_expression geclassificeerd.
Een boolean_expressionE is vereist om als volgt een waarde van het type boolte 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 trueopEte vinden en die implementatie wordt toegepast tijdens runtime. - Als er geen dergelijke operator wordt gevonden, treedt er een bindingstijdfout op.
ECMA C# draft specification