Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
12.1 Allmänt
Ett uttryck är en sekvens med operatorer och operander. Den här satsen definierar syntaxen, ordningen för utvärdering av operander och operatorer samt innebörden av uttryck.
12.2 Uttrycksklassificeringar
12.2.1 Allmänt
Resultatet av ett uttryck klassificeras som något av följande:
- Ett värde. Varje värde har en associerad typ.
- En variabel. Om inget annat anges skrivs en variabel uttryckligen och har en associerad typ, nämligen variabelns deklarerade typ. En implicit typad variabel har ingen associerad typ.
- En null-literal. Ett uttryck med den här klassificeringen kan implicit konverteras till en referenstyp eller nullbar värdetyp.
- En anonym funktion. Ett uttryck med den här klassificeringen kan implicit konverteras till en kompatibel ombudstyp eller uttrycksträdstyp.
- En tupl. Varje tuppel har ett fast antal element, var och en med ett uttryck och ett valfritt tuppelns elementnamn.
- En egenskapsåtkomst. Varje egenskapsåtkomst har en associerad typ, nämligen egenskapens typ. Dessutom kan en egenskapsåtkomst ha ett associerat instansuttryck. När en åtkomstmetod för en instansegenskap anropas, blir resultatet av att utvärdera instansuttrycket den instans som representeras av this(§12.8.14).
- Åtkomst för indexering. Varje indexerares åtkomst har en associerad typ, nämligen indexerarens elementtyp. Dessutom har en indexerare ett associerat instansuttryck och en associerad argumentlista. När en accessor av en indexeråtkomst anropas blir resultatet av utvärderingen av instansuttrycket den instans som representeras av this(§12.8.14), och resultatet av utvärderingen av argumentlistan utgör parameterlistan för anropet.
- Ingenting. Detta inträffar när uttrycket är ett anrop av en metod med en returtyp av void. Ett uttryck som klassificeras som ingenting är endast giltigt i samband med en statement_expression (§13.7) eller som brödtexten i en lambda_expression (§12.20).
För uttryck som förekommer som underuttryck av större uttryck, med de antecknade begränsningarna, kan resultatet också klassificeras som något av följande:
- Ett namnområde. Ett uttryck med denna klassificering kan endast visas som vänster sida av en member_access (§12.8.7). I andra sammanhang orsakar ett uttryck som klassificeras som ett namnområde ett kompileringsfel.
- En typ. Ett uttryck med denna klassificering kan endast visas som vänster sida av en member_access (§12.8.7). I andra sammanhang orsakar ett uttryck som klassificeras som en typ ett kompileringsfel.
- En metodgrupp, som är en uppsättning överlagrade metoder som härrör från en medlemssökning (§12.5). En metodgrupp kan ha ett associerat instansuttryck och en argumentlista av associerad typ. När en instansmetod anropas blir resultatet av utvärderingen av instansuttrycket den instans som representeras av this(§12.8.14). En metodgrupp tillåts i en invocation_expression (§12.8.10) eller en delegate_creation_expression (§12.8.17.5) och kan implicit konverteras till en kompatibel delegattyp (§10.8). I andra sammanhang orsakar ett uttryck som klassificeras som en metodgrupp ett kompileringsfel.
- Tillgång till ett evenemang Varje händelseåtkomst har en associerad typ, nämligen typen av händelse. Dessutom kan en händelseåtkomst ha ett associerat instansuttryck. En händelseåtkomst kan visas som den vänstra operanden för operatorerna +=och-=(§12.22.5). I andra sammanhang orsakar ett uttryck som klassificeras som en händelseåtkomst ett kompileringsfel. När en åtkomstmetod för en instanshändelse anropas blir resultatet av utvärderingen av instansuttrycket den instans som representeras avthis(§12.8.14).
- Ett utkastsuttryck som kan användas i flera kontexter för att utlösa ett undantag i ett uttryck. Ett throw-uttryck kan konverteras genom en implicit konvertering till valfri typ.
En egenskapsåtkomst eller indexeråtkomst omklassificeras alltid som ett värde genom att utföra ett anrop av get-accessorn eller set-accessorn. Den specifika åtkomsten bestäms av kontexten för egenskapen eller indexerarens åtkomst: Om åtkomsten är målet för en tilldelning anropas den angivna accessorn för att tilldela ett nytt värde (§12.22.2). Annars anropas get-accessorn för att hämta det aktuella värdet (§12.2.2).
En instansåtkomst är en egenskapsåtkomst, en händelseåtkomst eller en indexerartillgång på en instans.
12.2.2 Uttrycksvärden
De flesta konstruktioner som omfattar ett uttryck kräver i slutändan att uttrycket anger ett värde. I sådana fall uppstår ett kompileringsfel om det faktiska uttrycket anger ett namnområde, en typ, en metodgrupp eller ingenting. Men om uttrycket anger en egenskapsåtkomst, en indexerareåtkomst eller en variabel ersätts värdet för egenskapen, indexeraren eller variabeln implicit:
- Värdet för en variabel är helt enkelt det värde som för närvarande lagras på lagringsplatsen som identifieras av variabeln. En variabel anses definitivt tilldelad (§9.4) innan dess värde kan erhållas, eller på annat sätt uppstår ett kompileringsfel.
- Värdet för ett egenskapsåtkomstuttryck hämtas genom att anropa egenskapens get-accessor. Om egenskapen inte har någon get-accessor uppstår ett kompileringsfel. Annars utförs ett funktionsmedlemsanrop (§12.6.6) och resultatet av anropet blir värdet för egenskapsåtkomstuttrycket.
- Värdet för ett indexerares åtkomstuttryck hämtas genom att anropa indexerarens get-accessor. Om indexeraren inte har någon get-accessor uppstår ett kompileringsfel. Annars utförs ett funktionsmedlemsanrop (§12.6.6) med argumentlistan associerad med indexerarens åtkomstuttryck, och resultatet av anropet blir värdet för indexerarens åtkomstuttryck.
- Värdet för ett tuppeluttryck erhålls genom att tillämpa en implicit tuppelkonvertering (§10.2.13) på typen av tuppeluttrycket. Det är ett fel att hämta värdet för ett tupppeluttryck som inte har någon typ.
12.3 Statisk och dynamisk bindning
12.3.1 Allmänt
Bindning är processen att avgöra vad en åtgärd refererar till, baserat på typ eller värde för uttryck (argument, operander, mottagare). Till exempel bestäms bindningen av ett metodanrop baserat på typen av mottagare och argument. Bindningen av en operator bestäms baserat på typen av dess operander.
I C# bestäms bindningen av en åtgärd vanligtvis vid kompileringstid, baserat på kompileringstidstypen för dess underuttryck. På samma sätt identifieras och rapporteras felet vid kompileringstillfället om ett uttryck innehåller ett fel. Den här metoden kallas statisk bindning.
Men om ett uttryck är ett dynamiskt uttryck (d.v.s. har typen dynamic) anger detta att alla bindningar som det deltar i ska baseras på dess körningstyp i stället för den typ som det har vid kompileringstid. Bindningen av en sådan åtgärd skjuts därför upp till den tidpunkt då åtgärden ska köras under körningen av programmet. Detta kallas dynamisk bindning.
När en åtgärd är dynamiskt bunden utförs liten eller ingen kontroll vid kompileringstillfället. Om körningsbindningen misslyckas, rapporteras fel som undantag under körning.
Följande åtgärder i C# omfattas av bindning:
- Medlemsåtkomst: e.M
- Metodanrop: e.M(e₁,...,eᵥ)
- Delegera anrop: e(e₁,...,eᵥ)
- Elementåtkomst: e[e₁,...,eᵥ]
- Skapa objekt: ny C(e₁,...,eᵥ)
- Överbelastade unary-operatorer: +,-,!(endast logisk negation),~,++,--,true,false
- Överlagrade binära operatorer: +,-,*,/,%,&,&&,|,||,??,^,<<,>>,==,!=,>,<,>=,<=
- Tilldelningsoperatorer: =,= ref,+=,-=,*=,/=,%=,&=,|=,^=,<<=,>>=,??=
- Implicita och explicita konverteringar
När inga dynamiska uttryck ingår är C# som standard statisk bindning, vilket innebär att kompileringstidstyperna för underuttryck används i urvalsprocessen. Men när en av underuttrycken i de åtgärder som anges ovan är ett dynamiskt uttryck, är åtgärden i stället dynamiskt bunden.
Det är ett kompileringsfel om ett metodanrop är dynamiskt bundet och någon av parametrarna, inklusive mottagaren, är indataparametrar.
12.3.2 Bindningstid
Statisk bindning sker vid kompileringstid, medan dynamisk bindning sker vid körning. I följande underrubriker refererar termen bindningstid antingen till kompileringstid eller körningstid, beroende på när bindningen sker.
Exempel: Följande illustrerar begreppen statisk och dynamisk bindning och bindningstid:
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 första två anropen är statiskt bundna: överbelastningen av
Console.WriteLineplockas baserat på kompileringstidstypen för deras argument. Bindningstiden är därför kompileringstid.Det tredje anropet är dynamiskt bundet: överlagringen av
Console.WriteLineväljs baserat på körningstypen för argumentet. Detta beror på att argumentet är ett dynamiskt uttryck – dess kompileringstidstyp är dynamisk. Bindningstiden för det tredje anropet är därför körtid.slutexempel
12.3.3 Dynamisk bindning
Det här underavsnittet är informativt.
Med dynamisk bindning kan C#-program interagera med dynamiska objekt, dvs. objekt som inte följer de normala reglerna i C#-typsystemet. Dynamiska objekt kan vara objekt från andra programmeringsspråk med olika typer av system, eller objekt som är programmatiskt konfigurerade för att implementera sin egen bindningssemantik för olika åtgärder.
Den mekanism med vilken ett dynamiskt objekt implementerar sin egen semantik är implementeringsdefinierad. Ett givet gränssnitt – återigen implementeringsdefinierat – implementeras av dynamiska objekt för att signalera till C#-körmiljön att de har särskild semantik. Så när åtgärder på ett dynamiskt objekt är dynamiskt bundna, tar deras egna bindningssemantik, snarare än C# som anges i den här specifikationen, över.
Syftet med dynamisk bindning är att tillåta interoperation med dynamiska objekt, men C# tillåter dynamisk bindning för alla objekt, oavsett om de är dynamiska eller inte. Detta möjliggör en smidigare integrering av dynamiska objekt, eftersom resultatet av åtgärder på dem inte alltid är dynamiska objekt, men fortfarande är av en typ som är okänd för programmeraren vid kompileringstiden. Dynamisk bindning kan också hjälpa till att eliminera felbenägen reflektionsbaserad kod även om inga objekt är inblandade är dynamiska objekt.
12.3.4 Typer av underuttryck
När en åtgärd är statiskt bunden anses typen av underuttryck (t.ex. en mottagare och ett argument, ett index eller en operand) alltid vara kompileringstidstypen för uttrycket.
När en åtgärd är dynamiskt bunden bestäms typen av underuttryck på olika sätt beroende på underuttryckens kompileringstidstyp:
- En underuttryck av kompileringstidstypen 'dynamic' anses ha samma typ som det faktiska värdet som uttrycket utvärderas till vid körningstid.
- En underuttryck vars kompileringstidstyp är en typparameter anses ha den typ som typparametern är bunden till vid körning
- Annars anses underuttrycket ha sin kompileringstidstyp.
12.4 Operatorer
12.4.1 Allmänt
Uttryck konstrueras från operander och operatorer. Operatorerna för ett uttryck anger vilka åtgärder som ska tillämpas på operanderna.
Exempel: Exempel på operatorer är
+,-,*,/ochnew. Exempel på operander är literaler, fält, lokala variabler och uttryck. slutexempel
Det finns tre typer av operatorer:
- Unära operatorer. De unära operatorerna tar en operand och använder antingen prefixnotation (till exempel –x) eller postfixnotation (till exempelx++).
- Binära operatorer. De binära operatorerna tar två operander och alla använder infix-notation (till exempel x + y).
- Ternär operator. Det finns bara en ternär operator, ?:, och den tar tre operander och använder infixnotation (c ? x : y).
Ordningen för utvärdering av operatorer i ett uttryck bestäms av operatorernas prioritet och associativitet av operatörerna (§12.4.2).
Operander i ett uttryck utvärderas från vänster till höger.
Exempel: I
F(i) + G(i++) * H(i)anropas metodenFmed det gamla värdet föri. MetodenGanropas med det gamla värdetioch slutligen anropas metodenHmed det nya värdet i. Detta är separat från och inte relaterat till operatorprioret. slutexempel
Vissa operatorer kan överbelastas. Operatoröverlagring (§12.4.3) tillåter att användardefinierade operatorimplementeringar specificeras för operationer där en eller båda operanderna är av en användardefinierad klass eller strukturtyp.
12.4.2 Operatorprioritet och associativitet
När ett uttryck innehåller flera operatorer, styr företrädet hos operatorerna ordningen i vilken de enskilda operatorerna utvärderas.
Obs: Uttrycket
x + y * zutvärderas till exempel somx + (y * z)eftersom operatorn*har högre prioritet än operatorn för binär+. slutkommentar
Prioriteten för en operator fastställs genom definitionen av dess associerade grammatikproduktion.
Obs: En additive_expression består till exempel av en sekvens med multiplicative_expressionavgränsade med operatorerna
+eller-, vilket ger operatorerna+och-lägre prioritet än operatorerna*,/och%. slutkommentar
Obs: I följande tabell sammanfattas alla operatorer i prioritetsordning från högsta till lägsta:
Delavsnitt kategori Operatorer §12.8 Primär x.yx?.yf(x)a[x]a?[x]x++x--x!newtypeofdefaultcheckeduncheckeddelegatestackalloc§12.9 Unär +-!x~^++x--x(T)xawait x§12.10 Räckvidd ..§12.11 Multiplikativ */%§12.11 Tillsats +-§12.12 Skifta <<>>§12.13 Relations- och typtestning <><=>=isas§12.13 Jämlikhet ==!=§12.14 Logiskt OCH &§12.14 Logisk XOR ^§12.14 Logiskt ELLER \|§12.15 Villkorsstyrd OCH &&§12.15 Villkorsstyrd ELLER \|\|§12.16 och §12.17 Null-sammanslagning och utkastsuttryck ??throw x§12.19 Villkorlig ?:§12.22 och §12.20 Tilldelnings- och lambda-uttryck == ref*=/=%=+=-=<<=>>=&=^=\|==>??=slutkommentar
När en operand inträffar mellan två operatorer med samma prioritet styr associativitet för operatorerna i vilken ordning åtgärderna utförs:
- Förutom tilldelningsoperatorerna, intervalloperatorn och null-sammankopplingsoperatorn är alla binära operatorer vänster-associativa, vilket innebär att åtgärder utförs från vänster till höger.
Exempel: x + y + zutvärderas som(x + y) + z. slutexempel
- Tilldelningsoperatorerna, null coalescing-operatorn och den villkorsstyrda operatorn (?:) är höger-associativa, vilket innebär att åtgärder utförs från höger till vänster.Exempel: x = y = zutvärderas somx = (y = z). slutexempel
- Intervalloperatorn är icke-associativ, vilket innebär att varken vänster eller höger operand för en intervalloperator kan vara en range_expression.
Exempel: Både x..y..zochx..(y..z)är ogiltiga, liksom..icke-associativa. slutexempel
Prioritet och associativitet kan styras med parenteser.
Exempel:
x + y * zmultiplicerar förstymedzoch lägger sedan till resultatet ix, men(x + y) * zlägger först tillxochyoch multiplicerar sedan resultatet medz. slutexempel
12.4.3 Operator-överladdning
Alla unary och binära operatorer har fördefinierade implementeringar. Dessutom kan användardefinierade implementeringar införas genom att inkludera operatordeklarationer (§15.10) i klasser och structs. Användardefinierade operatorimplementeringar har alltid företräde framför fördefinierade operatorimplementeringar: Endast när det inte finns några tillämpliga användardefinierade operatorimplementeringar beaktas de fördefinierade operatorimplementeringarna enligt beskrivningen i §12.4.4 och §12.4.5.
De överlagbara unära operatorerna är:
+ - !(endast logisk negation)~ ++ -- true false
Endast de operatorer som anges ovan kan överbelastas. I synnerhet är det inte möjligt att överbelasta den null-förlåtande operatorn (postfix !, §12.8.9) eller det oföränderliga indexet från slutoperatorn (prefix ^, (§12.9.6)).
Obs! Även om
trueochfalseinte används uttryckligen i uttryck (och därför inte ingår i prioritetstabellen i §12.4.2), betraktas de som operatorer eftersom de anropas i flera uttryckskontexter: Booleska uttryck (§12.25) och uttryck som omfattar villkorsstyrda (§12.19) och villkorsstyrda logiska operatorer (§12.15). slutkommentar
De överlagbara binära operatorerna är:
+ - * / % & | ^ << >> == != > < <= >=
Endast de operatorer som anges ovan kan överbelastas. I synnerhet går det inte att överbelasta medlemsåtkomst, metodanrop eller operatorerna .., , =, &&, ||??, ?:, =>checkeduncheckednewtypeofdefault, , , asoch .is
När en binär operator är överbelastad överbelastas även motsvarande sammansatta tilldelningsoperator, om någon, implicit.
Exempel: En överbelastning av operatorn
*är också en överbelastning av operatorn*=. Detta beskrivs ytterligare i §12.22. slutexempel
Själva tilldelningsoperatorn (=) kan inte överbelastas. En tilldelning utför alltid ett enkelt lager av ett värde i en variabel (§12.22.2).
Typomvandlingsoperationer, såsom (T)x, överlagras genom att tillhandahålla användardefinierade konverteringar (§10.5).
Obs: Användardefinierade konverteringar påverkar inte beteendet för operatorerna
iselleras. slutkommentar
Elementåtkomst, till exempel a[x], anses inte vara en överbelastningsbar operator. I stället stöds användardefinierad indexering via indexerare (§15.9).
I uttryck refereras operatorer med operator notation, och i deklarationer refereras operatorer med hjälp av funktionell notation. I följande tabell visas relationen mellan operator- och funktionella notationer för unära och binära operatorer. I den första posten anger «op» alla överlagbara unary prefixoperatorer. I det andra inlägget anger «op» de unära postfixoperatorerna ++ och --. I den tredje posten anger «op» alla överlagrbara binära operatorer.
Obs: För ett exempel på överladdning av
++och--operatorer, se §15.10.2. slutkommentar
| Funktionell notation | |
|---|---|
| «op» x | operator «op»(x) | 
| x «op» | operator «op»(x) | 
| x «op» y | operator «op»(x, y) | 
Användardefinierade operatordeklarationer kräver alltid att minst en av parametrarna är av den klass- eller structtyp som innehåller operatordeklarationen.
Note: Därför är det inte möjligt för en användardefinierad operator att ha samma signatur som en fördefinierad operator. slutkommentar
Användardefinierade operatordeklarationer kan inte ändra syntax, prioritet eller associativitet för en operator.
Exempel: Operatorn
/är alltid en binär operator, har alltid den prioritetsnivå som anges i §12.4.2och är alltid vänster-associativ. slutexempel
Obs: Även om det är möjligt för en användardefinierad operator att utföra alla beräkningar som den önskar, avråds starkt från implementeringar som ger andra resultat än de som intuitivt förväntas. Till exempel bör en implementering av operatorn
==jämföra de två operanderna för likhet och returnera ett lämpligtboolresultat. slutkommentar
Beskrivningarna av enskilda operatörer i §12.9 till och med §12.22 anger de fördefinierade implementeringarna av operatörerna och eventuella ytterligare regler som gäller för varje operatör. Beskrivningarna använder termerna unärt operator-överlastningslösning, binärt operator-överlastningslösning, numerisk befordranoch lyfta operatordefinitioner som finns i följande avsnitt.
12.4.4 Lösning för överbelastning av en unär operator
En operation av formen «op» x eller x «op», där «op» är en överlagringsbar unär operator, och x är ett uttryck av typen X, bearbetas på följande sätt:
- Den uppsättning användardefinierade operatorer som tillhandahålls av Xför åtgärdenoperator «op»(x)bestäms med hjälp av reglerna i §12.4.6.
- Om uppsättningen med användardefinierade kandidatoperatorer inte är tom blir detta uppsättningen kandidatoperatorer för åtgärden. I annat fall blir de fördefinierade binära operator «op»-implementeringarna, inklusive deras hissade former, de kandidatoperatorer som ska användas för operationen. De fördefinierade implementeringarna av en viss operator anges i beskrivningen av operatorn. De fördefinierade operatorerna som tillhandahålls av en uppräknings- eller delegattyp ingår endast i den här uppsättningen när bindningstidstypen – eller den underliggande typen om den är en nullbar typ – av antingen operand är uppräknings- eller delegattypen.
- Reglerna för överbelastningsmatchning i §12.6.4 tillämpas på uppsättningen kandidatoperatorer för att välja den bästa operatören med avseende på argumentlistan (x), och den här operatorn blir resultatet av överlagringsmatchningsprocessen. Om det inte går att välja en enda bästa operator uppstår ett bindningstidsfel.
12.4.5 Binär operatoröverbelastningsupplösning
En åtgärd i formuläret x «op» y, där «op» är en överlagbar binär operator, x är ett uttryck av typen Xoch y är ett uttryck av typen Y, bearbetas på följande sätt:
- Uppsättningen med användardefinierade operatorer som tillhandahålls av XochYför åtgärdenoperator «op»(x, y)bestäms. Uppsättningen består av föreningen av de kandidatoperatörer som tillhandahålls avXoch de kandidatoperatörer som tillhandahålls avY, vilka var och en bestäms med hjälp av reglerna i §12.4.6. För den kombinerade uppsättningen sammanfogas kandidaterna på följande sätt:- Om XochYär identitetsvertibla, eller omXochYhärleds från en gemensam bastyp, uppstår endast delade kandidatoperatorer i den kombinerade uppsättningen en gång.
- Om det finns en identitetskonvertering mellan XochYoch en operator«op»Ysom tillhandahålls avYhar samma returtyp som en«op»Xsom tillhandahålls avXsamt operandtyperna av«op»Yhar en identitetskonvertering till motsvarande operandtyper av«op»X, så sker endast«op»Xi uppsättningen.
 
- Om 
- Om uppsättningen med användardefinierade kandidatoperatorer inte är tom blir detta uppsättningen kandidatoperatorer för åtgärden. I annat fall blir de fördefinierade binära operator «op»-implementeringarna, inklusive deras hissade former, de kandidatoperatorer som ska användas för operationen. De fördefinierade implementeringarna av en viss operator anges i beskrivningen av operatorn. För fördefinierade uppräknings- och ombudsoperatorer är de enda operatorer som anses vara de som tillhandahålls av en uppräknings- eller delegattyp som är bindningstidstypen för en av operanderna.
- Reglerna för överbelastningsmatchning i §12.6.4 tillämpas på uppsättningen kandidatoperatorer för att välja den bästa operatören med avseende på argumentlistan (x, y), och den här operatorn blir resultatet av överlagringsmatchningsprocessen. Om det inte går att välja en enda bästa operator uppstår ett bindningstidsfel.
12.4.6 Kandidatanvändardefinierade operatorer
Med tanke på en typ T och en åtgärd operator «op»(A), där «op» är en överlagbar operator och A är en argumentlista, bestäms uppsättningen med kandidatanvändardefinierade operatorer som tillhandahålls av T för operatorn «op»(A) enligt följande:
- Fastställ typen T₀. OmTär en nullbar värdetyp ärT₀dess underliggande typ. annars ärT₀lika medT.
- För alla operator «op»deklarationer iT₀och alla upplyfta former av sådana aktörer, om minst en operatör är tillämplig (§12.6.4.2) med avseende på argumentlistanA, består uppsättningen av kandidatoperatörer av alla sådana tillämpliga operatörer iT₀.
- Om T₀ärobjectär annars uppsättningen med kandidatoperatorer tom.
- Annars är uppsättningen kandidatoperatorer som tillhandahålls av T₀uppsättningen kandidatoperatorer som tillhandahålls av den direkta basklassen förT₀eller den effektiva basklassen förT₀omT₀är en typparameter.
12.4.7 Numeriska kampanjer
12.4.7.1 Allmänt
Det här underavsnittet är informativt.
§12.4.7 och dess underrubriker är en sammanfattning av den kombinerade effekten av:
- reglerna för implicita numeriska konverteringar (§10.2.3);
- reglerna för bättre konvertering (§12.6.4.7); och
- de tillgängliga aritmetiska operatorerna (§12.11), relationsoperatorerna (§12.13) och integral logiska (§12.14.2).
Numerisk befordran består av att automatiskt utföra vissa implicita konverteringar av operanderna för de fördefinierade odefinierade och binära numeriska operatorerna. Numerisk promotion är inte en distinkt mekanism, utan snarare en effekt av att tillämpa överbelastningsresolution på de fördefinierade operatorerna. Specifikt påverkar numerisk befordran inte utvärderingen av användardefinierade operatorer, även om användardefinierade operatorer kan implementeras för att ge liknande effekter.
Som ett exempel på numerisk befordran bör du överväga de fördefinierade implementeringarna av den binära * operatorn:
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);
När regler för överbelastningsmatchning (§12.6.4) tillämpas på denna uppsättning operatorer är effekten att välja den första av de operatorer för vilka implicita konverteringar finns från operandtyperna.
Exempel: För åtgärden
b * s, därbär enbyteochsär enshort, väljer överlagringsmatchningoperator *(int, int)som den bästa operatorn. Effekten är därför attbochskonverteras tillint, och typen av resultat ärint. När det gäller operationeni * d, däriär enintochdär endouble, väljeroverloadresolutionoperator *(double, double)som den bästa operatören. slutexempel
Slut på informativ text.
12.4.7.2 Unära numeriska främjanden
Det här underavsnittet är informativt.
Unär numerisk befordran sker för operander av de fördefinierade +, -och ~ unära operatorerna. Unary numerisk befordran består helt enkelt av att konvertera operander av typen sbyte, byte, short, ushorteller char till typen int. Dessutom, för unär – operatorn konverterar unär numerisk promotion operander av typen uint till typen long.
Slut på informativ text.
12.4.7.3 Binära numeriska promotioner
Det här underavsnittet är informativt.
Binär numerisk befordran sker för operanderna i den fördefinierade +, -, *, /, %, &, |, ^, ==, !=, >, <, >=och <= binära operatorer. Binär numerisk befordran konverterar implicit båda operanderna till en gemensam typ som, om det gäller icke-relationsoperatorer, också blir resultattypen för åtgärden. Binär numerisk befordran består av att tillämpa följande regler i den ordning de visas här:
- Om antingen operand är av typen decimalkonverteras den andra operanden till typendecimaleller ett bindningstidsfel inträffar om den andra operanden är av typenfloatellerdouble.
- Om någon av operanderna är av typen doublekonverteras annars den andra operanden till typendouble.
- Om någon av operanderna är av typen floatkonverteras annars den andra operanden till typenfloat.
- Om annars någon av operanderna är av typen ulong, konverteras den andra operanden till typenulong, eller ett bindningstidsfel uppstår om den andra operanden är avtype sbyte,short,intellerlong.
- Om någon av operanderna är av typen longkonverteras annars den andra operanden till typenlong.
- Om antingen operand är av typen uintoch den andra operanden är av typensbyte,shortellerintkonverteras båda operanderna till typenlong.
- Om någon av operanderna är av typen uintkonverteras annars den andra operanden till typenuint.
- I annat fall konverteras båda operanderna till typen int.
Obs: Den första regeln tillåter inte åtgärder som blandar
decimaltyp med typernadoubleochfloat. Regeln följer av det faktum att det inte finns några implicita konverteringar mellandecimal-typen ochdouble- ochfloattyperna. slutkommentar
Obs: Observera också att det inte är möjligt för en operande att vara av typen
ulongnär den andra operanden är av en signerad integraltyp. Anledningen är att det inte finns någon integrerad typ som kan representera hela intervallet avulongsamt de signerade integraltyperna. slutkommentar
I båda fallen ovan kan ett cast-uttryck användas för att explicit konvertera en operande till en typ som är kompatibel med den andra operanden.
Exempel: I följande kod
decimal AddPercent(decimal x, double percent) => x * (1.0 + percent / 100.0);ett bindningstidsfel uppstår eftersom en
decimalinte kan multipliceras med endouble. Felet löses genom att den andra operanden uttryckligen konverteras tilldecimal, enligt följande:decimal AddPercent(decimal x, double percent) => x * (decimal)(1.0 + percent / 100.0);slutexempel
Slut på informativ text.
12.4.8 Upplyfta operatorer
Lifted-operatorer tillåter att fördefinierade och användardefinierade operatorer som körs på icke-nullbara värdetyper också kan användas med nullbara former av dessa typer. Hissade operatorer är konstruerade från fördefinierade och användardefinierade operatorer som uppfyller vissa krav, enligt beskrivningen i följande:
- För de unary operatorerna +,++,-,--,!(logisk negation),^, och~, finns det en hävd form av en operator om både operand- och resultattyperna är icke-nullbara värdetyper. Det upplyfta formuläret skapas genom att en enda?modifierare läggs till i operand- och resultattyperna. Den lyftande operatorn genererar ettnullvärde om operanden ärnull. Annars packar den lyftade operatorn upp operanden, tillämpar den underliggande operatorn och omsluter resultatet.
- För de binära operatorerna +, ,-*,/,%,&|,^,.., ,<<och>>, finns det en hävd form av en operator om operand- och resultattyperna är alla värdetyper som inte går att nolla. Det upplyfta formuläret konstrueras genom att en enda?-modifier läggs till varje operand- och resultattyp. Den lyftade operatorn genererar ettnullvärde om en eller båda operanderna ärnull(ett undantag är&typens operatorer och|, enligt beskrivningenbool?i §12.14.5). Annars packar den lyftade operatorn upp operanderna, tillämpar den underliggande operatorn och omsluter resultatet.
- För likhetsoperatorerna ==och!=finns det en upplyft form av en operator om operandtyperna båda är icke-nullbara värdetyper och om resultattypen ärbool. Det upplyfta formuläret skapas genom att en enda "?"-modifierare läggs till varje operandtyp. Den lyfta operatorn anser att tvånull-värden är lika, och att ettnull-värde inte är lika med något icke-nullvärde. Om båda operanderna inte ärnullskriver den lyftande operatorn upp operanderna och tillämpar den underliggande operatorn för att skapaboolresultat.
- För relationsoperatorerna <,>,<=och>=finns det en upplyft form av en operator om operandtyperna båda är icke-nullbara värdetyper och om resultattypen ärbool. Det upplyfta formuläret skapas genom att en enda "?"-modifierare läggs till varje operandtyp. Den upphöjda operatorn genererar värdetfalseom en eller båda av operanderna ärnull. Annars packar den lyftade operatorn upp operanderna och tillämpar den underliggande operatorn för att skapa ettbool-resultat.
12.5 Medlemssökning
12.5.1 Allmänt
En medlemssökning är den process där innebörden av ett namn i kontexten för en typ bestäms. En medlemssökning kan ske som en del av utvärderingen av en simple_name (§12.8.4) eller en member_access (§12.8.7) i ett uttryck. Om simple_name eller member_access förekommer som primary_expression av en invocation_expression (§12.8.10.2), sägs medlemmen vara anropad.
Om en medlem är en metod eller händelse, eller om det är ett konstant fält eller en egenskap av antingen en delegattyp (§21) eller typen dynamic (§8.2.4), sägs medlemmen vara anropsbar.
Medlemssökningen tar inte bara hänsyn till namnet på en medlem utan även antalet typparametrar som medlemmen har och om medlemmen är tillgänglig. För medlemssökning har generiska metoder och kapslade generiska typer antalet typparametrar som anges i respektive deklaration och alla andra medlemmar har nolltypsparametrar.
En medlemssökning av ett namn N med argument av K typ i en typ T bearbetas på följande sätt:
- Först fastställs en uppsättning tillgängliga medlemmar med namnet N:- Om Tär en typparameter är uppsättningen en union av uppsättningar med tillgängliga medlemmar med namnetNi var och en av de typer som anges som en primär begränsning eller sekundär begränsning (§15.2.5) förT, tillsammans med uppsättningen tillgängliga medlemmar med namnetNiobject.
- Annars består uppsättningen av alla tillgängliga (§7.5) medlemmar med namnet NiT, inklusive ärvda medlemmar och de tillgängliga medlemmarna med namnetNiobject. OmTär en konstruerad typ erhålls uppsättningen medlemmar genom att ersätta typargument enligt beskrivningen i §15.3.3. Medlemmar som har enoverride-modifiering exkluderas från uppsättningen.
 
- Om 
- Om Kär noll tas sedan alla kapslade typer vars deklarationer innehåller typparametrar bort. OmKinte är noll tas alla medlemmar med ett annat antal typparametrar bort. NärKär noll tas metoder med typparametrar inte bort, eftersom typinferensprocessen (§12.6.3) kan härleda typargumenten.
- Om medlemmen anropas tas sedan alla icke-anropbara medlemmar bort från uppsättningen.
- Därefter tas medlemmar som är dolda av andra medlemmar bort från uppsättningen. För varje medlem S.Mi uppsättningen, därSär den typ där medlemmenMdeklareras, tillämpas följande regler:- Om Mär en konstant, fält, egenskap, händelse eller uppräkningsmedlem tas alla medlemmar som deklarerats i en bastyp avSbort från uppsättningen.
- Om Mär en typdeklaration tas alla icke-typer som deklarerats i en bastyp avSbort från uppsättningen och alla typdeklarationer med samma antal typparametrar somMdeklarerade i en bastyp avStas bort från uppsättningen.
- Om Mär en metod tas alla icke-metodmedlemmar som deklarerats i en bastyp avSbort från uppsättningen.
 
- Om 
- Därefter tas gränssnittsmedlemmar som är dolda av klassmedlemmar bort från uppsättningen. Det här steget har bara en effekt om Tär en typparameter ochThar både en annan effektiv basklass änobjectoch en icke-tom effektiv gränssnittsuppsättning (§15.2.5). För varje medlemS.Mi uppsättningen, därSär den typ där medlemmenMdeklareras, tillämpas följande regler omSär en annan klassdeklaration änobject:- Om Mär en konstant, fält, egenskap, händelse, uppräkningsmedlem eller typdeklaration, tas alla medlemmar som deklareras i en gränssnittsdeklaration bort från uppsättningen.
- Om Mär en metod tas alla icke-metodmedlemmar som deklarerats i en gränssnittsdeklaration bort från uppsättningen och alla metoder med samma signatur somMsom deklareras i en gränssnittsdeklaration tas bort från uppsättningen.
 
- Om 
- Efter att ha tagit bort dolda medlemmar bestäms slutligen resultatet av sökningen: - Om uppsättningen består av en enskild medlem som inte är en metod är den här medlemmen resultatet av sökningen.
- Annars, om uppsättningen endast innehåller metoder, är den här gruppen med metoder resultatet av sökningen.
- Annars är sökningen tvetydig och ett bindningstidsfel inträffar.
 
För medlemssökningar i andra typer än typparametrar och gränssnitt och medlemssökningar i gränssnitt som är strikt enkelarv (varje gränssnitt i arvskedjan har exakt noll eller ett direkt basgränssnitt) är effekten av uppslagsreglerna helt enkelt att härledda medlemmar döljer basmedlemmar med samma namn eller signatur. Sådana enkelarvsökningar är aldrig tvetydiga. De tvetydigheter som eventuellt kan uppstå vid medlemssökningar i flera arvsgränssnitt beskrivs i §19.4.11.
Obs: Den här fasen står bara för en typ av tvetydighet. Om medlemssökningen resulterar i en metodgrupp kan ytterligare användning av metodgruppen misslyckas på grund av tvetydighet, till exempel enligt beskrivningen i §12.6.4.1 och §12.6.6.2. slutkommentar
12.5.2 Bastyper
För medlemssökning anses en typ T ha följande bastyper:
- Om TärobjectellerdynamicharTingen bastyp.
- Om Tär en enum_typeär bastyperna förTklasstypernaSystem.Enum,System.ValueTypeochobject.
- Om Tär en struct_typeär bastyperna förTklasstypernaSystem.ValueTypeochobject.Anmärkning: En nullable_value_type är en struct_type (§8.3.1). slutkommentar 
- Om Tär en class_typeär bastyperna förTbasklasserna förT, inklusive klasstypenobject.
- Om Tär en interface_typeär bastyperna förTbasgränssnitten förToch klasstypenobject.
- Om Tär en array_typeär bastyperna förTklasstypernaSystem.Arrayochobject.
- Om Tär en delegate_typeär bastyperna förTklasstypernaSystem.Delegateochobject.
12.6 Funktionsmedlemmar
12.6.1 Allmänt
Funktionella medlemmar är medlemmar som innehåller körbara instruktioner. Funktionsmedlemmar är alltid medlemmar av typer och kan inte vara medlemmar i namnområden. C# definierar följande kategorier av funktionsmedlemmar:
- Metoder
- Egenskaper
- Evenemang
- Indexerare
- Användardefinierade operatorer
- Instanskonstruktorer
- Statiska konstruktorer
- Slutförare
Med undantag för finalizers och statiska konstruktorer (som inte kan anropas explicit) körs -uttrycken i funktionsmedlemmar via funktionsmedlemsanrop. Den faktiska syntaxen för att skriva ett funktionsmedlemsanrop beror på den specifika funktionsmedlemstypen.
Argumentlistan (§12.6.2) för ett funktionsmedlemsanrop innehåller faktiska värden eller variabelreferenser för funktionsmedlemmens parametrar.
Anrop av generiska metoder kan använda typinferens för att fastställa vilken uppsättning typargument som ska skickas till metoden. Denna process beskrivs i §12.6.3.
Anrop av metoder, indexerare, operatorer och instanskonstruktorer använder överbelastningsmatchning för att avgöra vilken av en kandidatuppsättning funktionsmedlemmar som ska anropas. Denna process beskrivs i §12.6.4.
När en viss funktionsmedlem har identifierats vid bindningstid, eventuellt genom överlagringsupplösning, beskrivs den faktiska exekveringsprocessen för att anropa funktionsmedlemmen i §12.6.6.
Note: Följande tabell sammanfattar bearbetningen som sker i konstruktioner som omfattar de sex kategorier av funktionsmedlemmar som kan anropas uttryckligen. I tabellen
e,x,yochvalueanger uttryck som klassificerats som variabler eller värden,Tanger ett uttryck som klassificerats som en typ,Fär det enkla namnet på en metod ochPär det enkla namnet på en egenskap.
Konstruera Exempel Beskrivning Metodanrop F(x, y)Överbelastningsupplösning används för att välja den bästa metoden Fi den innehållande klassen eller structen. Metoden anropas med argumentlistan(x, y). Om metoden inte ärstaticär instansuttrycketthis.T.F(x, y)Överbelastningsupplösning används för att välja den bästa metoden Fi en klass eller en strukturT. Ett bindningstidsfel uppstår om metoden inte ärstatic. Metoden anropas med argumentlistan(x, y).e.F(x, y)Överbelastningsupplösning används för att välja den bästa metoden Fi klassen, struct eller gränssnittet som anges av typene. Ett bindningstidsfel uppstår om metoden ärstatic. Metoden anropas med instansuttrycketeoch argumentlistan(x, y).Egenskapsåtkomst PGet-accessorn för egenskapen Pi den innehållande klassen eller strukturen anropas. Ett kompileringsfel uppstår omPär skrivskyddad. OmPinte ärstaticär instansuttrycketthis.P = valueSet-accessorn för egenskapen Pi den innehållande klassen eller struct anropas med argumentlistan(value). Ett kompileringsfel uppstår omPär skrivskyddad. OmPinte ärstaticär instansuttrycketthis.T.PHämtaren för egenskapen Pi klassen eller strukturenTanropas. Ett kompileringsfel uppstår omPinte ärstaticeller omPär skrivskyddad.T.P = valueSet-accessorn för egenskapen Pi klassen eller struct-Tanropas med argumentlistan(value). Ett kompileringsfel uppstår omPinte ärstaticeller omPär skrivskyddad.e.PGet-accessorn för egenskapen Pi klassen, strukturen eller gränssnittet som anges av typenEanropas med instansuttryckete. Ett bindningstidsfel uppstår omPärstaticeller omPär skrivskyddad.e.P = valueSet-accessorn för egenskapen Pi klassen, struct eller gränssnittet som anges av typen avEanropas med instansuttrycketeoch argumentlistan(value). Ett kompileringstidfel uppstår omPärstaticeller omPär skrivskyddat.Händelseåtkomst E += valueTilläggsåtkomsten för händelsen Ei den innehållande klassen eller strukturen anropas. OmEinte ärstaticär instansuttrycketthis.E -= valueTa bort-accessorn för händelsen Ei den innehållande klassen eller structen anropas. OmEinte ärstaticär instansuttrycketthis.T.E += valueAdd-åtkomsten för händelsen Ei klassen eller strukturenTanropas. Ett bindningstidsfel uppstår omEinte ärstatic.T.E -= valueRemove-accessorn för händelsen Ei klassen eller strukturenTanropas. Ett bindningstidsfel uppstår omEinte ärstatic.e.E += valueTilläggsåtkomsten för händelsen Ei klassen, strukturen eller gränssnittet som ges av typenEanropas med instansuttryckete. Ett bindningstidsfel uppstår omEärstatic.e.E -= valueTa bort-accessorn för händelsen Ei klassen, strukturen eller gränssnittet som anges av typenEanropas med instansuttryckete. Ett bindningstidsfel uppstår omEärstatic.Indexerarens åtkomst e[x, y]Överbelastningsupplösning används för att välja den bästa indexeraren i klassen, struct eller gränssnittet som anges av typen e. Indexerarens get-accessor anropas med instansuttrycketeoch argumentlistan(x, y). Ett bindningstidsfel uppstår om indexeraren är skrivskyddad.e[x, y] = valueÖverbelastningsupplösning används för att välja den bästa indexeraren i klassen, struct eller gränssnittet som anges av typen e. Indexerarens set-tillgångsmetod anropas med instansuttrycketeoch argumentlistan(x, y, value). Ett bindningstidsfel uppstår om indexeraren är skrivskyddad.Operatoranrop -xÖverlagringsupplösning används för att välja den bästa unära operatorn i klassen eller strukturen som anges av typen x. Den valda operatorn anropas med argumentlistan(x).x + yÖverbelastningslösning används för att välja den bästa binära operatorn i klasserna eller strukturerna som ges av typerna av xochy. Den valda operatorn anropas med argumentlistan(x, y).Instanskonstruktoranrop new T(x, y)Överbelastningsupplösning används för att välja den bästa instanskonstruktorn i en klass eller struktur T. Instanskonstruktorn anropas med argumentlistan(x, y).slutkommentar
12.6.2 Argumentlistor
12.6.2.1 Allmänt
Varje funktionsmedlem och ombudsanrop innehåller en argumentlista som innehåller faktiska värden eller variabelreferenser för funktionsmedlemmens parametrar. Syntaxen för att ange argumentlistan för ett funktionsmedlemsanrop beror på funktionsmedlemskategorin:
- För exempelvis konstruktorer, metoder, indexerare och ombud anges argumenten som en argument_list, enligt beskrivningen nedan. För indexerare, när du anropar set-funktionen, innehåller argumentlistan dessutom det uttryck som anges som den högra operand för tilldelningsoperatorn.
Obs: Det här ytterligare argumentet används inte för överlagringslösning, bara under anrop av den angivna åtkomstmetoden. slutkommentar 
- För egenskaper är argumentlistan tom när du anropar get-accessorn och består av det uttryck som anges som rätt operand för tilldelningsoperatorn när du anropar den angivna accessorn.
- För händelser består argumentlistan av det uttryck som anges som den högra operanden för +=- eller-=operatorn.
- För användardefinierade operatorer består argumentlistan av den enskilda operanden för den unary operatorn eller de två operanderna för den binära operatorn.
Argumenten för egenskaper (§15.7) och händelser (§15.8) skickas alltid som värdeparametrar (§15.6.2.2). Argumenten för användardefinierade operatorer (§15.10) skickas alltid som värdeparametrar (§15.6.2.2) eller indataparametrar (§9.2.8). Argumenten för indexerare (§15.9) skickas alltid som värdeparametrar (§15.6.2.2), indataparametrar (§9.2.8), eller parametermatriser (§15.6.2.4). Utdata och referensparametrar stöds inte för dessa kategorier av funktionsmedlemmar.
Argumenten för en instanskonstruktor, metod, indexerare eller delegeringsanrop specificeras som en 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
    ;
En argument_list består av ett eller flera arguments, avgränsade med kommatecken. Varje argument består av en valfri argument_name följt av en argument_value. Ett argument med en argument_name kallas för ett namngivet argument, medan ett argument utan argument_name är ett positionsargument.
argument_value kan ha något av följande formulär:
- Ett uttryck, som anger att argumentet skickas som en värdeparameter eller omvandlas till en indataparameter och sedan skickas som det, enligt (§12.6.4.2 och beskrivs i §12.6.2.3.
- Nyckelordet inföljt av en variable_reference (§9.5), som anger att argumentet skickas som en indataparameter (§15.6.2.3.2). En variabel ska definitivt tilldelas (§9.4) innan den kan skickas som en indataparameter.
- Nyckelordet refföljt av en variable_reference (§9.5), som anger att argumentet skickas som en referensparameter (§15.6.2.3.3). En variabel ska definitivt tilldelas (§9.4) innan den kan skickas som referensparameter.
- Nyckelordet outföljt av en variable_reference (§9.5), som anger att argumentet skickas som en utdataparameter (§15.6.2.3.4). En variabel anses definitivt tilldelad (§9.4) efter ett funktionsmedlemsanrop där variabeln skickas som en utdataparameter.
Formuläret bestämmer parameteröverföringsläge för argumentet: värde, indata, referenseller utdata. Men som nämnts ovan kan ett argument med värdeöverföringsläge omvandlas till ett med indataöverföringsläge.
Att skicka ett flyktigt fält (§15.5.4) som indata, utdata eller referensparameter orsakar en varning, eftersom fältet inte kan behandlas som flyktigt av den anropade metoden.
12.6.2.2 Motsvarande parametrar
För varje argument i en argumentlista måste det finnas en motsvarande parameter i funktionsmedlemmen eller ombudet som anropas.
Parameterlistan som används i följande bestäms på följande sätt:
- För virtuella metoder och indexerare som definierats i klasser väljs parameterlistan från den första deklarationen eller åsidosättningen av funktionsmedlemmen som hittades när den började med mottagarens statiska typ och söker igenom dess basklasser.
- För partiella metoder används parameterlistan för den definierande partiella metoddeklarationen.
- För alla andra funktionsmedlemmar och ombud finns det bara en enda parameterlista, som är den som används.
Positionen för ett argument eller en parameter definieras som antalet argument eller parametrar som föregår det i argumentlistan eller parameterlistan.
Motsvarande parametrar för funktionsmedlemsargument upprättas på följande sätt:
- Argument i argument_list för instanskonstruktorer, metoder, indexerare och ombud: - Ett positionsargument där en parameter förekommer på samma plats i parameterlistan motsvarar den parametern, såvida inte parametern är en parametermatris och funktionsmedlemmen anropas i dess expanderade formulär.
- Ett positionsargument för en funktionsmedlem med en parametermatris som anropas i dess expanderade form, som inträffar vid eller efter positionen för parametermatrisen i parameterlistan, motsvarar ett element i parametermatrisen.
- Ett namngivet argument motsvarar parametern med samma namn i parameterlistan.
- När du anropar tilldelningsåtkomstgivaren för indexerare, motsvarar uttrycket som anges som den högra operanden för tilldelningsoperatorn den implicita parameter valuei tilldelningsåtkomstdeklarationen.
 
- För egenskaper finns det inga argument när du anropar get-accessorn. När du anropar den angivna åtkomstorn motsvarar uttrycket som anges som rätt operand för tilldelningsoperatorn den implicita värdeparametern för den angivna åtkomstdeklarationen.
- För användardefinierade unary-operatorer (inklusive konverteringar) motsvarar den enda operanden den enda parametern i operatordeklarationen.
- För användardefinierade binära operatorer motsvarar den vänstra operanden den första parametern och den högra operanden motsvarar den andra parametern i operatordeklarationen.
- Ett namnlöst argument motsvarar ingen parameter när det kommer efter ett namngivet argument som inte står på rätt plats eller ett namngivet argument som motsvarar en parameterfält.
Note: Detta förhindrar att void M(bool a = true, bool b = true, bool c = true);anropas avM(c: false, valueB);. Det första argumentet används utanför position (argumentet används i första positionen, men parametern med namnetcär i tredje position), så följande argument bör namnges. Med andra ord tillåts icke-avslutande namngivna argument endast när namnet och positionen resulterar i att samma motsvarande parameter hittas. slutkommentar
12.6.2.3 Körningsutvärdering av argumentlistor
Under körningsprocessen av ett funktionsmedlemsanrop (§12.6.6) utvärderas uttrycken eller variabelreferenserna för en argumentlista i ordning, från vänster till höger, enligt följande:
- Om parameterns överföringsläge är värde för ett värdeargument - argumentuttrycket utvärderas och en implicit konvertering (§10.2) till motsvarande parametertyp utförs. Det resulterande värdet blir det initiala värdet för värdeparametern i funktionsmedlemsanropet. 
- Annars är parameterns överföringsläge inställt på att vara indata. Om argumentet är en variabelreferens och det finns en identitetskonvertering (§10.2.2) mellan argumentets typ och parameterns typ blir den resulterande lagringsplatsen lagringsplatsen som representeras av parametern i funktionsmedlemsanropet. Annars skapas en lagringsplats med samma typ som motsvarande parameter. Argumentuttrycket utvärderas och en implicit konvertering (§10.2) till motsvarande parametertyp utförs. Det resulterande värdet lagras på lagringsplatsen. Lagringsplatsen representeras av indataparametern i funktionsmedlemsanropet. - Exempel: Givet följande deklarationer och metodanrop: - static void M1(in int p1) { ... } int i = 10; M1(i); // i is passed as an input argument M1(i + 5); // transformed to a temporary input argument- I - M1(i)-metodanropet skickas själva- isom ett indataargument, eftersom det klassificeras som en variabel och har samma typ- intsom indataparametern. I- M1(i + 5)-metodanropet skapas en namnlös- intvariabel, initieras med argumentets värde och skickas sedan som ett indataargument. Se §12.6.4.2 och §12.6.4.4.- slutexempel 
 
- För indata, utdata eller referensargument utvärderas variabelreferensen och den resulterande lagringsplatsen blir lagringsplatsen som representeras av parametern i funktionsmedlemsanropet. För indata eller referensargument ska variabeln definitivt tilldelas vid tidpunkten för metodanropet. Om variabelreferensen anges som ett utdataargument eller är ett matriselement i en reference_typeutförs en körningskontroll för att säkerställa att elementtypen för matrisen är identisk med parametertypen. Om den här kontrollen misslyckas utlöses en - System.ArrayTypeMismatchException.
Obs: Den här körningskontrollen krävs på grund av matriskovarians (§17.6). slutkommentar
Exempel: I följande kod
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 } }Det andra anropet av
Fgör att enSystem.ArrayTypeMismatchExceptionkastas eftersom den faktiska elementtypen avbärstringoch inteobject.slutexempel
Metoder, indexerare och instanskonstruktorer kan deklarera sin rätt-mest-parameter som en parametermatris (§15.6.2.4). Sådana funktionsmedlemmar anropas antingen i sin normala form eller i sin utökade form beroende på vilken som är tillämplig (§12.6.4.2):
- När en funktionsmedlem med en parametermatris anropas i sin normala form ska argumentet för parametermatrisen vara ett enda uttryck som implicit kan konverteras (§10.2) till parametermatristypen. I det här fallet fungerar parametermatrisen exakt som en värdeparameter.
- När en funktionsmedlem med en parametermatris anropas i dess expanderade form ska anropet ange noll eller fler positionsargument för parametermatrisen, där varje argument är ett uttryck som implicit är konvertibelt (§10.2) till elementtypen för parametermatrisen. I det här fallet skapar anropet en instans av parametermatristypen med en längd som motsvarar antalet argument, initierar elementen i matrisinstansen med de angivna argumentvärdena och använder den nyligen skapade matrisinstansen som det faktiska argumentet.
Uttrycken för en argumentlista utvärderas alltid i textordning.
Exempel: Således, exemplet
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++); } }genererar utdata
x = 0, y = 1, z = 2 x = 4, y = -1, z = 3slutexempel
När en funktionsmedlem med en parametermatris anropas i sitt expanderade formulär med minst ett expanderat argument bearbetas anropet som om ett matrisskapandeuttryck med en matrisinitierare (§12.8.17.4) infogades runt de expanderade argumenten. En tom matris skickas när det inte finns några argument för parametermatrisen. Det är ospecificerat om referensen som skickas är till en nyligen allokerad eller befintlig tom matris.
Exempel: Givet denna deklaration
void F(int x, int y, params object[] args);följande anrop av metodens expanderade form
F(10, 20, 30, 40); F(10, 20, 1, "hello", 3.0);motsvarar exakt
F(10, 20, new object[] { 30, 40 }); F(10, 20, new object[] { 1, "hello", 3.0 });slutexempel
När argument utelämnas från en funktionsmedlem med motsvarande valfria parametrar skickas standardargumenten för funktionsmedlemsdeklarationen implicit. (Detta kan innebära att du skapar en lagringsplats enligt beskrivningen ovan.)
Note: Eftersom dessa alltid är konstanta påverkar deras utvärdering inte de återstående argumenten. slutkommentar
12.6.3 Typinferens
12.6.3.1 Allmänt
När en generisk metod anropas utan att ange typargument försöker en typinferens process att härleda typargument för anropet. Förekomsten av typinferens gör det möjligt att använda en mer praktisk syntax för att anropa en generisk metod och gör att programmeraren kan undvika att ange redundant typinformation.
Exempel:
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> } }Genom typinferens bestäms typargumenten
intochstringfrån argumenten till metoden.slutexempel
Typinferens sker som en del av bindningstidsbearbetningen av ett metodanrop (§12.8.10.2) och äger rum före överbelastningsupplösningssteget för anropet. När en viss metodgrupp anges i ett metodanrop och inga typargument anges som en del av metodanropet tillämpas typinferens på varje generisk metod i metodgruppen. Om typinferensen lyckas används de härledda typargumenten för att fastställa typerna av argument för efterföljande överbelastningsmatchning. Om överbelastningslösning väljer en generisk metod att anropa, används de infererade typargumenten som typargument för anropet. Om typinferensen för en viss metod misslyckas deltar inte den metoden i överbelastningsmatchning. Felet med typinferens i sig orsakar inte ett bindningstidsfel. Det leder dock ofta till ett bindningstidsfel när överlagringslösningen misslyckas med att hitta några tillämpliga metoder.
Om varje angivet argument inte motsvarar exakt en parameter i metoden (§12.6.2.2), eller om det finns en icke-valfri parameter utan motsvarande argument, misslyckas slutsatsdragningen omedelbart. Anta annars att den generiska metoden har följande signatur:
Tₑ M<X₁...Xᵥ>(T₁ p₁ ... Tₓ pₓ)
Med ett metodanrop i formuläret M(E₁ ...Eₓ) är uppgiften av typen slutsatsdragning att hitta unika typargument S₁...Sᵥ för var och en av typparametrarna X₁...Xᵥ så att anropet M<S₁...Sᵥ>(E₁...Eₓ) blir giltigt.
Processen för typinferens beskrivs nedan som en algoritm. En konform kompilator kan implementeras med en alternativ metod, förutsatt att den når samma resultat i alla fall.
Under inferensprocessen fastställs varje typparameter Xᵢ antingen  till en viss typ Sᵢ eller förblir inte fastställda  med en associerad uppsättning begränsningar. Var och en av begränsningarna är av någon typ T. Initialt är varje typvariabel Xᵢ ofixerad med en tom uppsättning gränser.
Typinferens sker i faser. Varje fas försöker härleda typargument för fler typvariabler baserat på resultaten från föregående fas. Den första fasen gör vissa inledande slutsatsdragningar av gränser, medan den andra fasen korrigerar typvariabler till specifika typer och härleder ytterligare gränser. Den andra fasen kan behöva upprepas ett antal gånger.
Obs! Typinferens används också i andra sammanhang, bland annat för konvertering av metodgrupper (§12.6.3.15) och för att hitta den bästa gemensamma typen av uttryck (§12.6.3.16). slutkommentar
12.6.3.2 Den första fasen
För vart och ett av metodargumenten Eᵢgörs en indatatypsinferens (§12.6.3.7) från Eᵢ till motsvarande parametertyp Tⱼ.
12.6.3.3 Den andra fasen
Den andra fasen fortsätter på följande sätt:
- Alla ofixade typvariabler Xᵢsom inte är beroende av (§12.6.3.6) ärXₑfasta (§12.6.3.13).
- Om det inte finns några sådana typvariabler, är alla ofixerade typvariabler Xᵢfixerade för vilka alla följande villkor gäller:- Det finns minst en typvariabel Xₑsom beror påXᵢ
- 
              Xᵢhar en uppsättning gränser som inte är tomma
 
- Det finns minst en typvariabel 
- Om det inte finns några sådana typvariabler och det fortfarande finns ofixerade typvariabler misslyckas typinferensen.
- Annars, om det inte finns några ytterligare ofixerade typvariabler, lyckas typinferensen.
- För alla argument Eᵢmed motsvarande parametertypTⱼdär utdatatyperna (§12.6.3.5) innehåller ofixerade typvariablerXₑ, men indatatyperna (§12.6.3.4) inte, görs en slutsatsdragning av utdatatyp (§12.6.3.8) frånEᵢtillTⱼ. Sedan upprepas den andra fasen.
12.6.3.4 Indatatyper
Om E är en metodgrupp eller en implicit typ av anonym funktion och T är en delegattyp eller uttrycksträdstyp är alla parametertyper av Tindatatyper avEmed typenT.
12.6.3.5 Utdatatyper
Om E är en metodgrupp eller en anonym funktion och T är en delegat eller uttrycksträdtyp är returtypen för T en utdatatyp avEmed typenT.
12.6.3.6 Beroende
En  typvariabel som Xᵢär direkt beroende av en  typvariabel Xₑ om det för vissa argument Eᵥ med typ TᵥXₑ inträffar i en indatatyp av Eᵥ med typ Tᵥ och Xᵢ inträffar i en utdatatyp av Eᵥ med typen Tᵥ.
              Xₑ
              beror påXᵢ om Xₑär direkt beroende avXᵢ eller om Xᵢär direkt beroende avXᵥ och Xᵥär beroende avXₑ. Därför är "beroende av" den transitiva men inte reflexiva stängningen av "beror direkt på".
12.6.3.7 Slutsatsdragningar av indatatyp
En indatatypsinferens görs från ett uttryck Etill en typ T på följande sätt:
- Om Eär ett tupppeluttryck (§12.8.6) med aritetNoch elementEᵢ, ochTär en tuppelns typ med aritetNmed motsvarande elementtyperTₑellerTär en nullbar värdetypT0?ochT0är en tuppelns typ med aritetNsom har en motsvarande elementtypTₑ, görs enEᵢindatatypsinferens frånEᵢtillTₑ.
- Om Eär en anonym funktion görs en explicit parametertypinferens (§12.6.3.9) frånEtillT
- I annat fall, om Ehar en typUoch motsvarande parameter är en värdeparameter (§15.6.2.2) görs en lägre gränsinferens (§12.6.3.11) frånUtillT.
- Om det annars Efinns en typUoch motsvarande parameter är en referensparameter (§15.6.2.3.3) eller utdataparameter (§15.6.2.3.4) görs en exakt slutsatsdragning (§12.6.3.10) frånUtillT.
- Annars, om Ehar en typUoch motsvarande parameter är en indataparameter (§15.6.2.3.2) ochEär ett indataargument, görs en exakt slutsatsdragning (§12.6.3.10) frånUtillT.
- I annat fall, om Ehar en typUoch motsvarande parameter är en indataparameter (§15.6.2.3.2) görs en lägre bunden slutsats (§12.6.3.11) frånUtillT.
- Annars görs ingen slutsatsdragning för det här argumentet.
12.6.3.8 Slutsatsdragningar av utdatatyp
En typinferens för utdata görs från ett uttryck Etill en typ T på följande sätt:
- Om Eär ett tuppeln uttryck med arityNoch elementEᵢ, ochTär en tuppeln typ med arityNmed motsvarande elementtyperTₑellerTär en nullable värdetypT0?ochT0är en tuppeln typ med arityNsom har en motsvarande elementtypTₑ, sedan för varjeEᵢutdatatyp slutsats görs frånEᵢtillTₑ.
- Om Eär en anonym funktion med härledd returtypU(§12.6.3.14) ochTär en ombudstyp eller uttrycksträdtyp med returtypTₓ, görs en lägre inferens (§12.6.3.11) frånUtillTₓ.
- Om Eär en metodgrupp ochTär en ombudstyp eller uttrycksträdstyp med parametertyperT₁...Tᵥoch returtypTₓ, och överlagringslösning förEmed typernaT₁...Tᵥger en enda metod med returtypU, görs en lägre gränsinferens frånUtillTₓ.
- Om Eannars är ett uttryck med typenUgörs en lägre slutsatsdragningfrånUtillT.
- Annars görs inga slutsatsdragningar.
12.6.3.9 Explicita parametertypinferenser
En explicit parametertypsinferens görs från ett uttryck Eför att en typ T på följande sätt:
- Om Eär en explicit typ av anonym funktion med parametertyperU₁...UᵥochTär en delegattyp eller uttrycksträdtyp med parametertyperV₁...Vᵥgörs för varjeUᵢexakt slutsatsdragning (§12.6.3.10) frånUᵢtill motsvarandeVᵢ.
12.6.3.10 Exakta slutsatsdragningar
En exakt slutsatsdragningfrån en typ Utill en typ V görs på följande sätt:
- Om Vär en av de ofixeradeXᵢ, läggsUtill i uppsättningen av exakta gränser förXᵢ.
- Annars bestäms uppsättningar V₁...VₑochU₁...Uₑgenom att kontrollera om något av följande fall gäller:- 
              Vär en matristypV₁[...]ochUär en matristypU₁[...]av samma rangordning
- 
              Vär typenV₁?ochUär typenU₁
- 
              Vär en konstruerad typC<V₁...Vₑ>ochUär en konstruerad typC<U₁...Uₑ>
 Om något av dessa fall gäller görs en exakt slutsatsdragning från varjeUᵢtill motsvarandeVᵢ.
 
- 
              
- Annars görs inga slutsatsdragningar.
12.6.3.11 Lägre bundna slutsatsdragningar
En lägre bunden slutsats från en typ Utill en typ V görs på följande sätt:
- Om Vär en av de ofixeradeXᵢ, läggsUtill i uppsättningen av lägre gränser förXᵢ.
- Annars, om Vär typenV₁?ochUär typenU₁?, görs en lägre gränsinferens frånU₁tillV₁.
- Annars bestäms uppsättningar U₁...UₑochV₁...Vₑgenom att kontrollera om något av följande fall gäller:- 
              Vär en matristypV₁[...]ochUär en matristypU₁[...]av samma rangordning
- 
              Vär en avIEnumerable<V₁>,ICollection<V₁>,IReadOnlyList<V₁>>,IReadOnlyCollection<V₁>ellerIList<V₁>ochUär en endimensionell matristypU₁[]
- 
              Vär en konstrueradclass,struct,interfaceellerdelegatetypC<V₁...Vₑ>och det finns en unik typC<U₁...Uₑ>så attU(eller, omUär en typparameter, dess effektiva basklass eller någon medlem i dess effektiva gränssnittsuppsättning) är identisk med,inheritsfrån (direkt eller indirekt) eller implementerar (direkt eller indirekt)C<U₁...Uₑ>.
- (Begränsningen "unikhet" innebär att i det fall gränssnittet C<T>{} class U: C<X>, C<Y>{}görs ingen slutsatsdragning vid slutsatsdragning frånUtillC<T>eftersomU₁kan varaXellerY.)
 Om något av dessa fall gäller görs en slutsats från varjeUᵢtill motsvarandeVᵢenligt följande:
- Om Uᵢinte är känd för att vara en referenstyp görs en exakt slutsatsdragning
- Annars, om Uär en matristyp, görs en inferens för nedre gräns .
- Om VärC<V₁...Vₑ>beror annars slutsatsdragningen på parameterni-thtyp avC:- Om det är covariant görs en nedre gräns-inferens .
- Om den är kontravariant skapas en övre gränsinferens.
- Om den är invariant görs en exakt slutsatsdragning.
 
 
- 
              
- Annars görs inga slutsatsdragningar.
12.6.3.12 Övre-bundna slutsatsdragningar
En övergripande slutsats från en typ Utill en typ V görs på följande sätt:
- Om Vär en av de ofixeradeXᵢläggsUtill i uppsättningen av övre gränser förXᵢ.
- Annars bestäms uppsättningar V₁...VₑochU₁...Uₑgenom att kontrollera om något av följande fall gäller:- 
              Uär en matristypU₁[...]ochVär en matristypV₁[...]av samma rangordning
- 
              Uär en avIEnumerable<Uₑ>,ICollection<Uₑ>,IReadOnlyList<Uₑ>,IReadOnlyCollection<Uₑ>ellerIList<Uₑ>ochVär en endimensionell matristypVₑ[]
- 
              Uär typenU1?ochVär typenV1?
- 
              Uär enC<U₁...Uₑ>Vclass, struct, interfaceellerdelegatetyp somidenticaltill,inheritsfrån (direkt eller indirekt) eller implementerar (direkt eller indirekt) en unik typC<V₁...Vₑ>
- (Begränsningen "unikhet" innebär att med tanke på ett gränssnitt C<T>{} class V<Z>: C<X<Z>>, C<Y<Z>>{}görs ingen slutsatsdragning vid slutsatsdragning frånC<U₁>tillV<Q>. Slutsatsdragningar görs inte frånU₁till antingenX<Q>ellerY<Q>.)
 Om något av dessa fall gäller görs en slutsats från varjeUᵢtill motsvarandeVᵢenligt följande:
- Om Uᵢinte är känd för att vara en referenstyp görs en exakt slutsatsdragning
- Om Vär en matristyp görs annars en övre gränsinferens
- Om UärC<U₁...Uₑ>beror annars slutsatsdragningen på parameterni-thtyp avC:- Om den är covariant skapas en övre gränsinferens.
- Om det är kontravariant görs en undre gräns inferens.
- Om den är invariant görs en exakt slutsatsdragning.
 
 
- 
              
- Annars görs inga slutsatsdragningar.
12.6.3.13 Fixering
En  variabel av typen Xᵢ med en uppsättning gränser är fast på följande sätt:
- Uppsättningen av kandidattyperUₑbörjar som alla typer inom gränserna förXᵢ.
- Varje bindning till Xᵢgranskas i sin tur: För varje exakt bunden U avXᵢtas alla typerUₑsom inte är identiska medUbort från kandidatuppsättningen. För varje lägre bundenUavXᵢalla typerUₑsom det finns inte en implicit konvertering frånUtas bort från kandidatuppsättningen. För varje övre gräns U avXᵢalla typerUₑfrån vilka det finns inte en implicit konvertering tillUtas bort från kandidatuppsättningen.
- Om det bland de återstående kandidattyperna Uₑfinns en unik typVsom det finns en implicit konvertering till från alla andra kandidattyper, ärXᵢfast tillV.
- Annars misslyckas typinferensen.
12.6.3.14 Uppskjuten returtyp
Den härledda returtypen för en anonym funktion F används under typinferens och överbelastningsmatchning. Den härledda returtypen kan bara fastställas för en anonym funktion där alla parametertyper är kända, antingen för att de uttryckligen anges, tillhandahålls via en anonym funktionskonvertering eller härleds under typinferens på en omslutande generisk metodanrop.
Den härledda effektiva returtypen bestäms på följande sätt:
- Om innehållet i Fär ett -uttryck som har en typ, är den härledda effektiva returtypen förFsamma som typen för det uttrycket.
- Om brödtexten Fi är ett block och uppsättningen uttryck i blocketsreturnuttryck har en bästa gemensamma typT(§12.6.3.16), ärFden härledda effektiva returtypenT.
- Annars kan en effektiv returtyp inte härledas för F.
Den härledda returtypen bestäms på följande sätt:
- Om Fär asynkront och brödtextenFi antingen är ett uttryck klassificerat som ingenting (§12.2), eller ett block där ingareturnuttryck har uttryck, är«TaskType»den härledda returtypen (§15.14.1).
- Om Fär asynkron och har en härledd effektiv returtypTär«TaskType»<T>»den härledda returtypen (§15.14.1).
- Om Finte är asynkron och har en härledd effektiv returtypTär den härledda returtypenT.
- Annars går det inte att härleda en returtyp för F.
Exempel: Som ett exempel på typinferens som involverar anonyma funktioner bör du överväga
Selecttilläggsmetod som deklarerats i klassenSystem.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); } } } }Om
System.Linqnamnområdet importerades med ettusing namespace-direktiv och gav en klassCustomermed enNameegenskap av typenstringkan metodenSelectanvändas för att välja namnen på en lista över kunder:List<Customer> customers = GetCustomerList(); IEnumerable<string> names = customers.Select(c => c.Name);Anrop av utökningsmetod (§12.8.10.3) av
Selectbearbetas genom att anropet omskrivs till ett statiskt metodanrop:IEnumerable<string> names = Enumerable.Select(customers, c => c.Name);Eftersom typargument inte uttryckligen angavs används typinferens för att härleda typargumenten. Först är kundens argument relaterat till källparametern, vilket härleder
TSourcevaraCustomer. Med hjälp av inferensprocessen för anonym funktionstyp som beskrivs ovan fårctypCustomer, och uttrycketc.Nameär relaterat till returtypen för väljareparametern, vilket härlederTResultvarastring. Anropet är därmed likvärdigt medSequence.Select<Customer,string>(customers, (Customer c) => c.Name)och resultatet är av typen
IEnumerable<string>.I följande exempel visas hur inferens av anonym funktionstyp gör att typinformation kan "flöda" mellan argument i en allmän metodanrop. Givet följande metod och anrop:
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); } }Typ-inferens för anropet fortsätter enligt följande: Först kopplas argumentet "1:15:30" till värdeparametern, vilket härleder
Xatt varastring. Sedan får parametern för den första anonyma funktionen,s, den härledda typenstring, och uttrycketTimeSpan.Parse(s)är relaterat till returtypen förf1, och härlederYatt varaSystem.TimeSpan. Slutligen får parametern för den andra anonyma funktionen,t, den härledda typenSystem.TimeSpan, och uttryckett.TotalHoursär relaterat till returtypen förf2, och härlederZatt varadouble. Resultatet av anropet är därför av typendouble.slutexempel
12.6.3.15 Typinferens för konvertering av metodgrupper
Liknande anrop av generiska metoder ska typinferens också tillämpas när en metodgrupp M som innehåller en generisk metod konverteras till en viss delegattyp D (§10.8). Givet en metod
Tₑ M<X₁...Xᵥ>(T₁ x₁ ... Tₑ xₑ)
och metodgruppen M tilldelas till ombudstypen D uppgiften med typinferens är att hitta typargument S₁...Sᵥ så att uttrycket:
M<S₁...Sᵥ>
blir kompatibel (§21.2) med D.
Till skillnad från typinferensalgoritmen för generiska metodanrop finns det i det här fallet bara argument typer, inget argument uttryck. I synnerhet finns det inga anonyma funktioner och därför inget behov av flera inferensfaser.
I stället betraktas alla Xᵢ som ofixerade, och en  med lägre bindning görs från varje argumenttyp Uₑ av Dför att motsvarande parametertyp Tₑ för M. Om inga gränser hittades för någon av Xᵢ misslyckas typinferensen. Annars är alla Xᵢfastställda till motsvarande Sᵢ, vilket är resultatet av typinferens.
12.6.3.16 Hitta den vanligaste typen av uttryck
I vissa fall måste en vanlig typ härledas för en uppsättning uttryck. I synnerhet hittas elementtyperna för implicit typade matriser och returtyperna av anonyma funktioner med blockkroppar på detta sätt.
Den bästa vanliga typen för en uppsättning uttryck E₁...Eᵥ bestäms på följande sätt:
- En ny  typvariabel Xintroduceras.
- För varje uttryck Eiutförs en inferens för utdatatypen (§12.6.3.8) från den tillX.
- 
              Xär fast (§12.6.3.13), om möjligt, och den resulterande typen är den bästa gemensamma typen.
- Annars misslyckas slutsatsdragningen.
Note: Intuitivt motsvarar den här slutsatsen att anropa en metod
void M<X>(X x₁ ... X xᵥ)medEᵢsom argument och härledaX. slutkommentar
12.6.4 Överbelastningsupplösning
12.6.4.1 Allmänt
Överbelastningsmatchning är en bindningstidsmekanism för att välja den bästa funktionsmedlemmen att anropa givet en argumentlista och en uppsättning kandidatfunktionsmedlemmar. Överbelastningsmatchning väljer den funktionsmedlem som ska anropas i följande distinkta kontexter i C#:
- Anrop av en metod som nämns i en invocation_expression (§12.8.10).
- Anrop av en instanskonstruktor som namnges i en object_creation_expression (§12.8.17.2).
- Anrop av en indexerartillgång via en element_access (§12.8.12).
- Anrop av en fördefinierad eller användardefinierad operator som anges i ett uttryck (§12.4.4 och §12.4.5).
Var och en av dessa kontexter definierar uppsättningen kandidatfunktionsmedlemmar och listan över argument på sitt eget unika sätt. Till exempel innehåller uppsättningen kandidater för ett metodanrop inte metoder som markerats med åsidosättning (§12.5), och metoder i en basklass är inte kandidater om någon metod i en härledd klass är tillämplig (§12.8.10.2).
När kandidatfunktionens medlemmar och argumentlistan har identifierats är valet av den bästa funktionsmedlemmen detsamma i alla fall:
- För det första reduceras uppsättningen kandidatfunktionsmedlemmar till de funktionsmedlemmar som gäller för den angivna argumentlistan (§12.6.4.2). Om den här reducerade uppsättningen är tom uppstår ett kompileringsfel.
- Sedan utses den bästa funktionsmedlemmen bland de tillämpliga kandidatfunktionsmedlemmarna. Om uppsättningen endast innehåller en funktionsmedlem, är den funktionsmedlemmen den bästa. Annars är den bästa funktionsmedlemmen den funktionsmedlem som är bättre än alla andra funktionsmedlemmar med avseende på den angivna argumentlistan, förutsatt att varje funktionsmedlem jämförs med alla andra funktionsmedlemmar som använder reglerna i §12.6.4.3. Om det inte finns exakt en funktionsmedlem som är bättre än alla andra funktionsmedlemmar är funktionsmedlemsanropet tvetydigt och ett bindningstidsfel inträffar.
Följande underklasuler definierar de exakta betydelserna av termerna tillämplig funktionsmedlem och bättre funktionsmedlem.
12.6.4.2 Tillämplig funktionsmedlem
En funktionsmedlem sägs vara en tillämplig funktionsmedlem med avseende på en argumentlista A när allt följande är sant:
- Varje argument i Amotsvarar en parameter i funktionsmedlemsdeklarationen enligt beskrivningen i §12.6.2.2, högst ett argument motsvarar varje parameter, och alla parametrar som inget argument motsvarar är en valfri parameter.
- För varje argument i Aär parameteröverföringsläget för argumentet identiskt med parameteröverföringsläget för motsvarande parameter och- för en värdeparameter eller en parametermatris finns en implicit konvertering (§10.2) från argumentuttrycket till typen av motsvarande parameter, eller
- för en referens- eller utdataparameter finns det en identitetskonvertering mellan typen av argumentuttryck (om någon) och typen av motsvarande parameter, eller
- för en indataparameter när motsvarande argument har inmodifierare, finns det en identitetskonvertering mellan typen av argumentuttrycket (om det finns) och typen av motsvarande parameter, eller
- för en indataparameter när motsvarande argument utelämnar in-modifieraren finns det en implicit konvertering (§10.2) från argumentuttrycket till typen av motsvarande parameter.
 
För en funktionsmedlem som innehåller en parametermatris, om funktionsmedlemmen är tillämplig enligt ovanstående regler, sägs den vara tillämplig i dess normala formulär. Om en funktionsmedlem som innehåller en parametermatris inte är tillämplig i sin normala form kan funktionsmedlemmen i stället vara tillämplig i dess expanderade formulär:
- Det expanderade formuläret skapas genom att parametermatrisen i funktionsmedlemsdeklarationen ersätts med noll eller fler värdeparametrar av elementtypen för parametermatrisen så att antalet argument i argumentlistan Amatchar det totala antalet parametrar. OmAhar färre argument än antalet fasta parametrar i funktionsmedlemsdeklarationen kan inte funktionsmedlemmens utökade form konstrueras och är därför inte tillämplig.
- I annat fall gäller det expanderade formuläret om något av följande gäller för varje argument i A:- parameteröverföringsläget för argumentet är identiskt med parameteröverföringsläget för motsvarande parameter och: - För en parameter med fast värde eller en värdeparameter som skapats av expansionen finns en implicit konvertering (§10.2) från argumentuttrycket till typen av motsvarande parameter. eller
- för en bireferensparameter är typen av argumentuttryck identisk med typen av motsvarande parameter.
 
- parameteröverföringsläget för argumentet är värde och parameteröverföringsläget för motsvarande parameter är inmatning, och en implicit konvertering (§10.2) finns från argumentuttrycket till typen av motsvarande parameter.
 
- parameteröverföringsläget för argumentet är identiskt med parameteröverföringsläget för motsvarande parameter och: 
När den implicita konverteringen från argumenttypen till parametertypen för en indataparameter är en dynamisk implicit konvertering (§10.2.10) är resultatet odefinierat.
Exempel: Givet följande deklarationer och metodanrop:
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 }slutexempel
- En statisk metod är endast tillämplig om metodgruppen är resultatet av ett simple_name eller en member_access via en typ.
- En instansmetod är endast tillämplig om metodgruppen är resultatet av en simple_name, en member_access via en variabel eller ett värde eller en base_access.
- Om metodgruppen är resultatet av en simple_nameär en instansmetod endast tillämplig om thisåtkomst tillåts §12.8.14.
 
- Om metodgruppen är resultatet av en simple_nameär en instansmetod endast tillämplig om 
- När metodgruppen är resultatet av en member_access som kan ske antingen via en instans eller en typ enligt beskrivningen i §12.8.7.2, gäller både instanser och statiska metoder.
- En allmän metod vars typargument (uttryckligen anges eller härleds) uppfyller inte alla deras begränsningar är inte tillämpligt.
- I samband med en metodgruppkonvertering ska det finnas en identitetskonvertering (§10.2.2) eller en implicit referenskonvertering (§10.2.8) från metodens returtyp till ombudets returtyp. Annars är kandidatmetoden inte tillämplig.
12.6.4.3 Bättre funktionsmedlem
För att fastställa den bättre funktionsmedlemmen skapas en avskalad argumentlista A som bara innehåller argumentuttrycken i den ordning de visas i den ursprungliga argumentlistan och utelämnar alla out eller ref argument.
Parameterlistor för var och en av kandidatfunktionsmedlemmarna skapas på följande sätt:
- Det expanderade formuläret används om funktionsmedlemmen endast var tillämplig i det expanderade formuläret.
- Valfria parametrar utan motsvarande argument tas bort från parameterlistan
- Referens- och utdataparametrar tas bort från parameterlistan
- Parametrarna sorteras om så att de sker på samma position som motsvarande argument i argumentlistan.
Med tanke på en argumentlista A med en uppsättning argumentuttryck {E₁, E₂, ..., Eᵥ} och två tillämpliga funktionsmedlemmar Mᵥ och Mₓ med parametertyper {P₁, P₂, ..., Pᵥ} och {Q₁, Q₂, ..., Qᵥ}, definieras Mᵥ som en bättre funktionsmedlem än Mₓ om
- för varje argument är den implicita konverteringen från EᵥtillQᵥinte bättre än den implicita konverteringen frånEᵥtillPᵥ, och
- för minst ett argument är konverteringen från EᵥtillPᵥbättre än konverteringen frånEᵥtillQᵥ.
Om parametertypsekvenserna {P₁, P₂, ..., Pᵥ} och {Q₁, Q₂, ..., Qᵥ} är likvärdiga (d.v.s. varje Pᵢ har en identitetskonvertering till motsvarande Qᵢ) tillämpas följande regler för att fastställa den bättre funktionsmedlemmen.
- Om Mᵢär en icke-generisk metod ochMₑär en allmän metod ärMᵢbättre änMₑ.
- Annars, om Mᵢär tillämpligt i sin normala form ochMₑhar en params-matris och endast är tillämplig i dess expanderade form, ärMᵢbättre änMₑ.
- Annars, om båda metoderna har params-matriser och endast är tillämpliga i deras expanderade former, och om params-matrisen för Mᵢhar färre element än params-matrisen förMₑ, ärMᵢbättre änMₑ.
- Om Mᵥannars har mer specifika parametertyper änMₓärMᵥbättre änMₓ. Låt{R1, R2, ..., Rn}och{S1, S2, ..., Sn}representera de oinstifierade och oexpandererade parametertyperna förMᵥochMₓ.Mᵥparametertyper är mer specifika änMₓomRxför varje parameter inte är mindre specifik änSxoch för minst en parameter ärRxmer specifik änSx:- En typparameter är mindre specifik än en icke-typparameter.
- Rekursivt är en konstruerad typ mer specifik än en annan konstruerad typ (med samma antal typargument) om minst ett typargument är mer specifikt och inget typargument är mindre specifikt än motsvarande typargument i det andra.
- En matristyp är mer specifik än en annan matristyp (med samma antal dimensioner) om elementtypen för den första är mer specifik än elementtypen för den andra.
 
- Om en medlem är en icke-lyftad operatör medan den andra är en lyftad operatör, är den icke-lyftade det bättre valet.
- Om ingen funktionsmedlem visade sig vara bättre och alla parametrar i Mᵥhar ett motsvarande argument medan standardargument måste ersättas med minst en valfri parameter iMₓärMᵥbättre änMₓ.
- Om Mᵥför minst en parameter använder bättre parameteröverföringsalternativ (§12.6.4.4) än motsvarande parameter iMₓoch ingen av parametrarna iMₓanvända det bättre parameteröverföringsalternativet änMᵥärMᵥbättre änMₓ.
- Annars är ingen funktionsmedlem bättre.
12.6.4.4 Bättre parameteröverföringsläge
Det är tillåtet att ha motsvarande parametrar i två överlagrade metoder som endast skiljer sig åt i parameteröverföringsläget, förutsatt att en av de två parametrarna har värdeöverföringsläge enligt följande:
public static void M1(int p1) { ... }
public static void M1(in int p1) { ... }
Givet int i = 10;, enligt §12.6.4.2, resulterar samtalen M1(i) och M1(i + 5) i att båda överbelastningarna är tillämpliga. I sådana fall är metoden med parameteröverföringsläget "värde" det bättre valet av parameteröverföringsläge.
Obs: Det finns inget sådant val för argument för indata, utdata eller referensöverföringslägen, eftersom dessa argument endast matchar exakt samma parameteröverföringslägen. slutkommentar
12.6.4.5 Bättre konvertering från uttryck
Med en implicit konvertering C₁ som konverterar från ett uttryck E till en typ T₁och en implicit konvertering C₂ som konverterar från ett uttryck E till en typ T₂är C₁ en bättre konvertering än C₂ om något av följande gäller:
- 
              Ematchar exaktT₁ochEmatchar inte exaktT₂(§12.6.4.6)
- 
              Eexakt matchar både eller inget avT₁ochT₂, ochT₁är ett bättre konverteringsmål änT₂(§12.6.4.7)
- 
              Eär en metodgrupp (§12.2),T₁är kompatibel (§21.4) med den enda bästa metoden från metodgruppen för konverteringC₁ochT₂är inte kompatibel med den enda bästa metoden från metodgruppen för konverteringC₂
12.6.4.6 Exakt matchande uttryck
Med ett uttryck E och en typ Tmatchar EexaktT om något av följande gäller:
- 
              Ehar en typSoch det finns en identitetskonvertering frånStillT
- 
              Eär en anonym funktion ärTantingen en ombudstypDeller en uttrycksträdstypExpression<D>och något av följande gäller:- Det finns en härledd returtyp Xför i kontexten förEparameterlistanD(§12.6.3.13) och det finns en identitetskonvertering frånXreturtypen tillD
- 
              Eär enasynclambda utan returvärde ochDhar en returtyp som är en icke-generisk«TaskType»
- Antingen Eär icke-asynkron ochDhar en returtypYellerEär asynkron ochDhar en returtyp«TaskType»<Y>(§15.14.1) och något av följande gäller:- Kroppen i Eär ett uttryck som matchar exaktY
- Kroppen i Eär ett block där varje retursats returnerar ett uttryck som exakt matcharY
 
- Kroppen i 
 
- Det finns en härledd returtyp 
12.6.4.7 Bättre konverteringsmål
Med två typer T₁ och T₂är T₁ ett bättre konverteringsmål än T₂ om något av följande gäller:
- Det finns en implicit konvertering från T₁tillT₂och det finns ingen implicit konvertering frånT₂tillT₁
- 
              T₁är«TaskType»<S₁>(§15.14.1),T₂är«TaskType»<S₂>, ochS₁är ett bättre konverteringsmål änS₂
- 
              T₁är«TaskType»<S₁>(§15.14.1),T₂är«TaskType»<S₂>, ochT₁är mer specialiserad änT₂
- 
              T₁ärS₁ellerS₁?därS₁är en signerad integrerad typ ochT₂ärS₂ellerS₂?därS₂är en osignerad integrerad typ. Specifikt:- 
              S₁ärsbyteochS₂ärbyte,ushort,uintellerulong
- 
              S₁ärshortochS₂ärushort,uintellerulong
- 
              S₁ärintochS₂äruintellerulong
- 
              S₁ärlongochS₂ärulong
 
- 
              
12.6.4.8 Överbelastning i generiska klasser
Anmärkning: Även om signaturer som deklareras ska vara unika (§8.6), är det möjligt att ersättning av typargument resulterar i identiska signaturer. I en sådan situation väljer överlagringslösningen de mest specifika (§12.6.4.3) av de ursprungliga signaturerna (innan ersättning av typargument), om det finns och rapporterar annars ett fel. slutkommentar
Exempel: Följande exempel visar överlagringar som är giltiga och ogiltiga enligt den här regeln:
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); }slutexempel
12.6.5 Kompileringstidskontroll av dynamisk medlemsanrop
Även om överbelastningsupplösningen för en dynamiskt bunden åtgärd sker vid körning, är det ibland möjligt att vid kompileringstillfället känna till listan över funktionsmedlemmar som en överlagring ska väljas från:
- För ett ombudsanrop (§12.8.10.4) är listan en funktionsmedlem med samma parameterlista som delegate_type av anropet
- För ett metodanrop (§12.8.10.2) på en typ, eller på ett värde vars statiska typ inte är dynamisk, är uppsättningen tillgängliga metoder i metodgruppen känd vid kompileringstid.
- För ett objektskapandeuttryck (§12.8.17.2) är uppsättningen tillgängliga konstruktorer i typen känd vid kompileringstid.
- För indexerareåtkomst (§12.8.12.4) är uppsättningen tillgängliga indexerare i mottagaren känd vid kompileringstid.
I dessa fall utförs en begränsad kompileringstidkontroll på varje medlem i den kända uppsättningen av funktionsmedlemmar, för att avgöra om någon av dem med säkerhet aldrig skulle kunna anropas vid körning. För varje funktionsmedlem skapas F en modifierad parameter och argumentlista:
- Om Fär en allmän metod och typargument har angetts ersätts de först med typparametrarna i parameterlistan. Men om typargument inte har angetts sker ingen sådan ersättning.
- Sedan utelämnas alla parametrar vars typ är öppen, det vill säga innehåller en typparameter, tillsammans med sina motsvarande argument, se §8.4.3.
För att F ska klara kontrollen skall följande gälla:
- Den ändrade parameterlistan för Fgäller för den ändrade argumentlistan enligt §12.6.4.2.
- Alla konstruerade typer i den ändrade parameterlistan uppfyller sina begränsningar (§8.4.5).
- Om typparametrarna för Fersattes i steget ovan uppfylls deras begränsningar.
- Om Fär en statisk metod ska metodgruppen inte ha orsakats av en member_access vars mottagare vid kompileringstiden är en variabel eller ett värde.
- Om Fär en instansmetod ska metodgruppen inte ha uppstått från en member_access vars mottagare vid kompileringstid är känd för att vara en typ.
Om ingen kandidat klarar det här testet uppstår ett kompileringsfel.
12.6.6 Funktionsmedlemsanrop
12.6.6.1 Allmänt
Detta underavsnitt beskriver den process som sker vid körtid för att anropa en viss funktionsmedlem. Det antas att en bindningstidsprocess redan har fastställt vilken medlem som ska anropas, eventuellt genom att tillämpa överbelastningsmatchning på en uppsättning kandidatfunktionsmedlemmar.
För att beskriva anropsprocessen delas funktionsmedlemmar in i två kategorier:
- Statiska funktionsmedlemmar. Det här är statiska metoder, statiska egenskapsåtkomster och användardefinierade operatorer. Statiska funktionsmedlemmar är alltid icke-virtuella.
- Instansfunktionsmedlemmar. Det här är instansmetoder, instanskonstruktorer, instansegenskapsaccessorer och indexeraccessorer. Instansfunktionsmedlemmar är antingen icke-virtuella eller virtuella och anropas alltid på en viss instans. Instansen beräknas av ett instansuttryck och blir tillgänglig inom funktionsmedlemmen som this(§12.8.14). För en instanskonstruktor anses instansuttrycket vara det nyligen allokerade objektet.
Körningsbearbetningen av ett funktionsmedlemsanrop består av följande steg, där M är funktionsmedlem och, om M är instansmedlem, E är instansuttrycket:
- Om Mär en statisk funktionsmedlem:- Argumentlistan utvärderas enligt beskrivningen i §12.6.2.
- 
              Manropas.
 
- Annars, om typen av Eär en värdetypV, ochMdeklareras eller åsidosätts iV:- 
              Eutvärderas. Om den här utvärderingen orsakar ett undantag körs inga ytterligare steg. För en instanskonstruktor består den här utvärderingen av att allokera lagring (vanligtvis från en körningsstack) för det nya objektet. I det här fallet klassificerasEsom en variabel.
- Om Einte klassificeras som en variabel, eller omVinte är en readonly struct typ (§16.2.2) ochMinte är en readonly funktionsmedlem (§16.4.12), ochEär en av:- en indataparameter (§15.6.2.3.2) eller
- ett readonlyfält (§15.5.3), eller
- en readonlyreferensvariabel eller retur (§9.7), skapas en tillfällig lokal variabel avEtypen och värdetEför tilldelas till variabeln.Eomklassificeras sedan som en referens till den tillfälliga lokala variabeln. Den tillfälliga variabeln är tillgänglig somthisinomM, men inte på något annat sätt. Det är alltså bara närEkan skrivas är det möjligt för anroparen att observera de ändringar somMgör ithis.
 
- Argumentlistan utvärderas enligt beskrivningen i §12.6.2.
- 
              Manropas. Variabeln som refereras avEblir variabeln som refereras avthis.
 
- 
              
- Annars: - 
              Eutvärderas. Om den här utvärderingen orsakar ett undantag körs inga ytterligare steg.
- Argumentlistan utvärderas enligt beskrivningen i §12.6.2.
- Om typen av Eär en value_typeutförs en boxningskonvertering (§10.2.9) för att konverteraEtill en class_type, ochEanses vara av den class_type i följande steg. Om value_type är en enum_type, är class_typeSystem.Enum;, annars är detSystem.ValueType.
- Värdet för Ekontrolleras för att vara giltigt. Om värdet förEär null genereras enSystem.NullReferenceExceptionoch inga ytterligare steg körs.
- Den funktionsmedlemsimplementering som ska anropas bestäms: - Om bindningstidstypen för Eär ett gränssnitt, är funktionsmedlemmen som ska anropas implementeringen avMsom tillhandahålls av instansens körningstidstyp som refereras avE. Den här funktionsmedlemmen bestäms genom att tillämpa reglerna för gränssnittsmappning (§19.6.5) för att fastställa implementeringen avMsom tillhandahålls av körningstypen för instansen som refereras avE.
- Annars om Mär en virtuell funktion, ska funktionsmedlemmen som ska anropas vara implementeringen avMsom tillhandahålls av körningstidenstypen för instansen som refereras avE. Denna funktionsmedlem bestäms genom tillämpning av reglerna för att fastställa den mest härledda implementeringen (§15.6.4) avMmed avseende på körtidstypen för den instans som refereras avE.
- Annars är Men icke-virtuell funktionsmedlem och funktionsmedlemmen som ska anropas ärMsig själv.
 
- Om bindningstidstypen för 
- Implementeringen av funktionsmedlem som fastställs i steget ovan anropas. Objektet som refereras av Eblir det objekt som refereras till av detta.
 
- 
              
Obs!§12.2 klassificerar egenskapsåtkomst som att anropa motsvarande funktionsmedlem, antingen
getaccessorn ellersetaccessorn. Processen ovan följs för att anropa den accessorn. slutkommentar
Resultatet av anropet av en instanskonstruktor (§12.8.17.2) är det värde som skapas. Resultatet av anropet av någon annan funktionsmedlem är värdet, om något, som returneras (§13.10.5) från dess kropp.
12.6.6.2 Anrop på boxade instanser
En funktionsmedlem som implementeras i en value_type kan anropas via en boxad instans av den value_type i följande situationer:
- När funktionsmedlemmen är en åsidosättning av en metod som ärvs från typen class_type och anropas via ett instansuttryck för den class_type.
Obs: class_type är alltid en av System.Object,System.ValueTypeellerSystem.Enum. slutkommentar
- När funktionsmedlemmen är en implementering av en gränssnittsfunktionsmedlem och anropas via ett instansuttryck för en interface_type.
- När funktionsmedlemmen anropas via ett ombud.
I dessa situationer anses den boxade instansen innehålla en variabel för value_type, och den här variabeln blir variabeln som refereras av detta i funktionsmedlemsanropet.
Obs: I synnerhet innebär det att när en funktionsmedlem anropas på en boxad instans är det möjligt för funktionsmedlemmen att ändra värdet i den boxade instansen. slutkommentar
12.7 Dekonstruktion
Dekonstruktion är en process där ett uttryck omvandlas till en tuppel av enskilda uttryck. Dekonstruktion används när målet för en enkel tilldelning är ett tupppeluttryck, för att hämta värden som ska tilldelas till vart och ett av tuppelns element.
Ett uttryck Edekonstrueras till ett tuppeluttryck med n element på följande sätt:
- Om Eär ett tupeluttryck mednelement, är resultatet av dekonstruktion uttrycketE.
- Annars, om Ehar tuppeltypen(T1, ..., Tn)mednelement, då utvärderasEtill en temporär variabel__voch resultatet av dekonstruktion är uttrycket(__v.Item1, ..., __v.Itemn).
- Om uttrycket E.Deconstruct(out var __v1, ..., out var __vn)vid kompileringstidpunkten resulterar i en unik instans eller extension method, utvärderas det uttrycket och resultatet av dekonstruktionen är uttrycket(__v1, ..., __vn). En sådan metod kallas för en dekonstruktor.
- Annars kan Einte dekonstrueras.
Här __v och __v1, ..., __vn referera till annars osynliga och otillgängliga tillfälliga variabler.
Obs: Ett uttryck av typen
dynamickan inte dekonstrueras. slutkommentar
12.8 Primära uttryck
12.8.1 Allmänt
Primära uttryck är de enklaste uttrycksformerna.
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
    ;
Obs! Den här grammatikregeln är inte ANTLR-klar eftersom den ingår i en uppsättning ömsesidigt vänsterrekursiva regler (
primary_expression, ,member_accessinvocation_expression,element_access,post_increment_expression,post_decrement_expression,null_forgiving_expressionpointer_member_accessochpointer_element_access) som ANTLR inte hanterar. Standardtekniker kan användas för att transformera grammatiken för att ta bort den ömsesidiga vänsterrekursionen. Denna standard gör inte detta eftersom inte alla parsningsstrategier kräver det (t.ex. en LALR-parser skulle inte) och att göra det skulle dölja strukturen och beskrivningen. slutkommentar
pointer_member_access (§24.6.3) och pointer_element_access (§24.6.4) är endast tillgängliga i osäker kod (§24).
12.8.2 Literaler
En primary_expression som består av en literal (§6.4.5) klassificeras som ett värde.
12.8.3 Interpolerade stränguttryck
En interpolated_string_expression består av $, $@eller @$, omedelbart följt av text inom " tecken. I den citerade texten finns det noll eller fler interpoleringar avgränsade med { och } tecken, som var och en omger ett uttryck och valfria formateringsspecifikationer.
Interpolerade stränguttryck har två formulär; regular (interpolated_regular_string_expression) och verbatim (interpolated_verbatim_string_expression); som lexikalt liknar, men skiljer sig semantiskt från, de två formerna av strängliteraler (§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
    : '}}'
    ;
Sex av de lexikala regler som definieras ovan är sammanhangskänsliga på följande sätt:
| Regel | kontextkrav | 
|---|---|
| Interpolerad_Regular_String_Mid | Identifieras endast efter en Interpolated_Regular_String_Start, mellan följande interpoleringar och före motsvarande Interpolated_Regular_String_End. | 
| Regular_Interpolation_Format | Identifieras endast inom en regular_interpolation och när startkolonet (:) inte är kapslat inom någon typ av hakparentes (parenteser/klammerparenteser/kvadrat). | 
| Interpolated_Regular_String_End | Identifieras endast efter en Interpolated_Regular_String_Start och endast om några mellanliggande token antingen är Interpolated_Regular_String_Mideller token som kan vara en del av regular_interpolation, inklusive token för eventuella interpolated_regular_string_expressionsom ingår i sådana interpoleringar. | 
| Interpolated_Verbatim_String_MidVerbatim_Interpolation_FormatInterpolated_Verbatim_String_End | Erkännandet av dessa tre regler följer motsvarande regler ovan, där varje nämnd regelbunden grammatikregel ersätts med motsvarande ordagrann regel. | 
Note: Ovanstående regler är kontextkänsliga eftersom deras definitioner överlappar andra tokens på språket. slutkommentar
Obs: Grammatiken ovan är inte ANTLR-klar på grund av kontextkänsliga lexikala regler. Precis som med andra lexergeneratorer stöder ANTLR kontextkänsliga lexikala regler, till exempel användning av dess lexikala lägen, men detta är en implementeringsinformation och därför inte en del av denna specifikation. slutkommentar
En interpolated_string_expression klassificeras som ett värde. Om den omedelbart konverteras till System.IFormattable eller System.FormattableString med en implicit interpolerad strängkonvertering (§10.2.5) har det interpolerade stränguttrycket den typen. Annars har den typ string.
Anmärkning: Skillnaderna mellan möjliga typer av interpolated_string_expression kan fastställas i dokumentationen för
System.String(§C.2) ochSystem.FormattableString(§C.3). slutkommentar
Innebörden av en interpolering, både regular_interpolation och verbatim_interpolation, är att formatera värdet för -uttrycket som en string antingen enligt det format som anges av Regular_Interpolation_Format eller Verbatim_Interpolation_Format, eller enligt ett standardformat för typen av uttryck. Den formaterade strängen ändras sedan av interpolation_minimum_width, om någon, för att skapa den slutliga string som ska interpoleras i interpolated_string_expression.
Obs: Hur standardformatet för en typ fastställs beskrivs i dokumentationen för
System.String(§C.2) ochSystem.FormattableString(§C.3). Beskrivningar av standardformat, som är identiska för Regular_Interpolation_Format och Verbatim_Interpolation_Format, finns i dokumentationen förSystem.IFormattable(§C.4) och i andra typer i standardbiblioteket (§C). slutkommentar
I en interpolation_minimum_width ska constant_expression ha en implicit konvertering till int. Låt fältbredden vara det absoluta värdet av detta konstantuttryck och justeringen vara tecknet (positiv eller negativ) på värdet av detta konstantuttryck:
- Om värdet för fältbredden är mindre än eller lika med längden på den formaterade strängen ändras inte den formaterade strängen.
- Annars är den formaterade strängen vadderad med blankstegstecken så att dess längd är lika med fältbredden: - Om justeringen är positiv är den formaterade strängen högerjusterad genom att lägga till utfyllnaden i förväg.
- Annars är den vänsterjusterad genom att lägga till utfyllnad.
 
Den övergripande innebörden av en interpolated_string_expression, inklusive ovanstående formatering och utfyllnad av interpoleringar, definieras genom en konvertering av uttrycket till en metodanrop: om uttryckets typ är System.IFormattable eller System.FormattableString den metoden är System.Runtime.CompilerServices.FormattableStringFactory.Create (§C.3) som returnerar ett värde av typen System.FormattableString; I annat fall ska typen vara string och metoden är string.Format (§C.2) som returnerar ett värde av typen string.
I båda fallen består argumentlistan för anropet av en formatsträngliteralen med formatspecifikationer för varje interpolation och ett argument för varje uttryck som motsvarar formatspecifikationerna.
Formatsträngliteralen konstrueras på följande sätt, där N är antalet interpolationer i interpolated_string_expression. Formatsträngsliteralen består i ordning av:
- Tecknen i Interpolated_Regular_String_Start eller Interpolated_Verbatim_String_Start
- Tecknen, om några, i Interpolated_Regular_String_Mid eller Interpolated_Verbatim_String_Mid
- Då om N ≥ 1för varje talIfrån0tillN-1- En platshållarspecifikation: - Ett vänster klammerparentes ({) tecken
- Decimalrepresentationen av I
- Om motsvarande regular_interpolation eller verbatim_interpolation sedan har ett interpolation_minimum_width, ett kommatecken (,) följt av decimalrepresentationen av värdet för constant_expression
- Tecknen i Regular_Interpolation_Format eller Verbatim_Interpolation_Format, om några, av den motsvarande regular_interpolation eller verbatim_interpolation
- Ett högerparentestecken (})
 
- Ett vänster klammerparentes (
- Tecknen i Interpolated_Regular_String_Mid eller Interpolated_Verbatim_String_Mid som omedelbart följer efter den motsvarande interpolationen, om någon
 
- En platshållarspecifikation: 
- Slutligen tecknen i Interpolated_Regular_String_End eller Interpolated_Verbatim_String_End.
De efterföljande argumenten är uttrycket :s från interpolationerna, om några, i följd.
När en interpolated_string_expression innehåller flera interpoleringar utvärderas uttrycken i dessa interpolationer i textordning från vänster till höger.
Exempel:
I det här exemplet används följande formatspecifikationsfunktioner:
- 
              X-formatsspecifikationen som formaterar heltal som hexadecimala tal med versaler,
- standardformatet för ett stringvärde är själva värdet.
- positiva justeringsvärden som högerjusterar inom den angivna minsta fältbredden,
- negativa justeringsvärden som vänsterjusterar inom den angivna minsta fältbredden.
- definierade konstanter för interpolation_minimum_width, och
- 
              {{och}}formateras som{respektive}.
Givet:
string text = "red";
int number = 14;
const int width = -4;
Då:
| interpolerat stränguttryck | motsvarande betydelse som string | Värde | 
|---|---|---|
| $"{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" | 
slutexempel
12.8.4 Enkla namn
En simple_name består av en identifierare, eventuellt följt av en typargumentlista:
simple_name
    : identifier type_argument_list?
    ;
En simple_name är antingen av formen I eller av formen I<A₁, ..., Aₑ>, där I är en enkel identifierare och I<A₁, ..., Aₑ> är en valfri typargumentlista. När inga type_argument_list har angetts bör du överväga att e vara noll. 
              simple_name utvärderas och klassificeras på följande sätt:
- Om eär noll och simple_name visas inom ett lokalt variabeldeklarationsutrymme (§7.3) som direkt innehåller en lokal variabel, parameter eller konstant med namnI, refererar simple_name till den lokala variabeln, parametern eller konstanten och klassificeras som en variabel eller ett värde.
- Om eär noll och simple_name visas i en allmän metoddeklaration men utanför attribut dess method_declaration, och om den deklarationen innehåller en typparameter med namnetI, refererar simple_name till den typparametern.
- Annars, för varje instanstyp T(§15.3.2), börjande med instanstypen för den omedelbart omslutande typdeklarationen och fortsättande med instanstypen för varje omslutande klass- eller structdeklaration (om sådan finns):- Om eär noll och deklarationen avTinnehåller en typparameter med namnetIrefererar simple_name till den typparametern.
- Annars, om en medlemssökning (§12.5) av IiTmed argument avetyp genererar en matchning:- Om Tär instanstypen för den omedelbart omslutande klassen eller structtypen och sökningen identifierar en eller flera metoder, är resultatet en metodgrupp med ett associerat instansuttryck avthis. Om en typargumentlista angavs används den för att anropa en generisk metod (§12.8.10.2).
- Annars, om Tär instanstypen för den omedelbart omslutande klassen eller strukturtpen, om sökningen identifierar en instansmedlem, och om referensen sker inom block av en instanskonstruktor, en instansmetod eller en instansaccessor (§12.2.1), är resultatet detsamma som en medlemsåtkomst (§12.8.7) i formenthis.I. Detta kan bara inträffa näreär noll.
- Annars är resultatet detsamma som en medlemsåtkomst (§12.8.7) i form av T.IellerT.I<A₁, ..., Aₑ>.
 
- Om 
 
- Om 
- I annat fall utvärderas följande steg tills en entitet finns för varje namnområde N, från och med namnområdet där simple_name inträffar, fortsätter med varje omslutande namnområde (om det finns) och slutar med det globala namnområdet:- Om eär noll ochIär namnet på ett namnområde iN, så:- Om platsen där simple_name inträffar omges av en namnområdesdeklaration för Noch namnområdesdeklarationen innehåller en extern_alias_directive eller using_alias_directive som associerar namnetImed ett namnområde eller typ, är simple_name tvetydigt och ett kompileringsfel inträffar.
- Annars refererar simple_name till namnområdet med namnet IiN.
 
- Om platsen där simple_name inträffar omges av en namnområdesdeklaration för 
- Annars, om Ninnehåller en tillgänglig typ med namnIochetypparametrar, så:- Om eär noll och platsen där simple_name inträffar omges av en namnområdesdeklaration förNoch namnområdesdeklarationen innehåller en extern_alias_directive eller using_alias_directive som associerar namnetImed ett namnområde eller typ, är simple_name tvetydigt och ett kompileringsfel inträffar.
- Annars refererar namespace_or_type_name till den typ som skapats med de angivna typargumenten.
 
- Om 
- Annars, om platsen där simple_name förekommer är omsluten av en namnområdesdeklaration för N:- Om eär noll och namnområdesdeklarationen innehåller en extern_alias_directive eller using_alias_directive som associerar namnetImed ett importerat namnområde eller typ refererar simple_name till namnområdet eller typen.
- Annars, om namnrymderna som importeras av using_namespace_directivei namnområdesdeklarationen innehåller exakt en typ med namn Iochetypparametrar, refererar simple_name till den typ som skapats med de angivna typargumenten.
- Annars, om namnrymderna som importeras av using_namespace_directivei namnområdesdeklarationen innehåller fler än en typ med namnet Iochetypparametrar, så är simple_name tvetydig och ett kompileringsfel inträffar.
 
- Om 
 Obs: Hela det här steget är exakt parallellt med motsvarande steg i bearbetningen av en namespace_or_type_name (§7.8). slutkommentar 
- Om 
- Annars, om eär noll ochIär identifieraren_, är simple_name en enkel ignorera, vilket är en form av deklarationsuttryck (§12.18).
- Annars är simple_name odefinierat och ett kompileringsfel inträffar.
12.8.5 Parentesiserade uttryck
En parenthesized_expression består av ett uttryck inom parenteser.
parenthesized_expression
    : '(' expression ')'
    ;
En parenthesized_expression utvärderas genom att utvärdera -uttrycket inom parenteserna. Om det uttrycket inom parenteserna anger ett namnområde eller en typ uppstår ett kompileringsfel. Annars är resultatet av parenthesized_expression resultatet av utvärderingen av det inneslutna -uttrycket.
12.8.6 Tuppeluttryck
En tuple_expression representerar en tuppel och består av två eller flera kommaavgränsade och valfritt namngivna uttrycksom omges av parenteser. En deconstruction_expression är en kortfattad syntax för en tuppel som innehåller implicita deklarationsuttryck.
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
    ;
En tuple_expression klassificeras som ett tubel.
En deconstruction_expressionvar (e1, ..., en) är en förkortning för tuple_expression(var e1, ..., var en) och följer samma beteende. Detta gäller rekursivt för alla kapslade deconstruction_tuplei deconstruction_expression. Varje identifierare kapslad inom en deconstruction_expression introducerar därmed ett deklarationsuttryck (§12.18). Därför kan en deconstruction_expression bara ske på vänster sida av ett enkelt tilldelningsuttryck.
Exempel: Följande kod deklarerar tre variabler: a, b och c. Var och en är ett heltal och tilldelas dess värde från tuppeln till höger om tilldelningen.
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.Alla enskilda element i tilldelningen kan vara ett dekonstruktionsuttryck. Följande dekonstruktionsuttryck tilldelar till exempel sex variabler via
af.var (a, b, (c, d, (e, f))) = (1, 2, (3, 4, (5, 6)));Observera i det här exemplet att strukturen för kapslade tupplar måste matcha på båda sidor av tilldelningen.
Om variablerna på vänster sida är implicit inskrivna måste motsvarande uttryck ha en typ:
(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 inferredslutexempel
Ett tuppeluttryck har en typ om och endast om vart och ett av dess elementuttryck Ei har en typ Ti. Typen ska vara en tupletyp med samma kardinalitet som tupleuttrycket, där varje element anges av följande:
- Om tuppeln i motsvarande position har ett namn Niska tuppelns typelement varaTi Ni.
- Om Eiär i formenNiellerE.NiellerE?.Niska tupelelementet varaTi Ni, såvida inget av följande gäller:- Ett annat element i tuppelns uttryck har namnet Ni, eller
- Ett annat tupleelement utan namn har ett tupleelementuttryck i formen av NiellerE.NiellerE?.Nieller
- 
              Niär av formenItemX, därXär en sekvens av icke-0-initierade decimalsiffror som kan representera positionen för ett tupelelement, ochXinte representerar elementets position.
 
- Ett annat element i tuppelns uttryck har namnet 
- I annat fall ska tupelelementets typ-element vara Ti.
Ett tuputtryck utvärderas genom att utvärdera varje elementuttryck i ordning från vänster till höger.
Ett tupppelvärde kan hämtas från ett tuppelns uttryck genom att konvertera det till en tuppelns typ (§10.2.13), genom att omklassificera det som ett värde (§12.2.2)) eller genom att göra det till mål för en dekonstruktionstilldelning (§12.22.2).
Exempel:
(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 typeI det här exemplet är alla fyra tuppeluttrycken giltiga. De första två,
t1ocht2, använder inte typen av tuppelns uttryck, utan tillämpar i stället en implicit tuppelkonvertering. När det gällert2förlitar sig den implicita tupleomvandlingen på de implicita konverteringarna från2tilllongoch frånnulltillstring. Det tredje tuppelns uttryck har en typ(int i, string)och kan därför omklassificeras som ett värde av den typen. Deklarationen avt4, å andra sidan, är ett fel: Tuppelns uttryck har ingen typ eftersom dess andra element inte har någon typ.if ((x, y).Equals((1, 2))) { ... };Det här exemplet visar att tupler ibland kan leda till flera lager av parenteser, särskilt när tuplarnas uttryck är det enda argumentet till metodanropet.
slutexempel
12.8.7 Medlemsåtkomst
12.8.7.1 Allmänt
En member_access består av en primary_expression, en predefined_typeeller en qualified_alias_memberföljt av en "." -token följt av en identifierare, eventuellt följt av en 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'
    ;
Den qualified_alias_member produktionen definieras i §14.8.
En member_access är antingen i formen E.I eller i formen E.I<A₁, ..., Aₑ>, där E är en primary_expression, predefined_type eller qualified_alias_member,I är ett enda identifierarnamn och <A₁, ..., Aₑ> är en valfri type_argument_list. När inga type_argument_list har angetts bör du överväga att e vara noll.
En member_access med en primary_expression av typen dynamic är dynamiskt bunden (§12.3.3). I det här fallet klassificeras medlemsåtkomsten som en egenskapsåtkomst av typen dynamic. Reglerna nedan för att fastställa innebörden av member_access tillämpas sedan vid körning, med hjälp av körningstypen i stället för kompileringstidstypen för primary_expression. Om den här körningsklassificeringen leder till en metodgrupp ska medlemsåtkomsten vara primäruttryck för en anropsexpression.
member_access utvärderas och klassificeras enligt följande:
- Om eär noll ochEär ett namnområde ochEinnehåller ett kapslat namnområde med namnetIblir resultatet det namnområdet.
- Annars, om Eär ett namnområde ochEinnehåller en tillgänglig typ med namnIochKtypparametrar, blir resultatet den typen som skapas med de angivna typargumenten.
- Om Eklassificeras som en typ, omEinte är en typparameter, och om en medlemssökning (§12.5) avIiEmedKtypparametrar genererar en matchning, utvärderasE.Ioch klassificeras enligt följande:Obs: När resultatet av en sådan medlemssökning är en metodgrupp och Kär noll kan metodgruppen innehålla metoder med typparametrar. Detta gör att sådana metoder kan övervägas för typarguments slutsatsdragning. slutkommentar- Om Iidentifierar en typ är resultatet den typen som konstruerats med en viss typargument.
- Om Iidentifierar en eller flera metoder blir resultatet en metodgrupp utan associerat instansuttryck.
- Om Iidentifierar en statisk egenskap blir resultatet en egenskapsåtkomst utan associerat instansuttryck.
- Om Iidentifierar ett statiskt fält:- Om fältet är skrivskyddat och referensen inträffar utanför den statiska konstruktorn för klassen eller structen där fältet deklareras, är resultatet ett värde, nämligen värdet för det statiska fältet IiE.
- Annars är resultatet en variabel, nämligen det statiska fältet IiE.
 
- Om fältet är skrivskyddat och referensen inträffar utanför den statiska konstruktorn för klassen eller structen där fältet deklareras, är resultatet ett värde, nämligen värdet för det statiska fältet 
- Om Iidentifierar en statisk händelse:- Om referensen inträffar inom klassen eller structen där händelsen deklareras, och händelsen deklarerades utan event_accessor_declarations (§15.8.1), bearbetas E.Iexakt som omIvar ett statiskt fält.
- Annars är resultatet en händelseåtkomst utan associerat instansuttryck.
 
- Om referensen inträffar inom klassen eller structen där händelsen deklareras, och händelsen deklarerades utan event_accessor_declarations (§15.8.1), bearbetas 
- Om Iidentifierar en konstant är resultatet ett värde, nämligen värdet för konstanten.
- Om Iidentifierar en uppräkningsmedlem är resultatet ett värde, nämligen värdet för den uppräkningsmedlemmen.
- Annars är E.Ien ogiltig medlemsreferens och ett kompileringsfel inträffar.
 
- Om 
- Om Eär en egenskapsåtkomst, indexerareåtkomst, variabel eller värde, vars typ ärToch en medlemssökning (§12.5) avIiTmed argument avKtyp skapar en matchning, utvärderasE.Ioch klassificeras enligt följande:- Först erhålls värdet för egenskaps- eller indexeråtkomst om Eär en egenskap eller index åtkomst (§12.2.2), och E omklassificeras som ett värde.
- Om Iidentifierar en eller flera metoder blir resultatet en metodgrupp med ett associerat instansuttryck avE.
- Om Iidentifierar en instansegenskap blir resultatet en egenskapsåtkomst med ett associerat instansuttryck avEoch en associerad typ som är egenskapens typ. OmTär en klasstyp väljs den associerade typen baserat på den första deklarationen eller åsidosättningen av egenskapen som hittas när man börjar medToch söker igenom dess basklasser.
- Om Tär en class_type ochIidentifierar ett instansfält för den class_type:- Om värdet för Eärnullgenereras enSystem.NullReferenceException.
- Annars, om fältet är skrivskyddat och referensen inträffar utanför en instanskonstruktor för klassen där fältet deklareras, är resultatet ett värde, nämligen värdet för fältet Ii objektet som refereras avE.
- Annars är resultatet en variabel, nämligen fältet Ii objektet som refereras avE.
 
- Om värdet för 
- Om Tär en struct_type ochIidentifierar ett instansfält för den struct_type:- Om Eär ett värde, eller om fältet är skrivskyddat och referensen inträffar utanför en instanskonstruktor för den struct där fältet deklareras, är resultatet ett värde, nämligen värdet för fältetIi den struct-instans som anges avE.
- Annars är resultatet en variabel, nämligen fältet Ii struct-instansen som anges avE.
 
- Om 
- Om Iidentifierar en instanshändelse:- Om referensen inträffar inom klassen eller structen där händelsen deklareras, och händelsen deklarerades utan event_accessor_declarations (§15.8.1), och referensen inte inträffar som vänster sida av a +=eller-=operator, bearbetasE.Iexakt som omIvar ett instansfält.
- Annars är resultatet en händelseåtkomst med ett associerat instansuttryck av E.
 
- Om referensen inträffar inom klassen eller structen där händelsen deklareras, och händelsen deklarerades utan event_accessor_declarations (§15.8.1), och referensen inte inträffar som vänster sida av 
 
- Först erhålls värdet för egenskaps- eller indexeråtkomst om 
- I annat fall görs ett försök att bearbeta E.Isom ett tilläggsmetodanrop (§12.8.10.3). Om detta misslyckas ärE.Ien ogiltig medlemsreferens och ett bindningstidsfel inträffar.
12.8.7.2 Identiska enkla namn och typnamn
I ett medlemsåtkomstformulär E.I, om E är en enda identifierare, och om innebörden av E som en simple_name (§12.8.4) är en konstant, fält, egenskap, lokal variabel eller parameter med samma typ som innebörden av E som en type_name (§7.8.1), ),  därefter tillåts båda möjliga betydelser av E. Medlemssökningen av E.I är aldrig tvetydig, eftersom I nödvändigtvis ska vara medlem av typen E i båda fallen. Med andra ord tillåter regeln bara åtkomst till statiska medlemmar och kapslade typer av E där ett kompileringsfel annars skulle ha inträffat.
Exempel:
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 } }Endast för exponeringsändamål, inom
A-klassen, avgränsas förekomsterna av denColor-identifierare som refererar tillColor-typ med«...», och de som refererar till fältetColoravgränsas inte.slutexempel
12.8.8 Null Villkorlig medlemsåtkomst
En null_conditional_member_access är en villkorsstyrd version av member_access (§12.8.7) och det är ett bindningstidsfel om resultattypen är void. För ett null-villkorsuttryck vilket resulterar i att resultattypen kan vara void, se (§12.8.11).
En null_conditional_member_access består av en primary_expression följt av de två tokenerna "?" och ".", följt av en identifierare med en valfri type_argument_list, följt av noll eller fler dependent_accesses som kan föregås av en null_forgiving_operator.
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?
    ;
Ett null_conditional_member_access uttryck E är i formen av P?.A. Innebörden av E bestäms på följande sätt:
- Om typen av - Pär en nullbar värdetyp:- Låt - Tvara typen av- P.Value.A.- Om - Tär en typparameter som inte är känd för att vara antingen en referenstyp eller en värdetyp som inte kan nulliseras uppstår ett kompileringsfel.
- Om - Tär en värdetyp som inte kan nollföras är typen av- E- T?, och innebörden av- Eär densamma som innebörden av:- ((object)P == null) ? (T?)null : P.Value.A- Förutom att - Pendast utvärderas en gång.
- Annars är typen av - E- Toch innebörden av- Eär densamma som innebörden av:- ((object)P == null) ? (T)null : P.Value.A- Förutom att - Pendast utvärderas en gång.
 
- Annars: - Låt - Tvara typen av uttryck- P.A.- Om - Tär en typparameter som inte är känd för att vara antingen en referenstyp eller en värdetyp som inte kan nulliseras uppstår ett kompileringsfel.
- Om - Tär en värdetyp som inte kan nollföras är typen av- E- T?, och innebörden av- Eär densamma som innebörden av:- ((object)P == null) ? (T?)null : P.A- Förutom att - Pendast utvärderas en gång.
- Annars är typen av - E- Toch innebörden av- Eär densamma som innebörden av:- ((object)P == null) ? (T)null : P.A- Förutom att - Pendast utvärderas en gång.
 
Obs: I ett uttryck av formen:
P?.A₀?.A₁om
Putvärderas tillnullutvärderas varkenA₀ellerA₁. Detsamma gäller om ett uttryck är en sekvens av null_conditional_member_access eller null_conditional_element_access§12.8.13 åtgärder.slutkommentar
En null_conditional_projection_initializer är en begränsning av null_conditional_member_access och har samma semantik. Den inträffar endast som projektionsinitierare i ett anonymt objektskapandeuttryck (§12.8.17.3).
12.8.9 Null-förlåtande uttryck
12.8.9.1 Allmänt
Ett null-förlåtande uttrycks värde, typ, klassificering (§12.2) och safe-context (§16.4.15) är värdet, typen, klassificeringen och säkerhetskontexten för dess primary_expression.
null_forgiving_expression
    : primary_expression null_forgiving_operator
    ;
null_forgiving_operator
    : '!'
    ;
              Note: Null-forgiving postfixoperatorn och den logiska negationens prefixoperator (§12.9.4), medan det representeras av samma lexikala tecken (!), är distinkta. Endast de senare får överbelastas (§15.10), definitionen av den null-förlåtande operatorn är fast. 
              slutkommentar
Det är ett kompileringsfel att tillämpa operatorn null-forgiving mer än en gång på samma uttryck, trots mellanliggande parenteser.
Exempel: följande är alla ogiltiga:
var p = q!!; // error: applying null_forgiving_operator more than once var s = ( ( m(t) ! ) )! // error: null_forgiving_operator applied twice to m(t)slutexempel
Resten av det här underavsnittet och de följande syskonunderavsnitten är villkorat normativa.
En kompilator som utför statisk nulltillståndsanalys (§8.9.5) måste följa följande specifikation.
Operatorn null-forgiving är en pseudoåtgärd för kompileringstid som används för att informera en kompilators statiska nulltillståndsanalys. Den har två användningsområden: för att åsidosätta en kompilators bestämning att ett uttryck kanske null; och för att åsidosätta en kompilator som utfärdar en varning om nullabilitet.
Att tillämpa operatorn null-forgiving på ett uttryck för vilket en kompilators statiska nulltillståndsanalys inte ger några varningar är inte ett fel.
12.8.9.2 Åsidosätta en "kanske null"-bestämning
Under vissa omständigheter kan en kompilators statiska nulltillståndsanalys avgöra att ett uttryck har null-tillståndet kanske null och utfärda en diagnostikvarning när annan information anger att uttrycket inte kan vara null. Om du tillämpar operatorn null-forgiving på ett sådant uttryck informeras kompilatorns statiska null-tillståndsanalys om att null-tillståndet är i inte null; som både förhindrar diagnostikvarningen och kan informera om pågående analys.
exempel: Tänk på följande:
#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;Om
IsValidreturnerartruekanppå ett säkert sätt avrefereras för att få åtkomst till egenskapenName, och varningen "avrefererar ett eventuellt nullvärde" kan ignoreras med hjälp av!.slutexempel
Exempel: Operatorn null-forgiving bör användas med försiktighet:
#nullable enable int B(int? x) { int y = (int)x!; // quash warning, throw at runtime if x is null return y; }Här tillämpas operatorn null-forgiving på en värdetyp och tar bort alla varningar på
x. Men omxärnullvid körning, kommer ett undantag att genereras eftersomnullinte kan omvandlas tillint.slutexempel
12.8.9.3 Åsidosätta andra null-analysvarningar
Förutom att åsidosätta bestämningar som kanske null som ovan, kan det finnas andra omständigheter där det är önskvärt att åsidosätta en kompilators statiska analys av null-tillstånd som påpekar att ett uttryck kräver en eller flera varningar. Tillämpning av null-forgiving-operatorn på ett sådant uttryck innebär en begäran till kompilatorn att inte utfärda varningar för uttrycket. Som svar kan en kompilator välja att inte utfärda varningar och kan också ändra sin ytterligare analys.
exempel: Tänk på följande:
#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; }Typerna av metod
Assignparametrar,lv&rv, ärstring?, därlvär en utdataparameter och utför en enkel tilldelning.Metod
Mskickar variabelns, av typenstring, somAssign:s utdataparameter. Kompilatorn ger en varning eftersomsinte är en nullbar variabel. Eftersom det andra argumentet iAssigninte kan vara null används null-förlåtelseoperatorn för att upphäva varningen.slutexempel
Slutet på villkorsstyrd normativ text.
12.8.10 Anropsuttryck
12.8.10.1 Allmänt
En invocation_expression används för att anropa en metod.
invocation_expression
    : primary_expression '(' argument_list? ')'
    ;
Ett primary_expression kan vara en null_forgiving_expression om och endast om det har en delegerad_typ.
En invocation_expression är dynamiskt bunden (§12.3.3) om minst något av följande gäller:
- 
              primary_expression har kompileringstidstyp dynamic.
- Minst ett argument för den valfria argument_list har kompileringstidstyp dynamic.
I det här fallet klassificeras invocation_expression som ett värde av typen dynamic. Reglerna nedan för att fastställa innebörden av invocation_expression tillämpas sedan vid körning, med körningstypen i stället för kompileringstidstypen för de primary_expression och argument som har kompileringstidstypen dynamic. Om primary_expression inte har kompileringstidstyp dynamicgenomgår metodanropet en begränsad kompileringstidskontroll enligt beskrivningen i §12.6.5.
En primary_expression för en invocation_expression ska vara en metodgrupp eller ett värde för en delegate_type. Om primary_expression är en metodgrupp är invocation_expression ett metodanrop (§12.8.10.2). Om primary_expression är ett värde av en delegattyp, är invocation_expression ett delegatanrop (§12.8.10.4). Om primary_expression varken är en metodgrupp eller ett värde för en delegate_typeuppstår ett bindningstidsfel.
Den valfria argument_list (§12.6.2) innehåller värden eller variabelreferenser för metodens parametrar.
Resultatet av utvärderingen av en invocation_expression klassificeras på följande sätt:
- Om invocation_expression anropar metoden returns-no-value (§15.6.1) eller en delegering för returns-no-value, är resultatet inget. Ett uttryck som klassificeras som ingenting tillåts endast i samband med en statement_expression (§13.7) eller som brödtexten i en lambda_expression (§12.20). Annars uppstår ett bindningstidsfel.
- Annars, om invocation_expression anropar en return-by-ref-metod (§15.6.1) eller ett return-by-ref-ombud, är resultatet en variabel med en associerad typ av returtyp för metoden eller ombudet. Om anropet är av en instansmetod och mottagaren är av en klasstyp T, väljs den associerade typen från den första deklarationen eller åsidosättningen av metoden som hittades när du började medToch söker igenom dess basklasser.
- I annat fall anropar invocation_expression en metod för return-by-value (§15.6.1) eller return-by-value-delegat, och resultatet är ett värde med en typ associerad med returtypen för metoden eller delegaten. Om anropet är av en instansmetod och mottagaren är av en klasstyp T, väljs den associerade typen från den första deklarationen eller åsidosättningen av metoden som hittades när du började medToch söker igenom dess basklasser.
12.8.10.2 Metodanrop
För ett metodanrop ska primäruttryck av anropsuttryck vara en metodgrupp. Metodgruppen identifierar den enda metod som ska anropas eller uppsättningen överlagrade metoder som du kan välja en specifik metod att anropa från. I det senare fallet baseras fastställandet av den specifika metod som ska anropas på kontexten som tillhandahålls av argumenttyperna i argument_list.
Bindningstidsbearbetningen av ett metodanrop av formuläret M(A), där M är en metodgrupp (eventuellt inklusive en type_argument_list), och A är en valfri argument_list, består av följande steg:
- Uppsättningen kandidatmetoder för metodanropet konstrueras. För varje metod Fassocierad med metodgruppenM:- Om Finte är generisk ärFen kandidat när:- 
              Mhar ingen typargumentlista och
- 
              Fgäller förA(§12.6.4.2).
 
- 
              
- Om Fär generisk ochMinte har någon typargumentlista ärFen kandidat när:- Typbestämning (§12.6.3) lyckas, bestämmer en lista med typargument för anropet och
- När de härledda typargumenten har ersatts med motsvarande metodtypsparametrar uppfyller alla konstruerade typer i parameterlistan över Fderas begränsningar (§8.4.5), och parameterlistan överFgäller förA(§12.6.4.2)
 
- Om Fär generisk ochMinnehåller en typargumentlista ärFen kandidat när:
 
- Om 
- Uppsättningen kandidatmetoder reduceras till att endast innehålla metoder från de mest härledda typerna: För varje metod C.Fi uppsättningen, därCär den typ där metodenFdeklareras, tas alla metoder som deklarerats i en bastyp avCbort från uppsättningen. OmCär en annan klasstyp änobjecttas dessutom alla metoder som deklarerats i en gränssnittstyp bort från uppsättningen.Obs: Den här senare regeln har bara en effekt när metodgruppen var resultatet av en medlemssökning på en typparameter med en annan effektiv basklass än objectoch en icke-tom effektiv gränssnittsuppsättning. slutkommentar
- Om den resulterande uppsättningen kandidatmetoder är tom avbryts ytterligare bearbetning längs följande steg, och i stället görs ett försök att bearbeta anropet som ett tilläggsmetodanrop (§12.8.10.3). Om detta misslyckas finns det inga tillämpliga metoder och ett bindningstidsfel inträffar.
- Den bästa metoden för uppsättningen kandidatmetoder identifieras med hjälp av reglerna för överbelastningslösning i §12.6.4. Om det inte går att identifiera en enda bästa metod är metodanropet tvetydigt och ett bindningstidsfel inträffar. Vid utförandet av överlagringsresolution beaktas parametrarna hos en generisk metod efter att typargumenten (antingen angivna eller härledda) har ersatts för motsvarande metodtypparametrar.
När en metod har valts och verifierats under bindningstiden enligt ovanstående steg bearbetas själva körningsanropet enligt reglerna för funktionsmedlemsanrop som beskrivs i §12.6.6.
Note: Den intuitiva effekten av de lösningsregler som beskrivs ovan är följande: Om du vill hitta den metod som anropas av en metodanrop börjar du med den typ som anges av metodens anrop och fortsätter upp i arvskedjan tills minst en tillämplig, tillgänglig, icke-åsidosättningsmetoddeklaration hittas. Utför sedan typinferens och överbelastningsmatchning på uppsättningen med tillämpliga, tillgängliga, icke-åsidosättningsmetoder som deklarerats i den typen och anropar den metod som valts. Om ingen metod hittades kan du i stället försöka bearbeta anropet som ett tilläggsmetodanrop. slutkommentar
12.8.10.3 Tilläggsmetodanrop
I ett metodanrop (§12.6.6.2) av en av formerna
«expr» . «identifier» ( )  
«expr» . «identifier» ( «args» )  
«expr» . «identifier» < «typeargs» > ( )  
«expr» . «identifier» < «typeargs» > ( «args» )
Om den normala bearbetningen av anropet inte hittar några tillämpliga metoder görs ett försök att bearbeta anropet som ett tilläggsmetodanrop. Om «expr» eller någon av «args» har kompileringstidstyp dynamic, tillämpas inte tilläggsmetoder.
Målet är att hitta den bästa type_nameC, så att motsvarande statiska metodanrop kan ske.
C . «identifier» ( «expr» )  
C . «identifier» ( «expr» , «args» )  
C . «identifier» < «typeargs» > ( «expr» )  
C . «identifier» < «typeargs» > ( «expr» , «args» )
En tilläggsmetod Cᵢ.Mₑ är giltig om:
- 
              Cᵢär en icke-generisk och icke-nästlad klass.
- Namnet på Mₑär identifierare
- 
              Mₑär tillgängligt och tillämpligt när det tillämpas på argumenten som en statisk metod enligt ovan
- Det finns en implicit identitet, referens eller boxningskonvertering från uttryck till typen av den första parametern i Mₑ.
Sökningen efter C fortsätter enligt följande:
- Från och med den närmaste omslutande namnområdesdeklarationen, fortsätter med varje omslutande namnområdesdeklaration och slutar med den innehållande kompileringsenheten, görs efterföljande försök att hitta en kandidatuppsättning med tilläggsmetoder: - Om det angivna namnområdet eller kompileringsenheten direkt innehåller icke-generiska typdeklarationer Cᵢmed berättigade tilläggsmetoderMₑär uppsättningen med dessa tilläggsmetoder kandidatuppsättningen.
- Om namnområden som importeras med hjälp av namnområdesdirektiv i det angivna namnområdet eller kompileringsenheten direkt innehåller icke-generiska typdeklarationer Cᵢmed berättigade tilläggsmetoderMₑ, är uppsättningen med dessa tilläggsmetoder kandidatuppsättningen.
 
- Om det angivna namnområdet eller kompileringsenheten direkt innehåller icke-generiska typdeklarationer 
- Om ingen kandidatuppsättning hittas i någon omslutande namnområdesdeklaration eller kompileringsenhet uppstår ett kompileringsfel.
- Annars tillämpas överlagringslösning på kandidatuppsättningen enligt beskrivningen i §12.6.4. Om ingen bästa metod hittas uppstår ett kompileringsfel.
- 
              Cär den typ inom vilken den bästa metoden deklareras som en tilläggsmetod.
Med C som mål bearbetas metodanropet sedan som en statisk metodanrop (§12.6.6).
Note: Till skillnad från instansmetodanropet utlöses inget undantag när uttryck utvärderas till en null-referens. I stället skickas det här
null-värdet till tilläggsmetoden precis som via ett vanligt statiskt metodanrop. Det är upp till implementeringen av tilläggsmetoden att bestämma hur ett sådant anrop ska besvaras. slutkommentar
Ovanstående regler innebär att instansmetoder har företräde framför tilläggsmetoder, att tilläggsmetoder som är tillgängliga i deklarationer för inre namnområden har företräde framför tilläggsmetoder som är tillgängliga i deklarationer för yttre namnområden och att tilläggsmetoder som deklareras direkt i ett namnområde har företräde framför tilläggsmetoder som importeras till samma namnområde med ett med hjälp av ett namnområdesdirektiv.
Exempel:
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) } }I exemplet har
B-metoden företräde framför den första tilläggsmetoden ochCmetoden har företräde framför båda tilläggsmetoderna.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(); } } }Utdata från det här exemplet är:
E.F(1) D.G(2) C.H(3)
D.Ghar företräde framförC.GochE.Fhar företräde framför bådeD.FochC.F.slutexempel
12.8.10.4 Anrop av delegater
För ett delegeringsanrop ska primäruttryck för anropsuttryck vara ett värde av typen delegattyp. Med tanke på att delegate_type vara funktionsmedlem med samma parameterlista som delegate_typeska delegate_type vara tillämpligt (§12.6.4.2) med avseende på argument_list i invocation_expression.
Körningsbearbetningen av ett delegeringsanrop av typen D(A), där D är ett primäruttryck av en delegeringstyp och A är en valfri argumentlista, består av följande steg:
- 
              Dutvärderas. Om den här utvärderingen orsakar ett undantag körs inga ytterligare steg.
- Argumentlistan Autvärderas. Om den här utvärderingen orsakar ett undantag körs inga ytterligare steg.
- Värdet för Dkontrolleras för att vara giltigt. Om värdet förDärnullgenereras enSystem.NullReferenceExceptionoch inga ytterligare steg körs.
- Annars är Den referens till en ombudsinstans. Funktionsmedlemsanrop (§12.6.6) utförs på var och en av de anropsbara entiteterna i ombudets anropslista. För anropsbara entiteter som består av en instans- och instansmetod är instansen för anrop den instans som finns i den anropsbara entiteten.
Mer information om flera anropslistor utan parametrar finns i §21.6 .
12.8.11 Null villkorsstyrd anropsuttryck
En null_conditional_invocation_expression är syntaktiskt antingen en null_conditional_member_access (§12.8.8) eller null_conditional_element_access (§12.8.13) där den sista dependent_access är ett anropsuttryck (§12.8.10).
En null_conditional_invocation_expression sker inom ramen för en statement_expression (§13.7), anonymous_function_body (§12.20.1) eller method_body (§15.6.1).
Till skillnad från den syntaktiskt likvärdiga null_conditional_member_access eller null_conditional_element_accesskan en null_conditional_invocation_expression klassificeras som ingenting.
null_conditional_invocation_expression
    : null_conditional_member_access null_forgiving_operator? '(' argument_list? ')'
    | null_conditional_element_access null_forgiving_operator? '(' argument_list? ')'
    ;
Den valfria null_forgiving_operator får endast inkluderas om null_conditional_member_access eller null_conditional_element_access har en delegate_type.
Ett null_conditional_invocation_expression uttryck E har formen P?A; där A är resten av den syntaktiskt likvärdiga null_conditional_member_access eller null_conditional_element_access, kommer A därför att börja med . eller [. Låt PA oss beteckna sammanlänkningen av P och A.
När E inträffar som en statement_expression är innebörden av E densamma som innebörden av -instruktionen:
if ((object)P != null) PA
förutom att P endast utvärderas en gång.
När E inträffar som en anonymous_function_body eller method_body innebörden av E beror på dess klassificering:
- Om - Eklassificeras som ingenting är dess innebörd densamma som innebörden av blocket:- { if ((object)P != null) PA; }- förutom att - Pendast utvärderas en gång.
- I annat fall är innebörden av - Esamma som innebörden av block:- { return E; }- och i sin tur beror innebörden av detta block på om - Eär syntaktiskt likvärdig med ett null_conditional_member_access (§12.8.8) eller null_conditional_element_access (§12.8.13).
12.8.12 Elementåtkomst
12.8.12.1 Allmänt
En element_access består av en primary_expression följt av en "[" token följt av en argument_list följt av en "]" token. 
              argument_list består av ett eller flera arguments, avgränsade med kommatecken.
element_access
    : primary_expression '[' argument_list ']'
    ;
När du känner igen ett primary_expression om alternativ för både element_access och pointer_element_access (§24.6.4) är tillämpliga, ska det senare väljas om den inbäddade primary_expression är av pekartyp (§24.3).
En primary_expression av en element_access får inte vara en array_creation_expression om den inte innehåller en array_initializer, eller en stackalloc_expression om den inte innehåller en stackalloc_initializer.
Obs! Den här begränsningen finns för att inte tillåta potentiellt förvirrande kod, till exempel:
var a = new int[3][1];som annars skulle tolkas som:
var a = (new int[3])[1];En liknande begränsning gäller för null_conditional_element_access (§12.8.13). slutkommentar
En element_access är dynamiskt bunden (§12.3.3) om minst något av följande gäller:
- 
              primary_expression har kompileringstidstyp dynamic.
- Minst ett uttryck för argument_list har kompileringstidstypen dynamic.
I det här fallet beror kompileringstidstypen för element_access på kompileringstidstypen för dess primary_expression: om den har en matristyp är kompileringstidstypen elementtypen för den matristypen. annars är dynamic kompileringstidstypen och element_access klassificeras som ett värde av typen dynamic. Reglerna nedan för att bestämma innebörden av element_access tillämpas sedan vid körning, genom att använda körningstypen istället för kompileringstidstypen för de uttryck av typen primary_expression och argument_list som har kompileringstidstypen dynamic. Om primary_expression inte har kompileringstidstyp dynamicgenomgår elementåtkomsten en begränsad kompileringstidskontroll enligt beskrivningen i §12.6.5.
Exempel:
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 dynamicslutexempel
Om primary_expression för en element_access är:
- ett värde av en matristyp, element_access är en matrisåtkomst (§12.8.12.2);
- ett värde av stringtypen är element_access en strängåtkomst (§12.8.12.3);
- I annat fall ska primary_expression vara en variabel eller ett värde för en klass, struct eller gränssnittstyp som har en eller flera indexerare, i vilket fall element_access är en indexerareåtkomst (§12.8.12.4).
12.8.12.2 Matrisåtkomst
För en matrisåtkomst får argument_list inte innehålla namngivna argument eller bireferensargument (§15.6.2.3).
Antalet uttryck i argument_list ska vara samma som rangordningen för array_type, och varje uttryck skall vara
- av typen int,uint,longellerulong; eller
- endast för åtkomst till en rangordningsmatris, av typen IndexellerRange; eller
- implicit kan konverteras till en eller flera av ovanstående typer.
Körningsbearbetningen av en matrisåtkomst i formuläret P[A], där P är en primary_expression av en array_type och A är en argument_list av indexuttryck, består av följande steg:
- 
              Putvärderas. Om den här utvärderingen orsakar ett undantag körs inga ytterligare steg.
- För varje indexuttryck i argument_list i ordning från vänster till höger: - Indexuttrycket utvärderas, låt typen av det resulterande värdet vara T;
- Det här värdet konverteras sedan till den första av typerna: int,uint,long,ulong, eller endast för åtkomst till en enskild rangordningsmatris,IndexellerRange; för vilken det finns en implicit konvertering (§10.2) från T .
- Om utvärderingen av ett indexuttryck eller den efterföljande implicita konverteringen orsakar ett undantag utvärderas inga ytterligare indexuttryck och inga ytterligare steg körs.
 
- Värdet för Pkontrolleras för att vara giltigt. Om värdet förPärnullgenereras enSystem.NullReferenceExceptionoch inga ytterligare steg körs.
- Om föregående steg har genererat ett enda indexvärde av typen Range:- Låt L vara längden på matrisen som refereras av P.
- 
              Aär giltigt med avseende på L (§18.3). Om det inte är det genereras enSystem.ArgumentOutOfRangeExceptionoch inga ytterligare steg körs.
- Startförskjutningen, S och antalet objekt, N, för Aavseende L bestäms enligt beskrivningen förGetOffsetAndLength(§18.3).
- Resultatet av matrisåtkomsten är en matris som innehåller en ytlig kopia av N-elementenPfrån och med index S. Om N är noll har matrisen noll element.
 
- Låt L vara längden på matrisen som refereras av 
Not: Både S och N kan vara noll ($24.3). Indexering av en tom matris är vanligtvis ogiltig, men indexering med ett tomt intervall som börjar på noll är giltigt och returnerar en tom matris. Definitionen tillåter också att S är L, det tidigare slutindexet (§18.1), i vilket fall N blir noll och en tom matris returneras. slutkommentar
Not: Det går inte att tilldela ett område med element i en matris till med hjälp av en matrisåtkomst. Detta skiljer sig från indexerarens åtkomst (§12.8.12.4) som kan, men inte behöver, stödja tilldelning till ett intervall av index som anges av ett
Rangevärde. slutkommentar
- Annars: - Resultatet av utvärderingen av matrisåtkomsten är en variabelreferens (§9.5) av matrisens elementtyp.
- Värdet för varje uttryck i argument_list kontrolleras mot de faktiska gränserna för varje dimension av matrisinstansen som refereras av P. Om ett eller flera värden ligger utanför intervallet, utlöses ettSystem.IndexOutOfRangeException-undantag och inga fler steg körs.
- Variabelreferensen för matriselementet som anges av indexuttrycken beräknas och detta blir resultatet av matrisåtkomsten.
 
12.8.12.3 Strängåtkomst
För strängåtkomst ska argument_list i element_access innehålla ett enda namnlöst värdeargument (§15.6.2.2) som ska vara:
- av typen int,IndexellerRange; eller
- implicit konvertibel till en eller flera av ovanstående typer.
Körningsbearbetningen av en strängåtkomst i formuläret P[A], där P är en primary_expression av string typen och A är ett enda uttryck, består av följande steg:
- 
              Putvärderas. Om den här utvärderingen orsakar ett undantag körs inga ytterligare steg.
- Indexuttrycket utvärderas, låt typen av det resulterande värdet vara T;
- Det här värdet konverteras sedan till den första av typerna: int,IndexellerRange; för vilka en implicit konvertering (§10.2) från T finns.
- Om utvärderingen av ett indexuttryck eller den efterföljande implicita konverteringen orsakar ett undantag utvärderas inga ytterligare indexuttryck och inga ytterligare steg körs.
- Värdet för Pkontrolleras för att vara giltigt. Om värdet förPärnullgenereras enSystem.NullReferenceExceptionoch inga ytterligare steg körs.
- Om föregående steg har genererat ett indexvärde av typen Range:- Resultatet av utvärderingen av strängåtkomsten är ett värde av stringtypen.
- Låt L vara längden på strängen som refereras av P.
- 
              Akontrolleras för att vara giltig med avseende på L (§18.3), om det inte är då enSystem.ArgumentOutOfRangeExceptionkastas och inga ytterligare steg körs.
- Startförskjutningen, S och antalet objekt, N, för Aavseende L bestäms enligt beskrivningen förGetOffsetAndLength(§18.3).
- Resultatet av strängåtkomsten är en sträng som bildas genom att kopiera N-tecknenPfrån S, om N är noll är strängen tom.
 
- Resultatet av utvärderingen av strängåtkomsten är ett värde av 
Not: Både S och N får vara noll (§18.3). Indexering av en tom sträng är vanligtvis ogiltig, men indexering med ett tomt intervall som börjar på noll är giltigt och returnerar en tom sträng. Definitionen gör också att S kan vara L, det tidigare slutindexet (§18.1), i vilket fall N blir noll och en tom sträng returneras. slutkommentar
- Annars: - Resultatet av utvärderingen av strängåtkomsten är ett värde av chartypen.
- Värdet för det konverterade indexuttrycket kontrolleras mot de faktiska gränserna för stränginstansen som refereras av P. Om värdet ligger inom intervallet genereras enSystem.IndexOutOfRangeExceptionoch inga ytterligare steg körs.
- Värdet för tecknet vid förskjutningen av det konverterade indexuttrycket med strängen Pblir resultatet av strängåtkomsten.
 
- Resultatet av utvärderingen av strängåtkomsten är ett värde av 
12.8.12.4 Indexer-åtkomst
För en indexerares åtkomst ska primary_expression för element_access vara en variabel eller ett värde för en klass, struct eller gränssnittstyp, och denna typ ska implementera en eller flera indexerare som är tillämpliga för argument_list av element_access. 
              Argument_list får inte innehålla out eller ref argument.
Bindningstiden för en indexeraråtkomst av formen P[A], där P är en primary_expression av en klass-, struct- eller interface-typ T, och A är en argument_list, består av följande steg:
- Uppsättningen indexerare som tillhandahålls av Tär konstruerad. Uppsättningen består av alla indexerare som deklareras iTeller en bastyp avTsom inte åsidosätter deklarationer och som är tillgängliga i den aktuella kontexten (§7.5).
- Uppsättningen reduceras till de indexerare som är tillämpliga och inte dolda av andra indexerare. Följande regler tillämpas på varje indexerare S.Ii uppsättningen, därSär den typ där indexerarenIdeklareras:- Om Iinte gäller förA(§12.6.4.2), tasIbort från uppsättningen.
- Om Igäller förA(§12.6.4.2), tas alla indexerare som deklarerats i en bastyp avSbort från uppsättningen.
- Om Igäller förA(§12.6.4.2) ochSär en annan klasstyp änobjecttas alla indexerare som deklarerats i ett gränssnitt bort från uppsättningen.
 
- Om 
- Om den resulterande uppsättningen kandidatindexerare är tom finns det inga tillämpliga indexerare och ett bindningstidsfel inträffar.
- Den bästa indexeraren för uppsättningen kandidatindexerare identifieras med hjälp av reglerna för överbelastningsupplösning i §12.6.4. Om det inte går att identifiera en enda bästa indexerare är indexerarens åtkomst tvetydig och ett bindningstidsfel inträffar.
- Åtkomsten till den bästa indexeraren kontrolleras: - Om indexerarens åtkomst är målet för en tilldelning ska indexeraren ha en set- eller ref get-accessor, annars uppstår ett bindningstidsfel.
- Annars ska indexeraren ha en get- eller ref get-accessor, annars uppstår ett bindningstidsfel.
 
Körningsbearbetningen av indexerarens åtkomst består av följande steg:
- Mål primary_expressionPutvärderas.
- Indexuttrycken för argument_listAutvärderas i ordning, från vänster till höger.
- Använda den bästa indexeraren som fastställs vid bindningstid:
12.8.13 Null Villkorlig elementåtkomst
En null_conditional_element_access består av en primary_expression följt av de två tokenerna "?" och "[", följt av en argument_list följt av en "]" token, följt av noll eller fler dependent_accesses som kan föregås av en null_forgiving_operator.
null_conditional_element_access
    : primary_expression '?' '[' argument_list ']'
      (null_forgiving_operator? dependent_access)*
    ;
En argument_list av en null_conditional_element_access får inte innehålla out eller ref argument.
Enprimary_expression av en null_conditional_element_access får inte vara en array_creation_expression om den inte innehåller en array_initializer eller en stackalloc_expression om den inte innehåller en stackalloc_initializer.
Obs! Den här begränsningen finns för att inte tillåta potentiellt förvirrande kod. En liknande begränsning gäller för element_access (§12.8.12) där ett exempel på vad som utesluts kan hittas. slutkommentar
En null_conditional_element_access är en villkorsstyrd version av element_access (§12.8.12) och det är ett bindningstidsfel om resultattypen är void. För ett null-villkorsuttryck vilket resulterar i att resultattypen kan vara void, se (§12.8.11).
Ett null_conditional_element_access-uttryck E är av formuläret P?[A]B; där B är de eventuella dependent_access. Innebörden av E bestäms på följande sätt:
- Om typen av - Pär en nullbar värdetyp:- Låt - Tvara typen av uttryck- P.Value[A]B.- Om - Tär en typparameter som inte är känd för att vara antingen en referenstyp eller en värdetyp som inte kan nulliseras uppstår ett kompileringsfel.
- Om - Tär en värdetyp som inte kan nollföras är typen av- E- T?, och innebörden av- Eär densamma som innebörden av:- ((object)P == null) ? (T?)null : P.Value[A]B- Förutom att - Pendast utvärderas en gång.
- Annars är typen av - E- Toch innebörden av- Eär densamma som innebörden av:- ((object)P == null) ? null : P.Value[A]B- Förutom att - Pendast utvärderas en gång.
 
- Annars: - Låt - Tvara typen av uttryck- P[A]B.- Om - Tär en typparameter som inte är känd för att vara antingen en referenstyp eller en värdetyp som inte kan nulliseras uppstår ett kompileringsfel.
- Om - Tär en värdetyp som inte kan nollföras är typen av- E- T?, och innebörden av- Eär densamma som innebörden av:- ((object)P == null) ? (T?)null : P[A]B- Förutom att - Pendast utvärderas en gång.
- Annars är typen av - E- Toch innebörden av- Eär densamma som innebörden av:- ((object)P == null) ? null : P[A]B- Förutom att - Pendast utvärderas en gång.
 
Obs: I ett uttryck av formen:
P?[A₀]?[A₁]om
Putvärderas tillnullutvärderas varkenA₀ellerA₁. Detsamma gäller om ett uttryck är en sekvens av null_conditional_element_access eller null_conditional_member_access§12.8.8 åtgärder.slutkommentar
12.8.14 Den här åtkomsten
En this_access består av nyckelordet this.
this_access
    : 'this'
    ;
En this_access tillåts endast i blocket av en instanskonstruktor, en instansmetod, en instansåtkomst (§12.2.1) eller en finaliserare. Den har någon av följande betydelser:
- När thisanvänds i en primary_expression inom en instanskonstruktor för en klass klassificeras den som ett värde. Värdets typ är instanstypen (§15.3.2) för klassen där användningen sker, och värdet är en referens till objektet som skapas.
- När thisanvänds i en primary_expression i en instansmetod eller instansåtkomst till en klass klassificeras den som ett värde. Värdets typ är instanstypen (§15.3.2) för klassen inom vilken användningen sker, och värdet är en referens till det objekt som metoden eller accessorn anropades för.
- När thisanvänds i en primary_expression inom en instanskonstruktor för en struct klassificeras den som en variabel. Variabeltypen är instanstypen (§15.3.2) för den struct inom vilken användningen sker och variabeln representerar den struct som skapas.- Om konstruktordeklarationen inte har någon konstruktorinitierare fungerar variabeln thisexakt på samma sätt som en utdataparameter av typen struct. Detta innebär särskilt att variabeln definitivt ska tilldelas i varje exekveringsväg för instanskonstruktorn.
- I annat fall fungerar variabeln thisexakt samma som enrefparameter av struct-typen. I synnerhet innebär det att variabeln anses vara ursprungligen tilldelad.
 
- Om konstruktordeklarationen inte har någon konstruktorinitierare fungerar variabeln 
- När thisanvänds i en primary_expression i en instansmetod eller instansåtkomst för en struct klassificeras den som en variabel. Variabeltypen är instanstypen (§15.3.2) för den struct inom vilken användningen sker.- Om metoden eller accessorn inte är en iterator (§15.15) eller asynkron funktion (§15.14) thisrepresenterar variabeln den struct för vilken metoden eller accessorn anropades.- Om structen är en readonly structfungerar variabelnthisexakt samma som en indataparameter av structtypen
- Annars beter sig variabeln thisexakt på samma sätt som enrefparameter av struct-typen
 
- Om structen är en 
- Om metoden eller accessorn är en iterator eller asynkron funktion representerar variabeln thisen kopia av den struktur för vilken metoden eller accessorn anropades och fungerar exakt på samma sätt som en värdeparameter av strukturtypen.
 
- Om metoden eller accessorn inte är en iterator (§15.15) eller asynkron funktion (§15.14) 
Användning av this i en primary_expression i en annan kontext än de som anges ovan är ett kompileringsfel. I synnerhet går det inte att referera till this i en statisk metod, en statisk egenskapsaccessor eller i en variable_initializer i en fältdeklaration.
12.8.15 Grundläggande åtkomst
En base_access består av nyckelordet base följt av antingen en ”.” token, en identifierare och en valfri type_argument_list, eller en argument_list inom hakparenteser.
base_access
    : 'base' '.' identifier type_argument_list?
    | 'base' '[' argument_list ']'
    ;
En base_access används för att komma åt basklassmedlemmar som är dolda av medlemmar med liknande namn i den aktuella klassen eller struct. En base_access tillåts endast i en instanskonstruktors brödtext, en instansmetod, en instansaccessor (§12.2.1), eller en finalisator. När base.I inträffar i en klass eller struct ska I ange en medlem i basklassen för den klassen eller structen. På samma sätt ska en tillämplig indexerare finnas i basklassen när base[E] inträffar i en klass.
Vid bindningstid utvärderas base_access uttryck av formen base.I och base[E] exakt som om de skrevs ((B)this).I och ((B)this)[E], där B är basklassen för klassen eller structen där konstruktionen förekommer. Därför motsvarar base.I och base[E]this.I och this[E], förutom att this betraktas som en instans av basklassen.
När en base_access refererar till en virtuell funktionsmedlem (en metod, egenskap eller indexerare) ändras fastställandet av vilken funktionsmedlem som ska anropas vid körning (§12.6.6). Den funktionsmedlem som anropas bestäms genom att hitta den mest härledda implementeringen (§15.6.4) av funktionsmedlemmen med avseende på B (i stället för med avseende på körningstyp this, vilket brukar vara fallet vid en icke-basåtkomst). Inom en överskrivning av en virtuell funktionsmedlem kan därför en base_access användas för att anropa den ärvda implementeringen av funktionsmedlemmen. Om funktionsmedlemmen som refereras av en base_access är abstrakt uppstår ett bindningstidsfel.
Note: Till skillnad från
thisärbaseinte ett uttryck i sig. Det är ett nyckelord som endast används i samband med en base_access eller en constructor_initializer (§15.11.2). slutkommentar
12.8.16 Postfix-inkrements- och minskningsoperatorer
post_increment_expression
    : primary_expression '++'
    ;
post_decrement_expression
    : primary_expression '--'
    ;
Operand för en postfix-inkrements- eller dekrementåtgärd ska vara ett uttryck som klassificeras som en variabel, en egenskapsåtkomst eller en indexeråtkomst. Resultatet av åtgärden är ett värde av samma typ som operanden.
Om primary_expression har kompileringstidstypen dynamic är operatorn dynamiskt bunden (§12.3.3), post_increment_expression eller post_decrement_expression har kompileringstyp dynamic och följande regler tillämpas vid körning med hjälp av körningstypen för primary_expression.
Om operanden för en postfix inkrement- eller dekrementoperation är en egenskaps- eller indexerartillgång, ska egenskapen eller indexeraren ha både en get- och en set-accessor. Om så inte är fallet uppstår ett bindningstidsfel.
Unary operator overload resolution (§12.4.4) används för att välja en specifik operatorimplementering. Fördefinierade operatorer för ++ och -- finns för följande typer: sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimaloch alla uppräkningstyper. De fördefinierade ++ operatorerna returnerar värdet som genereras genom att lägga till 1 till operanden, och de fördefinierade -- operatorerna returnerar värdet som genereras genom att subtrahera 1 från operanden. I en kontrollerad kontext, om resultatet av det här tillägget eller subtraktionen ligger utanför resultattypens intervall och resultattypen är en heltalstyp eller enum-typ, kastas en System.OverflowException.
Det ska finnas en implicit konvertering från returtypen för den valda unary-operatorn till typen av primary_expression, annars uppstår ett kompileringsfel.
Körningsbearbetningen av en postfix-inkrements- eller minskningsåtgärd för formuläret x++ eller x-- består av följande steg:
- Om xklassificeras som en variabel:- 
              xutvärderas för att skapa variabeln.
- Värdet för xsparas.
- Det sparade värdet för xkonverteras till operandtypen för den valda operatorn och operatorn anropas med det här värdet som argument.
- Värdet som returneras av operatorn konverteras till typen av xoch lagras på den plats som angavs i den tidigare utvärderingen avx.
- Det sparade värdet för xblir resultatet av åtgärden.
 
- 
              
- Om xklassificeras som åtkomst till en egenskap eller indexerare:- Instansuttrycket (om xinte ärstatic) och argumentlistan (omxär en indexeringsåtkomst) som är associerad medxutvärderas, och resultaten används i efterföljande hämtnings- och inställningsanrop av accessor.
- Get-accessorn för xanropas och det returnerade värdet sparas.
- Det sparade värdet för xkonverteras till operandtypen för den valda operatorn och operatorn anropas med det här värdet som argument.
- Värdet som returneras av operatorn konverteras till typen av xoch den angivna åtkomstorn förxanropas med det här värdet som värdeargument.
- Det sparade värdet för xblir resultatet av åtgärden.
 
- Instansuttrycket (om 
Operatorerna ++ och -- stöder även prefix notation (§12.9.7). Resultatet av x++ eller x-- är värdet för xföre åtgärden, medan resultatet av ++x eller --x är värdet för xefter åtgärden. I båda fallen har x samma värde efter åtgärden.
En operator ++ eller operator -- implementering kan anropas med antingen postfix eller prefix notation. Det går inte att ha separata operatorimplementeringar för de två notationerna.
12.8.17 Den nya operatorn
12.8.17.1 Allmänt
Operatorn new används för att skapa nya instanser av typer.
Det finns tre former av nya uttryck:
- Objektskapandeuttryck används för att skapa nya instanser av klasstyper och värdetyper.
- Uttryck för att skapa matriser används för att skapa nya instanser av matristyper.
- Uttryck för att skapa delegeringar används för att hämta instanser av delegattyper.
Operatorn new innebär att en instans av en typ skapas, men innebär inte nödvändigtvis allokering av minne. I synnerhet kräver instanser av värdetyper inget extra minne utöver de variabler som de finns i, och inga allokeringar sker när new används för att skapa instanser av värdetyper.
Notering: Delegeringsuttryck skapar inte alltid nya instanser. När uttrycket bearbetas på samma sätt som en metodgruppkonvertering (§10,8) eller en anonym funktionskonvertering (§10,7) kan detta leda till att en befintlig delegatinstans återanvänds. slutkommentar
12.8.17.2 Objektskapandeuttryck
12.8.17.2.1 Allmänt
En object_creation_expression används för att skapa en ny instans av en class_type eller en value_type.
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
    ;
Den typen av en object_creation_expression ska vara en class_type, en value_typeeller en type_parameter. Den typen får inte vara en tuple_type eller en abstrakt eller statisk class_type.
Den valfria argument_list (§12.6.2) är endast tillåten om typ är en class_type eller en struct_type.
Ett objektskapandeuttryck kan utelämna konstruktorns argumentlista och omsluta parenteser förutsatt att det innehåller en objektinitierare eller samlingsinitierare. Att utelämna konstruktorns argumentlista och omsluta parenteser motsvarar att ange en tom argumentlista.
Bearbetning av ett objektskapandeuttryck som innehåller en objektinitierare eller insamlingsinitiator består av att först bearbeta instanskonstruktorn och sedan bearbeta de initieringar av medlemmar eller element som anges av objektinitieraren (§12.8.17.2.2) eller insamlingsinitiator (§12.8.17.2.3).
Om något av argumenten i den valfria argumentlistan har kompileringstypen dynamic så binds objekt_skapat_uttryck dynamiskt (§12.3.3) och följande regler gäller vid körtid med körtidstypen för argumenten i argumentlistan som har kompileringstypen dynamic. Objektskapandet genomgår dock en begränsad kompileringstidskontroll enligt beskrivningen i §12.6.5.
Bindningstidsbehandlingen av en object_creation_expression av formuläret new T(A), där T är en class_type, eller en value_type, och A är en valfri argument_list, består av följande steg:
- Om Tär en value_type ochAinte finns:- 
              object_creation_expression är en standardkonstruktoranrop. Resultatet av object_creation_expression är ett värde av typen T, nämligen standardvärdet förTenligt definitionen i §8.3.3.
 
- 
              object_creation_expression är en standardkonstruktoranrop. Resultatet av object_creation_expression är ett värde av typen 
- Annars, om Tär en type_parameter ochAinte finns:- Om ingen begränsning av värdetyp eller konstruktor (§15.2.5) har angetts för Tuppstår ett bindningstidsfel.
- Resultatet av object_creation_expression är ett värde av den körningstyp som typparametern har bundits till, nämligen resultatet av att anropa standardkonstruktorn av den typen. Körningstypen kan vara en referenstyp eller en värdetyp.
 
- Om ingen begränsning av värdetyp eller konstruktor (§15.2.5) har angetts för 
- Annars, om Tär en class_type eller en struct_type:- Om Tär en abstrakt eller statisk class_typeuppstår ett kompileringsfel.
- Instanskonstruktorn som ska anropas bestäms med hjälp av reglerna för överbelastningsmatchning i §12.6.4. Uppsättningen kandidatinstanskonstruktorer består av alla tillgängliga instanskonstruktorer som deklareras i T, som gäller förA(§12.6.4.2). Om uppsättningen kandidatinstanskonstruktorer är tom, eller om en enda bästa instanskonstruktor inte kan identifieras, uppstår ett bindningstidsfel.
- Resultatet av object_creation_expression är ett värde av typen T, nämligen värdet som skapas genom att anropa instanskonstruktorn som fastställdes i steget ovan.
- Annars är object_creation_expression ogiltig och ett bindningstidsfel inträffar.
 
- Om 
Även om object_creation_expression är dynamiskt bunden är kompileringstidstypen fortfarande T.
Körningsbearbetningen av en object_creation_expression av formuläret new T(A), där T är class_type eller en struct_type och A är en valfri argument_list, består av följande steg:
- Om Tär en class_type:- En ny instans av klass Tallokeras. Om det inte finns tillräckligt med minne tillgängligt för att allokera den nya instansen genereras enSystem.OutOfMemoryExceptionoch inga ytterligare steg körs.
- Alla fält i den nya instansen initieras till standardvärdena (§9.3).
- Instanskonstruktorn anropas enligt reglerna för funktionsmedlemsanrop (§12.6.6). En referens till den nyligen allokerade instansen skickas automatiskt till instanskonstruktorn och instansen kan nås inifrån konstruktorn som detta.
 
- En ny instans av klass 
- Om Tär en struct_type:- En instans av typen Tskapas genom att en tillfällig lokal variabel allokeras. Eftersom en instanskonstruktor för en struct_type krävs för att definitivt tilldela ett värde till varje fält i instansen som skapas, krävs ingen initiering av den tillfälliga variabeln.
- Instanskonstruktorn anropas enligt reglerna för funktionsmedlemsanrop (§12.6.6). En referens till den nyligen allokerade instansen skickas automatiskt till instanskonstruktorn och instansen kan nås inifrån konstruktorn som detta.
 
- En instans av typen 
12.8.17.2.2 Objektinitierare
En objektinitierare anger värden för noll eller fler fält, egenskaper eller indexerade element i ett objekt.
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
    ;
En objektinitierare består av en sekvens av medlemsinitierare som omges av { och } token och avgränsas med kommatecken. Varje member_initializer ska ange ett mål för initieringen. En identifierare ska namnge ett tillgängligt fält eller en egenskap för objektet som initieras, medan en argument_list inom hakparenteser ska ange argument för en tillgänglig indexerare för objektet som initieras. Det är ett fel för en objektinitierare att inkludera fler än en medlemsinitierare för samma fält eller egenskap.
Obs: Även om en objektinitierare inte tillåts ange samma fält eller egenskap mer än en gång, finns det inga sådana begränsningar för indexerare. En objektinitierare kan innehålla flera initialiserarmål som refererar till indexerare och kan till och med använda samma indexeringsargument flera gånger. slutkommentar
Varje initializer_target följs av ett likhetstecken och antingen ett uttryck, en objektinitierare eller en samlingsinitierare. Det är inte möjligt för uttryck i objektinitieraren att referera till det nyligen skapade objektet som initieras.
I argument_list av en initializer_target finns det inget implicit stöd för argument av typen Index (§18.4.2) eller Range (§18.4.3).
En medlemsinitierare som anger ett uttryck efter att likhetstecknet bearbetas på samma sätt som en tilldelning (§12.22.2) till målet.
En medlemsinitierare som anger en objektinitierare efter likhetstecknet är en kapslad objektinitierare, dvs. en initiering av ett inbäddat objekt. I stället för att tilldela ett nytt värde till fältet eller egenskapen behandlas tilldelningarna i den kapslade objektinitieraren som tilldelningar till medlemmar i fältet eller egenskapen. Kapslade objektinitierare kan inte tillämpas på egenskaper med en värdetyp eller skrivskyddade fält med en värdetyp.
En medlemsinitierare som anger en insamlingsinitierare efter likhetstecknet är en initiering av en inbäddad samling. I stället för att tilldela en ny samling till målfältet, egenskapen eller indexeraren läggs elementen som anges i initieraren till i samlingen som refereras av målet. Målet ska vara av en samlingstyp som uppfyller kraven i §12.8.17.2.3.
När ett initialiserarmål refererar till en indexerare ska argumenten till indexeraren alltid utvärderas exakt en gång. Även om argumenten aldrig används (t.ex. på grund av en tom kapslad initiator) utvärderas de för sina biverkningar.
Exempel: Följande klass representerar en punkt med två koordinater:
public class Point { public int X { get; set; } public int Y { get; set; } }En instans av
Pointkan skapas och initieras på följande sätt:Point a = new Point { X = 0, Y = 1 };Detta har samma effekt som
Point __a = new Point(); __a.X = 0; __a.Y = 1; Point a = __a;där
__aär en i övrigt osynlig och otillgänglig tillfällig variabel.I följande klass visas en rektangel som skapats från två punkter och skapandet och initieringen av en
Rectangleinstans:public class Rectangle { public Point P1 { get; set; } public Point P2 { get; set; } }En instans av
Rectanglekan skapas och initieras på följande sätt:Rectangle r = new Rectangle { P1 = new Point { X = 0, Y = 1 }, P2 = new Point { X = 2, Y = 3 } };Detta har samma effekt som
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;där
__r,__p1och__p2är tillfälliga variabler som annars är osynliga och otillgängliga.Om
Rectanglekonstruktor allokerar de två inbäddadePoint-instanserna kan de användas för att initiera inbäddadePointinstanser i stället för att tilldela nya instanser:public class Rectangle { public Point P1 { get; } = new Point(); public Point P2 { get; } = new Point(); }Följande konstruktion kan användas för att initiera inbäddade
Pointinstanser i stället för att tilldela nya instanser:Rectangle r = new Rectangle { P1 = { X = 0, Y = 1 }, P2 = { X = 2, Y = 3 } };Detta har samma effekt som
Rectangle __r = new Rectangle(); __r.P1.X = 0; __r.P1.Y = 1; __r.P2.X = 2; __r.P2.Y = 3; Rectangle r = __r;slutexempel
12.8.17.2.3 Insamlingsinitierare
En insamlingsinitierare anger elementen i en samling.
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)*
    ;
En samlinginitierare består av en sekvens av elementinitierare som omges av { och } tokenar och avgränsas med kommatecken. Varje elementinitierare anger ett element som ska läggas till i samlingsobjektet som initieras och består av en lista över uttryck som omges av { och } token och avgränsas med kommatecken. En elementinitialisering med ett enda uttryck kan skrivas utan klammerparenteser, men får då inte vara ett tilldelningsuttryck, för att undvika tvetydighet med medlemsinitialisatorer. Den non_assignment_expression produktionen definieras i §12.23.
Exempel: Följande är ett exempel på ett uttryck för att skapa objekt som innehåller en samlingsinitierare:
List<int> digits = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };slutexempel
Samlingsobjektet som en insamlingsinitierare tillämpas på ska vara av en typ som implementerar System.Collections.IEnumerable eller ett kompileringsfel inträffar. För varje angivet element i ordning från vänster till höger tillämpas normal medlemssökning för att hitta en medlem med namnet Add. Om resultatet av medlemssökningen inte är en metodgrupp uppstår ett kompileringsfel. Annars tillämpas överbelastningsupplösning med uttryckslistan för elementinitieraren som argumentlista, och insamlingsinitieraren anropar den resulterande metoden. Samlingsobjektet ska därför innehålla en tillämplig instans- eller tilläggsmetod med namnet Add för varje elementinitierare.
Exempel: Följande visar en klass som representerar en kontakt med ett namn och en lista med telefonnummer samt skapande och initiering av en
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" } } }; } }som har samma effekt som
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;där
__clist,__c1och__c2är tillfälliga variabler som annars är osynliga och otillgängliga.slutexempel
12.8.17.3 Uttryck för skapande av anonyma objekt
En anonymous_object_creation_expression används för att skapa ett objekt av anonym typ.
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
    ;
En anonym objektinitierare deklarerar en anonym typ och returnerar en instans av den typen. En anonym typ är en namnlös klasstyp som ärver direkt från object. Medlemmarna i en anonym typ är en sekvens av skrivskyddade egenskaper som härleds från en anonym objektinitiator för att skapa en instans av typen. Mer specifikt en anonym objektinitierare av formuläret
              new {
              p₁=e₁,p₂=e₂, ... 
              pv=ev}
deklarerar en anonym typ av formuläret
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() { ... }
}
där varje «Tx» är typen av motsvarande uttryck «ex». Uttrycket som används i en member_declarator ska ha en typ. Det är alltså ett kompileringsfel för ett uttryck i en member_declarator vara null eller en anonym funktion.
Namnen på en anonym typ och parametern till dess Equals-metoden genereras automatiskt av kompilatorn och kan inte refereras till i programtext.
I samma program skapar två anonyma objektinitierare som anger en sekvens med egenskaper för samma namn och kompileringstidstyper i samma ordning instanser av samma anonyma typ.
Exempel: I exemplet
var p1 = new { Name = "Lawnmower", Price = 495.00 }; var p2 = new { Name = "Shovel", Price = 26.95 }; p1 = p2;tilldelningen på den sista raden tillåts eftersom
p1ochp2är av samma anonyma typ.slutexempel
Metoderna Equals och GetHashcode för anonyma typer åsidosätter de metoder som ärvts från objectoch definieras i termer av Equals och GetHashcode av egenskaperna, så att två instanser av samma anonyma typ är lika om och endast om alla deras egenskaper är lika.
En medlemsdeklarator kan förkortas till ett enkelt namn (§12.8.4), en medlemsåtkomst (§12.8.7), en nullvillkorlig projektioninitialisator §12.8.8 eller en basåtkomst (§12.8.15). Detta kallas för en projektionsinitierare och är en förkortning för en deklaration av och tilldelning till en egenskap med samma namn. Mer specifikt, medlemsdeklaratorer av formulären
              «identifier», «expr» . «identifier» och «expr» ? . «identifier»
är exakt likvärdiga med följande:
              «identifier» = «identifier», «identifier» = «expr» . «identifier» och «identifier» = «expr» ? . «identifier»
I en projektionsinitiering väljer identifieraren därför både värdet och det fält eller den egenskap som värdet tilldelas till. Intuitivt projekterar en projektion initierare inte bara ett värde, utan även namnet på värdet.
12.8.17.4 Matrisskapandeuttryck
En array_creation_expression används för att skapa en ny instans av en array_type.
array_creation_expression
    : 'new' non_array_type '[' expression_list ']' rank_specifier*
      array_initializer?
    | 'new' array_type array_initializer
    | 'new' rank_specifier array_initializer
    ;
Ett uttryck för att skapa matriser i det första formuläret allokerar en matrisinstans av den typ som är resultatet av att ta bort vart och ett av de enskilda uttrycken från uttryckslistan.
Exempel: Uttrycket för att skapa matrisen
new int[10,20]genererar en matrisinstans av typenint[,], och matrisens skapandeuttryck nyint[10][,]skapar en matrisinstans av typenint[][,]. slutexempel
Varje uttryck i uttryckslistan ska vara av typen int, uint, long, eller ulong, eller implicit konvertibel till en eller flera av dessa typer. Värdet för varje uttryck avgör längden på motsvarande dimension i den nyligen allokerade matrisinstansen. Eftersom längden på en matrisdimension ska vara icke-negativt är det ett kompileringsfel att ha ett konstant uttryck med ett negativt värde i uttryckslistan.
Förutom i ett osäkert sammanhang (§24.2) är layouten för matriser ospecificerad.
Om ett uttryck för att skapa matriser i det första formuläret innehåller en matrisinitierare ska varje uttryck i uttryckslistan vara en konstant och de rang- och dimensionslängder som anges i uttryckslistan ska matcha matrisinitierarens.
I ett uttryck för matrisskapande av det andra eller tredje formuläret ska rangordningen för den angivna matristypen eller rangspecificeraren matcha matrisinitierarens. De enskilda dimensionslängderna härleds från antalet element i var och en av motsvarande kapslingsnivåer i matrisinitieraren. Initieringsuttrycket i följande deklaration
var a = new int[,] {{0, 1}, {2, 3}, {4, 5}};
motsvarar exakt
var a = new int[3, 2] {{0, 1}, {2, 3}, {4, 5}};
Ett uttryck för att skapa matriser i det tredje formuläret kallas för ett implicit skrivet uttryck för skapande av matriser. Det liknar det andra formuläret, förutom att elementtypen för matrisen inte uttryckligen anges, men bestäms som den vanligaste typen (§12.6.3.16) av uppsättningen uttryck i matrisinitieraren. För en flerdimensionell array, dvs. en där rank_specifier innehåller minst ett kommatecken, består den här uppsättningen av alla uttrycksom finns i kapslade array_initializers.
Arrayinitierare beskrivs ytterligare i §17.7.
Resultatet av utvärderingen av ett uttryck för skapande av matris klassificeras som ett värde, nämligen en referens till den nyligen allokerade matrisinstansen. Körningsbearbetningen av ett uttryck för matrisskapande består av följande steg:
- Dimensionslängduttrycken för expression_list utvärderas i ordning, från vänster till höger. Efter utvärdering av varje uttryck utförs en implicit konvertering (§10.2) till någon av följande typer: int,uint,long,ulong. Den första typen i den här listan som en implicit konvertering finns för väljs. Om utvärderingen av ett uttryck eller den efterföljande implicita konverteringen orsakar ett undantag utvärderas inga ytterligare uttryck och inga ytterligare steg utförs.
- De beräknade värdena för dimensionslängderna verifieras enligt följande: Om ett eller flera av värdena är mindre än noll genereras en System.OverflowExceptionoch inga ytterligare steg körs.
- En matrisinstans med angivna dimensionslängder allokeras. Om det inte finns tillräckligt med minne tillgängligt för att allokera den nya instansen genereras en System.OutOfMemoryExceptionoch inga ytterligare steg körs.
- Alla element i den nya matrisinstansen initieras till standardvärdena (§9.3).
- Om matrisens skapandeuttryck innehåller en matrisinitierare utvärderas varje uttryck i matrisinitieraren och tilldelas motsvarande matriselement. Utvärderingarna och tilldelningarna utförs i den ordning som uttrycken skrivs i matrisinitieraren, med andra ord initieras elementen i ökad indexordning, och dimensionen längst till höger ökar först. Om utvärderingen av ett givet uttryck eller den efterföljande tilldelningen till motsvarande matriselement orsakar ett undantag initieras inga ytterligare element (och de återstående elementen har därmed sina standardvärden).
Ett uttryck för att skapa matriser tillåter instansiering av en matris med element av en matristyp, men elementen i en sådan matris ska initieras manuellt.
Exempel: Påståendet
int[][] a = new int[100][];skapar en endimensionell matris med 100 element av typen
int[]. Det ursprungliga värdet för varje element ärnull. Det är inte möjligt för samma matriscreeringsuttryck att också instansiera undermatriserna, och påståendetint[][] a = new int[100][5]; // Errorresulterar i ett kompileringsfel. Instansiering av undermatriserna kan i stället utföras manuellt, som i
int[][] a = new int[100][]; for (int i = 0; i < 100; i++) { a[i] = new int[5]; }slutexempel
Obs: När en matris med matriser har en "rektangulär" form, vilket innebär att undermatriserna har samma längd, är det mer effektivt att använda en flerdimensionell matris. I exemplet ovan skapar instansiering av matrisen med matriser 101 objekt – en yttre matris och 100 undermatriser. Däremot
int[,] a = new int[100, 5];skapar bara ett enskilt objekt, en tvådimensionell matris, och utför allokeringen i en enda instruktion.
slutkommentar
Exempel: Följande är exempel på implicit skrivna uttryck för skapande av matriser:
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" }; // ErrorDet sista uttrycket orsakar ett kompileringsfel eftersom varken
intellerstringimplicit kan konverteras till det andra, så det finns ingen vanlig typ. Ett explicit matrisskapande uttryck måste användas i det här fallet, till exempel genom att ange typen som ska varaobject[]. Alternativt kan ett av elementen gjutas till en gemensam bastyp, som sedan skulle bli den härledda elementtypen.slutexempel
Implicit skrivna matrisskapandeuttryck kan kombineras med anonyma objektinitierare (§12.8.17.3) för att skapa anonymt inskrivna datastrukturer.
Exempel:
var contacts = new[] { new { Name = "Chris Smith", PhoneNumbers = new[] { "206-555-0101", "425-882-8080" } }, new { Name = "Bob Harris", PhoneNumbers = new[] { "650-555-0199" } } };slutexempel
12.8.17.5 Delegeringsskapandeuttryck
En delegate_creation_expression används för att skapa en instans av en delegate_typ.
delegate_creation_expression
    : 'new' delegate_type '(' expression ')'
    ;
Argumentet för ett uttryck för att skapa delegat ska vara en metodgrupp, en anonym funktion eller ett värde av antingen kompileringstidstypen dynamic eller en delegate_type. Om argumentet är en metodgrupp identifierar det metoden och för en instansmetod det objekt som du vill skapa ett ombud för. Om argumentet är en anonym funktion definierar det direkt parametrarna och metodkroppen för delegatmålet. Om argumentet är ett värde identifierar det en delegerad instans som du kan skapa en kopia av.
Om uttrycket har kompileringstidstypen dynamictillämpas delegate_creation_expression dynamiskt (§12.8.17.5) och reglerna nedan tillämpas vid körning med uttryckets körningstyp. Annars tillämpas reglerna vid kompileringstid.
Bindningstidsbearbetningen av en delegate_creation_expression av formuläret ny D(E), där D är en delegate_type och E är ett uttryck, består av följande steg:
- Om - Eär en metodgrupp bearbetas delegeringsskapande uttrycket på samma sätt som en omvandling av metodgrupp (§10.8) från- Etill- D.
- Om - Eär en anonym funktion bearbetas uttrycket för att skapa en delegate på samma sätt som en konvertering av anonym funktion (§10,7) från- Etill- D.
- Om - Eär ett värde ska- Evara kompatibelt (§21.2) med- D, och resultatet är en referens till ett nyligen skapat ombud med en lista över anrop med en enda post som anropar- E.
Körningsbearbetningen av ett delegatskapandeuttryck av formen ny D(E), där D är en delegattyp och E är ett uttryck, består av följande steg:
- Om Eär en metodgrupp utvärderas delegeringsuttrycket som en metodgruppskonvertering (§10,8) frånEtillD.
- Om Eär en anonym funktion utvärderas skapandet av ombudet som en anonym funktionskonvertering frånEtillD(§10.7).
- Om Eär ett värde för en delegate_type:- 
              Eutvärderas. Om den här utvärderingen orsakar ett undantag körs inga ytterligare steg.
- Om värdet för Eärnullgenereras enSystem.NullReferenceExceptionoch inga ytterligare steg körs.
- En ny instans av delegattypen Dallokeras. Om det inte finns tillräckligt med minne tillgängligt för att allokera den nya instansen genereras enSystem.OutOfMemoryExceptionoch inga ytterligare steg körs.
- Den nya delegatinstansen initieras med en anropslista som har ett enda inlägg som anropar E.
 
- 
              
Anropslistan för ett ombud bestäms när ombudet instansieras och förblir sedan konstant under hela ombudets livslängd. Med andra ord går det inte att ändra målanropsbara entiteter för ett ombud när det har skapats.
Obs: Kom ihåg att när två ombud kombineras eller en tas bort från en annan, resulterar ett nytt ombud. inget befintligt ombud har ändrat sitt innehåll. slutkommentar
Det går inte att skapa en delegat som refererar till en egenskap, indexerare, användardefinierad operator, instanskonstruktor, finaliser eller statisk konstruktor.
Exempel: Som beskrivs ovan avgör parameterlistan och returtypen för ombudet vilken av de överlagrade metoderna som ska väljas när ett ombud skapas från en metodgrupp. I exemplet
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; }fältet
A.finitieras med en delegerare som refererar till den andraSquare-metoden, eftersom metoden matchar exakt parameterlistan och returtypen avDoubleFunc. Om den andraSquaremetoden inte hade funnits skulle ett kompileringsfel ha uppstått.slutexempel
12.8.18 Typ av operator
Operatorn typeof används för att hämta System.Type-objektet för en typ.
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
    : ','
    ;
Den första formen av typeof_expression består av ett typeof nyckelord följt av en parentestyp. Resultatet av ett uttryck av denna form är objektet System.Type för den angivna typen. Det finns bara ett System.Type objekt för en viss typ. Det innebär att för en typ Tär typeof(T) == typeof(T) alltid sant. Typen kan inte vara dynamic.
Den andra formen av typeof_expression består av ett typeof nyckelord följt av en parentesiserad unbound_type_name.
Obs! Grammatikreglerna för unbound_type_name och unbound_qualified_alias_member följer de för type_name (§7.8) och qualified_alias_member (§14.8.1) förutom att generic_dimension_specifier ersätts med type_argument_list. slutkommentar
När man erkänner operanden för en typeof_expression om både unbound_type_name och type_name är tillämpliga, nämligen när den varken innehåller en generic_dimension_specifier eller en type_argument_list, ska type_name väljas.
Obs! ANTLR gör det angivna valet automatiskt på grund av ordningen på alternativen för typeof_expression. slutkommentar
Innebörden av en unbound_type_name bestäms som om:
- Sekvensen med token konverteras till en type_name genom att ersätta varje generic_dimension_specifier med en type_argument_list med samma antal kommatecken och nyckelordet objectsom varje type_argument.
- Den resulterande type_name är en konstruerad typ (§7.8).
- Den unbound_type_name är sedan den obundna generiska typen som är associerad med den lösta konstruerade typen (§8.4).
Obs! Det finns inget krav på att en implementering ska transformera sekvensen av token, eller skapa den mellanliggande konstruerade typen, bara att den obundna generiska typen som fastställs är "som om" den här processen följdes. slutkommentar
Det är ett fel när typnamnet ska vara en nullbar referenstyp.
Resultatet av typeof_expression är det System.Type objektet för den resulterande obundna generiska typen.
Den tredje formen av typeof_expression består av ett typeof nyckelord följt av ett parentesiserat void nyckelord. Resultatet av ett uttryck för det här formuläret är det System.Type objekt som representerar frånvaron av en typ. Typobjektet som returneras av typeof(void) skiljer sig från det typobjekt som returneras för vilken typ som helst.
Note: Det här speciella
System.Type-objektet är användbart i klassbibliotek som tillåter reflektion över metoder i språket, där dessa metoder vill ha ett sätt att representera returtypen för alla metoder, inklusivevoid-metoder, med en instans avSystem.Type. slutkommentar
Operatorn typeof kan användas på en typparameter. Det är ett kompileringstidsfel om typnamnet är känt som en nullbar referenstyp. Resultatet är objektet System.Type för körtidstypen som var bunden till typparametern. Om körningstypen är en nullbar referenstyp blir resultatet den motsvarande icke-nullbara referenstypen. Den typeof-operatorn kan också användas på en konstruerad typ eller en obunden generisk typ (§8.4.4). Det System.Type objektet för en obundet allmän typ är inte detsamma som det System.Type objektet av instanstypen (§15.3.2). Instanstypen är alltid en slutet konstruktionstyp under körning, så System.Type-objektet beror på vilka körningstidsargument som används. Den obundna generiska typen har däremot inga typargument och ger samma System.Type objekt oavsett argument av körningstyp.
Exempel: Exemplet
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(); } }genererar följande utdata:
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]Observera att
intochSystem.Int32är av samma typ. Resultatet avtypeof(X<>)beror inte på typargumentet, men resultatet avtypeof(X<T>)gör det.slutexempel
12.8.19 Operatorns storlek
Operatorn sizeof returnerar antalet 8-bitars byte som används av en variabel av en viss typ. Den typ som anges som operand till storlek skall vara en unmanaged_type (§8.8).
sizeof_expression
    : 'sizeof' '(' unmanaged_type ')'
    ;
För vissa fördefinierade typer ger sizeof-operatorn ett konstant int värde enligt tabellen nedan:
| uttryck | resultat | 
|---|---|
| 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 | 
För en uppräkningstyp Tär resultatet av uttrycket sizeof(T) ett konstant värde som motsvarar storleken på dess underliggande typ, som nämnts ovan. För alla andra operandtyper anges operatorn sizeof i §24.6.9.
12.8.20 De kontrollerade och okontrollerade operatorerna
Operatorerna checked och unchecked används för att styra överflödeskontrollkontexten för aritmetiska åtgärder och konverteringar av integraltyp.
checked_expression
    : 'checked' '(' expression ')'
    ;
unchecked_expression
    : 'unchecked' '(' expression ')'
    ;
Operatorn checked utvärderar det inneslutna uttrycket i en kontrollerad kontext och unchecked-operatorn utvärderar det inneslutna uttrycket i en omarkerad kontext. En checked_expression eller unchecked_expression motsvarar exakt en parenthesized_expression (§12.8.5), förutom att det inneslutna uttrycket utvärderas i det angivna överflödeskontrollkontexten.
Överflödskontrollkontexten kan också styras via checked- och unchecked-instruktioner (§13.12).
Följande operationer påverkas av den överflödeskontrollkontext som har upprättats av de markerade och okontrollerade operatorerna och uttrycken:
- Fördefinierade ++operatorer och--operatorer (§12.8.16 och §12.9.7), när operanden är av en integral- eller uppräkningstyp.
- Den fördefinierade -unary operatorn (§12.9.3), när operanden är av en integraltyp.
- De fördefinierade +operatorerna ,-,*och/binära operatorer (§12.11), när båda operanderna är av integral- eller uppräkningstyper.
- Explicita numeriska konverteringar (§10.3.2) från en integral- eller uppräkningstyp till en annan integral- eller uppräkningstyp, eller från floatellerdoubletill en integral- eller uppräkningstyp.
När någon av ovanstående åtgärder ger ett resultat som är för stort för att representera i måltypen, styr kontexten där åtgärden utförs det resulterande beteendet:
- Om åtgärden i ett checkedsammanhang är ett konstant uttryck (§12.24) uppstår ett kompileringsfel. Annars genereras enSystem.OverflowExceptionnär åtgärden utförs vid körning.
- I en uncheckedkontext trunkeras resultatet genom att alla bitar i hög ordning som inte får plats i måltypen ignoreras.
För icke-konstanta uttryck (§12.24) (uttryck som utvärderas vid körning) som inte omges av några checked operatorer eller unchecked instruktioner, är standardkontexten för kontroll av spill avmarkerad, såvida inte externa faktorer (till exempel kompilatorväxlar och konfiguration av körningsmiljön) anropar för kontrollerad utvärdering.
För konstanta uttryck (§12.24) (uttryck som kan utvärderas fullständigt vid kompileringstid) kontrolleras alltid standardkontexten för spillkontroll. Om inte ett konstant uttryck uttryckligen placeras i en unchecked kontext orsakar spill som uppstår under kompileringstidsutvärderingen av uttrycket alltid kompileringsfel.
Brödtexten för en anonym funktion påverkas inte av checked eller unchecked kontexter där den anonyma funktionen inträffar.
Exempel: I följande kod
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 }inga kompileringsfel rapporteras eftersom inget av uttrycken kan utvärderas vid kompileringstid. Vid körningen genererar metoden
FenSystem.OverflowException, och metodenGreturnerar –727379968 (de lägre 32 bitarna i resultatet som inte ligger inom intervallet). Beteendet för metodenHberor på standardkontexten för spillkontroll för kompilering, men den är antingen samma somFeller samma somG.slutexempel
Exempel: I följande kod
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 översvämningar som uppstår vid utvärdering av konstantuttrycken i
FochHorsakar kompileringsfel som rapporteras eftersom uttrycken utvärderas i enchecked-kontext. Ett överflöde uppstår också när det konstanta uttrycket utvärderas iG, men eftersom utvärderingen sker i ettunchecked-sammanhang rapporteras inte överflödet.slutexempel
Operatorerna checked och unchecked påverkar bara överflödeskontrollkontexten för de åtgärder som finns i token "(" och ")". Operatorerna har ingen effekt på funktionsmedlemmar som anropas till följd av utvärderingen av det inneslutna uttrycket.
Exempel: I följande kod
class Test { static int Multiply(int x, int y) => x * y; static int F() => checked(Multiply(1000000, 1000000)); }användningen av
checkedi F påverkar inte utvärderingen avx * yiMultiply, såx * yutvärderas i standardkontexten för kontroll av spill.slutexempel
Operatorn unchecked är praktisk när du skriver konstanter av de signerade integraltyperna i hexadecimal notation.
Exempel:
class Test { public const int AllBits = unchecked((int)0xFFFFFFFF); public const int HighBit = unchecked((int)0x80000000); }Båda hexadecimala konstanterna ovan är av typen
uint. Eftersom konstanterna ligger utanförintintervallet, utan operatornunchecked, skulle gjutningarna tillintgenerera kompileringsfel.slutexempel
Obs: Operatorer och instruktioner för
checkedochuncheckedgör det möjligt för programmerare att kontrollera vissa aspekter av vissa numeriska beräkningar. Beteendet för vissa numeriska operatorer beror dock på deras operanders datatyper. Om du till exempel multiplicerar två decimaler resulterar det alltid i ett undantag vid spill även inom en explicit omarkerad konstruktion. På samma sätt resulterar multiplikation av två flyttal aldrig i ett undantag vid överflöd, även inom en explicit kontrollstruktur. Dessutom påverkas inte andra operatorer av kontrollläget, oavsett om de är standard eller explicita. slutkommentar
12.8.21 Standardvärdeuttryck
Ett standardvärdeuttryck används för att hämta standardvärdet (§9.3) av en typ.
default_value_expression
    : explicitly_typed_default
    | default_literal
    ;
explicitly_typed_default
    : 'default' '(' type ')'
    ;
default_literal
    : 'default'
    ;
En default_literal representerar ett standardvärde (§9.3). Den har ingen typ, men kan konverteras till vilken typ som helst genom en standardliteralkonvertering (§10.2.16).
Resultatet av en default_value_expression är standardvärdet (§9.3) av den explicita typen i en explicitly_typed_default, eller måltypen för konverteringen för en default_value_expression.
Ett default_value_expression är ett konstant uttryck (§12.24) om typen är en av:
- en referenstyp
- en typparameter som är känd för att vara en referenstyp (§8.2);
- någon av följande värdetyper: sbyte,byte,short,ushort,int,uint,long,ulong,char,float,double,decimal,bool,; eller
- vilken enumereringstyp som helst.
12.8.22 Stack-allokering
Ett uttryck för stackallokering allokerar ett minnesblock från call stacken. Den körningsstacken är ett minnesområde där lokala variabler lagras. Körningsstacken är inte en del av den hanterade heapen. Det minne som används för lokal variabellagring återställs automatiskt när den aktuella funktionen returneras.
Reglerna för säker kontext för ett stackallokeringsuttryck beskrivs i §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
    ;
              unmanaged_type (§8.8) anger vilken typ av objekt som ska lagras på den nyligen allokerade platsen och uttryck anger antalet objekt. Tillsammans anger dessa den nödvändiga allokeringsstorleken. Typen av uttryck ska implicit konverteras till typen int.
Eftersom storleken på en stackallokering inte kan vara negativ är det ett kompileringsfel att ange antalet objekt som en constant_expression som utvärderas till ett negativt värde.
Under körning, om antalet objekt som ska allokeras är ett negativt värde, är beteendet odefinierat. Om det är noll görs ingen allokering och värdet som returneras är implementeringsdefinierat. Om det inte finns tillräckligt med minne tillgängligt för att allokera objekten genereras en System.StackOverflowException.
När en stackalloc_initializer finns:
- Om unmanaged_type utelämnas, härleds det enligt reglerna för bästa vanliga typ (§12.6.3.16) för uppsättningen av stackalloc_element_initializers.
- Om konstantuttryck utelämnas, härleds det till antalet stackalloc-elementinitialiseringar.
- Om konstantuttryck finns ska detta uttryck vara lika med antalet stackalloc_element_initializer:er.
Varje stackalloc_element_initializer ska ha en implicit konvertering till unmanaged_type (§10.2). stackalloc_element_initializerinitierar element i det allokerade minnet i ökande ordning, med början med elementet vid index noll. I avsaknad av en stackalloc_initializerär innehållet i det nyligen allokerade minnet odefinierat.
Om en stackalloc_expression inträffar direkt som initieringsuttrycket för en local_variable_declaration (§13.6.2), där local_variable_type antingen är en pekartyp (§24.3) eller härledd (var), är resultatet av stackalloc_expression en pekare av typen T* (§24.9). I det här fallet måste stackalloc_expression visas i osäker kod. Annars är resultatet av en stackalloc_expression en instans av typen Span<T>, där T är unmanaged_type:
- 
              Span<T>(§C.3) är en ref-strukturtyp (§16.2.3), som presenterar ett minnesblock, här blocket som allokerats av stackalloc_expression, som en indexerbar samling av typat element (T).
- Resultatets Length-egenskap returnerar antalet allokerade objekt.
- Resultatets indexerare (§15,9) returnerar en variable_reference (§9,5) till en post i det allokerade blocket och är intervallkontrollerad.
Stackallokeringsinitierare tillåts inte i catch- eller finally-blocken (§13.11).
Obs: Det finns inget sätt att uttryckligen frigöra minne som allokerats med hjälp av
stackalloc. slutkommentar
Alla stackallokerade minnesblock som skapats under exekveringen av en funktionsmedlem frigörs automatiskt när den funktionsmedlemmen returnerar.
Förutom operatorn stackalloc tillhandahåller C# inga fördefinierade konstruktioner för hantering av icke-skräpinsamlat minne. Sådana tjänster tillhandahålls vanligtvis av stöd för klassbibliotek eller importeras direkt från det underliggande operativsystemet.
Exempel:
// 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; } }När det gäller
span8resulterarstackalloci enSpan<int>, som konverteras av en implicit operator tillReadOnlySpan<int>. På samma sätt konverteras den resulterandespan9förSpan<double>till den användardefinierade typenWidget<double>med hjälp av konverteringen, som visas. slutexempel
12.8.23 Operatorns namn
En nameof_expression används för att hämta namnet på en programentitet som en konstant sträng.
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
    ;
Eftersom nameof inte är ett nyckelord är en nameof_expression alltid syntaktiskt tvetydig med ett anrop av det enkla namnet nameof. Om ett namnsökning (§12.8.4) av namnet nameof lyckas behandlas uttrycket av kompatibilitetsskäl som en invocation_expression – oavsett om anropet är giltigt. Annars är det en nameof_expression.
Enkla namn och medlemsåtkomstsökningar utförs på named_entity vid kompileringstid, enligt de regler som beskrivs i §12.8.4 och §12.8.7. Men om uppslag som beskrivs i §12.8.4 och §12.8.7 resulterar i ett fel eftersom en instansmedlem hittades i ett statiskt sammanhang, genererar en nameof_expression inget sådant fel.
Det är ett kompileringsfel för en named_entity som anger att en metodgrupp ska ha en type_argument_list. Det är ett kompileringsfel om en named_entity_target har typen dynamic.
En nameof_expression är ett konstant uttryck med typen stringoch har ingen effekt vid körning. Mer specifikt så utvärderas inte dess named_entity och ignoreras vid definitiv tilldelningsanalys (§9.4.4.22). Dess värde är den sista identifieraren för named_entity före den valfria slutgiltiga type_argument_list, transformerad på följande sätt:
- Prefixet "@", om det används, tas bort.
- Varje unicode_escape_sequence omvandlas till motsvarande Unicode-tecken.
- Alla formaterings_tecken tas bort.
Dessa är samma omvandlingar som tillämpas i §6.4.3 vid testning av likhet mellan identifierare.
Exempel: Följande illustrerar resultatet av olika
nameofuttryck, förutsatt att en allmän typList<T>deklareras inomSystem.Collections.Genericnamnområde: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 { } }Potentiellt överraskande delar av det här exemplet är lösningen på
nameof(System.Collections.Generic)till bara "Generic" i stället för det fullständiga namnområdet ochnameof(TestAlias)till "TestAlias" i stället för "String". slutexempel
12.8.24 Anonyma metoduttryck
En anonymous_method_expression är ett av två sätt att definiera en anonym funktion. Dessa beskrivs ytterligare i §12.20.
12.9 Unary operator
12.9.1 Allmänt
Operatorerna +, , -( ! logisk negation §12.9.4 ), ~, ^, ++, --, cast och await operatorer kallas unary operatorer.
Obs: Den postfixa null-förlåtande operatorn (§12.8.9),
!, på grund av dess natur som endast påverkar kompileringstiden och inte kan överbelastas, undantas från ovanstående lista. slutkommentar
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) och addressof_expression (§24.6.5) är endast tillgängliga i osäker kod (§24).
Om operand av en unary_expression har kompileringstidstypen dynamic, är den dynamiskt bunden (§12.3.3). I det här fallet:
- kompileringstidstypen för unary_expression är: - 
              Indexför indexet^från slutoperatören (§12.9.6)
- 
              dynamicför alla andra unary-operatorer. och
 
- 
              
- den lösning som beskrivs nedan sker vid körning med hjälp av körningstypen för operanden.
12.9.2 Unary plus operator
För en operation av formen +x, tillämpas operatöröverbelastningslösning (§12.4.4) för att välja en specifik operatörsimplementering. Operand konverteras till parametertypen för den valda operatorn och typen av resultat är operatorns returtyp. De fördefinierade unära plusoperatorerna är:
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);
För var och en av dessa operatorer är resultatet bara operandens värde.
Uppliftade (§12.4.8) former av de icke-uppliftade fördefinierade unära plusoperatörer som definieras ovan är också fördefinierade.
12.9.3 Unär negativ operator
För en operation av formen –x, tillämpas operatöröverbelastningslösning (§12.4.4) för att välja en specifik operatörsimplementering. Operand konverteras till parametertypen för den valda operatorn och typen av resultat är operatorns returtyp. De fördefinierade unary minus-operatorerna är:
- Heltalsnegation - int operator –(int x); long operator –(long x);- Resultatet beräknas genom att subtrahera - Xfrån noll. Om värdet för- Xär det minsta representable-värdet av operandetypen (−2³¹ för- inteller −2⁶³ för- long), kan den matematiska negationen av- Xinte representeras inom operandtypen. Om detta inträffar inom en- checked-kontekst kastas en- System.OverflowException; om det inträffar inom en- unchecked-kontekst är resultatet operandens värde och överflödet rapporteras inte.- Om negationsoperatorns operand är av typen - uintkonverteras den till typen- longoch resultatets typ är- long. Ett undantag är regeln som tillåter att- int-värdet- −2147483648(−2³¹) skrivs som decimal heltal (§6.4.5.3).- Om negationsoperatorns operand är av typen - ulonguppstår ett kompileringsfel. Ett undantag är regeln som tillåter att- long-värdet- −9223372036854775808(−2⁶³) skrivs som en decimal heltalsliteral (§6.4.5.3)
- Flyttal negation: - float operator –(float x); double operator –(double x);- Resultatet är värdet för - Xmed dess tecken inverterat. Om- xär- NaNblir resultatet också- NaN.
- Negation av decimaltal - decimal operator –(decimal x);- Resultatet beräknas genom att subtrahera - Xfrån noll. Decimalnegation motsvarar att använda den unära minusoperatorn av typen- System.Decimal.
Lyftas (§12.4.8) bildar av de unlifted fördefinierade unary minus operatörerna som definieras ovan, är också fördefinierade.
12.9.4 Logisk negationsoperator
För en operation av formen !x, tillämpas operatöröverbelastningslösning (§12.4.4) för att välja en specifik operatörsimplementering. Operand konverteras till parametertypen för den valda operatorn och typen av resultat är operatorns returtyp. Det finns bara en fördefinierad logisk negationsoperator:
bool operator !(bool x);
Den här operatorn beräknar operandens logiska negation: Om operanden är trueblir resultatet false. Om operand är falseblir resultatet true.
Lyfta former (§12.4.8) av den icke-lyfta fördefinierade logiska negationsoperatorn som definieras ovan är också fördefinierade.
              Observera: De prefix logiska negations- och postfix null-förlåtande operatorerna (§12.8.9), även om de representeras av samma lexikala token (!), är distinkta. 
              slutkommentar
12.9.5 Bitvis komplementoperator
För en operation av formen ~x, tillämpas operatöröverbelastningslösning (§12.4.4) för att välja en specifik operatörsimplementering. Operand konverteras till parametertypen för den valda operatorn och typen av resultat är operatorns returtyp. De fördefinierade bitvis kompletterande operatorerna är:
int operator ~(int x);
uint operator ~(uint x);
long operator ~(long x);
ulong operator ~(ulong x);
För var och en av dessa operatorer är resultatet av åtgärden det bitvisa komplementet av x.
Varje uppräkningstyp E tillhandahåller implicit följande bitvis komplementoperator:
E operator ~(E x);
Resultatet av utvärderingen av ~x, där X är ett uttryck av en uppräkningstyp E med en underliggande typ U, är exakt detsamma som att utvärdera (E)(~(U)x), förutom att konverteringen till E alltid utförs som i en unchecked kontext (§12.8.20).
Lyfta (§12.4.8) versioner av de olyfta fördefinierade bitvisa komplementsoperatorer som definieras ovan är också fördefinierade.
12.9.6 Index från slutpunktsoperator
Den oföränderliga ^ operatorn kallas indexet från slutpunktsoperatorn (som i dagligt tal kallas för hattoperatorn). Den här operatorn är inte överbelastad (§12.4.3) och det finns en enda fördefinierad operator:
Index operator ^(int x);
Resultatet av en åtgärd i formuläret ^x är ett värde från slutet Index (§18.2) som motsvarar resultatet av uttrycket:
new Index(x, true)
Precis som med andra unary_expressions kan operanden ha en kompileringstidstyp av dynamic (§12.9.1) och vara dynamiskt bunden (§12.3.3). Resultatets kompileringstidstyp är alltid Index.
En lyftad (§12.4.8) form av indexet från-avslutar operatören är också fördefinierad.
12.9.7 Inkrements- och minskningsoperatorer för prefix
pre_increment_expression
    : '++' unary_expression
    ;
pre_decrement_expression
    : '--' unary_expression
    ;
Operand för en prefixinkrement- eller prefixdekrementoperation ska vara ett uttryck som klassificeras som en variabel, en egenskapsåtkomst eller en indexeringsåtkomst. Resultatet av åtgärden är ett värde av samma typ som operanden.
Om operanden för en prefixinkrement- eller prefixdekrementoperation är en egenskaps- eller indexerartillgång, ska egenskapen eller indexeraren ha både en 'get'- och en 'set'-accessor. Om så inte är fallet uppstår ett bindningstidsfel.
Unary operator overload resolution (§12.4.4) används för att välja en specifik operatorimplementering. Fördefinierade operatorer för ++ och -- finns för följande typer: sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimaloch alla uppräkningstyper. De fördefinierade ++ operatorerna returnerar värdet som genereras genom att lägga till 1 till operanden, och de fördefinierade -- operatorerna returnerar värdet som genereras genom att subtrahera 1 från operanden. I en checked kontext, om resultatet av det här tillägget eller subtraktionen ligger utanför resultattypens intervall och resultattypen är en integrerad typ eller uppräkningstyp, genereras en System.OverflowException.
Det ska finnas en implicit konvertering från returtypen för den valda unary-operatorn till typen av unary_expression, annars uppstår ett kompileringsfel.
Körningsbearbetningen av en prefixoperation för inkrement eller dekrement av formuläret ++x eller --x består av följande steg:
- Om xklassificeras som en variabel:- 
              xutvärderas för att skapa variabeln.
- Värdet för xkonverteras till operandtypen för den valda operatorn och operatorn anropas med det här värdet som argument.
- Värdet som returneras av operatorn konverteras till typen x. Det resulterande värdet lagras på den plats som anges av utvärderingen avxoch blir resultatet av åtgärden.
 
- 
              
- Om xklassificeras som åtkomst till en egenskap eller indexerare:- Instansuttrycket (om xinte ärstatic) och argumentlistan (omxär en indexeringsåtkomst) som är associerad medxutvärderas, och resultaten används i efterföljande hämtnings- och inställningsanrop av accessor.
- Anropet för get-återgivaren av xutförs.
- Värdet som returneras av get-accessorn konverteras till operandtypen för den valda operatorn och operatorn anropas med det här värdet som argument.
- Värdet som returneras av operatorn konverteras till typen x. Set-accessorn förxanropas med det här värdet som värdeargument.
- Det här värdet blir också resultatet av åtgärden.
 
- Instansuttrycket (om 
Operatörerna ++ och -- stöder också postfix notation (§12.8.16). Resultatet av x++ eller x-- är värdet för x före åtgärden, medan resultatet av ++x eller --x är värdet för x efter åtgärden. I båda fallen har x samma värde efter åtgärden.
En operator ++ eller operator -- implementering kan anropas med antingen postfix eller prefix notation. Det går inte att ha separata operatorimplementeringar för de två notationerna.
Lyfta (§12.4.8) former av de o-lyfta fördefinierade prefix-inkrement- och decrement-operatorer som definieras ovan är också fördefinierade.
12.9.8 Gjutna uttryck
En cast_expression används för att explicit konvertera ett uttryck till en viss typ.
cast_expression
    : '(' type ')' unary_expression
    ;
En cast_expression av formen (T)E, där T är en typ och E är en unary_expression, utför en explicit konvertering (§10.3) av värdet för E till typ T. Om det inte finns någon explicit konvertering från E till Tinträffar ett bindningstidsfel. Annars är resultatet det värde som genereras av den explicita konverteringen. Resultatet klassificeras alltid som ett värde, även om E anger en variabel.
Grammatiken för en cast_expression leder till vissa syntaktiska tvetydigheter.
Exempel: Uttrycket
(x)–ykan antingen tolkas som en cast_expression (en uppsättning–yatt skrivax) eller som en additive_expression kombinerad med en parenthesized_expression (som beräknar värdetx – y). slutexempel
För att lösa cast_expression tvetydigheter finns följande regel: En sekvens med en eller flera token (§6.4) som omges av parenteser anses vara början på en cast_expression endast om minst ett av följande är sant:
- Sekvensen med token är korrekt grammatik för en typ, men inte för ett uttryck.
- Sekvensen med token är korrekt grammatik för en typ, och token omedelbart efter slutparenteserna är token "~", token "!", token "(", en identifierare (§6.4.3), en literal (§6.4.5), eller nyckelord (§6.4.4) utomasochis.
Termen "korrekt grammatik" ovan innebär endast att sekvensen av token ska överensstämma med den specifika grammatiska produktionen. Den tar särskilt inte hänsyn till den faktiska innebörden av några komponenter.
Exempel: Om
xochyär identifierare ärx.ykorrekt grammatik för en typ, även omx.yfaktiskt inte anger någon typ. slutexempel
Obs: Från disambiguationsregeln följer det att om
xochyär identifierare(x)y,(x)(y)och(x)(-y)är cast_expressions, men(x)-yär det inte, även omxidentifierar en typ. Men omxär ett nyckelord som identifierar en fördefinierad typ (till exempelint) är alla fyra formulären cast_expression(eftersom ett sådant nyckelord eventuellt inte kan vara ett uttryck av sig självt). slutkommentar
12.9.9 Invänta uttryck
12.9.9.1 Allmänt
Operatorn await används för att pausa utvärderingen av den omslutande asynkrona funktionen tills den asynkrona åtgärd som representeras av operand har slutförts.
await_expression
    : 'await' unary_expression
    ;
En await_expression tillåts endast i en asynkron funktions brödtext (§15.14). Inom den närmaste omslutande asynkrona funktionen får en await_expression inte förekomma på följande platser:
- Inuti en kapslad (icke-asynkron) anonym funktion
- Inuti blocket av en lock_statement
- I en anonym funktionskonvertering till en uttrycksträdstyp (§10.7.3)
- I ett osäkert sammanhang
Note: En await_expression kan inte förekomma på de flesta platser inom en query_expressioneftersom de syntaktiskt omvandlas till att använda lambda-uttryck som inte är asynkrona. slutkommentar
I en asynkron funktion ska await inte användas som en available_identifier även om ordagrant identifierare @await kan användas. Det finns därför ingen syntaktisk tvetydighet mellan await_expressions och olika uttryck som omfattar identifierare. Utanför asynkrona funktioner fungerar await som en normal identifierare.
Operand för en await_expression kallas uppgift. Den representerar en asynkron åtgärd som kanske eller kanske inte är slutförd när await_expression utvärderas. Syftet med await-operatorn är att pausa körningen av den omslutande asynkrona funktionen tills den väntade uppgiften är klar och sedan inhämta resultatet.
12.9.9.2 Väntande uttryck
Uppgiften för en await_expression måste vara väntande. Ett uttryck t är väntbar om något av följande villkor är uppfyllda:
- 
              tär av kompileringstidstypdynamic
- 
              thar en tillgänglig instans eller tilläggsmetod som heterGetAwaiterutan parametrar och inga typparametrar, och en returtypAför vilken alla följande villkor gäller:- 
              Aimplementerar gränssnittetSystem.Runtime.CompilerServices.INotifyCompletion(kallas därefterINotifyCompletionför korthet)
- 
              Ahar en tillgänglig, läsbar instansegenskapIsCompletedav typenbool
- 
              Ahar en tillgänglig instansmetodGetResultutan parametrar och inga typparametrar
 
- 
              
Syftet med GetAwaiter-metoden är att hämta en awaiter för uppgiften. Typen A kallas vänttyp för vänta-uttrycket.
Syftet med egenskapen IsCompleted är att avgöra om uppgiften redan är slutförd. I så fall behöver du inte pausa utvärderingen.
Syftet med metoden INotifyCompletion.OnCompleted är att registrera en "fortsättning" på uppgiften. d.v.s. ett ombud (av typen System.Action) som anropas när uppgiften är klar.
Syftet med metoden GetResult är att få resultatet av uppgiften när den är klar. Det här resultatet kan vara en lyckad slutföring, eventuellt med ett resultatvärde, eller så kan det vara ett undantag som kastas av metoden GetResult.
12.9.9.3 Klassificering av inväntningsuttryck
Uttrycket await t klassificeras på samma sätt som uttrycket (t).GetAwaiter().GetResult(). Om returtypen för GetResult därför är voidklassificeras await_expression som ingenting. Om den har en icke-void returtyp Tklassificeras await_expression som ett värde av typen T.
12.9.9.4 Körningsutvärdering av await-uttryck
Vid körning utvärderas uttrycket await t enligt följande:
- En awaiter ahämtas genom att utvärdera uttrycket(t).GetAwaiter().
- En boolberhålls genom att utvärdera uttrycket(a).IsCompleted.
- Om bärfalseberor utvärderingen på omaimplementerar gränssnittetSystem.Runtime.CompilerServices.ICriticalNotifyCompletion(kallas därefterICriticalNotifyCompletionför korthet). Den här kontrollen görs vid bindningstillfället. d.v.s. vid körning omahar kompileringstidstypendynamicoch vid kompileringstillfället annars. Låtrbeteckna återtagandedelegaten (§15.14):- Om ainte implementerarICriticalNotifyCompletionutvärderas uttrycket((a) as INotifyCompletion).OnCompleted(r).
- Om aimplementerarICriticalNotifyCompletionutvärderas uttrycket((a) as ICriticalNotifyCompletion).UnsafeOnCompleted(r).
- Utvärderingen pausas sedan och kontrollen returneras till den aktuella anroparen för funktionen async.
 
- Om 
- Antingen omedelbart efter (om bärtrue), eller vid senare anrop av återupptagningsdelegaten (ombärfalse), utvärderas uttrycket(a).GetResult(). Om det returnerar ett värde är det värdet resultatet av await_expression. Annars är resultatet ingenting.
En inväntares implementering av gränssnittsmetoderna INotifyCompletion.OnCompleted och ICriticalNotifyCompletion.UnsafeOnCompleted bör leda till att ombudet r anropas högst en gång. I annat fall är beteendet för den omslutande asynkrona funktionen odefinierat.
12.10 Intervalloperator
Operatorn .. kallas för intervalloperator .
range_expression
    : unary_expression
    | unary_expression? '..' unary_expression?
    ;
Den fördefinierade intervalloperatorn är:
Range operator ..(Index x, Index y);
Intervalloperatorn är inte överbelastad (§12.4.3).
Alla intervalluttryck behandlas med formuläret x..y, där:
- 
              xär den vänstra operanden om den finns, annars uttrycket0; och
- 
              yär rätt operand om den finns, annars uttrycket^0.
Resultatet av åtgärden är ett Range (§18.3) värde som motsvarar resultatet av uttrycket:
new Range(x, y)
Om antingen eller båda operanderna i ett intervalluttryck har kompileringstidstypen dynamic, är uttrycket dynamiskt bundet (§12.3.3). Resultatets kompileringstidstyp är alltid Range.
En lyft (§12.4.8) bildar av spännaoperatorn är också fördefinierad.
Intervalloperatorn är icke-associativ (§12.4.2).
12.11 Aritmetiska operatorer
12.11.1 Allmänt
Operatorerna *, /, %, +och - kallas aritmetiska operatorer.
multiplicative_expression
    : range_expression
    | multiplicative_expression '*' range_expression
    | multiplicative_expression '/' range_expression
    | multiplicative_expression '%' range_expression
    ;
additive_expression
    : multiplicative_expression
    | additive_expression '+' multiplicative_expression
    | additive_expression '-' multiplicative_expression
    ;
Om en operand av en aritmetikoperator har kompileringstidstypen dynamic, är uttrycket dynamiskt bundet (§12.3.3). I det här fallet är kompileringstidstypen för uttrycket dynamic, och lösningen som beskrivs nedan sker vid körning med körningstypen för de operander som har kompileringstypen dynamic.
12.11.2 Multiplikationsoperator
För en åtgärd av formuläret x * ytillämpas binär operatoröverbelastningsmatchning (§12.4.5) för att välja en specifik operatorimplementering. Operanderna konverteras till parametertyperna för den valda operatorn och typen av resultat är operatorns returtyp.
De fördefinierade multiplikationsoperatorerna visas nedan. Operatorerna beräknar alla produkten av x och y.
- Heltalsmultiplikation - int operator *(int x, int y); uint operator *(uint x, uint y); long operator *(long x, long y); ulong operator *(ulong x, ulong y);- I en - checkedkontext kastas en- System.OverflowExceptionom produkten ligger utanför resultattypens intervall. I ett- unchecked-kontext rapporteras inte spill, och signifikanta bitar i högre ordning utanför resultattypens intervall kastas bort.
- Flyttalsmultiplikation - float operator *(float x, float y); double operator *(double x, double y);- Produkten beräknas enligt reglerna i IEC 60559-aritmetik. I följande tabell visas resultatet av alla möjliga kombinationer av icke-ändliga värden, nollor, infiniteter och NaN. I tabellen är - xoch- ypositiva ändliga värden.- zär resultatet av- x * y, avrundat till närmaste värde som kan representeras. Om resultatet är för stort för måltypen är- zoändlighet. På grund av avrundning kan- zvara noll trots att varken- xeller- yär noll.- +y- -y- +0- -0- +∞- -∞- NaN- +x- +z- -z- +0- -0- +∞- -∞- NaN- -x- -z- +z- -0- +0- -∞- +∞- NaN- +0- +0- -0- +0- -0- NaN- NaN- NaN- -0- -0- +0- -0- +0- NaN- NaN- NaN- +∞- +∞- -∞- NaN- NaN- +∞- -∞- NaN- -∞- -∞- +∞- NaN- NaN- -∞- +∞- NaN- NaN- NaN- NaN- NaN- NaN- NaN- NaN- NaN- (Om inget annat anges innebär användningen av "" i flyttalstabellerna i §12.11.2– - +att värdet är positivt, användningen av "- -" innebär att värdet är negativt, och avsaknaden av ett tecken innebär att värdet kan vara positivt eller negativt eller inte har något tecken (NaN).)
- Decimal multiplikation: - decimal operator *(decimal x, decimal y);- Om storleken på det resulterande värdet är för stort för att representera i decimalformatet genereras en - System.OverflowException. På grund av avrundning kan resultatet bli noll även om ingen av operanderna är noll. Resultatets skala, före avrundning, är summan av skalningarna för de två operanderna. Decimal multiplikation motsvarar att använda multiplikationsoperatorn av typen- System.Decimal.
Lyfta (§12.4.8) former av de icke-lyfta fördefinierade multiplikationsoperatorerna som definieras ovan är också fördefinierade.
12.11.3 Divisionsoperatör
För en åtgärd av formuläret x / ytillämpas binär operatoröverbelastningsmatchning (§12.4.5) för att välja en specifik operatorimplementering. Operanderna konverteras till parametertyperna för den valda operatorn och typen av resultat är operatorns returtyp.
De fördefinierade divisionsoperatorerna visas nedan. Operatorerna beräknar alla kvoten för x och y.
- Heltalsdivision: - int operator /(int x, int y); uint operator /(uint x, uint y); long operator /(long x, long y); ulong operator /(ulong x, ulong y);- Om värdet för rätt operand är noll genereras en - System.DivideByZeroException.- Divisionen avrundar resultatet mot noll. Således är det absoluta värdet för resultatet det största möjliga heltalet som är mindre än eller lika med det absoluta värdet för kvoten för de två operanderna. Resultatet är noll eller positivt när de två operanderna har samma tecken och noll eller negativa när de två operanderna har motsatta tecken. - Om den vänstra operanden är det minsta representerbara - inteller- longvärde och den högra operanden är- –1, uppstår en översvämning. I ett- checked-kontekst leder detta till att en- System.ArithmeticException(eller en underklass därav) kastas. I ett- unchecked-sammanhang är det implementeringsdefinierat om en- System.ArithmeticException(eller en underklass därav) kastas eller om överskridningen inte rapporteras och resultatet blir värdet av den vänstra operanden.
- Flyttalsdelning: - float operator /(float x, float y); double operator /(double x, double y);- Kvoten beräknas enligt reglerna i IEC 60559-aritmetik. I följande tabell visas resultatet av alla möjliga kombinationer av icke-ändliga värden, nollor, infiniteter och NaN. I tabellen är - xoch- ypositiva ändliga värden.- zär resultatet av- x / y, avrundat till närmaste värde som kan representeras.- +y- -y- +0- -0- +∞- -∞- NaN- +x- +z- -z- +∞- -∞- +0- -0- NaN- -x- -z- +z- -∞- +∞- -0- +0- NaN- +0- +0- -0- NaN- NaN- +0- -0- NaN- -0- -0- +0- NaN- NaN- -0- +0- NaN- +∞- +∞- -∞- +∞- -∞- NaN- NaN- NaN- -∞- -∞- +∞- -∞- +∞- NaN- NaN- NaN- NaN- NaN- NaN- NaN- NaN- NaN- NaN- NaN
- Decimaldivision - decimal operator /(decimal x, decimal y);- Om värdet för rätt operand är noll genereras en - System.DivideByZeroException. Om storleken på det resulterande värdet är för stort för att representera i decimalformatet genereras en- System.OverflowException. På grund av avrundning kan resultatet bli noll även om den första operanden inte är noll. Resultatets skala, före avrundning, är den närmaste skalan till önskad skala som bevarar ett resultat som är lika med det exakta resultatet. Den önskade skalan är skalan för- xmindre skalan för- y.- Decimaldelning motsvarar att använda divisionsoperatorn av typen - System.Decimal.
Lyfts (§12.4.8) bildar av de odefinierade fördefinierade uppdelningsoperatorerna som definieras ovan, är också fördefinierade.
12.11.4 Restoperator
För en åtgärd av formuläret x % ytillämpas binär operatoröverbelastningsmatchning (§12.4.5) för att välja en specifik operatorimplementering. Operanderna konverteras till parametertyperna för den valda operatorn och typen av resultat är operatorns returtyp.
De fördefinierade restoperatorerna visas nedan. Operatorerna beräknar resten av divisionen mellan x och y.
- Heltalsrester: - int operator %(int x, int y); uint operator %(uint x, uint y); long operator %(long x, long y); ulong operator %(ulong x, ulong y);- Resultatet av - x % yär värdet som genereras av- x – (x / y) * y. Om- yär noll utlöses en- System.DivideByZeroException.- Om den vänstra operanden är det minsta - int- eller- long-värdet och den högra operanden är- –1genereras en- System.OverflowExceptionom och bara om- x / yskulle utlösa ett undantag.
- Flyttalsrester: - float operator %(float x, float y); double operator %(double x, double y);- I följande tabell visas resultatet av alla möjliga kombinationer av icke-ändliga värden, nollor, infiniteter och NaN. I tabellen är - xoch- ypositiva ändliga värden.- zär resultatet av- x % yoch beräknas som- x – n * y, där n är det största möjliga heltalet som är mindre än eller lika med- x / y. Den här metoden för att beräkna resten motsvarar den som används för heltalsoperor, men skiljer sig från definitionen IEC 60559 (där- när det heltal som är närmast- x / y).- +y- -y- +0- -0- +∞- -∞- NaN- +x- +z- +z- NaN- NaN- +x- +x- NaN- -x- -z- -z- NaN- NaN- -x- -x- NaN- +0- +0- +0- NaN- NaN- +0- +0- NaN- -0- -0- -0- NaN- NaN- -0- -0- NaN- +∞- NaN- NaN- NaN- NaN- NaN- NaN- NaN- -∞- NaN- NaN- NaN- NaN- NaN- NaN- NaN- NaN- NaN- NaN- NaN- NaN- NaN- NaN- NaN
- Decimaler: - decimal operator %(decimal x, decimal y);- Om värdet för rätt operand är noll genereras en - System.DivideByZeroException. Den är implementeringsdefinierad när en- System.ArithmeticException(eller en underklass därav) genereras. Ett genomförande i enlighet med detta får inte utlösa ett undantag för- x % yi alla fall där- x / yinte utlöser ett undantag. Resultatets skala, före avrundning, är den större av skalningarna för de två operanderna, och tecknet för resultatet, om det inte är noll, är detsamma som för- x.- Decimalrest är motsvarande att använda restoperatorn av typen - System.Decimal.- Note: Dessa regler säkerställer att resultatet för alla typer aldrig har motsatt tecken på den vänstra operanden. slutkommentar 
Lyfta (§12.4.8) former av de olyfta fördefinierade restoperatorerna som definieras ovan är också fördefinierade.
12.11.5 Additionsoperator
För en åtgärd av formuläret x + ytillämpas binär operatoröverbelastningsmatchning (§12.4.5) för att välja en specifik operatorimplementering. Operanderna konverteras till parametertyperna för den valda operatorn och typen av resultat är operatorns returtyp.
De fördefinierade tilläggsoperatorerna visas nedan. För numeriska typer och uppräkningstyper beräknar de fördefinierade additionsoperatorerna summan av de två operanderna. När en eller båda operanderna är av typen stringsammanfogar de fördefinierade tilläggsoperatorerna strängrepresentationen av operanderna.
- Heltalstillägg: - int operator +(int x, int y); uint operator +(uint x, uint y); long operator +(long x, long y); ulong operator +(ulong x, ulong y- I en - checked-kontext, om summan ligger utanför resultattypens intervall, genereras en- System.OverflowException. I ett- unchecked-kontext rapporteras inte spill, och signifikanta bitar i högre ordning utanför resultattypens intervall kastas bort.
- Flyttalsaddition: - float operator +(float x, float y); double operator +(double x, double y);- Summan beräknas enligt reglerna i IEC 60559-aritmetik. I följande tabell visas resultatet av alla möjliga kombinationer av icke-ändliga värden, nollor, infiniteter och NaN. I tabellen är - xoch- yicke-nollfinita värden och- zär resultatet av- x + y. Om- xoch- yhar samma storlek men motsatta tecken är- zpositiv noll. Om- x + yär för stor för att representeras i måltypen är- zen oändlighet med samma tecken som- x + y.- y- +0- -0- +∞- -∞- NaN- x- z- x- x- +∞- -∞- NaN- +0- y- +0- +0- +∞- –∞- NaN- -0- y- +0- -0- +∞- -∞- NaN- +∞- +∞- +∞- +∞- +∞- NaN- NaN- -∞- -∞- -∞- -∞- NaN- -∞- NaN- NaN- NaN- NaN- NaN- NaN- NaN- NaN
- Decimaltillägg: - decimal operator +(decimal x, decimal y);- Om storleken på det resulterande värdet är för stort för att representera i decimalformatet genereras en - System.OverflowException. Resultatets skala, före avrundning, är den större av de två operandernas skalor.- Decimaltillägg motsvarar att använda additionsoperatorn av typen - System.Decimal.
- Uppräkningstillägg. Varje uppräkningstyp tillhandahåller implicit följande fördefinierade operatorer, där - Eär uppräkningstypen och- Uär den underliggande typen av- E:- E operator +(E x, U y); E operator +(U x, E y);- Vid körning utvärderas dessa operatorer exakt som - (E)((U)x + (U)y).
- Strängsammanfogning: - string operator +(string x, string y); string operator +(string x, object y); string operator +(object x, string y);- Dessa överlagringar av den binära - +operatorn utför strängsammanfogning. Om en operand av strängkonkatenation är- null, ersätts den med en tom sträng. Annars konverteras alla icke-- stringoperander till dess strängrepresentation genom att anropa den virtuella- ToStringmetod som ärvs från typen- object. Om- ToStringreturnerar- nullersätts en tom sträng.- Exempel: - 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 } }- Utdata som visas i kommentarerna är det typiska resultatet på ett US-English system. Exakt utdata kan bero på de regionala inställningarna för körningsmiljön. Själva strängsammanfogningsoperatorn beter sig på samma sätt i varje enskilt fall, men de - ToString-metoder som implicit anropas under körningen kan påverkas av regionala inställningar.- slutexempel - Resultatet av strängsammanfogningsoperatorn är en - stringsom består av tecknen i den vänstra operanden följt av tecknen i den högra operanden. Strängsammanfogningsoperatorn returnerar aldrig ett- nullvärde. En- System.OutOfMemoryExceptionkan genereras om det inte finns tillräckligt med minne tillgängligt för att allokera den resulterande strängen.
- Delegera kombination. Varje delegetyp tillhandahåller implicit följande fördefinierade operator, där - Där delegetypen:- D operator +(D x, D y);- Om den första operanden är - nullär resultatet av åtgärden värdet för den andra operanden (även om det också är- null). Om den andra operanden annars är- nullär resultatet av åtgärden värdet för den första operanden. Annars är resultatet av åtgärden en ny delegatinstans vars anropslista består av elementen i anropslistan för den första operanden, följt av elementen i anropslistan för den andra operanden. Den resulterande delegatens anropslista är alltså sammanlänkningen av anropslistorna för de två operanderna.- Obs! Exempel på ombudskombinationer finns i §12.11.6 och §21.6. Eftersom - System.Delegateinte är en ombudstyp definieras inte operatorn + för den. slutkommentar
Lyfta (§12.4.8) former av de icke-lyfta fördefinierade tilläggsoperatorerna som definieras ovan är också fördefinierade.
12.11.6 Subtraktionsoperator
För en åtgärd av formuläret x – ytillämpas binär operatoröverbelastningsmatchning (§12.4.5) för att välja en specifik operatorimplementering. Operanderna konverteras till parametertyperna för den valda operatorn och typen av resultat är operatorns returtyp.
De fördefinierade subtraktionsoperatorerna visas nedan. Operatorerna subtraherar alla y från x.
- Subtraktion av heltal - int operator –(int x, int y); uint operator –(uint x, uint y); long operator –(long x, long y); ulong operator –(ulong x, ulong y- I en - checked-kontekst, om skillnaden ligger utanför resultattypens intervall, genereras en- System.OverflowException. I ett- unchecked-kontext rapporteras inte spill, och signifikanta bitar i högre ordning utanför resultattypens intervall kastas bort.
- Flyttalsundertraktion: - float operator –(float x, float y); double operator –(double x, double y);- Skillnaden beräknas enligt reglerna för IEC 60559-aritmetik. I följande tabell visas resultatet av alla möjliga kombinationer av icke-ändliga värden, nollor, infiniteter och NaN. I tabellen är - xoch- yicke-nollfinita värden och- zär resultatet av- x – y. Om- xoch- yär lika är- zpositiv noll. Om- x – yär för stor för att representeras i måltypen är- zen oändlighet med samma tecken som- x – y.- y- +0- -0- +∞- -∞- NaN- x- z- x- x- -∞- +∞- NaN- +0- -y- +0- +0- -∞- +∞- NaN- -0- -y- -0- +0- -∞- +∞- NaN- +∞- +∞- +∞- +∞- NaN- +∞- NaN- -∞- -∞- -∞- -∞- -∞- NaN- NaN- NaN- NaN- NaN- NaN- NaN- NaN- NaN- (I tabellen ovan anger - -yinlägg negation av- y, inte att värdet är negativt.)
- Decimalsubtraktion - decimal operator –(decimal x, decimal y);- Om storleken på det resulterande värdet är för stort för att representera i decimalformatet genereras en - System.OverflowException. Resultatets skala, före avrundning, är den större av de två operandernas skalor.- Decimal subtraktion motsvarar att använda subtraktionsoperatorn av typen - System.Decimal.
- Uppräkningsundertraktion. Varje enumtyp tillhandahåller implicit följande fördefinierade operator, där - Eär enumtypen och- Uär den underliggande typen av- E:- U operator –(E x, E y);- Den här operatorn utvärderas exakt som - (U)((U)x – (U)y). Med andra ord beräknar operatorn skillnaden mellan ordningsvärdena för- xoch- y, och typen av resultatet är den underliggande typen av uppräkning.- E operator –(E x, U y);- Den här operatorn utvärderas exakt som - (E)((U)x – y). Med andra ord subtraherar operatorn ett värde från den underliggande typen av uppräkning, vilket ger ett värde för uppräkningen.
- Borttagning av delegering. Varje delegetyp tillhandahåller implicit följande fördefinierade operator, där - Där delegetypen:- D operator –(D x, D y);- Semantiken är följande: - Om den första operanden är nullblir resultatet av åtgärdennull.
- Om den andra operanden annars är nullär resultatet av åtgärden värdet för den första operanden.
- Annars representerar båda operanderna icke-tomma anropslistor (§21.2).
- Om listorna jämför lika, enligt vad som bestäms av operatorn för ombudsjämlikhet (§12.13.9), blir nullresultatet av åtgärden .
- Annars är resultatet av åtgärden en ny anropslista som består av den första operandens lista med den andra operandens poster borttagna från den, förutsatt att den andra operandens lista är en underlista för den första. (För att fastställa likhet mellan underlistor jämförs motsvarande poster som för operatorn för ombudsjämlikhet.) Om den andra operandens lista matchar flera underlistor med sammanhängande poster i den första operandens lista tas den sista matchande underlistan över angränsande poster bort.
- Annars är resultatet av operationen värdet av den vänstra operanden.
 
- Om listorna jämför lika, enligt vad som bestäms av operatorn för ombudsjämlikhet (§12.13.9), blir 
 - Ingen av operandernas listor (om några) ändras i processen. - Exempel: - 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 } }- slutexempel 
- Om den första operanden är 
Lyfta (§12.4.8) former av de oolyfta fördefinierade subtraktionsoperatorerna som definieras ovan är också fördefinierade.
12.12 Skiftoperatorer
Operatorerna << och >> används för att utföra bitväxlingsåtgärder.
shift_expression
    : additive_expression
    | shift_expression '<<' additive_expression
    | shift_expression right_shift additive_expression
    ;
Om en operand av en shift_expression har kompileringstidstypen dynamic, är uttrycket dynamiskt bundet (§12.3.3). I det här fallet är kompileringstidstypen för uttrycket dynamic, och lösningen som beskrivs nedan sker vid körning med körningstypen för de operander som har kompileringstypen dynamic.
För en åtgärd av formuläret x << count eller x >> counttillämpas binär operatoröverbelastningsmatchning (§12.4.5) för att välja en specifik operatorimplementering. Operanderna konverteras till parametertyperna för den valda operatorn och typen av resultat är operatorns returtyp.
När en överlagrad skiftoperator deklareras ska den första operandens typ alltid vara den klass eller struct som innehåller operatordeklarationen, och typen av den andra operanden ska alltid vara int.
De fördefinierade skiftoperatorerna visas nedan.
- Skift vänster: - int operator <<(int x, int count); uint operator <<(uint x, int count); long operator <<(long x, int count); ulong operator <<(ulong x, int count);- Operatorn - <<skiftar- xåt vänster av ett antal bitar som beräknas enligt beskrivningen nedan.- De höga bitarna utanför resultattypens intervall för - xignoreras, de återstående bitarna flyttas åt vänster och de tomma bitpositionerna i låg ordning är inställda på noll.
- Skift höger: - int operator >>(int x, int count); uint operator >>(uint x, int count); long operator >>(long x, int count); ulong operator >>(ulong x, int count);- Operatorn - >>skiftar- xrätt med ett antal bitar som beräknas enligt beskrivningen nedan.- När - xär av typen- inteller- longignoreras de låga bitarna i- x, de återstående bitarna flyttas åt höger och de tomma bitpositionerna i hög ordning är inställda på noll om- xär icke-negativ och anges till en om- xär negativ.- När - xär av typen- uinteller- ulongignoreras de lågordningsbitar av- x, de återstående bitarna flyttas åt höger och de tomma bitpositionerna i hög ordning är inställda på noll.
För de fördefinierade operatorerna beräknas antalet bitar som ska flyttas enligt följande:
- När typen av xärintelleruintbestäms skiftantalet av de fem lägst ordnade bitarna icount. Med andra ord beräknas skiftantalet fråncount & 0x1F.
- När typen av xärlongellerulonganges skiftantalet av de sex lägsta bitarna avcount. Med andra ord beräknas skiftantalet fråncount & 0x3F.
Om det resulterande skiftantalet är noll returnerar skiftoperatorerna helt enkelt värdet för x.
Skiftåtgärder orsakar aldrig spill och ger samma resultat i markerade och avmarkerade kontexter.
När den vänstra operanden i >>-operatorn är av en signerad integraltyp utför operatorn en aritmetik skift höger där värdet för operandens viktigaste bit (teckenbiten) sprids till tomma bitpositioner i hög ordning. När den vänstra operanden i >>-operatorn är av en osignerad integraltyp utför operatorn en logisk skifta åt höger där tomma bitpositioner i hög ordning alltid är inställda på noll. För att utföra den motsatta åtgärden som härleds från operandtypen kan explicita avgjutningar användas.
Exempel: Om
xär en variabel av typenint, utför åtgärdenunchecked ((int)((uint)x >> y))en logisk högerförskjutning förx. slutexempel
Lyfta (§12.4.8) former av de olyfta fördefinierade skiftoperatorerna som definieras ovan är också fördefinierade.
12.13 Relations- och typtestningsoperatorer
12.13.1 Allmänt
Operatorerna ==, !=, <, >, <=, >=, isoch as kallas för operatorerna för relations- och typtestning.
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
    ;
Not: Uppslagning för den högra operanden av
is-operatören måste först testas som en typ, och sedan som ett uttryck som kan omfatta flera token. Om operanden är ett uttryck måste mönsteruttrycket ha företräde minst så högt som shift_expression. slutkommentar
Operatören is beskrivs i §12.13.12 och operatören as beskrivs i §12.13.13.
Operatorerna ==, !=, <, >, <= och >= är jämförelseoperatorer.
Om en default_literal (§12.8.21) används som operande av en <, >, <=eller >= operator uppstår ett kompileringsfel.
Om en default_literal används som båda operanderna för en ==- eller != operator uppstår ett kompileringsfel. Om en default_literal används som vänster operand för is- eller as-operatorn uppstår ett kompileringsfel.
Om en operand av en jämförelseoperator har kompileringstidstypen dynamic, är uttrycket dynamiskt bundet (§12.3.3). I det här fallet är kompileringstidstypen för uttrycket dynamic, och lösningen som beskrivs nedan sker vid körning med hjälp av körningstypen för de operander som har kompileringstidstypen dynamic.
För en drift av formuläret x «op» y, där «op» är en jämförelseoperator, tillämpas överbelastningsupplösning (§12.4.5) för att välja en specifik operatorimplementering. Operanderna konverteras till parametertyperna för den valda operatorn och typen av resultat är operatorns returtyp. Om båda operanderna i en equality_expression är null literal utförs inte överlagringsupplösningen och uttrycket utvärderas till ett konstant värde på true eller false beroende på om operatorn är == eller !=.
De fördefinierade jämförelseoperatorerna beskrivs i följande underavsnitt. Alla fördefinierade jämförelseoperatorer returnerar ett resultat av typen bool, enligt beskrivningen i följande tabell.
| Åtgärd | resultat | 
|---|---|
| x == y | trueomxär lika medyfalseannars | 
| x != y | trueomxinte är lika medyfalseannars | 
| x < y | trueomxär mindre änyfalseannars | 
| x > y | trueomxär större änyfalseannars | 
| x <= y | trueomxär mindre än eller lika medyfalseannars | 
| x >= y | trueomxär större än eller lika medyfalseannars | 
12.13.2 Jämförelseoperatorer för heltal
De fördefinierade heltalsjämförelseoperatorerna är:
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);
Var och en av dessa operatorer jämför de numeriska värdena för de två heltalsopernderna och returnerar ett bool värde som anger om den specifika relationen är true eller false.
Lyfts (§12.4.8) former av de odefinierade fördefinierade heltalsjämförelseoperatorer som definieras ovan är också fördefinierade.
12.13.3 Jämförelseoperatorer för flyttals
De fördefinierade flyttalsjämförelseoperatorerna är:
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);
Operatörerna jämför operanderna enligt reglerna i IEC 60559-standarden:
Om någon av operanderna är NaN är resultatet false för alla operatorer utom !=, för vilka resultatet true. För två operander ger x != y alltid samma resultat som !(x == y). Men när en eller båda operanderna är NaN ger <, >, <=och >= operatorerna inte samma resultat som den logiska negationen för den motsatta operatorn.
Exempel: Om någon av
xochyär NaN ärx < yfalse, men!(x >= y)ärtrue. slutexempel
När ingen av operanderna är NaN jämför operatorerna värdena för de två flyttalsopernderna med avseende på ordningen
–∞ < –max < ... < –min < –0.0 == +0.0 < +min < ... < +max < +∞
där min och max är de minsta och största positiva finita värdena som kan representeras i det angivna flyttalsformatet. Viktiga effekter av den här ordningen är:
- Negativa och positiva nollor anses vara lika.
- En negativ oändlighet anses vara mindre än alla andra värden, men lika med en annan negativ oändlighet.
- En positiv oändlighet anses vara större än alla andra värden, men lika med en annan positiv oändlighet.
Upphöjda (§12.4.8) former av de icke-upphöjda fördefinierade flyttalsjämförelseoperatorerna som tidigare har fördefinierats är också fördefinierade.
12.13.4 Decimal jämförelseoperatorer
De fördefinierade decimaljämförelseoperatorerna är:
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);
Var och en av dessa operatorer jämför de numeriska värdena för de två decimalopereringarna och returnerar ett bool värde som anger om den specifika relationen är true eller false. Varje decimaljämförelse motsvarar att använda motsvarande relations- eller likhetsoperator av typen System.Decimal.
Upphöjda (§12.4.8) former av de opåverkade redan definierade decimaljämförelseoperatorerna som definieras ovan är också redan definierade.
12.13.5 Booleska likhetsoperatorer
De fördefinierade booleska likhetsoperatorerna är:
bool operator ==(bool x, bool y);
bool operator !=(bool x, bool y);
Resultatet av == är true om både x och y är true eller om både x och y är false. Annars blir resultatet false.
Resultatet av != är false om både x och y är true eller om både x och y är false. Annars blir resultatet true. När operanderna är av typen boolgenererar operatorn != samma resultat som operatorn ^.
Lyfta (§12.4.8) former av de fördefinierade o-lyfta booleska likhetsoperatorerna definierade ovan är också fördefinierade.
12.13.6 Uppräkningsjämförelseoperatorer
Varje uppräkningstyp tillhandahåller implicit följande fördefinierade jämförelseoperatorer
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);
Resultatet av utvärderingen av x «op» y, där x och y är uttryck av en uppräkningstyp E med en underliggande typ U, och «op» är en av jämförelseoperatorerna, är exakt samma som att utvärdera ((U)x) «op» ((U)y). Med andra ord jämför jämförelseoperatorerna för uppräkningstypen helt enkelt de underliggande integralvärdena för de två operanderna.
Lyfta (§12.4.8) former av de olyfta fördefinierade enummerationsjämförelseoperatorer som nämns ovan är också fördefinierade.
12.13.7 Likhetsoperatorer för referenstyp
Varje klasstyp C tillhandahåller implicit följande fördefinierade operatorer för likhet mellan referenstyper:
bool operator ==(C x, C y);
bool operator !=(C x, C y);
om inte fördefinierade likhetsoperatorer annars finns för C (till exempel när C är string eller System.Delegate).
Operatorerna returnerar resultatet av att jämföra de två referenserna för likhet eller icke-likhet. 
              operator == returnerar true om och endast om x och y refererar till samma instans eller båda är null, medan operator != returnerar true om och bara om operator == med samma operander skulle returnera false.
Förutom normala tillämplighetsregler (§12.6.4.2) kräver de fördefinierade referenstypjämlikhetsoperatorerna något av följande för att vara tillämpligt:
- Båda operanderna är ett värde av en typ som är känd som en reference_type eller literalen null. Dessutom finns en identitets- eller explicit referenskonvertering (§10.3.5) från antingen operand till den andra operandens typ.
- En operand är literalen null, och den andra operanden är ett värde av typenTdärTär en type_parameter som inte är känd för att vara en värdetyp och inte har begränsningen för värdetyp.- Om Tvid körningen är en värdetyp som inte kan nollföras blir resultatet av==falseoch resultatet av!=true.
- Om vid körning Tär en nullbar värdetyp beräknas resultatet frånHasValueoperandens egenskap, enligt beskrivningen i (§12.13.10).
- Om Tvid körning är en referenstyp blir resultatettrueom operanden ärnullochfalseannars.
 
- Om 
Om inget av dessa villkor är sant uppstår ett bindningstidsfel.
Obs: Viktiga konsekvenser av dessa regler är:
- Det är ett bindningstidsfel att använda de fördefinierade likhetsoperatorerna för referenstyper för att jämföra två referenser som är kända för att vara olika vid bindningstid. Om till exempel bindningstidstyperna för operanderna är två klasstyper, och om ingen av dem härleds från den andra, skulle det vara omöjligt för de två operanderna att referera till samma objekt. Åtgärden anses därför vara ett bindningstidsfel.
- De fördefinierade referenstypsoperatorerna tillåter inte att värdetypoperatorer jämförs (förutom när typparametrar jämförs med
null, som hanteras särskilt).- Operander av fördefinierade likhetsoperatorer för referenstyper boxas aldrig. Det skulle vara meningslöst att utföra sådana boxningsåtgärder, eftersom referenser till de nyligen allokerade boxade instanserna nödvändigtvis skulle skilja sig från alla andra referenser.
För en operation i formen
x == yellerx != y, om det finns någon tillämplig användardefinieradoperator ==elleroperator !=, kommer operatorns regler för överlagringslösning (§12.4.5) att välja den operatorn istället för den fördefinierade likhetsoperatorn för referenstyper. Det är alltid möjligt att välja den fördefinierade referenstypjämlikhetsoperatorn genom att uttryckligen kasta en eller båda operanderna till typenobject.slutkommentar
Exempel: I det följande exemplet kontrolleras om ett argument för en obegränsad parametertyp är
null.class C<T> { void F(T x) { if (x == null) { throw new ArgumentNullException(); } ... } }Den
x == nullkonstruktionen är tillåten även omTkan representera en värdetyp som inte kan nulleras, och resultatet definieras helt enkelt somfalsenärTär en värdetyp som inte kan upphävas.slutexempel
För en operation av formen x == y eller x != y, om det finns några tillämpliga operator == eller operator !=, väljer överlagringsresolution för operatorer (§12.4.5) den operatorn i stället för den fördefinierade referenstypslikhetsoperatorn.
Note: Det är alltid möjligt att välja den fördefinierade referenstypers likhetsoperator genom att uttryckligen kasta båda operanderna till typen
object. slutkommentar
Exempel: Exemplet
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); } }genererar utdata
True False False FalseVariablerna
sochtrefererar till två distinkta stränginstanser som innehåller samma tecken. Den första jämförelsen utdataTrueeftersom den fördefinierade strängjämlikhetsoperatorn (§12.13.8) väljs när båda operanderna är av typenstring. Alla återstående jämförelser ger utdataFalseeftersom överbelastningen av typenoperator ==istringinte är tillämplig om någon av operanderna har en bindningstidstyp avobject.Observera att ovanstående teknik inte är meningsfull för värdetyper. Exemplet
class Test { static void Main() { int i = 123; int j = 123; Console.WriteLine((object)i == (object)j); } }Producerar
Falseeftersom typkonverteringar skapar referenser till två separata instanser av inneslutnaint-värden.slutexempel
12.13.8 Operatorer för strängjämlikhet
De fördefinierade operatorerna för strängjämlikhet är:
bool operator ==(string x, string y);
bool operator !=(string x, string y);
Två string värden anses vara lika med när något av följande är sant:
- Båda värdena är null.
- Båda värdena är icke-nullreferenser till stränginstanser som har identiska längder och identiska tecken i varje teckenposition.
Operatorerna för strängjämlikhet jämför strängvärden i stället för strängreferenser. När två separata stränginstanser innehåller exakt samma teckensekvens är värdena för strängarna lika, men referenserna är olika.
Obs! Enligt beskrivningen i §12.13.7 kan likhetsoperatorerna för referenstyp användas för att jämföra strängreferenser i stället för strängvärden. slutkommentar
12.13.9 Delegera likhetsoperatorer
De fördefinierade operatorerna för delegeringsjämlikhet är:
bool operator ==(System.Delegate x, System.Delegate y);
bool operator !=(System.Delegate x, System.Delegate y);
Två delegatinstanser anses vara likvärdiga på följande sätt:
- Om någon av de delegerade instanserna är nullär de lika om och endast om båda ärnull.
- Om delegaterna har olika körtyper är de inte lika.
- Om båda de delegerade instanserna har en anropslista (§21.2) är dessa instanser lika om och endast om deras anropslistor är lika långa, och varje post i en anropslista är lika (enligt definitionen nedan) med motsvarande post, i ordning, i den andras anropslista.
Följande regler styr likheten mellan anropslistposter:
- Om två anropslistposter båda refererar till samma statiska metod är posterna lika.
- Om två anropslistposter båda refererar till samma icke-statiska metod på samma målobjekt (som definieras av referensparalikhetsoperatorerna) är posterna lika.
- Anropslista poster som produceras från utvärderingen av semantiskt identiska anonyma funktioner (§12.20) med samma (eventuellt tomma) uppsättning av insamlade yttre variabelinstanser tillåts (men krävs inte) vara lika.
Om operatörens överbelastningsmatchning matchar antingen delegera likhetsoperatorn och bindningstidstyperna för båda operanderna är ombudstyper enligt beskrivningen i §21 i stället System.Delegateför , och det inte finns någon identitetskonvertering mellan operandtyperna av bindningstyp, uppstår ett bindningstidsfel.
Obs: Den här regeln förhindrar jämförelser som aldrig kan betrakta icke-
nullvärden som lika på grund av referenser till instanser av olika typer av ombud. slutkommentar
12.13.10 Likhetsoperatorer mellan nullbara värdetyper och null-literalen
Operatorerna == och != tillåter att den ena operanden är ett värde av en nullbar värdetyp och den andra är den null literalen, även om det inte finns någon fördefinierad eller användardefinierad operator (i obelyft eller upplyft form) för åtgärden.
För en operation av en av formerna
x == null    null == x    x != null    null != x
om x är ett uttryck av en nullbar värdetyp, om operatorns överbelastningsupplösning (§12.4.5) inte kan hitta en tillämplig operator, beräknas resultatet i stället från egenskapen HasValue för x. Mer specifikt översätts de två första formulären till !x.HasValueoch de två sista formulären översätts till x.HasValue.
12.13.11 Tuppelns likhetsoperatorer
Tuppelns likhetsoperatorer tillämpas parvis på elementen i tuppelns operander i lexikal ordning.
Om varje operand x och y för en ==- eller !=-operator klassificeras antingen som en tuppel eller som ett värde med en tuppeltyp (§8.3.11), kommer operatorn att vara en tuppellikhetsoperator.
Om en operande e klassificeras som en tuppel ska elementen e1...en vara resultatet av att utvärdera elementuttrycken i tuppelexpressionen. Annars, om e är ett värde av typen tuppel, ska elementen vara t.Item1...t.Itemn där t är resultatet av att utvärdera e.
Operanderna x och y för en tupplars likhetsoperator ska ha samma aritet, eller så uppstår ett kompileringstids-fel. För varje elementpar xi och yiska samma likhetsoperator tillämpas och ge ett resultat av typen bool, dynamic, en typ som har en implicit konvertering till booleller en typ som definierar true och false operatorer.
Tuppelns likhetsoperator x == y utvärderas på följande sätt:
- Den vänstra operanden xutvärderas.
- Den högra operanden yutvärderas.
- För varje elementpar xiochyii lexikal ordning:- Operatorn xi == yiutvärderas och ett resultat av typenboolerhålls på följande sätt:- Om jämförelsen gav ett boolär det resultatet.
- Om jämförelsen annars gav en dynamicanropas operatornfalsedynamiskt på den, och det resulterandeboolvärdet negeras med operatorn för logisk negation (!).
- Om jämförelsetypen annars har en implicit konvertering till booltillämpas konverteringen.
- Om jämförelsetypen annars har en operator falseanropas operatorn och det resulterandeboolvärdet negeras med operatorn för logisk negation (!).
 
- Om jämförelsen gav ett 
- Om den resulterande boolärfalsesker ingen ytterligare utvärdering och resultatet av likhetsoperatorn för tupler ärfalse.
 
- Operatorn 
- Om alla elementjämförelser gav true, blir resultatet av tuplens likhetsoperatortrue.
Tuppelns likhetsoperator x != y utvärderas på följande sätt:
- Den vänstra operanden xutvärderas.
- Den högra operanden yutvärderas.
- För varje elementpar xiochyii lexikal ordning:- Operatorn xi != yiutvärderas och ett resultat av typenboolerhålls på följande sätt:- Om jämförelsen gav ett boolär det resultatet.
- Om jämförelsen annars gav en dynamicanropas operatorntruedynamiskt på den, och det resulterandeboolvärdet är resultatet.
- Om jämförelsetypen annars har en implicit konvertering till booltillämpas konverteringen.
- Om jämförelsetypen annars har en operator trueanropas operatorn och det resulterandeboolvärdet är resultatet.
 
- Om jämförelsen gav ett 
- Om den resulterande boolärtruesker ingen ytterligare utvärdering och resultatet av likhetsoperatorn för tupler ärtrue.
 
- Operatorn 
- Om alla elementjämförelser gav false, blir resultatet av tuplens likhetsoperatorfalse.
12.13.12 Is-operatorn
Det finns två former av is-operatorn. Den ena är operatorn är av typen, som har en typ till höger. **  
Den andra operatorn är is-pattern, som har ett mönster på högersidan.
12.13.12.1 Operatorn är av typen
Operatorn av typen används för att kontrollera om ett objekts körningstyp är kompatibel med en viss typ. Kontrollen utförs vid körning. Resultatet av åtgärden E is T, där E är ett uttryck och T är en typ annan än dynamic, är ett booleskt värde som anger om E är icke-null och kan konverteras till typ T genom en referenskonvertering, en boxningskonvertering, en avboxningskonvertering, en omslutande konvertering eller en omkastningskonvertering.
Åtgärden utvärderas på följande sätt:
- Om Eär en anonym funktion eller metodgrupp uppstår ett kompileringsfel.
- Om Eärnullliteral, eller om värdet förEärnull, blir resultatetfalse.
- Annars:
- Låt Rvara körningstyp förE.
- Låt Dhärledas frånRpå följande sätt:
- Om Rär en nullbar värdetyp ärDden underliggande typen avR.
- Annars är DR.
- Resultatet beror på DochTpå följande sätt:
- Om Tär en referenstyp, är resultatettrueom så är fallet:- det finns en identitetskonvertering mellan DochT,
- 
              Där en referenstyp och en implicit referenskonvertering frånDtillTfinns, eller
- Antingen: Där en värdetyp och det finns en boxningskonvertering frånDtillT.
 Eller:Där en värdetyp ochTär en gränssnittstyp som implementeras avD.
 
- det finns en identitetskonvertering mellan 
- Om Tär en nullbar värdetyp, är resultatettrueomDär den underliggande typen avT.
- Om Tär en värdetyp som inte kan nullerastrueresultatet omDochTär av samma typ.
- Annars blir resultatet false.
Användardefinierade konverteringar beaktas inte av is-operatorn.
Obs: Eftersom
isoperatorn utvärderas vid körningen har alla typargument ersatts och det inte finns några öppna typer (§8.4.3) att överväga. slutkommentar
Obs: Operatorn
iskan förstås i termer av kompileringstidstyper och konverteringar enligt följande, därCär kompileringstidstypen förE:
- Om kompileringstidstypen för
eär samma somT, eller om en implicit referenskonvertering (§10.2.8), boxningskonvertering (§10.2.9), isoleringskonvertering (§10.6), eller en explicit öppningskonvertering (§10.6) finns från kompileringstidstypen avEtillT:
- Om
Cär av en värdetyp som inte kan vara null, är resultatet av åtgärdentrue.- Annars motsvarar resultatet av operationen att utvärdera
E != null.- Om en explicit referenskonvertering (§10.3.5) eller omboxningskonvertering (§10.3.7) finns från
CtillT, eller omCellerTär en öppen typ (§8.4.3), ska körningskontroller enligt ovan utföras.- Annars är ingen referens-, boxnings-, omslutnings- eller omskrivningskonvertering av
Etill typenTmöjlig, och resultatet av åtgärden ärfalse. En kompilator kan implementera optimeringar baserat på kompileringstidstypen.slutkommentar
12.13.12.2 Is-pattern-operatorn
Operatorn is-pattern används för att kontrollera om värdet som beräknas av ett uttryck matchar ett angivet mönster (§11). Kontrollen utförs vid körning. Resultatet av is-pattern-operatorn är sant om värdet matchar mönstret. annars är det falskt.
För ett uttryck av formen E is P, där E är ett relationsuttryck av typen T och P är ett mönster, är det ett kompileringstidfel om något av följande gäller:
- 
              Eanger inget värde eller har ingen typ.
- Mönstret Pär inte tillämpligt (§11.2) på typenT.
12.13.13 As-operatorn
Operatorn as används för att explicit konvertera ett värde till en viss referenstyp eller nullbar värdetyp. Till skillnad från ett gjutet uttryck (§12.9.8) genererar operatorn as aldrig ett undantag. Om den angivna konverteringen inte är möjlig är det resulterande värdet i stället null.
I en åtgärd i formuläret E as Tska E vara ett uttryck och T ska vara en referenstyp, en typparameter som är känd för att vara en referenstyp eller en nullbar värdetyp. Dessutom ska minst något av följande vara sant, eller på annat sätt uppstår ett kompileringsfel:
- En identitet (§10.2.2), implicit nullable (§10.2.6), implicit referens (§10.2.8), boxning (§10.2.9), explicit nullable (§10.3.4), explicit referens (§10.3.5), eller wrapping (§8.3.12) konvertering finns mellan EochT.
- Typen av EellerTär en öppen typ.
- 
              Eärnullbokstavligt talat.
Om kompileringstidstypen för E inte är dynamicgenererar åtgärden E as T samma resultat som
E is T ? (T)(E) : (T)null
förutom att E endast utvärderas en gång. En kompilator kan förväntas optimera E as T för att utföra högst en körningstypkontroll i stället för de två körningstypkontrollerna som antyds i expansionen ovan.
Om kompileringstidstypen för E är dynamic, till skillnad från gjutningsoperatorn är as operatorn inte dynamiskt bunden (§12.3.3). Därför är expansionen i det här fallet:
E is T ? (T)(object)(E) : (T)null
Observera att vissa konverteringar, till exempel användardefinierade konverteringar, inte är möjliga med operatorn as och i stället bör utföras med hjälp av cast-uttryck.
Exempel: I exemplet
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 } }typparametern
TavGär känd för att vara en referenstyp, eftersom den har klassbegränsningen. TypparameternUavHär dock inte; därför tillåts inte användningen avas-operatorn iH.slutexempel
12.14 Logiska operatorer
12.14.1 Allmänt
Operatorerna &, ^och | kallas logiska operatorer.
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
    ;
Om en operand av en logisk operator har kompileringstidstypen dynamic, är uttrycket dynamiskt bundet (§12.3.3). I det här fallet är kompileringstidstypen för uttrycket dynamic, och lösningen som beskrivs nedan sker vid körning med hjälp av körningstypen för de operander som har kompileringstidstypen dynamic.
För en drift av formuläret x «op» y, där «op» är en av de logiska operatorerna, tillämpas överbelastningsupplösning (§12.4.5) för att välja en specifik operatorimplementering. Operanderna konverteras till parametertyperna för den valda operatorn och typen av resultat är operatorns returtyp.
De fördefinierade logiska operatorerna beskrivs i följande underavsnitt.
12.14.2 Heltals logiska operatorer
De fördefinierade logiska heltalsoperatorerna är:
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);
Den & operatorn beräknar det bitvis logiska OCH för de två operanderna beräknar |-operatorn den bitvis logiska OR:en för de två operanderna, och ^-operatorn beräknar den bitvis logiska exklusiva OR:en för de två operanderna. Inga överflöden är möjliga från dessa operationer.
Lyfta (§12.4.8) versioner av de ej lyfta fördefinierade heltaliga logiska operatorerna som definieras ovan är också fördefinierade.
12.14.3 Logiska uppräkningsoperatorer
Varje uppräkningstyp E tillhandahåller implicit följande fördefinierade logiska operatorer:
E operator &(E x, E y);
E operator |(E x, E y);
E operator ^(E x, E y);
Resultatet av utvärderingen av x «op» y, där x och y är uttryck av en uppräkningstyp E med en underliggande typ Uoch «op» är en av de logiska operatorerna, är exakt samma som att utvärdera (E)((U)x «op» (U)y). Med andra ord utför uppräkningstypen logiska operatorer helt enkelt den logiska åtgärden på den underliggande typen av de två operanderna.
Lyfta (§12.4.8) former av de o-lyfta fördefinierade uppräknade logiska operatorerna som definieras ovan är också fördefinierade.
12.14.4 Booleska logiska operatorer
De fördefinierade booleska logiska operatorerna är:
bool operator &(bool x, bool y);
bool operator |(bool x, bool y);
bool operator ^(bool x, bool y);
Resultatet av x & y är true om både x och y är true. Annars blir resultatet false.
Resultatet av x | y är true om antingen x eller y är true. Annars blir resultatet false.
Resultatet av x ^ y är true om x är true och y är falseeller x är false och y är true. Annars blir resultatet false. När operanderna är av typen boolberäknar ^-operatorn samma resultat som operatorn !=.
12.14.5 Nullbar boolesk och | Operatörer
Den null-bar booleska typen bool? kan representera tre värden: true, falseoch null.
Precis som med de andra binära operatorerna är lyftna former av logiska operatorer & och | (§12.14.4) också fördefinierade:
bool? operator &(bool? x, bool? y);
bool? operator |(bool? x, bool? y);
Semantiken för operatorerna lifted & och | definieras av följande tabell:
| 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 | 
Note: Den
bool?typen liknar den trevärdestyp som används för booleska uttryck i SQL. Tabellen ovan följer samma semantik som SQL, medan tillämpning av reglerna i §12.4.8 för&och|operatörer inte skulle göra det. Reglerna i §12.4.8 tillhandahåller redan SQL-liknande semantik för den lyfta^operatorn. slutkommentar
12.15 Villkorsstyrda logiska operatorer
12.15.1 Allmänt
Operatorerna && och || kallas för villkorsstyrda logiska operatorer. De kallas också logiska operatorer som "kortsluter".
conditional_and_expression
    : inclusive_or_expression
    | conditional_and_expression '&&' inclusive_or_expression
    ;
conditional_or_expression
    : conditional_and_expression
    | conditional_or_expression '||' conditional_and_expression
    ;
Operatorerna && och || är villkorliga versioner av operatorerna & och |:
- Åtgärden x && ymotsvarar åtgärdenx & y, förutom attyendast utvärderas omxinte ärfalse.
- Åtgärden x || ymotsvarar åtgärdenx | y, förutom attyendast utvärderas omxinte ärtrue.
Observera: Anledningen till att kortslutning sker genom villkoren "inte sant" och "inte falskt" är att möjliggöra för användardefinierade villkorsoperatorer att definiera när kortslutning ska tillämpas. Användardefinierade typer kan vara i ett tillstånd där
operator truereturnerarfalseochoperator falsereturnerarfalse. I dessa fall skulle varken&&eller||kortsluta. slutkommentar
Om en operand av en villkorsstyrd logisk operator har kompileringstidstypen dynamic, är uttrycket dynamiskt bundet (§12.3.3). I det här fallet är kompileringstidstypen för uttrycket dynamic, och lösningen som beskrivs nedan sker vid körning med hjälp av körningstypen för de operander som har kompileringstidstypen dynamic.
En operation av formen x && y eller x || y bearbetas genom tillämpning av överbelastningsupplösning (§12.4.5) som om operationen hade skrivits x & y eller x | y. Då
- Om överbelastningsmatchningen inte hittar en enda bästa operator, eller om överbelastningsupplösningen väljer en av de fördefinierade heltals logiska operatorerna eller nullbara booleska logiska operatorer (§12.14.5), uppstår ett bindningstidsfel.
- Annars, om den valda operatorn är en av de fördefinierade booleska logiska operatorerna (§12.14.4), bearbetas åtgärden enligt beskrivningen i §12.15.2.
- I annat fall är den valda operatorn en användardefinierad operatör och åtgärden bearbetas enligt beskrivningen i §12.15.3.
Det går inte att överbelasta de villkorliga logiska operatorerna direkt. Men eftersom de villkorsstyrda logiska operatorerna utvärderas i termer av de vanliga logiska operatorerna anses överbelastningar av de vanliga logiska operatorerna, med vissa begränsningar, även överbelastningar av de villkorsstyrda logiska operatorerna. Detta beskrivs ytterligare i §12.15.3.
12.15.2 Booleska villkorsstyrda logiska operatorer
När operanderna i && eller || är av typen bool, eller när operanderna är av typer som inte definierar en tillämplig operator & eller operator |, men definierar implicita konverteringar till bool, bearbetas åtgärden på följande sätt:
- Åtgärden x && yutvärderas somx ? y : false. Med andra ord utvärderasxförst och konverteras till typenbool. Omxsedan ärtrueutvärderasyoch konverteras till typenbooloch detta blir resultatet av åtgärden. Annars är resultatet av åtgärdenfalse.
- Åtgärden x || yutvärderas somx ? true : y. Med andra ord utvärderasxförst och konverteras till typenbool. Omxsedan ärtrueblir resultatet av åtgärdentrue. Annars utvärderas och konverterasytill typenbooloch detta blir resultatet av åtgärden.
12.15.3 Användardefinierade villkorsstyrda logiska operatorer
När operanderna i && eller || är av typer som deklarerar en tillämplig användardefinierad operator & eller operator |, ska båda följande vara sanna, där T är den typ där den valda operatorn deklareras:
- Returtypen och typen av varje parameter för den valda operatorn ska vara T. Med andra ord ska operatorn beräkna den logiska OCH eller den logiska OR:en för två operander av typenToch returnera ett resultat av typenT.
- 
              Tskall innehålla deklarationer avoperator trueochoperator false.
Ett bindningstidsfel uppstår om något av dessa krav inte uppfylls. Annars utvärderas åtgärden && eller || genom att kombinera användardefinierade operator true eller operator false med den valda användardefinierade operatorn:
- Åtgärden x && yutvärderas somT.false(x) ? x : T.&(x, y), därT.false(x)är ett anrop avoperator falsesom deklareras iTochT.&(x, y)är ett anrop av den valdaoperator &. Med andra ord utvärderasxförst ochoperator falseanropas på resultatet för att avgöra omxär definitivt falskt. Omxär definitivt falskt är resultatet av åtgärden det värde som tidigare beräknats förx. Annars utvärderasyoch den valdaoperator &anropas på värdet som tidigare beräknats förxoch värdet som beräknas föryför att generera resultatet av åtgärden.
- Åtgärden x || yutvärderas somT.true(x) ? x : T.|(x, y), därT.true(x)är ett anrop avoperator truesom deklareras iTochT.|(x, y)är ett anrop av den valdaoperator |. Med andra ord utvärderasxförst ochoperator trueanropas på resultatet för att avgöra omxär definitivt sant. Omxär definitivt sant är resultatet av åtgärden det värde som tidigare beräknats förx. Annars utvärderasyoch den valdaoperator |anropas på värdet som tidigare beräknats förxoch värdet som beräknas föryför att generera resultatet av åtgärden.
I någon av dessa operationer utvärderas uttrycket som ges av x bara en gång, och uttrycket som ges av y utvärderas antingen inte alls eller exakt en gång.
12.16 Operatorn null coalescing
Operatorn ?? kallas för null coalescing-operatorn.
null_coalescing_expression
    : conditional_or_expression
    | conditional_or_expression '??' null_coalescing_expression
    | throw_expression
    ;
I ett null-sammanslutande uttryck av formen a ?? b, om a inte ärnull, är resultatet a; annars blir resultatet b. Åtgärden utvärderar endast b om a är null.
Operatorn null coalescing är höger-associativ, vilket innebär att åtgärder grupperas från höger till vänster.
Exempel: Ett uttryck av formen
a ?? b ?? cutvärderas soma ?? (b ?? c). I allmänna termer returnerar ett uttryck för formuläretE1 ?? E2 ?? ... ?? ENden första operander som inte ärnull, ellernullom alla operander ärnull. slutexempel
Vilken typ av uttryck a ?? b beror på vilka implicita konverteringar som är tillgängliga på operanderna. I prioritetsordning är typen av a ?? bA₀, A, eller B, där A är typen av a (förutsatt att a har en typ), B är typen av b(förutsatt att b har en typ), och A₀ är den underliggande typen av A om A är en nullbar värdetyp eller A annat. Mer specifikt bearbetas a ?? b på följande sätt:
- Om Adet finns och är en ohanterad typ (§8.8) eller om den är en värdetyp som inte kan nulliseras uppstår ett kompileringsfel.
- Om Afinns ochbär ett dynamiskt uttryck är resultattypen annarsdynamic. Vid körning utvärderasaförst. Omainte ärnullkonverterasatilldynamicoch detta blir resultatet. Annars utvärderasboch detta blir resultatet.
- Annars, om Afinns och är en nullbar värdetyp och det finns en implicit konvertering frånbtillA₀, är resultattypenA₀. Vid körning utvärderasaförst. Omainte ärnullbehandlasasom typA₀, och detta blir resultatet. Annars utvärderasboch konverteras till typenA₀, och detta blir resultatet.
- Annars är resultattypen Aombfinns och det finns en implicit konvertering frånAtillA. Vid körning utvärderasaförst. Omainte ärnullbliraresultatet. Annars utvärderasboch konverteras till typenA, och detta blir resultatet.
- Annars, om Afinns och är en nullbar värdetyp,bhar en typBoch det finns en implicit konvertering frånA₀tillB, är resultattypenB. Vid körning utvärderasaförst. Omainte ärnullochapackas upp till typA₀och konverteras till typB, vilket blir resultatet. Annars utvärderasboch blir resultatet.
- Annars, om bhar en typBoch det finns en implicit konvertering frånatillB, är resultattypenB. Vid körning utvärderasaförst. Omainte ärnullkonverterasatill typBoch detta blir resultatet. Annars utvärderasboch blir resultatet.
- Annars är aochbinkompatibla och ett kompileringsfel inträffar.
Exempel:
T M<T>(T a, T b) => a ?? b; string s = M(null, "text"); int i = M(19, 23);Typparametern
Tför metodenMär obegränsad. Därför kan typargumentet vara en referenstyp, eller nullbar värdetyp, som visas i det första anropet tillM. Typargumentet kan också vara en värdetyp som inte kan vara null, vilket visas i det andra anropet tillM. När typargumentet är en icke-nullbar värdetyp är värdet av uttrycketa ?? ba.slutexempel
12.17 Operatorn för utkastsuttryck
throw_expression
    : 'throw' null_coalescing_expression
    ;
En throw_expression kastar värdet som skapas genom att utvärdera null_coalescing_expression. Uttrycket ska implicit konverteras till System.Exceptionoch resultatet av utvärderingen av uttrycket konverteras till System.Exception innan det utlöses. Beteendet vid körningen av utvärderingen av ett throw-uttryck är detsamma som specificerat för en throw-instruktion (§13.10.6).
En throw_expression har ingen typ. En throw_expression kan konverteras till varje typ genom en implicit throw-konvertering.
Ett utkastsuttryck ska endast förekomma i följande syntaktiska kontexter:
- Som den andra eller tredje operanden för en ternär villkorsoperator (?:).
- Som den andra operanden för en null coalescing-operatör (??).
- Som kroppen av en lambda eller medlem med uttrycksbaserad kropp.
12.18 Deklarationsuttryck
Ett deklarationsuttryck deklarerar en lokal variabel.
declaration_expression
    : local_variable_type identifier
    ;
local_variable_type
    : type
    | 'var'
    ;
              simple_name_ anses också vara ett deklarationsuttryck om enkel namnsökning inte hittade någon associerad deklaration (§12.8.4). När det används som ett deklarationsuttryck kallas _ ett enkelt ignorera. Det är semantiskt likvärdigt med var _, men tillåts på fler platser.
Ett deklarationsuttryck ska endast förekomma i följande syntaktiska sammanhang:
- Som en outargument_value i en argument_list.
- Som ett enkelt kasserande _som består av vänster sida av en enkel tilldelning (§12.22.2).
- Som en tuple_element i en eller flera rekursivt kapslade tuple_expressions, vars yttersta del består av den vänstra sidan av en dekonstruerande tilldelning. En deconstruction_expression ger upphov till deklarationsuttryck i den här positionen, även om deklarationsuttrycken inte är syntaktiskt närvarande.
Note: Det innebär att ett deklarationsuttryck inte kan parenteseras. slutkommentar
Det är ett fel att en implicit typvariabel som deklareras med en declaration_expression refereras inom argument_list där den deklareras.
Det är ett fel att en variabel som deklarerats med en declaration_expression refereras till inom en dekonstruktionstilldelning där den förekommer.
Ett deklarationsuttryck som är en enkel borttagning eller där local_variable_type är identifieraren var klassificeras som en implicit typad variabel. Uttrycket har ingen typ och typen av den lokala variabeln härleds baserat på den syntaktiska kontexten enligt följande:
- I en argument_list är variabelns härledda typ den deklarerade typen av motsvarande parameter.
- Som på vänstersidan av en enkel tilldelning är den härledda typen av variabel typen på högersidan av tilldelningen.
- I en tuple_expression på vänster sida av en enkel tilldelning är den härledda typen av variabel typen av det motsvarande tupelelementet på höger sida (efter att tilldelningen dekonstruerats).
Annars klassificeras deklarationsuttrycket som en uttryckligen typad variabel, och uttryckets typ samt den deklarerade variabeln ska vara den som anges av lokal_variabel_typ.
Ett deklarationsuttryck med identifieraren _ är ett ignorerande (§9.2.9.2), och introducerar inget namn på variabeln. Ett deklarationsuttryck med en annan identifierare än _ introducerar det namnet i det närmaste omslutande lokala variabeldeklarationsutrymmet (§7.3).
Exempel:
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 _);Deklarationen av
s1visar både explicita och implicit inskrivna deklarationsuttryck. Den härledda typen avb1ärbooleftersom det är typen av motsvarande utdataparameter iM1. EfterföljandeWriteLinekan komma åti1ochb1, som har införts i den omgivande omfattningen.Deklarationen av
s2visar ett försök att användai2i det kapslade anropet tillM, vilket inte är tillåtet, eftersom referensen inträffar i argumentlistan däri2deklarerades. Å andra sidan tillåts referensen tillb2i det slutliga argumentet, eftersom den inträffar efter slutet av listan över kapslade argument därb2deklarerades.Deklarationen av
s3visar användningen av både implicit och explicit typade deklarationsuttryck som ignoreras. Eftersom underskott inte deklarerar en namngiven variabel, är flera förekomster av identifieraren_tillåtna.(int i1, int _, (var i2, var _), _) = (1, 2, (3, 4), 5);Det här exemplet visar användningen av implicita och explicit inskrivna deklarationsuttryck för både variabler och ignoreras i en dekonstruktionstilldelning. simple_name
_motsvararvar _när ingen deklaration av_hittas.void M1(out int i) { ... } void M2(string _) { M1(out _); // Error: `_` is a string M1(out var _); }Det här exemplet visar användningen av
var _för att tillhandahålla en implicit typad bortkastad när_inte är tillgänglig, eftersom den anger en variabel i det omgivande omfånget.slutexempel
12.19 Villkorsstyrd operator
Operatorn ?: kallas för villkorsoperator. Det kallas ibland även för ternary-operatorn.
conditional_expression
    : null_coalescing_expression
    | null_coalescing_expression '?' expression ':' expression
    | null_coalescing_expression '?' 'ref' variable_reference ':'
      'ref' variable_reference
    ;
Ett utkastsuttryck (§12.17) tillåts inte i en villkorsstyrd operator om ref det finns.
Ett villkorsuttryck för formuläret b ? x : y utvärderar först villkoret b. Om b sedan är trueutvärderas x och blir resultatet av åtgärden. Annars utvärderas y och blir resultatet av operationen. Ett villkorsuttryck utvärderar aldrig både x och y.
Villkorsoperatorn är höger-associativ, vilket innebär att åtgärder grupperas från höger till vänster.
Exempel: Ett uttryck av formen
a ? b : c ? d : eutvärderas soma ? b : (c ? d : e). slutexempel
Den första operanden för ?:-operatorn ska vara ett uttryck som implicit kan konverteras till booleller ett uttryck av en typ som implementerar operator true. Om inget av dessa krav uppfylls uppstår ett kompileringsfel.
Om ref finns:
- Det ska finnas en identitetskonvertering mellan typerna av de två variable_referenceoch resultatets typ kan vara av båda typerna. Om någon av typerna är dynamicföredrar typinferensdynamic(§8.7). Om någon av typerna är en tuple (§8.3.11), innehåller typinferens elementnamnen när elementnamnen i samma ordningsplacering matchar i båda tuplarna.
- Resultatet är en variabelreferens som kan skrivas om båda variable_referenceär skrivbara.
Obs: När
reffinns returnerar conditional_expression en variabelreferens som kan tilldelas till en referensvariabel med operatorn= refeller skickas som en referens-/indata-/utdataparameter. slutkommentar
Om ref inte finns kontrollerar den andra och tredje operanderna, x och y, för ?: operatorn typen av villkorsuttryck:
- Om xhar typXochyhar typY- Om det finns en identitetskonvertering mellan XochYär resultatet den vanligaste typen av uttryck (§12.6.3.16). Om någon av typerna ärdynamicföredrar typinferensdynamic(§8.7). Om någon av typerna är en tuple (§8.3.11), innehåller typinferens elementnamnen när elementnamnen i samma ordningsplacering matchar i båda tuplarna.
- Om det annars finns en implicit konvertering (§10.2) från XtillY, men inte frånYtillX, ärYtypen av villkorsuttryck.
- Annars, om det finns en implicit uppräkningskonvertering (§10.2.4) från XtillY, ärYtypen av villkorsuttryck.
- Annars, om det finns en implicit uppräkningskonvertering (§10.2.4) från YtillX, ärXtypen av villkorsuttryck.
- Om det annars finns en implicit konvertering (§10.2) från YtillX, men inte frånXtillY, ärXtypen av villkorsuttryck.
- Annars kan ingen uttryckstyp fastställas och ett kompileringsfel inträffar.
 
- Om det finns en identitetskonvertering mellan 
- Om bara en av xochyhar en typ, och bådexochyimplicit konverteras till den typen, är det typen av villkorsuttryck.
- Annars kan ingen uttryckstyp fastställas och ett kompileringsfel inträffar.
Körningstiden för ett ref-villkorsuttryck av formen b ? ref x : ref y består av följande steg:
- Först, utvärderas b, och sedan bestämsbool-värdet förb.- Om det finns en implicit konvertering från typen av btillboolutförs den implicita konverteringen för att skapa ettboolvärde.
- Annars anropas operator truesom definieras av typenbför att skapa ettboolvärde.
 
- Om det finns en implicit konvertering från typen av 
- Om det boolvärde som skapas av steget ovan ärtrueutvärderasxoch den resulterande variabelreferensen blir resultatet av villkorsuttrycket.
- Annars utvärderas yoch den resulterande variabelreferensen blir resultatet av villkorsuttrycket.
Körningsbearbetningen av ett villkorsuttryck av typen b ? x : y består av följande steg:
- Först, utvärderas b, och sedan bestämsbool-värdet förb.- Om det finns en implicit konvertering från typen av btillboolutförs den implicita konverteringen för att skapa ettboolvärde.
- Annars anropas operator truesom definieras av typenbför att skapa ettboolvärde.
 
- Om det finns en implicit konvertering från typen av 
- Om det boolvärde som skapas av steget ovan ärtrueutvärderas och konverterasxtill typen av villkorsuttryck, och detta blir resultatet av villkorsuttrycket.
- Annars utvärderas och konverteras ytill typen av villkorsuttryck, och detta blir resultatet av villkorsuttrycket.
12.20 Anonyma funktionsuttryck
12.20.1 Allmänt
En anonym funktion är ett uttryck som representerar en "infogad" metoddefinition. En anonym funktion har inte ett värde eller en typ i sig själv, men den kan konverteras till en kompatibel delegat- eller uttrycksträdstyp. Utvärderingen av en anonym funktionskonvertering beror på måltypen för konverteringen: Om det är en delegattyp utvärderas konverteringen till ett delegatvärde som refererar till metoden som den anonyma funktionen definierar. Om det är en uttrycksträdstyp utvärderas konverteringen till ett uttrycksträd som representerar metodens struktur som en objektstruktur.
Obs: Av historiska skäl finns det två syntaktiska smaker av anonyma funktioner, nämligen lambda_expressions och anonymous_method_expressions. I nästan alla syften är lambda_expressions mer koncisa och uttrycksfulla än anonymous_method_expressions, som förblir i språket för bakåtkompatibilitet. slutkommentar
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
    ;
Vid erkännande av en anonymous_function_body om både null_conditional_invocation_expression och uttryck alternativ är tillämpliga, skall den förra väljas.
Obs: Överlappningen av och prioriteten mellan alternativen här är enbart för beskrivande bekvämlighet; grammatikreglerna kan utarbetas för att ta bort överlappningen. ANTLR och andra grammatiksystem använder samma bekvämlighet, så anonymous_function_body har de angivna semantiken automatiskt. slutkommentar
Obs: När det behandlas som ett uttryckskulle ett syntaktiskt formulär som
x?.M()vara ett fel om resultattypen förMärvoid(§12.8.13). Men när den betraktas som en null_conditional_invocation_expressiontillåts resultattypen varavoid. slutkommentar
Exempel: Resultattypen för
List<T>.Reverseärvoid. I följande kod är brödtexten i det anonyma uttrycket en null_conditional_invocation_expression, så det är inte ett fel.Action<List<int>> a = x => x?.Reverse();slutexempel
Operatorn => har samma prioritet som tilldelning (=) och är rätt associativ.
En anonym funktion med async modifieraren är en asynkron funktion och följer de regler som beskrivs i §15.14.
Parametrarna för en anonym funktion i form av en lambda_expression kan skrivas explicit eller implicit. I en uttryckligen angiven parameterlista anges typen av varje parameter explicit. I en implicit typ av parameterlista härleds typerna av parametrarna från kontexten där den anonyma funktionen inträffar, särskilt när den anonyma funktionen konverteras till en kompatibel delegattyp eller uttrycksträdstyp, ger den typen parametertyperna (§10.7).
I en lambda_expression med en enda, implicit typad parameter kan parenteserna utelämnas från parameterlistan. Med andra ord en anonym funktion i formuläret
( «param» ) => «expr»
kan förkortas till
«param» => «expr»
Parameterlistan för en anonym funktion i form av en anonymous_method_expression är valfri. Om detta anges ska parametrarna uttryckligen skrivas. Annars kan den anonyma funktionen konverteras till en delegat med en parameterlista som inte innehåller utdataparametrar.
Ett block kropp för en anonym funktion kan alltid nås (§13.2).
Exempel: Några exempel på anonyma funktioner följer nedan:
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 omittedslutexempel
Beteendet för lambda_expressions och anonymous_method_expressions är detsamma förutom följande punkter:
- anonymous_method_expressiontillåter att parameterlistan utelämnas helt och hållet, vilket ger konverteringsbarhet för att delegera typer av valfri lista med värdeparametrar.
- lambda_expressiontillåter att parametertyper utelämnas och härleds medan anonymous_method_expressionkräver att parametertyper uttryckligen anges.
- Brödtexten i en lambda_expression kan vara ett uttryck eller ett block medan brödtexten i en anonymous_method_expression ska vara ett block.
- Endast lambda_expressions har konverteringar till kompatibla uttrycksträdstyper (§8.6).
12.20.2 Anonyma funktionssignaturer
Den anonymous_function_signature av en anonym funktion definierar namnen och eventuellt typerna av parametrarna för den anonyma funktionen. Omfattningen av parametrarna för den anonyma funktionen är anonymous_function_body (§7.7). Tillsammans med parameterlistan (om tillämpligt) utgör den anonyma metodkroppen ett deklarationsutrymme (§7.3). Det är alltså ett kompileringsfel om namnet på en parameter i den anonyma funktionen matchar namnet på en lokal variabel, lokal konstant eller parameter vars omfång inkluderar anonymous_method_expression eller lambda_expression.
Om en anonym funktion har en explicit_anonymous_function_signaturebegränsas uppsättningen med kompatibla ombudstyper och uttrycksträdstyper till dem som har samma parametertyper och modifierare i samma ordning (§10.7). Till skillnad från metodgruppkonverteringar (§10.8) stöds inte kontraavvikelse för anonyma funktionsparametertyper. Om en anonym funktion inte har någon anonymous_function_signaturebegränsas uppsättningen med kompatibla ombudstyper och uttrycksträdstyper till de som inte har några utdataparametrar.
Observera att en anonymous_function_signature inte kan innehålla attribut eller en parametermatris. Ett anonymous_function_signature kan dock vara kompatibelt med en ombudstyp vars parameterlista innehåller en parametermatris.
Observera också att konverteringen till en uttrycksträdstyp, även om den är kompatibel, fortfarande kan misslyckas vid kompileringstid (§8.6).
12.20.3 Anonyma funktionsorgan
Huvuddel (uttryck eller block) för en anonym funktion omfattas av följande regler:
- Om den anonyma funktionen innehåller en signatur är parametrarna som anges i signaturen tillgängliga i brödtexten. Om den anonyma funktionen inte har någon signatur kan den konverteras till en ombudstyp eller uttryckstyp med parametrar (§10.7), men parametrarna kan inte nås i brödtexten.
- Med undantag för bireferensparametrar som anges i signaturen (om någon) för den närmaste omslutande anonyma funktionen är det ett kompileringsfel för brödtexten att komma åt en bireferensparameter.
- Förutom parametrar som anges i signaturen (om någon) för den närmaste omslutande anonyma funktionen är det ett kompileringsfel för funktionen att komma åt en parameter av typen ref struct.
- När typen av thisär en structtyp är det ett kompileringsfel om koden försöker komma åtthis. Detta gäller om åtkomsten är explicit (som ithis.x) eller implicit (som ixdärxär en instansmedlem i struct). Den här regeln förbjuder helt enkelt sådan åtkomst och påverkar inte om medlemssökning resulterar i en medlem i structen.
- Brödtexten har åtkomst till de yttre variablerna (§12.20.6) för den anonyma funktionen. Åtkomsten till en yttre variabel refererar till instansen av variabeln som är aktiv när lambda_expression eller anonymous_method_expression utvärderas (§12.20.7).
- Det är ett kompileringsfel som gör att brödtexten innehåller en goto-instruktion, enbreak-instruktion eller encontinue-instruktion vars mål ligger utanför brödtexten eller i brödtexten i en innesluten anonym funktion.
- En return-instruktion i brödtexten returnerar kontrollen från ett anrop av den närmaste omslutande anonyma funktionen, inte från den omslutande funktionsmedlemmen.
Det är uttryckligen ospecificerat om det finns något sätt att köra blocket för en anonym funktion annat än genom utvärdering och anrop av lambda_expression eller anonymous_method_expression. I synnerhet kan en kompilator välja att implementera en anonym funktion genom att syntetisera en eller flera namngivna metoder eller typer. Namnen på sådana syntetiserade element skall vara av ett formulär som är reserverat för kompilatoranvändning (§6.4.3).
12.20.4 Överbelastningsupplösning
Anonyma funktioner i en argumentlista deltar i typinferens och överlagringslösning. Se §12.6.3 och §12.6.4 för de exakta reglerna.
Exempel: I följande exempel visas effekten av anonyma funktioner på överbelastningsupplösningen.
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; } }Klassen
ItemList<T>har tvåSummetoder. Var och en tar ettselectorargument, som extraherar värdet som ska summeras från ett listobjekt. Det extraherade värdet kan antingen vara eninteller endoubleoch den resulterande summan är också antingen eninteller endouble.De
Summetoderna kan till exempel användas för att beräkna summor från en lista med detaljrader i en ordning.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( ... ) { ... } }Vid den första anropet av
orderDetails.Sumär båda metodernaSumgiltiga eftersom den anonyma funktionend => d.UnitCountär kompatibel med bådeFunc<Detail,int>ochFunc<Detail,double>. Överbelastningslösningen väljer dock den förstaSum-metoden eftersom konverteringen tillFunc<Detail,int>är bättre än konverteringen tillFunc<Detail,double>.I den andra anropet av
orderDetails.Sumgäller endast den andraSum-metoden eftersom den anonyma funktionend => d.UnitPrice * d.UnitCountgenererar ett värde av typendouble. Överbelastningsupplösningen väljer därför den andra metodenSumför anropet.slutexempel
12.20.5 Anonyma funktioner och dynamisk bindning
En anonym funktion får inte vara mottagare, argument eller operand för en dynamiskt bunden åtgärd.
12.20.6 Yttre variabler
12.20.6.1 Allmänt
Varje lokal variabel, värdeparameter eller parametermatris vars omfång omfattar lambda_expression eller anonymous_method_expression kallas för en yttre variabel av den anonyma funktionen. I en instansfunktionsmedlem i en klass betraktas det här värdet som en värdeparameter och är en yttre variabel för alla anonyma funktioner som ingår i funktionsmedlemmen.
12.20.6.2 Insamlade yttre variabler
När en yttre variabel refereras till av en anonym funktion sägs den yttre variabeln ha avbildats av den anonyma funktionen. Normalt är livslängden för en lokal variabel begränsad till körning av blocket eller instruktionen som den är associerad med (§9.2.9.1). Livslängden för en insamlad yttre variabel utökas dock åtminstone tills det ombuds- eller uttrycksträd som skapats från den anonyma funktionen blir berättigat till skräpinsamling.
Exempel: I exemplet
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()); } }den lokala variabeln
xtas om hand av den anonyma funktionen och livslängden förxförlängs, åtminstone tills delegeringen som returneras frånFblir berättigad till skräpinsamling. Eftersom varje anrop av den anonyma funktionen fungerar på samma instans avxär resultatet av exemplet:1 2 3slutexempel
När en lokal variabel eller en värdeparameter registreras av en anonym funktion anses den lokala variabeln eller parametern inte längre vara en fast variabel (§24.4), utan anses i stället vara en flyttbar variabel. Insamlade yttre variabler kan dock inte användas i en fixed instruktion (§24.7), så adressen för en infångade yttre variabel kan inte tas.
Obs: Till skillnad från en okapslad variabel kan en fångad lokal variabel samtidigt exponeras för flera trådar. slutkommentar
12.20.6.3 Instansiering av lokala variabler
En lokal variabel anses vara instansierad när körningen anger variabelns omfång.
Exempel: När till exempel följande metod anropas instansieras och initieras den lokala variabeln
xtre gånger – en gång för varje iteration av loopen.static void F() { for (int i = 0; i < 3; i++) { int x = i * 2 + 1; ... } }Om du flyttar deklarationen av
xutanför loopen resulterar det dock i en enda instansiering avx:static void F() { int x; for (int i = 0; i < 3; i++) { x = i * 2 + 1; ... } }slutexempel
När den inte registreras finns det inget sätt att se exakt hur ofta en lokal variabel instansieras– eftersom instansieringarnas livslängd är uppdelade är det möjligt för varje instansiering att helt enkelt använda samma lagringsplats. Men när en anonym funktion fångar en lokal variabel blir effekterna av instansiering uppenbara.
Exempel: Exemplet
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(); } } }genererar utdata:
1 3 5Men när deklarationen av
xflyttas utanför loopen: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(); } } }utdata är:
5 5 5Observera att en kompilator tillåts (men inte krävs) för att optimera de tre instanserna i en enda delegatinstans (§10.7.2).
slutexempel
Om en for-loop deklarerar en iterationsvariabel anses själva variabeln deklareras utanför loopen.
Exempel: Om exemplet ändras för att avbilda själva iterationsvariabeln:
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(); } } }Endast en instans av iterationsvariabeln registreras, vilket ger utdata:
3 3 3slutexempel
Det är möjligt för anonyma funktionsdelegater att dela vissa insamlade variabler men ändå ha separata instanser av andra.
Exempel: Till exempel om
Fändras tillstatic 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 tre delegaterna fångar samma instans av
xmen separata instanser avy, och resultatet är:1 1 2 1 3 1slutexempel
Separata anonyma funktioner kan avbilda samma instans av en yttre variabel.
Exempel: I exemplet:
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 två anonyma funktionerna avbildar samma instans av den lokala variabeln
x, och de kan därför "kommunicera" via den variabeln. Utdata från exemplet är:5 10slutexempel
12.20.7 Utvärdering av anonyma funktionsuttryck
En anonym funktion F ska alltid konverteras till en delegattyp D eller ett uttrycksträd E, antingen direkt eller genom att utföra ett uttryck för delegate-skapande new D(F). Denna konvertering avgör resultatet av den anonyma funktionen enligt beskrivningen i §10.7.
12.20.8 Implementeringsexempel
Det här underavsnittet är informativt.
Det här delavsnittet beskriver en möjlig implementering av omvandling av anonyma funktioner med avseende på andra C#-konstruktioner. Implementeringen som beskrivs här baseras på samma principer som används av en kommersiell C#-kompilator, men det är inte på något sätt en föreskriven implementering, och det är inte heller den enda möjliga. Den nämner bara kort konverteringar till uttrycksträd, eftersom deras exakta semantik ligger utanför den här specifikationens omfång.
Resten av det här underavsnittet ger flera exempel på kod som innehåller anonyma funktioner med olika egenskaper. För varje exempel tillhandahålls en motsvarande översättning till kod som endast använder andra C#-konstruktioner. I exemplen antas identifieraren D representera följande ombudstyp:
public delegate void D();
Den enklaste formen av en anonym funktion är en som inte fångar upp några yttre variabler:
delegate void D();
class Test
{
    static void F()
    {
        D d = () => Console.WriteLine("test");
    }
}
Detta kan översättas till en delegat-instansiering som refererar till en kompilatorgenererad statisk metod där koden för den anonyma funktionen placeras:
delegate void D();
class Test
{
    static void F()
    {
        D d = new D(__Method1);
    }
    static void __Method1()
    {
        Console.WriteLine("test");
    }
}
I följande exempel refererar den anonyma funktionen till instansmedlemmar i this:
delegate void D();
class Test
{
    int x;
    void F()
    {
        D d = () => Console.WriteLine(x);
    }
}
Detta kan översättas till en kompilatorgenererad instansmetod som innehåller koden för den anonyma funktionen:
delegate void D();
class Test
{
   int x;
   void F()
   {
       D d = new D(__Method1);
   }
   void __Method1()
   {
       Console.WriteLine(x);
   }
}
I det här exemplet samlar den anonyma funktionen in en lokal variabel:
delegate void D();
class Test
{
    void F()
    {
        int y = 123;
        D d = () => Console.WriteLine(y);
    }
}
Livslängden för den lokala variabeln måste nu utökas till minst livslängden för den anonyma funktionsdelegaten. Detta kan uppnås genom att "hissa" den lokala variabeln till ett fält i en kompilatorgenererad klass. Instansiering av den lokala variabeln (§12.20.6.3) motsvarar sedan att skapa en instans av den kompilatorgenererade klassen, och åtkomst till den lokala variabeln motsvarar åtkomst till ett fält i instansen av den kompilatorgenererade klassen. Dessutom blir den anonyma funktionen en instansmetod för den kompilatorgenererade klassen:
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);
        }
    }
}
Slutligen samlar följande anonyma funktion in this samt två lokala variabler med olika livslängd:
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);
       }
   }
}
Här skapas en kompilatorgenererad klass för varje block där lokalbefolkningen samlas in så att lokalbefolkningen i de olika blocken kan ha oberoende livslängder. En instans av __Locals2, den kompilatorgenererade klassen för det inre blocket, innehåller den lokala variabeln z och ett fält som refererar till en instans av __Locals1. En instans av __Locals1, den kompilatorgenererade klassen för det yttre blocket, innehåller den lokala variabeln y och ett fält som refererar till this för den omslutande funktionsmedlemmen. Med dessa datastrukturer är det möjligt att nå alla insamlade yttre variabler via en instans av __Local2, och koden för den anonyma funktionen kan därför implementeras som en instansmetod för den klassen.
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);
        }
    }
}
Samma teknik som används här för att samla in lokala variabler kan också användas när du konverterar anonyma funktioner till uttrycksträd: referenser till de kompilatorgenererade objekten kan lagras i uttrycksträdet och åtkomst till de lokala variablerna kan representeras som fältåtkomster på dessa objekt. Fördelen med den här metoden är att den gör att de "lyfta" lokala variablerna kan delas mellan delegater och uttrycksträd.
Slut på informativ text.
12.21 Frågeuttryck
12.21.1 Allmänt
Frågeuttryck tillhandahålla en språkintegrerad syntax för frågor som liknar relations- och hierarkiska frågespråk som SQL och 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
    ;
Ett frågeuttryck börjar med en from-sats och slutar med antingen en select- eller group-sats. Den inledande from-satsen kan följas av noll eller fler from, let, where, join eller orderby-satser. Varje from-sats är en generator som introducerar en intervallvariabel som sträcker sig över elementen i en sekvens. Varje let-sats introducerar en intervallvariabel som representerar ett värde som beräknas med hjälp av tidigare intervallvariabler. Varje where-sats är ett filter som exkluderar objekt från resultatet. Varje join-sats jämför angivna nycklar i källsekvensen med nycklar i en annan sekvens, vilket ger matchande par. Varje orderby-sats ordnar om objekt enligt angivna villkor. Den sista select- eller group-satsen anger resultatets form när det gäller intervallvariablerna. Slutligen kan en into-sats användas för att "skarva" frågor genom att behandla resultatet av en fråga som en generator i en efterföljande fråga.
12.21.2 Tvetydigheter i frågeuttryck
Frågeuttryck använder ett antal kontextuella nyckelord (§6.4.4): ascending, by, descending, equals, from, group, into, join, let, on, orderby, select och where.
För att undvika tvetydigheter som kan uppstå vid användning av dessa identifierare både som nyckelord och enkla namn betraktas dessa identifierare som nyckelord var som helst i ett frågeuttryck, såvida de inte är prefix med "@" (§6.4.4) i vilket fall de betraktas som identifierare. I det här syftet är ett frågeuttryck ett uttryck som börjar med "fromidentifierare" följt av valfri token förutom ";", "=" eller ",".
12.21.3 Översättning av frågeuttryck
12.21.3.1 Allmänt
C#-språket anger inte körningssemantiken för frågeuttryck. I stället översätts frågeuttryck till anrop av metoder som följer frågeuttrycksmönstret (§12.21.4). Mer specifikt översätts frågeuttryck till anrop av metoder med namnet Where, Select, SelectMany, Join, GroupJoin, OrderBy, OrderByDescending, ThenBy, ThenByDescending, GroupByoch Cast. Dessa metoder förväntas ha särskilda signaturer och returtyper enligt beskrivningen i §12.21.4. Dessa metoder kan vara instansmetoder för objektet som efterfrågas eller tilläggsmetoder som är externa för objektet. Dessa metoder implementerar den faktiska utförandet av frågan.
Översättningen från frågeuttryck till metodanrop är en syntaktisk mappning som inträffar innan någon typbindning eller överlagringsmatchning har utförts. Efter översättning av frågeuttryck bearbetas de resulterande metodanropen som vanliga metodanrop, och detta kan i sin tur avslöja kompileringstidsfel. Dessa felvillkor omfattar, men är inte begränsade till, metoder som inte finns, argument av fel typer och generiska metoder där typinferens misslyckas.
Ett frågeuttryck bearbetas genom att upprepade gånger tillämpa följande översättningar tills inga ytterligare minskningar är möjliga. Översättningarna visas i programordning: varje avsnitt förutsätter att översättningarna i föregående avsnitt har utförts fullständigt, och när de är uttömda kommer ett avsnitt inte senare att ses över i bearbetningen av samma frågeuttryck.
Det är ett kompileringstidsfel för ett frågeuttryck att inkludera en tilldelning till en intervallvariabel eller användningen av en intervallvariabel som argument för en referens- eller utdataparameter.
Vissa översättningar infogar intervallvariabler med transparenta identifierare markerade med *. Dessa beskrivs ytterligare i §12.21.3.8.
12.21.3.2 Frågeuttryck med fortsättningar
Ett frågeuttryck med en fortsättning som följer dess frågetext
from «x1» in «e1» «b1» into «x2» «b2»
översätts till
from «x2» in ( from «x1» in «e1» «b1» ) «b2»
Översättningarna i följande avsnitt förutsätter att frågor inte har några fortsättningar.
Exempel: Exemplet:
from c in customers group c by c.Country into g select new { Country = g.Key, CustCount = g.Count() }översätts till:
from g in (from c in customers group c by c.Country) select new { Country = g.Key, CustCount = g.Count() }den slutliga översättningen av vilken är
customers. GroupBy(c => c.Country). Select(g => new { Country = g.Key, CustCount = g.Count() })slutexempel
12.21.3.3 Explicita intervallvariabeltyper
En from-sats som uttryckligen anger en intervallvariabeltyp
from «T» «x» in «e»
översätts till
from «x» in ( «e» ) . Cast < «T» > ( )
En join-sats som uttryckligen anger en intervallvariabeltyp
join «T» «x» in «e» on «k1» equals «k2»
översätts till
join «x» in ( «e» ) . Cast < «T» > ( ) on «k1» equals «k2»
Översättningarna i följande avsnitt förutsätter att frågor inte har några explicita intervallvariabeltyper.
Exempel: Exemplet
from Customer c in customers where c.City == "London" select cöversätts till
from c in (customers).Cast<Customer>() where c.City == "London" select cden slutliga översättningen av
customers. Cast<Customer>(). Where(c => c.City == "London")slutexempel
Obs: Explicita intervallvariabeltyper är användbara för att fråga samlingar som implementerar det icke-generiska
IEnumerable-gränssnittet, men inte det allmännaIEnumerable<T>-gränssnittet. I exemplet ovan skulle detta vara fallet om kunderna var av typenArrayList. slutkommentar
12.21.3.4 Degenerera frågeuttryck
Ett frågeuttryck för formuläret
from «x» in «e» select «x»
översätts till
( «e» ) . Select ( «x» => «x» )
Exempel: Exemplet
from c in customers select cöversätts till
(customers).Select(c => c)slutexempel
Ett degenererat frågeuttryck är ett som trivialt väljer elementen i källan.
Obs! Senare faser av översättningen (§12.21.3.6 och §12.21.3.7) tar bort degenererade frågor som introduceras genom andra översättningssteg genom att ersätta dem med källan. Det är dock viktigt att se till att resultatet av ett frågeuttryck aldrig är själva källobjektet. Annars kan resultatet av en sådan fråga oavsiktligt exponera privata data (t.ex. en elementmatris) för en anropare. Därför skyddar det här steget degenererade frågor som skrivits direkt i källkoden genom att uttryckligen anropa
Selectpå källan. Det är sedan upp till implementerarna förSelectoch andra frågeoperatorer att se till att dessa metoder aldrig returnerar själva källobjektet. slutkommentar
12.21.3.5 Från, låt, var, kopplings- och orderby-satser
Ett frågeuttryck med en andra from-sats följt av en select-sats
from «x1» in «e1»  
from «x2» in «e2»  
select «v»
översätts till
( «e1» ) . SelectMany( «x1» => «e2» , ( «x1» , «x2» ) => «v» )
Exempel: Exemplet
from c in customers from o in c.Orders select new { c.Name, o.OrderID, o.Total }översätts till
(customers). SelectMany(c => c.Orders, (c,o) => new { c.Name, o.OrderID, o.Total } )slutexempel
Ett frågeuttryck med en andra from-sats följt av en frågetext Q som innehåller en icke-tom uppsättning frågetextsatser:
from «x1» in «e1»
from «x2» in «e2»
Q
översätts till
from * in («e1») . SelectMany( «x1» => «e2» ,
                              ( «x1» , «x2» ) => new { «x1» , «x2» } )
Q
Exempel: Exemplet
from c in customers from o in c.Orders orderby o.Total descending select new { c.Name, o.OrderID, o.Total }översätts till
from * in (customers). SelectMany(c => c.Orders, (c,o) => new { c, o }) orderby o.Total descending select new { c.Name, o.OrderID, o.Total }den slutliga översättningen av
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 })där
xär en kompilatorgenererad identifierare som annars är osynlig och otillgänglig.slutexempel
Ett let uttryck tillsammans med dess föregående from-sats:
from «x» in «e»  
let «y» = «f»  
...
översätts till
from * in ( «e» ) . Select ( «x» => new { «x» , «y» = «f» } )  
...
Exempel: Exemplet
from o in orders let t = o.Details.Sum(d => d.UnitPrice * d.Quantity) where t >= 1000 select new { o.OrderID, Total = t }översätts till
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 }den slutliga översättningen av
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 })där
xär en kompilatorgenererad identifierare som annars är osynlig och otillgänglig.slutexempel
Ett where uttryck tillsammans med dess föregående from-sats:
from «x» in «e»  
where «f»  
...
översätts till
from «x» in ( «e» ) . Where ( «x» => «f» )  
...
En join-sats omedelbart följt av en select-sats
from «x1» in «e1»  
join «x2» in «e2» on «k1» equals «k2»  
select «v»
översätts till
( «e1» ) . Join( «e2» , «x1» => «k1» , «x2» => «k2» , ( «x1» , «x2» ) => «v» )
Exempel: Exemplet
from c in customers join o in orders on c.CustomerID equals o.CustomerID select new { c.Name, o.OrderDate, o.Total }översätts till
(customers).Join( orders, c => c.CustomerID, o => o.CustomerID, (c, o) => new { c.Name, o.OrderDate, o.Total })slutexempel
En join-sats följt av en frågetextsats:
from «x1» in «e1»  
join «x2» in «e2» on «k1» equals «k2»  
...
översätts till
from * in ( «e1» ) . Join(  
«e2» , «x1» => «k1» , «x2» => «k2» ,
( «x1» , «x2» ) => new { «x1» , «x2» })  
...
En join-into-sats omedelbart följt av en select-sats
from «x1» in «e1»  
join «x2» in «e2» on «k1» equals «k2» into «g»  
select «v»
översätts till
( «e1» ) . GroupJoin( «e2» , «x1» => «k1» , «x2» => «k2» ,
                     ( «x1» , «g» ) => «v» )
En join into-sats följt av en frågetextsats
from «x1» in «e1»  
join «x2» in «e2» on «k1» equals «k2» into *g»  
...
översätts till
from * in ( «e1» ) . GroupJoin(  
   «e2» , «x1» => «k1» , «x2» => «k2» , ( «x1» , «g» ) => new { «x1» , «g» })
...
Exempel: Exemplet
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 }översätts till
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 }den slutliga översättningen av
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 })där
xochyär kompilatorgenererade identifierare som annars är osynliga och otillgängliga.slutexempel
En orderby-sats och dess föregående from-sats:
from «x» in «e»  
orderby «k1» , «k2» , ... , «kn»  
...
översätts till
from «x» in ( «e» ) .
OrderBy ( «x» => «k1» ) .
ThenBy ( «x» => «k2» ) .
... .
ThenBy ( «x» => «kn» )
...
Om en ordering-sats anger en indikator för fallande riktning, skapas ett anrop av OrderByDescending eller ThenByDescending i stället.
Exempel: Exemplet
from o in orders orderby o.Customer.Name, o.Total descending select ohar den slutliga översättningen
(orders) .OrderBy(o => o.Customer.Name) .ThenByDescending(o => o.Total)slutexempel
Följande översättningar förutsätter att det inte finns några let, where, join- eller orderby-satser och inte mer än en inledande from-sats i varje frågeuttryck.
12.21.3.6 Välj satser
Ett frågeuttryck för formuläret
from «x» in «e» select «v»
översätts till
( «e» ) . Select ( «x» => «v» )
utom när «v» är identifieraren «x»är översättningen helt enkelt
( «e» )
Exempel: Exemplet
from c in customers.Where(c => c.City == "London") select cöversätts helt enkelt till
(customers).Where(c => c.City == "London")slutexempel
12.21.3.7 Gruppsatser
En group-sats
from «x» in «e» group «v» by «k»
översätts till
( «e» ) . GroupBy ( «x» => «k» , «x» => «v» )
utom när «v» är identifieraren «x», är översättningen
( «e» ) . GroupBy ( «x» => «k» )
Exempel: Exemplet
from c in customers group c.Name by c.Countryöversätts till
(customers).GroupBy(c => c.Country, c => c.Name)slutexempel
12.21.3.8 Transparenta identifierare
Vissa översättningar matar in intervallvariabler med transparenta identifierare som anges av *. Transparenta identifierare finns bara som ett mellanliggande steg i översättningsprocessen för frågeuttryck.
När en frågeöversättning matar in en transparent identifierare sprider ytterligare översättningssteg den transparenta identifieraren till anonyma funktioner och anonyma objektinitierare. I dessa sammanhang har transparenta identifierare följande beteende:
- När en transparent identifier förekommer som en parameter i en anonym funktion, är medlemmarna av den associerade anonyma typen automatiskt i omfånget i kroppen av den anonyma funktionen.
- När en medlem med en transparent identifierare finns i omfånget, finns även dess medlemmar i omfånget.
- När en transparent identifierare förekommer som medlemsdeklarator i en anonym objektinitialisering introduceras en medlem med en transparent identifierare.
I översättningsstegen som beskrivs ovan introduceras alltid transparenta identifierare tillsammans med anonyma typer, med avsikten att samla in flera intervallvariabler som medlemmar i ett enda objekt. En implementering av C# tillåts använda en annan mekanism än anonyma typer för att gruppera flera intervallvariabler. Följande översättningsexempel förutsätter att anonyma typer används och visar en möjlig översättning av transparenta identifierare.
Exempel: Exemplet
from c in customers from o in c.Orders orderby o.Total descending select new { c.Name, o.Total }översätts till
from * in (customers).SelectMany(c => c.Orders, (c,o) => new { c, o }) orderby o.Total descending select new { c.Name, o.Total }som dessutom översätts till
customers .SelectMany(c => c.Orders, (c,o) => new { c, o }) .OrderByDescending(* => o.Total) .Select(\* => new { c.Name, o.Total })som, när transparenta identifierare raderas, motsvarar
customers .SelectMany(c => c.Orders, (c,o) => new { c, o }) .OrderByDescending(x => x.o.Total) .Select(x => new { x.c.Name, x.o.Total })där
xär en kompilatorgenererad identifierare som annars är osynlig och otillgänglig.Exemplet
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 }översätts till
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 }som reduceras ytterligare till
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 })den slutliga översättningen av
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 })där
xochyär kompilatorgenererade identifierare som annars är osynliga och otillgängliga. slutexempel
12.21.4 Frågeuttrycksmönstret
Mönstret för frågeuttryck upprättar ett metodmönster som typer kan implementera för att stödja frågeuttryck.
En allmän typ C<T> stöder frågeuttrycksmönstret om dess offentliga medlemsmetoder och de offentligt tillgängliga tilläggsmetoderna kan ersättas med följande klassdefinition. Metoderna för medlemmar och tillgängliga tillägg kallas för "form" av en allmän typ C<T>. En allmän typ används för att illustrera rätt relationer mellan parameter- och returtyper, men det är också möjligt att implementera mönstret för icke-generiska typer.
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; }
}
Metoderna ovan använder de allmänna ombudstyperna Func<T1, R> och Func<T1, T2, R>, men de kan lika gärna ha använt andra typer av ombud eller uttrycksträd med samma relationer i parameter- och returtyper.
Note: Den rekommenderade relationen mellan
C<T>ochO<T>som säkerställer att metodernaThenByochThenByDescendingendast är tillgängliga på resultatet av enOrderByellerOrderByDescending. slutkommentar
Obs: Den rekommenderade formen på resultatet av
GroupBy– en sekvens med sekvenser, där varje inre sekvens har ytterligare enKeyegenskap. slutkommentar
Note: Eftersom frågeuttryck översätts till metodanrop med hjälp av en syntaktisk mappning har typerna stor flexibilitet i hur de implementerar något eller alla frågeuttrycksmönster. Till exempel kan metoderna i mönstret implementeras som instansmetoder eller som tilläggsmetoder eftersom de två har samma anropssyntax, och metoderna kan begära ombud eller uttrycksträd eftersom anonyma funktioner kan konverteras till båda. Typer som endast implementerar vissa av frågeuttrycksmönstret stöder endast översättningar av frågeuttryck som mappas till de metoder som typen stöder. slutkommentar
Obs: Namnområdet
System.Linqinnehåller en implementering av frågeuttrycksmönstret för alla typer som implementerarSystem.Collections.Generic.IEnumerable<T>-gränssnittet. slutkommentar
12.22 Tilldelningsoperatorer
12.22.1 Allmänt
Alla utom en av tilldelningsoperatorerna tilldelar ett nytt värde till en variabel, en egenskap, en händelse eller ett indexerarelement. Undantaget, = ref, tilldelar en variabelreferens (§9.5) till en referensvariabel (§9.7).
assignment
    : unary_expression assignment_operator expression
    ;
assignment_operator
    : '=' 'ref'? | '+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' | '<<=' | '??='
    | right_shift_assignment
    ;
Den vänstra operanden för en tilldelning ska vara ett uttryck som klassificeras som en variabel, eller, med undantag för = ref, kan det vara egenskapsaccess, indexeraccess, händelseaccess eller en tuppel. Ett deklarationsuttryck tillåts inte direkt som en vänster operand, men kan inträffa som ett steg i utvärderingen av en dekonstruktionstilldelning.
Operatorn = kallas enkel tilldelningsoperator. Den tilldelar värdet eller värdena för den högra operanden till variabeln, egenskapen, indexerelementet eller tuppelns element som anges av den vänstra operanden. Den vänstra operanden för den enkla tilldelningsoperatorn får inte vara en händelseåtkomst, med undantag för vad som beskrivs i §15.8.2. Den enkla tilldelningsoperatören beskrivs i §12.22.2.
Operatorn = ref kallas referenstilldelningsoperatorn. Det gör det högra operanden, som ska vara en variable_reference (§9.5), till referenten till referensvariabeln som definieras av den vänstra operanden. Referenstilldelningsoperatören beskrivs i §12.22.3.
Andra tilldelningsoperatorer än operatorn = och = ref kallas för sammansatta tilldelningsoperatorer. Dessa operatorer bearbetas på följande sätt:
- För ??=-operatorn, utvärderas och tilldelas den högra operanden endast om värdet på den vänstra operanden ärnull, till variabeln, egenskapen eller indexeraren som anges av den vänstra operanden.
- Annars utförs den angivna åtgärden på de två operanderna och sedan tilldelas det resulterande värdet till variabeln, egenskapen eller indexeraren som anges av den vänstra operanden. De sammansatta tilldelningsoperatörerna beskrivs i §12.22.4.
Operatorerna += och -= med ett händelseåtkomstuttryck som den vänstra operanden kallas händelsetilldelningsoperatorer. Ingen annan tilldelningsoperator är giltig med en händelseåtkomst som den vänstra operanden. Händelsetilldelningsoperatörerna beskrivs i §12.22.5.
Tilldelningsoperatorerna är höger-associativa, vilket innebär att åtgärder grupperas från höger till vänster.
Exempel: Ett uttryck av formen
a = b = cutvärderas soma = (b = c). slutexempel
12.22.2 Enkel tilldelning
Operatorn = kallas för enkel tilldelningsoperator.
Om den vänstra operanden i en enkel tilldelning är av formatet E.P eller E[Ei] där E har kompileringstidstypen dynamic, är tilldelningen dynamiskt bunden (§12.3.3). I det här fallet är kompileringstidstypen för tilldelningsuttrycket dynamic, och lösningen som beskrivs nedan sker vid körning baserat på körningstypen för E. Om den vänstra operanden är av formuläret E[Ei] där minst ett element i Ei har kompileringstidstypen dynamicoch kompileringstidstypen för E inte är en matris, är den resulterande indexerarens åtkomst dynamiskt bunden, men med begränsad kompileringstidskontroll (§12.6.5).
En enkel tilldelning där den vänstra operanden klassificeras som en tuppel kallas också en dekonstruerande tilldelning. Om något av tuppelns element i den vänstra operanden har ett elementnamn uppstår ett kompileringsfel. Om något av vänsteroperandens tupplelement är en declaration_expression och något annat element inte är en declaration_expression eller en enkel discard, uppstår ett kompileringsfel.
Typen av enkel tilldelning x = y är typen av en tilldelning till x av y, som bestäms rekursivt på följande sätt:
- Om xär ett tupppeluttryck(x1, ..., xn)ochykan dekonstrueras till ett tupppeluttryck(y1, ..., yn)mednelement (§12.7), och varje tilldelning tillxiavyihar typenTi, har tilldelningen typen(T1, ..., Tn).
- Om xannars klassificeras som en variabel är variabeln intereadonly,xhar en typTochyhar en implicit konvertering tillThar tilldelningen typenT.
- Om xannars klassificeras som en implicit typvariabel (dvs. ett implicit skrivet deklarationsuttryck) ochyhar en typT, är variabelns härledda typToch tilldelningen har typenT.
- Om xklassificeras som en egenskap eller som ett indexerarattribut, och egenskapen eller indexeraren har en tillgänglig åtkomstmetod för att sätta värde,xhar typenT, ochyhar en implicit konvertering tillT, då har tilldelningen typenT.
- Annars är tilldelningen inte giltig och ett bindningstidsfel inträffar.
Exekveringen vid körning av en enkel tilldelning i formen x = y med typen T utförs som en tilldelning till x av y med typen T, och består av följande rekursiva steg:
- 
              xutvärderas om det inte redan har utvärderats.
- Om xklassificeras som en variabel utvärderasyoch konverteras vid behov tillTgenom en implicit konvertering (§10.2).- Om variabeln som anges av xär ett matriselement i en reference_typeutförs en körningskontroll för att säkerställa att värdet som beräknas föryär kompatibelt med matrisinstansen därxär ett element. Kontrollen lyckas omyärnull, eller om en implicit referenskonvertering (§10.2.8) finns från den typ av instans som refereras avytill den faktiska elementtypen för matrisinstansen som innehållerx. Annars utlöses enSystem.ArrayTypeMismatchException.
- Värdet som är resultatet av utvärderingen och konverteringen av ylagras på den plats som anges av utvärderingen avxoch returneras som ett resultat av tilldelningen.
 
- Om variabeln som anges av 
- Om xklassificeras som åtkomst till en egenskap eller indexerare:- 
              yutvärderas och konverteras vid behov tillTgenom en implicit konvertering (§10.2).
- Set-accessorn för xanropas med värdet som är resultatet av utvärderingen och konverteringen avysom dess värdeargument.
- Värdet som är resultatet av utvärderingen och konverteringen av yreturneras som ett resultat av tilldelningen.
 
- 
              
- Om xklassificeras som tuplen(x1, ..., xn)med aritetn.- 
              ydekonstrueras mednelement till ett tupppeluttrycke.
- en resultattuple tskapas genom att konverteraetillTmed hjälp av en implicit tuppelkonvertering.
- För varje xii ordning från vänster till höger utförs en tilldelning tillxiavt.Itemi, dock utvärderas intexiigen.
- 
              treturneras som ett resultat av tilldelningen.
 
- 
              
Obs!: om kompileringstidstypen för
xärdynamic, och det finns en implicit omvandling från kompileringstidstypen förytilldynamic, krävs ingen körningslösning. slutkommentar
Obs: Matrisens samavvikelseregler (§17.6) tillåter ett värde av en matristyp
A[]vara en referens till en instans av en matristypB[], förutsatt att det finns en implicit referenskonvertering frånBtillA. På grund av dessa regler kräver tilldelning till ett matriselement i en reference_type en körningskontroll för att säkerställa att värdet som tilldelas är kompatibelt med matrisinstansen. I exempletstring[] sa = new string[10]; object[] oa = sa; oa[0] = null; // OK oa[1] = "Hello"; // OK oa[2] = new ArrayList(); // ArrayTypeMismatchExceptionden senaste tilldelningen orsakar att en
System.ArrayTypeMismatchExceptiongenereras, eftersom en referens till enArrayListinte kan lagras i ett element i enstring[].slutkommentar
När en egenskap eller indexerare som deklareras i en struct_type är målet för en tilldelning, ska instansuttrycket som är associerat med egenskapen eller indexerarens åtkomst klassificeras som en variabel. Om instansuttrycket klassificeras som ett värde uppstår ett bindningstidsfel.
Obs: På grund av §12.8.7gäller samma regel även för fält. slutkommentar
Exempel: Med tanke på deklarationerna:
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; } } }i exemplet
Point p = new Point(); p.X = 100; p.Y = 100; Rectangle r = new Rectangle(); r.A = new Point(10, 10); r.B = p;tilldelningarna till
p.X,p.Y,r.Aochr.Btillåts eftersompochrär variabler. Men i exempletRectangle r = new Rectangle(); r.A.X = 10; r.A.Y = 10; r.B.X = 100; r.B.Y = 100;tilldelningarna är alla ogiltiga eftersom
r.Aochr.Binte är variabler.slutexempel
12.22.3 Referenstilldelning
Operatorn = ref kallas referenstilldelning operator.
Den vänstra operanden ska vara ett uttryck som binder till en referensvariabel (§9.7), en referensparameter (förutom this), en utdataparameter eller en indataparameter. Den högra operanden ska vara ett uttryck som ger ett variable_reference som anger ett värde av samma typ som den vänstra operanden.
Det är ett kompileringsfel om ref-safe-context (§9.7.2) av den vänstra operanden är bredare än ref-safe-context för den högra operanden.
Rätt operande ska definitivt tilldelas vid tidpunkten för referenstilldelningen.
När den vänstra operanden binder till en utdataparameter är det ett fel om den utdataparametern inte har tilldelats definitivt i början av referenstilldelningsoperatorn.
Om den vänstra operanden är en skrivbar referens (d.v.s. den anger något annat än en ref readonly lokal parameter eller indataparameter) ska den högra operanden vara en skrivbar variable_reference. Om den högra operandvariabeln är skrivbar kan den vänstra operanden vara en skrivbar eller skrivskyddad referens.
Åtgärden gör den vänstra operanden till ett alias för variabeln höger operand. Aliaset kan göras skrivskyddat även om rätt operandvariabel är skrivbar.
Referenstilldelningsoperatorn ger en variable_reference av den tilldelade typen. Den är skrivbar om den vänstra operanden är skrivbar.
Referenstilldelningsoperatören får inte läsa den lagringsplats som anges av rätt operande.
Exempel: Här är några exempel på hur du använder
= 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 }slutexempel
Obs: När du läser kod med hjälp av en
= ref-operator kan det vara frestande att läsarefdelen som en del av operanden. Detta är särskilt förvirrande när operanden är ett villkorsuttryck?:. När du till exempel läserref int a = ref b ? ref x : ref y;är det viktigt att läsa detta som att= refär operatorn ochb ? ref x : ref yden högra operanden:ref int a = ref (b ? ref x : ref y);. Det är viktigt att uttrycketref binte del av den instruktionen, även om det kan visas så vid första anblicken. slutkommentar
12.22.4 Sammansatt tilldelning
Om den vänstra operanden i en sammansatt tilldelning är av formatet E.P eller E[Ei] där E har kompileringstidstypen dynamic, är tilldelningen dynamiskt bunden (§12.3.3). I det här fallet är kompileringstidstypen för tilldelningsuttrycket dynamic, och lösningen som beskrivs nedan sker vid körning baserat på körningstypen för E. Om den vänstra operanden är av formuläret E[Ei] där minst ett element i Ei har kompileringstidstypen dynamicoch kompileringstidstypen för E inte är en matris, är den resulterande indexerarens åtkomst dynamiskt bunden, men med begränsad kompileringstidskontroll (§12.6.5).
              a ??= b motsvarar (T) (a ?? (a = b)), förutom att a utvärderas endast en gång, där T är typen av a när typen av b är dynamisk och annars T är typen av a ?? b.
I annat fall bearbetas en åtgärd av formuläret x «op»= y genom att tillämpa binär operatoröverbelastningsmatchning (§12.4.5) som om åtgärden skrevs x «op» y. Då
- Om returtypen för den valda operatorn implicit kan konverteras till typen xutvärderas åtgärden somx = x «op» y, förutom attxutvärderas bara en gång.
- Annars, om den valda operatorn är en fördefinierad operator, om returtypen för den valda operatorn uttryckligen är konvertibel till typen av x, och omyimplicit kan konverteras till typen avxeller operatorn är en skiftoperator, utvärderas åtgärden somx = (T)(x «op» y), därTär typen avx, förutom attxendast utvärderas en gång.
- Annars är den sammansatta tilldelningen ogiltig och ett bindningstidsfel inträffar.
Termen "utvärderas bara en gång" innebär att i utvärderingen av x «op» ysparas resultatet av eventuella komponentuttryck för x tillfälligt och återanvänds sedan när tilldelningen utförs till x.
Exempel: I tilldelningen
A()[B()] += C(), därAär en metod som returnerarint[]ochBochCär metoder som returnerarint, anropas metoderna bara en gång, i den ordningA,B,C. slutexempel
När den vänstra operanden för en sammanlagd tilldelning är en egenskapsåtkomst eller indexeråtkomst, ska egenskapen eller indexeraren ha både en get-accessor och en set-accessor. Om så inte är fallet uppstår ett bindningstidsfel.
Med den andra regeln ovan kan x «op»= y utvärderas som x = (T)(x «op» y) i vissa sammanhang. Regeln finns så att de fördefinierade operatorerna kan användas som sammansatta operatorer när den vänstra operanden är av typen sbyte, byte, short, ushorteller char. Även om båda argumenten är av en av dessa typer ger de fördefinierade operatorerna ett resultat av typen int, enligt beskrivningen i §12.4.7.3. Utan en rollbesättning skulle det därför inte vara möjligt att tilldela resultatet till den vänstra operanden.
Den intuitiva effekten av regeln för fördefinierade operatorer är helt enkelt att x «op»= y tillåts om både x «op» y och x = y tillåts.
Exempel: I följande kod
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; // OKDen intuitiva orsaken till varje fel är att en motsvarande enkel tilldelning också skulle ha varit ett fel.
slutexempel
Obs: Detta innebär också att sammansatta tilldelningsåtgärder stöder lyfta operatorer. Eftersom en sammansatt tilldelning
x «op»= yutvärderas som antingenx = x «op» yellerx = (T)(x «op» y), omfattar utvärderingsreglerna implicit förhöjda operatorer. slutkommentar
12.22.5 Händelsetilldelning
Om den vänstra operand för a += or -= operatorn klassificeras som en händelseåtkomst utvärderas uttrycket enligt följande:
- Instansuttrycket av åtkomst till händelsen, om det finns något, utvärderas.
- Den högra operanden för +=- eller-=-operatorn utvärderas och konverteras vid behov till typen av vänster operande genom en implicit konvertering (§10.2).
- En händelseåtkomst till händelsen anropas med en argumentlista som består av det värde som beräknades i föregående steg. Om operatorn var +=, anropas tilläggsåtkomstorn; om operatorn var-=, anropas borttagningsåtkomstorn.
Ett uttryck för händelsetilldelning ger inget värde. Därför är ett händelsetilldelningsuttryck endast giltigt i samband med en statement_expression (§13.7).
12.23 Uttryck
Ett uttryck är antingen en non_assignment_expression eller en tilldelning.
expression
    : non_assignment_expression
    | assignment
    ;
non_assignment_expression
    : declaration_expression
    | conditional_expression
    | lambda_expression
    | query_expression
    ;
12.24 Konstanta uttryck
Ett konstant uttryck är ett uttryck som ska utvärderas fullständigt vid kompileringstid.
constant_expression
    : expression
    ;
Ett konstant uttryck ska antingen ha värdet null eller någon av följande typer:
- 
              sbyte,byte,short,ushort,int,uint,long,ulong,char, ,float,double,decimal,bool, ;string
- en uppräkningstyp. eller
- ett standardvärdeuttryck (§12.8.21) för en referenstyp.
Endast följande konstruktioner tillåts i konstanta uttryck:
- Literaler (inklusive nullliteral).
- Referenser till constmedlemmar i klass-, struct- och gränssnittstyper.
- Referenser till medlemmar i uppräkningstyper.
- Referenser till lokala konstanter.
- Parentesiserade underuttryck, som i sig är konstanta uttryck.
- Gjutna uttryck.
- 
              checkedochuncheckeduttryck.
- 
              nameofuttryck.
- De fördefinierade unära operatorerna +,-,!(logisk negation), och~.
- De fördefinierade +,-,*,/,%,<<,>>,&,|,^,&&,||,==,!=,<,>,<=och>=binära operatorer.
- Den villkorliga operatorn ?:.
- Den !null-förlåtande operatorn (§12.8.9).
- 
              sizeofuttryck, förutsatt att den ohanterade typen är en av de typer som anges i §24.6.9 för vilkasizeofreturnerar ett konstant värde.
- Standardvärdeuttryck, förutsatt att typen är en av de typer som anges ovan.
Följande konverteringar tillåts i konstanta uttryck:
- Identitetskonverteringar
- Numeriska konverteringar
- Uppräkningskonverteringar
- Konverteringar av konstanta uttryck
- Implicita och explicita referenskonverteringar, förutsatt att källan till konverteringarna är ett konstant uttryck som utvärderas till värdet null.
Notering: Andra konverteringar, inklusive boxning, avboxning och implicita referenskonverteringar av icke-
null-värden, är inte tillåtna i konstanta uttryck. slutkommentar
Exempel: I följande kod
class C { const object i = 5; // error: boxing conversion not permitted const object str = "hello"; // error: implicit reference conversion }initieringen av
iär ett fel eftersom en boxningskonvertering krävs. Initieringen avsträr ett fel eftersom en implicit referenskonvertering från ett värde som inte ärnullkrävs.slutexempel
När ett uttryck uppfyller kraven som anges ovan utvärderas uttrycket vid kompileringstid. Detta gäller även om uttrycket är ett underuttryck av ett större uttryck som innehåller icke-konstanta konstruktioner.
Kompileringstidsutvärderingen av konstanta uttryck använder samma regler som körningsutvärdering av icke-konstanta uttryck, förutom att när körningsutvärderingen skulle ha genererat ett undantag orsakar kompileringstidsutvärdering ett kompileringsfel.
Om inte ett konstant uttryck uttryckligen placeras i en unchecked kontext, orsakar spill som uppstår i aritmetiska åtgärder av integraltyp och konverteringar under kompileringstidsutvärderingen av uttrycket alltid kompileringsfel (§12.8.20).
Konstanta uttryck krävs i de kontexter som anges nedan och detta anges i grammatiken med hjälp av constant_expression. I dessa sammanhang uppstår ett kompileringsfel om ett uttryck inte kan utvärderas fullständigt vid kompilering.
- Deklarationer av konstanter (§15.4)
- Uppräkningsmedlemsdeklarationer (§20.4)
- Standardargument för parameterlistor (§15.6.2)
- 
              caseetiketter för enswitch-instruktion (§13.8.3).
- 
              goto caseuttalanden (§13.10.4)
- Dimensionslängder i ett matrisskapande uttryck (§12.8.17.4) som innehåller en initialiserare.
- Attribut (§23)
- I en constant_pattern (§11.2.3)
En implicit konstant uttryckskonvertering (§10.2.11) tillåter att ett konstant uttryck av typen int konverteras till sbyte, byte, short, ushort, uinteller ulong, förutsatt att värdet för det konstanta uttrycket ligger inom måltypens intervall.
12.25 Booleska uttryck
En boolean_expression är ett uttryck som ger ett resultat av typen bool; antingen direkt eller via tillämpning av operator true i vissa sammanhang enligt följande:
boolean_expression
    : expression
    ;
Det kontrollerande villkorsuttrycket för en if_statement (§13.8.2), while_statement (§13.9.2), do_statement (§13.9.3), eller for_statement (§13.9.4) är en boolean_expression. Operatorns ?: kontrollerande villkorsuttryck (§12.19) följer samma regler som en boolean_expression, men av operatorprioritetsskäl klassificeras som en null_coalescing_expression.
En boolean_expressionE krävs för att kunna skapa ett värde av typen bool, enligt följande:
- Om E är implicit konverterbar till boolkommer den implicita konverteringen att tillämpas under körning.
- Annars används enäringsöverbelastningslösning (§12.4.4) för att hitta den bästa möjliga unika implementeringen av operator truepåE, och den implementeringen tillämpas vid programkörningstid.
- Om ingen sådan operator hittas uppstår ett bindningstidsfel.
ECMA C# draft specification