12 uttryck
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 typvariabel har ingen associerad typ.
- En nullliteral. 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 tuppeln. Varje tuppeln 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 åtkomst till en instansegenskap anropas blir resultatet av utvärderingen av instansuttrycket den instans som representeras av
this
(§12.8.14). - En indexerares åtkomst. Varje indexerares åtkomst har en associerad typ, nämligen indexerarens elementtyp. Dessutom har en indexerare åtkomst ett associerat instansuttryck och en associerad argumentlista. När en åtkomst till en indexerare anropas blir resultatet av utvärderingen av instansuttrycket den instans som representeras av
this
(§12.8.14), och resultatet av utvärderingen av argumentlistan blir parameterlistan för anropet. - Ingenting. Detta inträffar när uttrycket är ett anrop av en metod med returtypen
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.19).
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 bara 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 bara 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 till följd av ett medlemsuppslag (§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.6) och kan implicit konverteras till en kompatibel delegattyp (§10.8). I andra sammanhang orsakar ett uttryck som klassificeras som en metodgrupp ett kompileringsfel. - En händelseåtkomst. 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.21.5). I andra sammanhang orsakar ett uttryck som klassificeras som en händelseåtkomst ett kompileringsfel. När en åtkomst till 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 är flera kontexter för att utlösa ett undantag i ett uttryck. Ett utkastsuttryck kan konverteras av en implicit konvertering till valfri typ.
En egenskapsåtkomst eller indexerares åtkomst omklassificeras alltid som ett värde genom att utföra ett anrop av get-accessorn eller den angivna åtkomsten. 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.21.2). Annars anropas get-accessorn för att hämta det aktuella värdet (§12.2.2).
En instansåtkomst är en egenskapsåtkomst på en instans, en händelseåtkomst på en instans eller en indexerares åtkomst.
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 ska 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 som är associerad med indexerarens åtkomstuttryck, och resultatet av anropet blir värdet för indexerarens åtkomstuttryck.
- Värdet för ett tupppeluttryck erhålls genom att tillämpa en implicit tuppelns konvertering (§10.2.13) på typen av tuppelns uttryck. 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 för 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 av kompilatorn om ett uttryck innehåller ett fel. Den här metoden kallas statisk bindning.
Men om ett uttryck är ett dynamiskt uttryck (dvs. 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 av kompilatorn. Om körningsbindningen i stället misslyckas rapporteras fel som undantag vid 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 underinställningar refererar termen bindningstid till antingen kompileringstid eller körning, beroende på när bindningen äger rum.
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: överlagringen av
Console.WriteLine
plockas baserat på kompileringstidstypen för deras argument. Bindningstiden är därför kompileringstid.Det tredje anropet är dynamiskt bundet: överlagringen av
Console.WriteLine
vä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örning.slutexempel
12.3.3 Dynamisk bindning
Den här underklienten är informativ.
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 implementeringsdefinierad – implementeras av dynamiska objekt för att signalera till C#-körningen att de har särskilda 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 kanske inte själva ä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 kompileringstypsdynamik anses ha den typ av faktiskt värde som uttrycket utvärderas till vid körning
- 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:
- Unary-operatorer. De unary operatorerna tar en operand och använder antingen prefix notation (till exempel
–x
) eller postfix notation (till exempelx++
). - Binära operatorer. De binära operatorerna tar två operander och alla använder infix-notation (till exempel
x + y
). - Ternary-operator. Endast en ternary-operator,
?:
, finns. Den tar tre operander och använder infix-notation (c ? x : y
).
Ordningen för utvärdering av operatorer i ett uttryck bestäms av operatorernas prioritet och associativitet (§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 metodenF
med det gamla värdeti
, och sedan anropas metodenG
med det gamla värdeti
, och slutligen anropas metodenH
med 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 anges för åtgärder där en eller båda operanderna är av en användardefinierad klass eller structtyp.
12.4.2 Operatorprioritet och associativitet
När ett uttryck innehåller flera operatorer styr operatorernas prioritet i vilken ordning de enskilda operatorerna utvärderas.
Obs! Uttrycket
x + y * z
utvärderas till exempel somx + (y * z)
eftersom operatorn*
har högre prioritet än den binära+
operatorn. slutkommentar
Prioriteten för en operator fastställs genom definitionen av dess associerade grammatikproduktion.
Obs! Till exempel består en additive_expression av en sekvens med multiplicative_expressions avgränsade med
+
eller-
operatorer, vilket ger+
operatorerna och-
lägre prioritet än operatorerna*
,/
och%
. slutkommentar
Obs! Följande tabell sammanfattar alla operatorer i prioritetsordning från högsta till lägsta:
Underklient Kategori Operatorer §12.8 Primär x.y
x?.y
f(x)
a[x]
a?[x]
x++
x--
x!
new
typeof
default
checked
unchecked
delegate
stackalloc
§12.9 Unär +
-
!x
~
++x
--x
(T)x
await x
§12.10 Multiplicative *
/
%
§12.10 Tillsats +
-
§12.11 Shift <<
>>
§12.12 Relations- och typtestning <
>
<=
>=
is
as
§12.12 Tillämplig ==
!=
§12.13 Logiskt OCH &
§12.13 Logisk XOR ^
§12.13 Logiskt ELLER \|
§12.14 Villkorsstyrd OCH &&
§12.14 Villkorsstyrd ELLER \|\|
§12.15 och §12.16 Null-sammanslagning och utkastsuttryck ??
throw x
§12.18 Villkorsstyrd ?:
§12.21 och §12.19 Tilldelnings- och lambda-uttryck =
= ref
*=
/=
%=
+=
-=
<<=
>>=
&=
^=
\|=
=>
slutkommentar
När en operande inträffar mellan två operatorer med samma prioritet styr operatorernas associativitet ordningen i vilken åtgärderna utförs:
- Förutom tilldelningsoperatorerna och operatorn null coalescing ä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 + z
utvä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 = z
utvärderas somx = (y = z)
. slutexempel
Prioritet och associativitet kan styras med parenteser.
Exempel:
x + y * z
multiplicerarz
y
först med och lägger sedan till resultatet ix
, men(x + y) * z
lägger först tillx
ochy
multiplicerar sedan resultatetz
med . slutexempel
12.4.3 Överlagring av operatör
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 unary-operatorerna är:
+ - !
(endast logisk negation) ~ ++ -- true false
Obs! Även om
true
ochfalse
inte 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.24) och uttryck som involverar villkorsstyrda (§12.18) och villkorsstyrda logiska operatorer (§12.14). slutkommentar
Obs! Operatorn null-forgiving (postfix
!
, §12.8.9) är inte en överlagbar operator. slutkommentar
De överlagbara binära operatorerna är:
+ - * / % & | ^ << >> == != > < <= >=
Endast de operatorer som anges ovan kan överbelastas. I synnerhet är det inte möjligt att överbelasta medlemsåtkomst, metodanrop eller operatorerna =
, , &&
, ||
, ??
, unchecked
checked
new
typeof
=>
?:
, , default
as
och .is
När en binär operator är överbelastad överbelastas även motsvarande sammansatta tilldelningsoperator, om någon, implicit.
Exempel: En överlagring av operatorn
*
är också en överlagring av operatorn*=
. Detta beskrivs ytterligare i §12.21. slutexempel
Tilldelningsoperatorn (=)
kan inte överbelastas. En tilldelning utför alltid ett enkelt lager av ett värde i en variabel (§12.21.2).
Cast-åtgärder, till exempel (T)x
, överbelastas genom att tillhandahålla användardefinierade konverteringar (§10.5).
Obs! Användardefinierade konverteringar påverkar inte operatorernas
is
as
beteende. slutkommentar
Elementåtkomst, till exempel a[x]
, anses inte vara en överlagringsbar 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 noteringar för unary- och binära operatorer. I den första posten anger «op» alla överlagbara unary prefixoperatorer. I den andra posten anger «op» det unary postfixet ++
och --
operatorerna. I den tredje posten anger «op» alla överlagrbara binära operatorer.
Obs! Ett exempel på överlagring av operatorerna
++
och--
se §15.10.2. slutkommentar
Operator notation | 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.
Obs! 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.2 och är alltid vänstersociativ. slutexempel
Obs! Även om det är möjligt för en användardefinierad operator att utföra alla beräkningar som den vill, rekommenderas starkt implementeringar som ger andra resultat än de som intuitivt förväntas. En implementering av operatorn
==
bör till exempel jämföra de två operanderna för likhet och returnera ett lämpligtbool
resultat. slutkommentar
Beskrivningarna av enskilda operatörer i §12.9 till och med §12.21 anger de fördefinierade implementeringarna av operatörerna och eventuella ytterligare regler som gäller för varje operatör. Beskrivningarna använder termerna unary operator overload resolution, binary operator overload resolution, numeric promotion och lifted operator definitions of which are found in the following subclauses.
12.4.4 Unary operatoröverbelastningsupplösning
En åtgärd i formuläret «op» x
eller x «op»
, där «op» är en överlaglig unary-operator och x
är ett uttryck av typen X
, bearbetas på följande sätt:
- Den uppsättning av kandidatanvändare som tillhandahålls av
X
å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. Annars blir de fördefinierade binära
operator «op»
implementeringarna, inklusive deras hävda formulär, uppsättningen kandidatoperatorer för åtgärden. 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 denna operatör blir resultatet av överlagringslösningsprocessen. 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 X
, och y
är ett uttryck av typen Y
, bearbetas på följande sätt:
- Uppsättningen med användardefinierade kandidatoperatorer som tillhandahålls av
X
ochY
för åtgärdenoperator «op»(x, y)
bestäms. Uppsättningen består av förbundet mellan de kandidatoperatörer som tillhandahålls avX
och de kandidatoperatörer som tillhandahålls avY
, 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
X
ochY
är identitetsvertibla, eller omX
ochY
härleds från en gemensam bastyp, förekommer endast delade kandidatoperatorer i den kombinerade uppsättningen en gång. - Om det finns en identitetskonvertering mellan
X
ochY
har en operator«op»Y
som tillhandahålls avY
samma returtyp som en«op»X
som tillhandahålls avX
och operandtyperna«op»Y
av har en identitetskonvertering till motsvarande operandtyper«op»X
, sker endast«op»X
i uppsättningen.
- Om
- Om uppsättningen med användardefinierade kandidatoperatorer inte är tom blir detta uppsättningen kandidatoperatorer för åtgärden. Annars blir de fördefinierade binära
operator «op»
implementeringarna, inklusive deras hävda formulär, uppsättningen kandidatoperatorer för åtgärden. 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 denna operatör blir resultatet av överlagringslösningsprocessen. Om det inte går att välja en enda bästa operator uppstår ett bindningstidsfel.
12.4.6 Användardefinierade operatorer för kandidat
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
operatorn «op»(A)
enligt följande:
- Fastställ typen
T₀
. OmT
är en nullbar värdetypT₀
är dess underliggande typ. AnnarsT₀
är den 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 kandidatoperatorer av alla sådana tillämpliga operatörer iT₀
. - Annars, om
T₀
ärobject
, är uppsättningen med kandidatoperatorer tom. - Annars är uppsättningen kandidatoperatorer som tillhandahålls av
T₀
uppsättningen kandidatoperatorer som tillhandahålls av den direkta basklassenT₀
för , eller den effektiva basklassenT₀
för omT₀
är en typparameter.
12.4.7 Numeriska kampanjer
12.4.7.1 Allmänt
Den här underklienten är informativ.
§12.4.7 och dess delklienter ä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 (§12.10), relationsoperatorerna (§12.12) och integralen logiska (§12.13.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 befordran är inte en distinkt mekanism, utan snarare en effekt av att tillämpa överbelastningsmatchning på de fördefinierade operatorerna. Numerisk befordran påverkar inte specifikt utvärdering av användardefinierade operatorer, även om användardefinierade operatorer kan implementeras för att uppvisa 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å den här uppsättningen 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 enbyte
ochs
är enshort
, väljer överlagringsupplösningenoperator *(int, int)
som den bästa operatorn. Effekten är därför attb
ochs
konverteras tillint
, och typen av resultat ärint
. På samma sätt, för åtgärdeni * d
, däri
är enint
ochd
är endouble
,overload
väljeroperator *(double, double)
upplösningen som den bästa operatorn. slutexempel
Slut på informativ text.
12.4.7.2 Icke-numeriska kampanjer
Den här underklienten är informativ.
Unary numeriska befordran sker för operander för fördefinierade +
, -
, och ~
unary operatorer. Unary numerisk befordran består helt enkelt av att konvertera operander av typen sbyte
, , short
byte
, ushort
, eller char
för att skriva int
. För den unary – operatorn konverterar unary numeriska kampanjer operander av typen uint
till typ long
.
Slut på informativ text.
12.4.7.3 Binära numeriska kampanjer
Den här underklienten är informativ.
Binär numerisk befordran sker för operanderna för de fördefinierade +
operatorerna , , -
*
, /
, %
, , &
, |
, ^
==
>
!=
, , <
, >=
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 någon av operanderna är av typen
decimal
konverteras den andra operanden till typendecimal
, eller så uppstår ett bindningsfel om den andra operanden är av typenfloat
ellerdouble
. - Annars konverteras den andra operanden om
double
någon av operanderna är av typendouble
. - Annars konverteras den andra operanden om
float
någon av operanderna är av typenfloat
. - Annars, om någon av operanderna är av typen
ulong
, konverteras den andra operanden till typenulong
, eller så uppstår ett bindningstidsfel om den andra operanden är avtype sbyte
,short
,int
ellerlong
. - Annars konverteras den andra operanden om
long
någon av operanderna är av typenlong
. - Annars konverteras båda operanderna till
long
typen om någon av operanderna är av typensbyte
uint
,short
ellerint
. - Annars konverteras den andra operanden om
uint
någon av operanderna är av typenuint
. - Annars konverteras båda operanderna till att skriva
int
.
Obs! Den första regeln tillåter inte åtgärder som blandar
decimal
typen med typernadouble
ochfloat
. Regeln följer av det faktum att det inte finns några implicita konverteringar mellandecimal
typen och typernadouble
ochfloat
. slutkommentar
Obs! Observera också att det inte är möjligt för en operand att vara av typen
ulong
när den andra operanden är av en signerad integraltyp. Anledningen är att det inte finns någon integrerad typ som kan representera hela intervallet avulong
såväl som 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
decimal
inte 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
Med hävda operatorer kan fördefinierade och användardefinierade operatorer som använder icke-nullbara värdetyper även 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 lyfte operatorn genererar ettnull
värde om operand ärnull
. Annars packar den lyftade operatorn upp operanden, tillämpar den underliggande operatorn och omsluter resultatet. - För de binära operatorerna
+
, ,*
-
,/
,%
,&
,^
|
, ,<<
och>>
, finns en hävd form av en operator om operand- och resultattyperna alla typer av icke-nullbara värden. Det upplyfta formuläret skapas genom att en enda?
modifierare läggs till i varje operand och resultattyp. Den lyftade operatorn genererar ettnull
värde om en eller båda operanderna är (ett undantag är&
null
typens operatorer och|
, enligt beskrivningenbool?
i §12.13.5). Annars packar den lyftade operatorn upp operanderna, tillämpar den underliggande operatorn och omsluter resultatet. - För likhetsoperatorerna
==
och!=
finns en hävd form av en operator om operandtyperna både är icke-nullbara värdetyper och om resultattypen ärbool
. Det upplyfta formuläret skapas genom att en enda?
modifierare läggs till i varje operandtyp. Den lyftande operatorn anser att tvånull
värden är lika med och ettnull
värde som är ojämlikt för alla icke-värdennull
. Om båda operanderna inte är -null
, skriver den lyftade operatorn upp operanderna och tillämpar den underliggande operatorn för att producera resultatetbool
. - För relationsoperatorerna
<
finns ,>
,<=
och>=
, en upplyft form av en operator om operandtyperna både är icke-nullbara värdetyper och om resultattypen ärbool
. Det upplyfta formuläret skapas genom att en enda?
modifierare läggs till i varje operandtyp. Den lyfte operatorn genererar värdetfalse
om en eller båda operanderna ärnull
. I annat fall skriver den lyftade operatorn upp operanderna och tillämpar den underliggande operatorn för att generera resultatetbool
.
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 inträffar som primary_expression av en invocation_expression (§12.8.10.2), sägs medlemmen anropas.
Om en medlem är en metod eller händelse, eller om det är en konstant, fält eller egenskap av antingen en delegattyp (§20) 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 K
typargument i en typ T
bearbetas på följande sätt:
- Först bestäms en uppsättning tillgängliga medlemmar med namnet
N
:- Om
T
är en typparameter är uppsättningen en union av uppsättningarna med tillgängliga medlemmar som namngesN
i 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 med tillgängliga medlemmar med namnetN
iobject
. - I annat fall består uppsättningen av alla tillgängliga (§7.5) medlemmar med namnet
N
iT
, inklusive ärvda medlemmar och de tillgängliga medlemmar som nämnsN
iobject
. OmT
är en konstruerad typ erhålls uppsättningen medlemmar genom att ersätta typargument enligt beskrivningen i §15.3.3. Medlemmar som innehåller enoverride
modifierare undantas från uppsättningen.
- Om
K
Om är noll tas sedan alla kapslade typer vars deklarationer innehåller typparametrar bort. OmK
inte ä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.M
i uppsättningen, därS
är den typ där medlemmenM
deklareras, 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 avS
bort från uppsättningen. - Om
M
är en typdeklaration tas alla icke-typer som deklarerats i en bastyp avS
bort från uppsättningen, och alla typdeklarationer med samma antal typparametrar somM
deklareras i en bastyp tasS
bort från uppsättningen. - Om
M
är en metod tas alla icke-metodmedlemmar som deklarerats i en bastyp bortS
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 ochT
har både en annan effektiv basklass änobject
och en icke-tom effektiv gränssnittsuppsättning (§15.2.5). För varje medlemS.M
i uppsättningen, därS
är den typ där medlemmenM
deklareras, 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 deklarerats 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 somM
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 enarvsökningar är aldrig tvetydiga. De tvetydigheter som eventuellt kan uppstå vid medlemssökningar i flera arvsgränssnitt beskrivs i §18.4.6.
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
ärobject
ellerdynamic
har du ingenT
bastyp. - Om
T
är en enum_type är bastypernaT
klasstypernaSystem.Enum
,System.ValueType
ochobject
. - Om
T
är en struct_type är bastypernaT
klasstypernaSystem.ValueType
ochobject
.Obs! En nullable_value_type är en struct_type (§8.3.1). slutkommentar
- Om
T
är en class_type är bastypernaT
för basklasserna förT
, inklusive klasstypenobject
. - Om
T
är en interface_type är bastypernaT
T
för basgränssnitten för och klasstypenobject
. - Om
T
är en array_type är bastypernaT
klasstyperSystem.Array
ochobject
. - Om
T
är en delegate_type är bastypernaT
klasstypernaSystem.Delegate
ochobject
.
12.6 Funktionsmedlemmar
12.6.1 Allmänt
Funktionsmedlemmar ä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
- Händelser
- Indexerare
- Användardefinierade operatörer
- 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 en funktionsmedlemsanrop beror på den specifika funktionsmedlemskategorin.
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 överbelastningsmatchning, beskrivs den faktiska körningsprocessen för att anropa funktionsmedlemmen i §12.6.6.
Obs! Följande tabell sammanfattar bearbetningen som sker i konstruktioner som omfattar de sex kategorier av funktionsmedlemmar som uttryckligen kan anropas. I tabellen , , , och anger uttryck som klassificerats som variabler eller värden anger
T
ett uttryck klassificerat som en typ,F
är det enkla namnet på en metod ochP
är det enkla namnet på en egenskap.value
y
x
e
Konstruera Exempel beskrivning Metodanrop F(x, y)
Överbelastningsupplösning används för att välja den bästa metoden F
i den innehållande klassen eller structen. Metoden anropas med argumentlistan(x, y)
. Om metoden intestatic
är ärthis
instansuttrycket .T.F(x, y)
Överbelastningsupplösning används för att välja den bästa metoden F
i klassen eller structT
. Ett bindningstidsfel uppstår om metoden intestatic
är . Metoden anropas med argumentlistan(x, y)
.e.F(x, y)
Överlagringsupplösning används för att välja den bästa metoden F
i klassen, struct eller gränssnittet som anges av typene
. Ett bindningstidsfel uppstår om metoden ärstatic
. Metoden anropas med instansuttryckete
och argumentlistan(x, y)
.Egenskapsåtkomst P
Get-accessorn för egenskapen P
i den innehållande klassen eller struct anropas. Ett kompileringsfel inträffar omP
det är skrivskyddat. OmP
intestatic
är ärthis
instansuttrycket .P = value
Set-accessorn för egenskapen P
i den innehållande klassen eller struct anropas med argumentlistan(value)
. Ett kompileringsfel inträffar omP
det är skrivskyddat. OmP
intestatic
är ärthis
instansuttrycket .T.P
Get-accessorn för egenskapen P
i klassen eller structT
anropas. Ett kompileringsfel inträffar omP
det intestatic
är eller omP
det är skrivskyddat.T.P = value
Set-accessorn för egenskapen P
i klassen eller structT
anropas med argumentlistan(value)
. Ett kompileringsfel inträffar omP
det intestatic
är eller omP
det är skrivskyddat.e.P
Get-accessorn för egenskapen P
i klassen, struct eller gränssnittet som anges av typen avE
anropas med instansuttryckete
. Ett bindningstidsfel uppstår omP
ärstatic
eller omP
är skrivskyddat.e.P = value
Set-accessorn för egenskapen P
i klassen, struct eller gränssnittet som anges av typen avE
anropas med instansuttryckete
och argumentlistan(value)
. Ett bindningstidsfel uppstår omP
ärstatic
eller omP
är skrivskyddat.Händelseåtkomst E += value
Tilläggsåtkomsten för händelsen E
i den innehållande klassen eller struct anropas. OmE
intestatic
är ärthis
instansuttrycket .E -= value
Borttagningsåtkomstorn för händelsen E
i den innehållande klassen eller struct anropas. OmE
intestatic
är ärthis
instansuttrycket .T.E += value
Tilläggsåtkomsten för händelsen E
i klassen eller structT
anropas. Ett bindningstidsfel inträffar omE
det intestatic
är .T.E -= value
Borttagningsåtkomstorn för händelsen E
i klassen eller structT
anropas. Ett bindningstidsfel inträffar omE
det intestatic
är .e.E += value
Tilläggsåtkomsten för händelsen E
i klassen, struct eller gränssnittet som anges av typen avE
anropas med instansuttryckete
. Ett bindningstidsfel inträffar omE
ärstatic
.e.E -= value
Ta bort-accessorn för händelsen E
i klassen, struct eller gränssnittet som anges av typen avE
anropas med instansuttryckete
. Ett bindningstidsfel inträffar omE
ärstatic
.Indexerarens åtkomst e[x, y]
Överlagringsmatchning 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 instansuttryckete
och argumentlistan(x, y)
. Ett bindningstidsfel uppstår om indexeraren är skrivskyddad.e[x, y] = value
Överlagringsmatchning används för att välja den bästa indexeraren i klassen, struct eller gränssnittet som anges av typen e
. Indexerarens uppsättningsåtkomst anropas med instansuttryckete
och 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 unary-operatorn i klassen eller structen som anges av typen x
. Den valda operatorn anropas med argumentlistan(x)
.x + y
Överlagringsupplösning tillämpas för att välja den bästa binära operatorn i klasserna eller structs som anges av typerna av x
ochy
. Den valda operatorn anropas med argumentlistan(x, y)
.Instanskonstruktoranrop new T(x, y)
Överbelastningsmatchning tillämpas för att välja den bästa instanskonstruktorn i klassen eller structen 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 innehåller argumentlistan dessutom det uttryck som anges som rätt operand för tilldelningsoperatorn när du anropar den angivna åtkomsten.
Obs! Det här ytterligare argumentet används inte för överbelastningsmatchning, bara under anrop av den angivna åtkomstorn. 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 operatorn
+=
eller-=
. - 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 ombudsanrop anges 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 argument, 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 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
in
fö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
ref
fö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 en referensparameter. - Nyckelordet
out
fö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äget för argumentet: värde, indata, referens respektive 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 kanske inte 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.
- För indexerare motsvarar det uttryck som anges som rätt operand för tilldelningsoperatorn den implicita
value
parametern för den angivna åtkomstdeklarationen när du anropar den angivna åtkomstgivaren.
- 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 är efter ett argument med namnet out-of-position eller ett namngivet argument som motsvarar en parametermatris.
Obs! Detta förhindrar
void M(bool a = true, bool b = true, bool c = true);
att 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örningsbearbetningen 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 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: Med 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 metodanropet
M1(i)
i
skickas själva som ett indataargument, eftersom det klassificeras som en variabel och har samma typint
som indataparametern. I metodanropetM1(i + 5)
skapas en namnlösint
variabel, 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_type utfö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
F
orsakar att enSystem.ArrayTypeMismatchException
genereras eftersom den faktiska elementtypenb
ärstring
och 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å vilket som är tillämpligt (§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 är konvertibelt (§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 parametermatrisens elementtyp. 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: Därför är 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 = 3
slutexempel
När en funktionsmedlem med en parametermatris anropas i sitt expanderade formulär med minst ett expanderat argument bearbetas anropet som om ett matrisskapande uttryck med en matrisinitierare (§12.8.17.5) 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 deklarationen
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.)
Obs! Eftersom dessa alltid är konstanta påverkas inte utvärderingen av återstående argument. 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 typinferensprocess 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
int
ochstring
från argumenten till metoden.slutexempel
Typinferens sker som en del av bindningstidsbearbetningen av ett metodanrop (§12.8.10.2) och äger rum före överbelastningsmatchningssteget 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 överlagringsmatchning väljer en allmän metod som den som ska anropas används argumenten av den här typen 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 av 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 är varje typparameter Xᵢ
antingen fast i en viss typ Sᵢ
eller ofixerad med en associerad uppsättning gränser. Var och en av gränserna är av någon typ T
. Inledningsvis ä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, inklusive för konvertering av metodgrupper (§12.6.3.14) och för att hitta den bästa gemensamma typen av uttryck (§12.6.3.15). slutkommentar
12.6.3.2 Den första fasen
För vart och ett av metodargumenten Eᵢ
:
- Om
Eᵢ
är en anonym funktion görs en explicit parametertypinferens (§12.6.3.8) frånEᵢ
tillTᵢ
- Om det annars
Eᵢ
finns en typU
och motsvarande parameter är en värdeparameter (§15.6.2.2) görs en lägre slutsatsdragning (§12.6.3.10) frånU
till.Tᵢ
- Om det annars
Eᵢ
finns en typU
och 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.9) frånU
till.Tᵢ
- Annars, om
Eᵢ
har en typU
och motsvarande parameter är en indataparameter (§15.6.2.3.2) ochEᵢ
är ett indataargument, görs en exakt slutsatsdragning (§12.6.3.9) frånU
till.Tᵢ
- Om
Eᵢ
den har en typU
och motsvarande parameter är en indataparameter (§15.6.2.3.2) görs annars en lägre bunden slutsats (§12.6.3.10) frånU
till.Tᵢ
- Annars görs ingen slutsatsdragning för det här argumentet.
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.12). - Om det inte finns några sådana typvariabler, korrigeras
Xᵢ
alla variabler av typen ofix som alla följande undantag gäller för:- Det finns minst en typvariabel
Xₑ
som är beroende avXᵢ
Xᵢ
har en icke-tom uppsättning gränser
- 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.7) frånEᵢ
till.Tᵢ
Sedan upprepas den andra fasen.
12.6.3.4 Indatatyper
Om E
är en metodgrupp eller implicit typad anonym funktion och T
är en ombudstyp eller en uttrycksträdstyp är alla parametertyper T
av indatatyper avE
med typ.T
12.6.3.5 Utdatatyper
Om E
är en metodgrupp eller en anonym funktion och T
är en ombudstyp eller uttrycksträdstyp är returtypen T
en utdatatyp avE
med typen .T
12.6.3.6 Beroende
En ofixerad typvariabel Xᵢ
beror direkt på en ofixerad typvariabel Xₑ
om för vissa argument Eᵥ
med typ Xₑ
Tᵥ
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ₑ
beror direkt påXᵢ
eller om Xᵢ
är direktXᵥ
beroende av och Xᵥ
är beroende avXₑ
. Således är "beroende av" den transitiva men inte reflexiva stängningen av "beror direkt på".
12.6.3.7 Slutsatsdragningar av utdatatyp
En slutsatsdragning av utdatatyp görs från ett uttryck E
till en typ T på följande sätt:
- Om
E
är en anonym funktion med härledd returtypU
(§12.6.3.13) ochT
är en ombudstyp eller uttrycksträdtyp med returtypTₓ
, görs en lägre inferens (§12.6.3.10) frånU
till.Tₓ
- Annars, om
E
är en metodgrupp ochT
är en ombudstyp eller uttrycksträdstyp med parametertyperT₁...Tᵥ
och returtypTₓ
, och överlagringsmatchning avE
med typernaT₁...Tᵥ
ger en enda metod med returtypU
, görs en lägre slutsatsdragning frånU
till.Tₓ
- Annars, om
E
är ett uttryck med typenU
, görs en lägre slutsatsdragning frånU
till.T
- Annars görs inga slutsatsdragningar.
12.6.3.8 Explicita parametertypinferenser
En explicit parametertypsinferens görs från ett uttryck E
till en typ T
på följande sätt:
- Om
E
är en explicit typ av anonym funktion med parametertyperU₁...Uᵥ
ochT
är en trädtyp för ombud eller uttryck med parametertyperV₁...Vᵥ
görs för varjeUᵢ
exakt slutsatsdragning (§12.6.3.9) frånUᵢ
till motsvarande .Vᵢ
12.6.3.9 Exakta slutsatsdragningar
En exakt slutsatsdragning från en typ U
till en typ V
görs på följande sätt:
- Om
V
är en av de ofixadeXᵢ
U
läggs den till i uppsättningen med exakta gränser för .Xᵢ
- Annars anges
V₁...Vₑ
ochU₁...Uₑ
bestäms genom att kontrollera om något av följande fall gäller:V
är en matristypV₁[...]
ochU
är en matristypU₁[...]
med samma rangordningV
ä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 var och enUᵢ
till motsvarandeVᵢ
.
- Annars görs inga slutsatsdragningar.
12.6.3.10 Lägre bundna slutsatsdragningar
En lägre slutsatsdragning från en typ U
till en typ V
görs på följande sätt:
- Om
V
är en av de ofixadeXᵢ
U
läggs den till i uppsättningen med lägre gränser för .Xᵢ
- Annars, om
V
är typenV₁?
ochU
är typenU₁?
, görs en lägre bunden slutsatsdragning frånU₁
tillV₁
. - Annars anges
U₁...Uₑ
ochV₁...Vₑ
bestäms genom att kontrollera om något av följande fall gäller:V
är en matristypV₁[...]
ochU
är en matristypU₁[...]
med samma rangordningV
är en avIEnumerable<V₁>
,ICollection<V₁>
,IReadOnlyList<V₁>>
ellerIReadOnlyCollection<V₁>
IList<V₁>
ochU
är en endimensionell matristypU₁[]
V
är en konstrueradclass
,struct
,interface
ellerdelegate
typC<V₁...Vₑ>
och det finns en unik typC<U₁...Uₑ>
somU
(eller, omU
är en typparameter
, dess effektiva basklass eller någon medlem i dess effektiva gränssnittsuppsättning) är identisk med,inherits
från (direkt eller indirekt) eller implementerar (direkt eller indirekt)C<U₁...Uₑ>
.- (Begränsningen "unikhet" innebär att i ärendegränssnittet
C<T>{} class U: C<X>, C<Y>{}
görs ingen slutsatsdragning vid slutsatsdragning frånU
tillC<T>
eftersomU₁
kan varaX
ellerY
.)
Om något av dessa fall gäller görs en slutsats från var ochUᵢ
en till motsvarandeVᵢ
enligt följande: - Om
Uᵢ
det inte är känt att det är en referenstyp görs en exakt slutsatsdragning - Om är en matristyp görs annars
U
en slutsatsdragning med lägre bindning - Annars, om
V
ärC<V₁...Vₑ>
då slutsatsdragning beror på typparameterni-th
förC
:- Om den är covariant görs en lägre bunden slutsatsdragning .
- Om det är kontravariant görs en övre gränsinferens .
- Om det är invariant görs en exakt slutsatsdragning .
- Annars görs inga slutsatsdragningar.
12.6.3.11 Övre bundna slutsatsdragningar
En övre bindnings slutsatsdragning från en typ U
till en typ V
görs på följande sätt:
- Om
V
är en av de ofixadeXᵢ
U
läggs den till i uppsättningen med övre gränser för .Xᵢ
- Annars anges
V₁...Vₑ
ochU₁...Uₑ
bestäms genom att kontrollera om något av följande fall gäller:U
är en matristypU₁[...]
ochV
är en matristypV₁[...]
med samma rangordningU
är en avIEnumerable<Uₑ>
,ICollection<Uₑ>
,IReadOnlyList<Uₑ>
ellerIReadOnlyCollection<Uₑ>
IList<Uₑ>
ochV
är en endimensionell matristypVₑ[]
U
är typenU1?
ochV
är typenV1?
U
är konstruerad klass, struct, gränssnitt eller ombudstypC<U₁...Uₑ>
ochV
är en ellerdelegate
enclass, struct, interface
typ som äridentical
till,inherits
frå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 när du härleder 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 var ochUᵢ
en till motsvarandeVᵢ
enligt följande: - Om
Uᵢ
det inte är känt att det är en referenstyp görs en exakt slutsatsdragning - Om är en matristyp görs annars
V
en övre gränsinferens - Annars, om
U
ärC<U₁...Uₑ>
då slutsatsdragning beror på typparameterni-th
förC
:- Om den är covariant görs en övre gränsinferens .
- Om det är kontravariant görs en lägre bunden slutsatsdragning .
- Om det är invariant görs en exakt slutsatsdragning .
- Annars görs inga slutsatsdragningar.
12.6.3.12 Fixering
En ofixerad typvariabel Xᵢ
med en uppsättning gränser har åtgärdats på följande sätt:
- Uppsättningen kandidattyper
Uₑ
börjar som uppsättningen med alla typer i uppsättningen med gränser förXᵢ
. - Varje bindning
Xᵢ
till granskas i tur och ordning: För varje exakt bunden U avXᵢ
alla typerUₑ
som inte är identiska medU
tas bort från kandidatuppsättningen. För varje lägre gränsU
avXᵢ
alla typerUₑ
som det inte finns någon implicit konvertering frånU
tas bort från kandidatuppsättningen. För varje övre gräns U avXᵢ
alla typerUₑ
som det inte finns någon implicit konvertering till tasU
bort från kandidatuppsättningen. - Om det bland de återstående kandidattyperna
Uₑ
finns en unik typV
som det finns en implicit konvertering till från alla andra kandidattyper,Xᵢ
så är den fast påV
. - Annars misslyckas typinferensen.
12.6.3.13 Uppskjuten returtyp
Den härledda returtypen för en anonym funktion F
används vid typinferens och överlagringsmatchning. 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är här typen av härledd effektiv retur bestäms på följande sätt:
- Om brödtexten
F
i är ett uttryck som har en typ är den härledda effektiva returtypenF
den typ av uttryck som används. - Om brödtexten
F
i är ett block och uppsättningen uttryck i blocketsreturn
-uttryck har en bästa gemensamma typT
(§12.6.3.15), ärT
den härledda effektiva returtypenF
. - Annars kan en effektiv returtyp inte härledas för
F
.
Den här här typen av uppskjuten retur bestäms på följande sätt:
- Om
F
är asynkront och brödtextenF
i antingen är ett uttryck klassificerat som ingenting (§12.2), eller ett block där ingareturn
uttryck har uttryck, är«TaskType»
den härledda returtypen (§15.15.1). - Om
F
är asynkron och har en härledd effektiv returtypT
är«TaskType»<T>»
den härledda returtypen (§15.15.1). - Om
F
är icke-asynkron och har en härledd effektiv returtypT
ärT
den härledda returtypen . - 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 den
Select
tilläggsmetod som deklareratsSystem.Linq.Enumerable
i klassen: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); } } } }
System.Linq
Förutsatt att namnområdet importerades med ettusing namespace
direktiv och en klassCustomer
med enName
egenskap av typenstring
,Select
kan metoden anvä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 tilläggsmetod (§12.8.10.3) av
Select
bearbetas genom att anropet skrivs om till en statisk 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 kundargumentet relaterat till källparametern och härleds
TSource
till att varaCustomer
. Sedan, med hjälp av den inferensprocess för anonym funktionstyp som beskrivs ovan,c
ges typenCustomer
, och uttrycketc.Name
är relaterat till returtypen för väljareparametern, vilket härlederTResult
till att varastring
. Anropet motsvarar såledesSequence.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. Med 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); } }
typinferens för anropet fortsätter enligt följande: Först är argumentet "1:15:30" relaterat till värdeparametern, vilket
X
innebär att det är sträng. 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
, som härledsY
System.TimeSpan
till . 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
, som härledsZ
double
till . Resultatet av anropet är därför av typendouble
.slutexempel
12.6.3.14 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 den metodgrupp M
som tilldelas till ombudstypen D
är uppgiften av typen slutsatsdragning att hitta typargument S₁...Sᵥ
så att uttrycket:
M<S₁...Sᵥ>
blir kompatibel (§20.2) med D
.
Till skillnad från typinferensalgoritmen för generiska metodanrop finns det i det här fallet bara argumenttyper, inga argumentuttryck. I synnerhet finns det inga anonyma funktioner och därför inget behov av flera inferensfaser.
I stället anses alla Xᵢ
vara ofixerade och en slutsatsdragning med lägre bindning görs från varje argumenttyp D
Uₑ
till motsvarande parametertyp Tₑ
för .M
Xᵢ
Om inga gränser hittades misslyckas typinferensen. Annars är alla Xᵢ
fasta till motsvarande Sᵢ
, vilket är resultatet av typinferens.
12.6.3.15 Hitta den vanligaste typen av uttryck
I vissa fall måste en vanlig typ härledas för en uppsättning uttryck. I synnerhet finns elementtyperna för implicit inskrivna matriser och returtyperna av anonyma funktioner med blockkroppar på det här sättet.
Den vanligaste typen för en uppsättning uttryck bestäms E₁...Eᵥ
på följande sätt:
- En ny variabel
X
av typen ofix introduceras. - För varje uttryck
Ei
utförs en inferens av utdatatyp (§12.6.3.7) från det tillX
. X
är fast (§12.6.3.12), om möjligt, och den resulterande typen är den bästa gemensamma typen.- Annars misslyckas slutsatsdragningen.
Obs! Intuitivt motsvarar den här slutsatsdragningen 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 med namnet i en invocation_expression (§12.8.10).
- Anrop av en instanskonstruktor med namnet i en object_creation_expression (§12.8.17.2).
- Anrop av en indexerare via en element_access (§12.8.12).
- Anrop av en fördefinierad eller användardefinierad operator som refereras 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 som å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 finns den bästa funktionsmedlemmen från uppsättningen med tillämpliga kandidatfunktionsmedlemmar. Om uppsättningen endast innehåller en funktionsmedlem är funktionsmedlemmen den bästa funktionsmedlemmen. 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 underetiketter definierar de exakta betydelserna för de villkor som gäller 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 av följande är sant:
- Varje argument i
A
motsvarar 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 det 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
in
modifieraren, 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 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 sin normala form. 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 utökade form:
- 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
A
matchar det totala antalet parametrar. OmA
har 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 för varje argument i
A
är något av följande sant:- 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-passing-läget för argumentet är värde och parameteröverföringsläget för motsvarande parameter är indata, 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: Med 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 en simple_name eller en member_access genom en typ.
- En instansmetod gäller endast 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 själva i den ordning de visas i den ursprungliga argumentlistan och utelämnar alla out
argument 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.
Givet en argumentlista A
med en uppsättning argumentuttryck och två tillämpliga funktionsmedlemmar Mᵥ
{E₁, E₂, ..., Eᵥ}
och Mₓ
med parametertyper {P₁, P₂, ..., Pᵥ}
och {Q₁, Q₂, ..., Qᵥ}
, Mᵥ
definieras 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 är likvärdiga (dvs. var Pᵢ
och {Q₁, Q₂, ..., Qᵥ}
en 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 är denMᵢ
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,Mᵢ
så är bättre änMₑ
. - Annars, om båda metoderna har params-matriser och endast är tillämpliga i deras expanderade former, och om params-matrisen
Mᵢ
i har färre element än params-matrisen iMₑ
, är detMᵢ
bättre änMₑ
. - Annars, om
Mᵥ
har mer specifika parametertyper änMₓ
, är detMᵥ
bättre änMₓ
. Let{R1, R2, ..., Rn}
and{S1, S2, ..., Sn}
represent the uninstantiated and unexpanded parameter types ofMᵥ
andMₓ
.Mᵥ
's parametertyper är mer specifika änMₓ
s om för varje parameterRx
inte är mindre specifik änSx
, och för minst en parameterRx
är mer specifik änSx
:- En typparameter är mindre specifik än en parameter av typen inte.
- 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 annars är en icke-lyftad operatör och den andra är en lyftad operatör är den icke-lyftade bättre.
- Om ingen funktionsmedlem
Mᵥ
visade sig vara bättre och alla parametrar i har ett motsvarande argument, medan standardargument måste ersättas med minst en valfri parameter iMₓ
, är detMᵥ
bättre änMₓ
. - Om för minst en parameter
Mᵥ
använder det bättre parameter-passing-valet (§12.6.4.4) än motsvarande parameter iMₓ
och ingen av de parametrar somMₓ
används är det bättre parameteröverföringsalternativet änMᵥ
,Mᵥ
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, anropar M1(i)
och M1(i + 5)
resulterar i att båda överbelastningarna är tillämpliga. I sådana fall är metoden med värdets parameteröverföringsläge det bättre valet för 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
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 C₁
T₂
är en bättre konvertering än C₂
om något av följande gäller:
E
matcharT₁
exakt ochE
matcharT₂
inte exakt (§12.6.4.6)E
exakt 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 (§20.4) med den enskilt 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
Givet ett uttryck E
och en typ E
T
matchar T
exakt om något av följande gäller:
E
har en typS
och det finns en identitetskonvertering frånS
tillT
E
är en anonym funktion,T
är antingen en ombudstypD
eller en uttrycksträdstypExpression<D>
och något av följande gäller:- Det finns en härledd returtyp
X
för i kontexten förE
parameterlistanD
(§12.6.3.12) och det finns en identitetskonvertering frånX
returtypen förD
E
är enasync
lambda utan returvärde ochD
har en returtyp som inte är generisk«TaskType»
- Antingen
E
är icke-asynkron ochD
har en returtypY
ellerE
är asynkron ochD
har en returtyp«TaskType»<Y>
(§15.15.1), och något av följande gäller:- Brödtexten
E
i är ett uttryck som exakt matcharY
- Brödtexten
E
i är ett block där varje retursats returnerar ett uttryck som exakt matcharY
- Brödtexten
- Det finns en härledd returtyp
12.6.4.7 Bättre konverteringsmål
Givet två typer T₁
och T₂
, T₁
är ett bättre konverteringsmål än T₂
om något av följande gäller:
- En implicit konvertering från
T₁
tillT₂
finns och ingen implicit konvertering frånT₂
tillT₁
finns T₁
är«TaskType»<S₁>
(§15.15.1),T₂
är«TaskType»<S₂>
, ochS₁
är ett bättre konverteringsmål änS₂
T₁
är«TaskType»<S₁>
(§15.15.1),T₂
är«TaskType»<S₂>
, ochT₁
är mer specialiserad änT₂
T₁
ärS₁
ellerS₁?
därS₁
är en signerad integraltyp ochT₂
ärS₂
ellerS₂?
därS₂
är en osignerad integraltyp. Specifikt:S₁
ärsbyte
ochS₂
ärbyte
,ushort
,uint
, ellerulong
S₁
ärshort
ochS₂
ärushort
,uint
, ellerulong
S₁
ärint
ochS₂
äruint
, ellerulong
S₁
ärlong
ochS₂
ärulong
12.6.4.8 Överlagring i generiska klasser
Obs! Även om signaturer som deklarerats 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 (före ersättning av typargument), om det finns och rapporterar annars ett fel. slutkommentar
Exempel: I följande exempel visas ö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 enskild funktionsmedlem med samma parameterlista som delegate_type för 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.3) är uppsättningen tillgängliga indexerare i mottagaren känd vid kompileringstid.
I dessa fall utförs en begränsad kompileringstidskontroll på varje medlem i den kända uppsättningen funktionsmedlemmar för att se om det kan vara känt att vissa aldrig anropas vid körning. För varje funktionsmedlem F
skapas en modifierad parameter och en argumentlista:
- Först, om
F
är en generisk metod och typargument angavs, ersätts de med typparametrarna i parameterlistan. Men om typargument inte har angetts sker ingen sådan ersättning. - Sedan genereras alla parametrar vars typ är öppen (d.v.s. innehåller en typparameter, se §8.4.3) tillsammans med motsvarande parametrar.
För F
att kontrollen ska kunna genomföras skall alla följande gälla:
- Den ändrade parameterlistan för
F
gäller för den ändrade argumentlistan enligt §12.6.4.2. - Alla konstruerade typer i listan över ändrade parametrar uppfyller sina begränsningar (§8.4.5).
- Om typparametrarna
F
för ersattes i steget ovan uppfylls deras begränsningar. - Om
F
är en statisk metod ska metodgruppen inte ha resulterat från en member_access vars mottagare vid kompileringstid är en variabel eller ett värde. - Om
F
är en instansmetod ska metodgruppen inte ha resulterat från en member_access vars mottagare vid kompileringstiden är en typ.
Om ingen kandidat klarar det här testet uppstår ett kompileringsfel.
12.6.6 Funktionsmedlemsanrop
12.6.6.1 Allmänt
Den här underklienten beskriver den process som sker vid körning 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, instansegenskapsåtkomster och indexerare. 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 funktionsmedlemmen och, om M
är en instansmedlem, E
är instansuttrycket:
Om
M
är en statisk funktionsmedlem:- Argumentlistan utvärderas enligt beskrivningen i §12.6.2.
M
anropas.
Annars, om typen av
E
är en värdetypV
, ochM
deklareras eller åsidosätts iV
:E
utvä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 falletE
klassificeras som en variabel.- Om
E
inte klassificeras som en variabel, eller omV
inte är en skrivskyddad structtyp (§16.2.2), ochE
är en av:- en indataparameter (§15.6.2.3.2), eller
- ett
readonly
fält (§15.5.3), eller - en
readonly
referensvariabel eller retur (§9.7),
sedan skapas en tillfällig lokal variabel av
E
typen och värdetE
för tilldelas till variabeln.E
omklassificeras sedan som en referens till den tillfälliga lokala variabeln. Den tillfälliga variabeln är tillgänglig somthis
inomM
, men inte på något annat sätt. Det är alltså bara närE
som kan skrivas är det möjligt för anroparen att observera de ändringar somM
gör ithis
.- Argumentlistan utvärderas enligt beskrivningen i §12.6.2.
M
anropas. Variabeln som refereras avE
blir variabeln som refereras avthis
.
Annars:
E
utvä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_type utförs en boxningskonvertering (§10.2.9) för att konverteraE
till en class_type ochE
anses vara av den class_type i följande steg. Om value_type är en enum_type ärSystem.Enum;
class_type annarsSystem.ValueType
. - Värdet
E
för är markerat för att vara giltigt. Om värdetE
för är null genereras enSystem.NullReferenceException
och inga ytterligare steg körs. - Den funktionsmedlemsimplementering som ska anropas bestäms:
- Om bindningstidstypen
E
för är ett gränssnitt är funktionsmedlemmen som ska anropas implementeringen avM
som tillhandahålls av körningstypen för den instans som refereras avE
. Den här funktionsmedlemmen bestäms genom att tillämpa reglerna för gränssnittsmappning (§18.6.5) för att fastställa implementeringen avM
tillhandahållen av körningstypen för instansen som refereras avE
. - Annars, om
M
är en virtuell funktionsmedlem, är funktionsmedlemmen som ska anropas implementeringen avM
som tillhandahålls av körningstypen för den instans som refereras avE
. Den här funktionsmedlemmen bestäms genom att tillämpa reglerna för att fastställa den mest härledda implementeringen (§15.6.4)M
med avseende på körningstypen för den instans som refereras avE
. - Annars
M
är en icke-virtuell funktionsmedlem och funktionsmedlemmen som ska anropas ärM
sig själv.
- Om bindningstidstypen
- Implementeringen av funktionsmedlem som fastställs i steget ovan anropas. Objektet som refereras av
E
blir det objekt som refereras till av detta.
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 kommer alltid att vara en av
System.Object
,System.ValueType
ellerSystem.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 inramade instansen. slutkommentar
12.7 Dekonstruktion
Dekonstruktion är en process där ett uttryck omvandlas till en tuppeln 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 E
dekonstrueras till ett tupppeluttryck med n
element på följande sätt:
- Om
E
är ett tupppeluttryck medn
element är resultatet av dekonstruktion själva uttrycketE
. - Annars, om
E
har en tuppeln typ(T1, ..., Tn)
medn
element,E
sedan utvärderas till en tillfällig variabel__v
, och resultatet av dekonstruktion är uttrycket(__v.Item1, ..., __v.Itemn)
. - Annars utvärderas uttrycket om uttrycket
E.Deconstruct(out var __v1, ..., out var __vn)
vid kompileringstid matchar en unik instans eller tilläggsmetod, och resultatet av dekonstruktionen är uttrycket(__v1, ..., __vn)
. En sådan metod kallas för en dekonstruktion. - Annars
E
kan inte dekonstrueras.
__v
Här och __v1, ..., __vn
referera till annars osynliga och otillgängliga tillfälliga variabler.
Obs! Ett typuttryck
dynamic
kan inte dekonstrueras. slutkommentar
12.8 Primära uttryck
12.8.1 Allmänt
Primära uttryck är de enklaste uttrycksformerna.
primary_expression
: primary_no_array_creation_expression
| array_creation_expression
;
primary_no_array_creation_expression
: literal
| interpolated_string_expression
| simple_name
| parenthesized_expression
| tuple_expression
| member_access
| null_conditional_member_access
| invocation_expression
| element_access
| null_conditional_element_access
| this_access
| base_access
| post_increment_expression
| post_decrement_expression
| null_forgiving_expression
| object_creation_expression
| delegate_creation_expression
| anonymous_object_creation_expression
| typeof_expression
| sizeof_expression
| checked_expression
| unchecked_expression
| default_value_expression
| nameof_expression
| anonymous_method_expression
| pointer_member_access // unsafe code support
| pointer_element_access // unsafe code support
| stackalloc_expression
;
Obs! Dessa grammatikregler är inte ANTLR-redo eftersom de ingår i en uppsättning ömsesidigt vänsterrekursiva regler (
primary_expression
,primary_no_array_creation_expression
,member_access
,invocation_expression
,element_access
,post_increment_expression
, ,post_decrement_expression
ochnull_forgiving_expression
pointer_member_access
pointer_element_access
) som ANTLR inte hanterar. Standardtekniker kan användas för att transformera grammatiken för att ta bort den ömsesidiga vänsterrekursionen. Detta har inte gjorts 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 (§23.6.3) och pointer_element_access (§23.6.4) är endast tillgängliga i osäker kod (§23).
Primära uttryck delas mellan array_creation_expressions och primary_no_array_creation_expressions. Genom att behandla array_creation_expression på det här sättet, i stället för att lista den tillsammans med de andra enkla uttrycksformulären, kan grammatiken inte tillåta potentiellt förvirrande kod som
object o = new int[3][1];
som annars skulle tolkas som
object o = (new int[3])[1];
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; regelbundna (interpolated_regular_string_expression) och ordagranna (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 enligt följande:
Regel | Kontextuella krav |
---|---|
Interpolated_Regular_String_Mid | Identifieras endast efter en Interpolated_Regular_String_Start, mellan följande interpolationer 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_Mids eller token som kan ingå i regular_interpolations, inklusive token för alla interpolated_regular_string_expressionsom ingår i sådana interpolationer. |
Interpolated_Verbatim_String_Mid Verbatim_Interpolation_Format Interpolated_Verbatim_String_End | Erkännandet av dessa tre regler följer motsvarande regler ovan med varje angiven vanlig grammatikregel ersatt av motsvarande ordagranna . |
Obs! 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 implementeringsdetalj 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 typen string
.
Obs! Skillnaderna mellan möjliga typer som en 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 ett 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 producera den slutgiltiga string
som ska interpoleras till 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 för den här constant_expression och justeringen vara tecknet (positivt eller negativt) för värdet för den här constant_expression:
- 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 typen av uttrycket ä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
; annars 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ängliteral 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ängliteralen består av i ordning:
- Tecknen i Interpolated_Regular_String_Start eller Interpolated_Verbatim_String_Start
- Tecknen i Interpolated_Regular_String_Mid eller Interpolated_Verbatim_String_Mid, om några
N ≥ 1
Om sedan för varje talI
från0
tillN-1
:- En platshållarspecifikation:
- Ett vänster klammerparentestecken (
{
) - Decimalrepresentationen av
I
- Om motsvarande regular_interpolation eller verbatim_interpolation sedan har en 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 av motsvarande regular_interpolation eller verbatim_interpolation
- Ett höger klammerparentestecken (
}
)
- Ett vänster klammerparentestecken (
- Tecknen i Interpolated_Regular_String_Mid eller Interpolated_Verbatim_String_Mid omedelbart efter motsvarande interpolation, om någon
- En platshållarspecifikation:
- Slutligen tecknen i Interpolated_Regular_String_End eller Interpolated_Verbatim_String_End.
De efterföljande argumenten är uttrycken från interpolationerna, om några, i ordning.
När en interpolated_string_expression innehåller flera interpolationer utvärderas uttrycken i dessa interpoleringar i textordning från vänster till höger.
Exempel:
I det här exemplet används följande formatspecifikationsfunktioner:
- formatspecifikationen
X
som formaterar heltal som versaler hexadecimalt, - standardformatet för ett
string
vä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}
.
Given:
string text = "red";
int number = 14;
const int width = -4;
Sedan:
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 i formuläret I
eller i formuläret I<A₁, ..., Aₑ>
, där I
är en enda identifierare och I<A₁, ..., Aₑ>
är en valfri type_argument_list. När ingen type_argument_list har angetts bör du överväga e
att vara noll. Simple_name utvärderas och klassificeras enligt följande:
- 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 namnetI
, 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 attributen för 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), som börjar med instanstypen för den omedelbart omslutande typdeklarationen och fortsätter med instanstypen för varje omslutande klass eller structdeklaration (om någon):- Om
e
är noll och deklarationen avT
innehåller en typparameter med namnetI
refererar simple_name till den typparametern. - Annars, om ett medlemsuppslag (§12.5) av
I
iT
mede
typargument skapar 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 structtypen, om sökningen identifierar en instansmedlem och om referensen inträffar inom blocket för en instanskonstruktor, en instansmetod eller en instansåtkomstor (§12.2.1), blir resultatet detsamma som en medlemsåtkomst (§12.8.7) av formuläretthis.I
. Detta kan bara inträffa näre
är noll. - Annars är resultatet detsamma som en medlemsåtkomst (§12.8.7) av formuläret
T.I
ellerT.I<A₁, ..., Aₑ>
.
- Om
- Om
- I annat fall utvärderas följande steg tills en entitet finns för varje namnområde
N
som börjar 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
N
och namnområdesdeklarationen innehåller en extern_alias_directive eller using_alias_directive som associerar namnetI
med ett namnområde eller en typ, är simple_name tvetydig och ett kompileringsfel inträffar. - Annars refererar simple_name till namnområdet med namnet
I
iN
.
- Om platsen där simple_name inträffar omges av en namnområdesdeklaration för
- Annars, om
N
innehåller en tillgänglig typ med namnI
oche
typparametrar, så:- Om
e
är noll och platsen där simple_name inträffar omges av en namnområdesdeklaration förN
och namnområdesdeklarationen innehåller en extern_alias_directive eller using_alias_directive som associerar namnetI
med ett namnområde eller typ, är simple_name tvetydig 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 inträffar omges 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 namnetI
med 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
I
oche
typparametrar, refererar simple_name till den typen som skapats med de angivna typargumenten. - Om namnrymderna som importerats av using_namespace_directivei namnområdesdeklarationen innehåller fler än en typ med namn
I
- oche
typparametrar , är simple_name tvetydigt 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.17). - Annars är simple_name odefinierat och ett kompileringsfel inträffar.
12.8.5 Parentesiserade uttryck
En parenthesized_expression består av ett uttryck som omges av parenteser.
parenthesized_expression
: '(' expression ')'
;
En parenthesized_expression utvärderas genom att uttrycket utvärderas inom parenteserna. Om 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 Tupplar
En tuple_expression representerar en tupplar 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 tupplar som innehåller implicit inskrivna 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 en tuppl.
En deconstruction_expression var (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.17). Därför kan en deconstruction_expression bara ske till vänster i en enkel tilldelning.
Ett tupplaruttryck har en typ om och endast om vart och ett av dess elementuttryck Ei
har en typ Ti
. Typen ska vara en tuppelns typ av samma aritet som tuppelns uttryck, där varje element anges av följande:
- Om tuppelns element i motsvarande position har ett namn
Ni
ska tuppelns typelement varaTi Ni
. - Annars, om
Ei
är av formuläretNi
ellerE.Ni
ellerE?.Ni
då tuppeln typelementet skall varaTi Ni
, såvida inte något av följande gäller:- Ett annat element i tuppelns uttryck har namnet
Ni
, eller - Ett annat tupppelelement utan namn har ett tupppelelementuttryck av formuläret
Ni
ellerE.Ni
ellerE?.Ni
, eller Ni
är av formatetItemX
, därX
är en sekvens med icke-initierade0
decimalsiffror som kan representera positionen för ett tupplar ochX
som inte representerar elementets position.
- Ett annat element i tuppelns uttryck har namnet
- I annat fall ska tuppelns typelement vara
Ti
.
Ett tupplaruttryck utvärderas genom att utvärdera vart och ett av dess elementuttryck i ordning från vänster till höger.
Ett tuppelns värde kan hämtas från ett tupppeluttryck 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.21.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 type
I det här exemplet är alla fyra tuppelns uttryck giltiga. De första två,
t1
ocht2
, använder inte typen av tuppelns uttryck, utan tillämpar i stället en implicit tuppelns konvertering. När det gällert2
, förlitar sig den implicita tuppelns konvertering på implicita konverteringar från2
tilllong
och frånnull
tillstring
. 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 tupplar ibland kan leda till flera lager parenteser, särskilt när tuppelns uttryck är det enda argumentet till en metodanrop.
slutexempel
12.8.7 Medlemsåtkomst
12.8.7.1 Allmänt
En member_access består av en primary_expression, en predefined_type eller en qualified_alias_member fö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 formuläret E.I
eller i formuläret E.I<A₁, ..., Aₑ>
, där E
är en primary_expression, predefined_type eller qualified_alias_member, I
är en enda identifierare och <A₁, ..., Aₑ>
är en valfri type_argument_list. När ingen type_argument_list har angetts bör du överväga e
att vara noll.
En member_access med en primary_expression av typen dynamic
är dynamiskt bunden (§12.3.3). I det här fallet klassificerar kompilatorn 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 denna körningsklassificering leder till en metodgrupp ska medlemsåtkomsten vara primary_expression för en invocation_expression.
Member_access utvärderas och klassificeras på följande sätt:
- Om
e
är noll ochE
är ett namnområde ochE
innehåller ett kapslat namnområde med namnetI
blir resultatet det namnområdet. - Annars, om
E
är ett namnområde ochE
innehåller en tillgänglig typ med namnI
- ochK
typparametrar, är resultatet den typen som konstruerats med de angivna typargumenten. - Om
E
klassificeras som en typ, omE
inte är en typparameter, och om en medlemssökning (§12.5) iI
E
medK
typparametrar genererar en matchning,E.I
utvärderas och klassificeras så här:Obs! När resultatet av en sådan medlemssökning är en metodgrupp och
K
är noll, kan metodgruppen innehålla metoder som har typparametrar. Detta gör att sådana metoder kan övervägas för typarguments slutsatsdragning. slutkommentar- Om
I
identifierar en typ är resultatet den typen som konstruerats med alla angivna typargument. - Om
I
identifierar en eller flera metoder blir resultatet en metodgrupp utan associerat instansuttryck. - Om
I
identifierar en statisk egenskap blir resultatet en egenskapsåtkomst utan associerat instansuttryck. - Om
I
identifierar 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
I
iE
. - Annars är resultatet en variabel, nämligen det statiska fältet
I
iE
.
- 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
I
identifierar 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 den
E.I
exakt som omI
det var 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 den
- Om
I
identifierar en konstant är resultatet ett värde, nämligen värdet för den konstanten. - Om
I
identifierar en uppräkningsmedlem är resultatet ett värde, nämligen värdet för den uppräkningsmedlemmen. - Annars
E.I
är en ogiltig medlemsreferens och ett kompileringsfel inträffar.
- Om
- Om
E
är en egenskapsåtkomst, indexerareåtkomst, variabel eller värde, vars typ ärT
, och en medlemssökning (§12.5) avI
medT
K
typargument skapar en matchning, utvärderas och klassificeras såE.I
här:E
Om är en egenskaps- eller indexerareåtkomst erhålls först värdet för egenskapen eller indexerarens åtkomst (§12.2.2) och E omklassificeras som ett värde.- Om
I
identifierar en eller flera metoder är resultatet en metodgrupp med ett associerat instansuttryck avE
. - Om
I
identifierar en instansegenskap blir resultatet en egenskapsåtkomst med ett associerat instansuttryck avE
och en associerad typ som är egenskapens typ. OmT
är en klasstyp väljs den associerade typen från den första deklarationen eller åsidosättningen av egenskapen som hittades när du började medT
och söker igenom dess basklasser. - Om
T
är en class_type ochI
identifierar ett instansfält för den class_type:- Om värdet
E
för ärnull
genereras 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
I
i objektet som refereras avE
. - Annars är resultatet en variabel, nämligen fältet
I
i objektet som refereras avE
.
- Om värdet
- Om
T
är en struct_type ochI
identifierar 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, blir resultatet ett värde, nämligen värdet för fältetI
i den struct-instans som anges avE
. - Annars är resultatet en variabel, nämligen fältet
I
i den struct-instans som ges avE
.
- Om
- Om
I
identifierar 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 sker som vänster sida av
a +=
eller-=
operatorn, bearbetas denE.I
exakt som omI
det var 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 sker som vänster sida av
- I annat fall görs ett försök att bearbeta
E.I
som ett tilläggsmetodanrop (§12.8.10.3). Om detta misslyckasE.I
är en ogiltig medlemsreferens och ett bindningstidsfel inträffar.
12.8.7.2 Identiska enkla namn och typnamn
I en medlemsåtkomst av formuläret E.I
, om E
är en enskild 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), tillåts båda möjliga betydelser av E
. Medlemssökningen av E.I
är aldrig tvetydig, eftersom I
den nödvändigtvis ska vara medlem av typen E
i båda fallen. Med andra ord tillåter regeln bara åtkomst till de statiska medlemmarna och kapslade typer av E
var 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 } }
I expository-syfte avgränsas endast
A
de förekomster av identifierarenColor
som refererar tillColor
typen av i klassen av«...»
, och de som refererar tillColor
fältet är inte det.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 villkorligt null-uttryck där resultattypen kan visas void
(§12.8.11).
En null_conditional_member_access består av en primary_expression följt av de två tokensna "?
" och ".
", följt av en identifierare med en valfri type_argument_list, följt av noll eller fler dependent_accesses som kan föregripas 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 av formuläret P?.A
. Innebörden av E
bestäms på följande sätt:
Om typen av
P
är en nullbar värdetyp:Låt vara
T
typen avP.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 icke-nullbar värdetyp ärT?
typen ,E
och innebörden avE
är densamma som innebörden av:((object)P == null) ? (T?)null : P.Value.A
Förutom att
P
utvärderas bara en gång.Annars är
T
typen avE
, och innebörden avE
är densamma som innebörden av:((object)P == null) ? (T)null : P.Value.A
Förutom att
P
utvärderas bara en gång.
Annars:
Låt vara
T
typen av uttrycketP.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 icke-nullbar värdetyp ärT?
typen ,E
och innebörden avE
är densamma som innebörden av:((object)P == null) ? (T?)null : P.A
Förutom att
P
utvärderas bara en gång.Annars är
T
typen avE
, och innebörden avE
är densamma som innebörden av:((object)P == null) ? (T)null : P.A
Förutom att
P
utvärderas bara en gång.
Obs! I ett uttryck i formuläret:
P?.A₀?.A₁
sedan om
P
utvärderas tillnull
varkenA₀
ellerA₁
utvärderas. 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 en projektionsinitierare i ett anonymt objektskapandeuttryck (§12.8.17.7).
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.12) är värdet, typen, klassificeringen och säkerhetskontexten för dess primary_expression.
null_forgiving_expression
: primary_expression null_forgiving_operator
;
null_forgiving_operator
: '!'
;
Obs! Operatorerna för postfixet null-forgiving och prefix logisk negation (§12.9.4), som representeras av samma lexikala token (!
), är distinkta. Endast den senare får åsidosättas (§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 den här underklienten och följande underklienter för syskon är villkorligt 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 är null och för att åsidosätta en kompilator som utfärdar en varning om nullbarhet.
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 operatorn null-forgiving tillämpas på ett sådant uttryck informeras kompilatorns statiska nulltillståndsanalys om att null-tillståndet inte är null, vilket 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
IsValid
returnerartrue
kan ,p
på ett säkert sätt derefereras för att komma åt dessName
egenskap, och varningen "dereferencing av ett eventuellt null-värde" kan ignoreras med hjälp av!
.slutexempel
Exempel: Operatorn null-forgiving bör användas med försiktighet. Tänk på följande:
#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 quashes alla varningar på
x
. Men omx
ärnull
vid körning genereras ett undantag somnull
inte kan gjutas tillint
.slutexempel
12.8.9.3 Åsidosätta andra null-analysvarningar
Förutom att åsidosätta kanske null-bestämningar som ovan kan det finnas andra omständigheter där det är önskvärt att åsidosätta en kompilators statiska null-tillståndsanalys som bestämning av att ett uttryck kräver en eller flera varningar. Tillämpa operatorn null-forgiving på sådana uttrycksbegäranden att kompilatorn inte utfärdar några 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 metodens
Assign
parametrar,lv
&rv
, ärstring?
, medlv
en utdataparameter, och den utför en enkel tilldelning.Metoden
M
skickar variabelns
, av typenstring
, somAssign
utdataparameter. Kompilatorn använde en varning eftersoms
den inte är en nullbar variabel.Assign
Eftersom det andra argumentet inte kan vara null används operatorn null-forgiving för att stoppa 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? ')'
;
Primary_expression kan vara en null_forgiving_expression om och bara om den har en delegate_type.
En invocation_expression är dynamiskt bunden (§12.3.3) om minst något av följande gäller:
- Primary_expression har kompileringstidstypen
dynamic
. - Minst ett argument av den valfria argument_list har kompileringstidstypen
dynamic
.
I det här fallet klassificerar kompilatorn 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 dynamic
genomgå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 för en delegate_type är invocation_expression ett ombudsanrop (§12.8.10.4). Om primary_expression varken är en metodgrupp eller ett värde för en delegate_type uppstå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 ett ombud för return-no-value är resultatet ingenting. 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.19). Annars uppstår ett bindningstidsfel.
- Om invocation_expression anropar en return-by-ref-metod (§15.6.1) eller ett ombud för return-by-ref ä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 klasstyp
T
, väljs den associerade typen från den första deklarationen eller åsidosätter metoden som hittades när den började medT
och söker igenom dess basklasser. - I annat fall anropar invocation_expression en metod för return-by-value (§15.6.1) eller ombud för retur per värde, och resultatet är ett värde med en associerad typ av returtyp för metoden eller ombudet. Om anropet är av en instansmetod och mottagaren är av klasstyp
T
, väljs den associerade typen från den första deklarationen eller åsidosätter metoden som hittades när den började medT
och söker igenom dess basklasser.
12.8.10.2 Metodanrop
För ett metodanrop ska primary_expression av invocation_expression 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
F
som är associerad med metodgruppenM
:- Om
F
är icke-generiskt,F
är en kandidat när:M
har ingen typargumentlista, ochF
gäller förA
(§12.6.4.2).
- Om
F
är generisk ochM
inte har någon typargumentlista,F
är en kandidat när:- Typinferens (§12.6.3) lyckas och härleder 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
F
sina begränsningar (§8.4.5), och parameterlistan förF
är tillämplig förA
(§12.6.4.2)
- Om
F
är generisk ochM
innehåller en typargumentlista,F
är en 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.F
i uppsättningen, därC
är den typ där metodenF
deklareras, tas alla metoder som deklareras i en bastyp bortC
från uppsättningen. OmC
är en annan klasstyp änobject
tas 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 ett medlemsuppslag på en typparameter som har en annan effektiv basklass än
object
och 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 överbelastningsmatchning i §12.6.4. Om det inte går att identifiera en enda bästa metod är metodanropet tvetydigt och ett bindningstidsfel inträffar. När du utför överbelastningsmatchning beaktas parametrarna för en generisk metod efter att ha ersatt typargumenten (tillhandahålls eller härleds) för motsvarande metodtypparametrar.
När en metod har valts och verifierats vid bindningstid enligt ovanstående steg bearbetas det faktiska körningsanropet enligt reglerna för funktionsmedlemsanrop som beskrivs i §12.6.6.
Obs! Den intuitiva effekten av de lösningsregler som beskrivs ovan är följande: Om du vill hitta den metod som anropas av ett 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 metoddeklaration som inte åsidosätter 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 ett av formulären
«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 konstruktionen som en 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 berättigad om:
Cᵢ
är en icke-generisk klass som inte är kapslad- Namnet på är identifieraren
Mₑ
Mₑ
är tillgänglig och tillämplig när den tillämpas på argumenten som en statisk metod enligt ovan- Det finns en implicit identitet, referens eller boxningskonvertering från uttr till typen av den första parametern för
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 namnrymder 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 överbelastningsupplö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 hjälp av C
som mål bearbetas metodanropet sedan som en statisk metodanrop (§12.6.6).
Obs! Till skillnad från en instansmetodanrop utlöses inget undantag när expr utvärderas till en null-referens. I stället skickas det här
null
värdet till tilläggsmetoden som det skulle vara via en vanlig statisk 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
B
har metoden 's företräde framför den första tilläggsmetoden, ochC
metoden 's 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.G
tar precendece överC.G
, ochE.F
har företräde framför bådeD.F
ochC.F
.slutexempel
12.8.10.4 Delegera anrop
För ett ombudsanrop ska primary_expression för invocation_expression vara ett värde för en delegate_type. Med tanke på att delegate_type vara funktionsmedlem med samma parameterlista som delegate_type, ska delegate_type vara tillämpligt (§12.6.4.2) med avseende på argument_list av invocation_expression.
Körningsbearbetningen av ett ombudsanrop av formuläret D(A)
, där D
är en primary_expression av en delegate_type och A
är en valfri argument_list, består av följande steg:
D
utvärderas. Om den här utvärderingen orsakar ett undantag körs inga ytterligare steg.- Argumentlistan
A
utvärderas. Om den här utvärderingen orsakar ett undantag körs inga ytterligare steg. - Värdet
D
för är markerat för att vara giltigt. Om värdetD
för ärnull
genereras enSystem.NullReferenceException
och inga ytterligare steg körs. - Annars
D
är en 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 §20.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.19.1) eller method_body (§15.6.1).
Till skillnad från den syntaktiskt likvärdiga null_conditional_member_access eller null_conditional_element_access kan 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 kan inkluderas om och endast om null_conditional_member_access eller null_conditional_element_access har en delegate_type.
Ett null_conditional_invocation_expression uttryck E
är av formuläret P?A
, där A
är resten av den syntaktiskt likvärdiga null_conditional_member_access eller null_conditional_element_access, A
börjar därför med .
eller [
. Låt PA
oss beteckna sammanfogningen av P
och A
.
När E
inträffar som en statement_expression är innebörden av E
samma som innebörden av -instruktionen:
if ((object)P != null) PA
förutom att P
utvärderas bara en gång.
När E
inträffar som en anonymous_function_body eller method_body beror innebörden av E
dess klassificering:
Om
E
klassificeras som ingenting är dess betydelse densamma som innebörden av blocket:{ if ((object)P != null) PA; }
förutom att
P
utvärderas bara en gång.Annars är innebörden av
E
samma som innebörden av blocket:{ return E; }
och i sin tur beror innebörden av detta block på om
E
är syntaktiskt likvärdig med en 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_no_array_creation_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 argument, avgränsade med kommatecken.
element_access
: primary_no_array_creation_expression '[' argument_list ']'
;
Argument_list för en element_access får inte innehålla out
eller ref
argument.
En element_access är dynamiskt bunden (§12.3.3) om minst något av följande gäller:
- Primary_no_array_creation_expression har kompileringstidstypen
dynamic
. - Minst ett uttryck för argument_list har kompileringstidstyp
dynamic
och primary_no_array_creation_expression inte har någon matristyp.
I det här fallet klassificerar kompilatorn element_access som ett värde av typen dynamic
. Reglerna nedan för att fastställa innebörden av element_access tillämpas sedan vid körning, med hjälp av körningstypen i stället för kompileringstidstypen för de primary_no_array_creation_expression och argument_list uttryck som har kompileringstidstypen dynamic
. Om primary_no_array_creation_expression inte har kompileringstidstyp dynamic
genomgår elementåtkomsten en begränsad kompileringstidskontroll enligt beskrivningen i §12.6.5.
Om primary_no_array_creation_expression för en element_access är ett värde för en array_type är element_access en matrisåtkomst (§12.8.12.2). Annars ska primary_no_array_creation_expression vara en variabel eller ett värde för en klass, struct eller gränssnittstyp som har en eller flera indexerare medlemmar, i vilket fall element_access är en indexerare åtkomst (§12.8.12.3).
12.8.12.2 Matrisåtkomst
För en matrisåtkomst ska primary_no_array_creation_expression för element_access vara ett värde för en array_type. Dessutom tillåts inte argument_list för en matrisåtkomst att innehålla namngivna argument. Antalet uttryck i argument_list ska vara samma som rangordningen för array_type, och varje uttryck ska vara av typen int
, uint
, long
eller ulong,
ska implicit konverteras till en eller flera av dessa typer.
Resultatet av utvärderingen av en matrisåtkomst är en variabel av elementtypen för matrisen, nämligen matriselementet som valts av värdena för uttrycken i argument_list.
Körningsbearbetningen av en matrisåtkomst i formuläret P[A]
, där P
är en primary_no_array_creation_expression av en array_type och A
är en argument_list, består av följande steg:
P
utvärderas. Om den här utvärderingen orsakar ett undantag körs inga ytterligare steg.- Indexuttrycken för argument_list utvärderas i ordning, från vänster till höger. Efter utvärdering av varje indexuttryck 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 indexuttrycket till exempel är av typenshort
utförs en implicit konvertering tillint
, eftersom implicita konverteringar frånshort
tillint
och frånshort
ärlong
möjliga. 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
P
för är markerat för att vara giltigt. Om värdetP
för ärnull
genereras enSystem.NullReferenceException
och inga ytterligare steg körs. - Värdet för varje uttryck i argument_list kontrolleras mot de faktiska gränserna för varje dimension i matrisinstansen som refereras av
P
. Om ett eller flera värden ligger inom intervallet genereras ettSystem.IndexOutOfRangeException
och inga ytterligare steg körs. - Platsen för matriselementet som anges av indexuttrycken beräknas och den här platsen blir resultatet av matrisåtkomsten.
12.8.12.3 Indexer-åtkomst
För en indexerares åtkomst ska primary_no_array_creation_expression av 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.
Bindningstiden för en indexerares åtkomst till formuläret P[A]
, där P
är en primary_no_array_creation_expression av en klass, struct eller gränssnittstyp 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 iT
eller en bastyp avT
som inte är åsidosättningsdeklarationer 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.I
i uppsättningen, därS
är den typ där indexerarenI
deklareras:- Om
I
inte är tillämpligt med avseendeA
på (§12.6.4.2) tas denI
bort från uppsättningen. - Om
I
är tillämpligt med avseendeA
på (§12.6.4.2) tas alla indexerare som deklarerats i bastypen avS
bort från uppsättningen. - Om
I
är tillämpligt med avseendeA
på (§12.6.4.2) ochS
är en annan klasstyp änobject
, tas 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 överbelastningsmatchning 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.
- Indexuttrycken för argument_list utvärderas i ordning, från vänster till höger. Resultatet av bearbetningen av indexerarens åtkomst är ett uttryck som klassificeras som en indexerareåtkomst. Indexerarens åtkomstuttryck refererar till indexeraren som fastställdes i steget ovan och har ett associerat instansuttryck för
P
och en associerad argumentlista medA
och en associerad typ som är indexerarens typ. OmT
är en klasstyp väljs den associerade typen från den första deklarationen eller åsidosättningen av indexeraren som hittas när du börjar medT
och söker igenom dess basklasser.
Beroende på i vilken kontext den används orsakar en indexerare åtkomst anrop av antingen get-accessorn eller indexerarens uppsättningsåtkomst. Om indexerarens åtkomst är målet för en tilldelning anropas den angivna åtkomstgivaren för att tilldela ett nytt värde (§12.21.2). I alla andra fall anropas get-accessorn för att hämta det aktuella värdet (§12.2.2).
12.8.13 Null Villkorlig elementåtkomst
En null_conditional_element_access består av en primary_no_array_creation_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_no_array_creation_expression '?' '[' argument_list ']'
(null_forgiving_operator? dependent_access)*
;
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 villkorligt null-uttryck där resultattypen kan visas void
(§12.8.11).
Ett null_conditional_element_access uttryck E
är av formuläret P?[A]B
, där B
finns de eventuella dependent_accesses. Innebörden av E
bestäms på följande sätt:
Om typen av
P
är en nullbar värdetyp:Låt vara
T
typen av uttrycketP.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 icke-nullbar värdetyp ärT?
typen ,E
och innebörden avE
är densamma som innebörden av:((object)P == null) ? (T?)null : P.Value[A]B
Förutom att
P
utvärderas bara en gång.Annars är
T
typen avE
, och innebörden avE
är densamma som innebörden av:((object)P == null) ? null : P.Value[A]B
Förutom att
P
utvärderas bara en gång.
Annars:
Låt vara
T
typen av uttrycketP[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 icke-nullbar värdetyp ärT?
typen ,E
och innebörden avE
är densamma som innebörden av:((object)P == null) ? (T?)null : P[A]B
Förutom att
P
utvärderas bara en gång.Annars är
T
typen avE
, och innebörden avE
är densamma som innebörden av:((object)P == null) ? null : P[A]B
Förutom att
P
utvärderas bara en gång.
Obs! I ett uttryck i formuläret:
P?[A₀]?[A₁]
om
P
utvärderas tillnull
varkenA₀
ellerA₁
utvärderas. 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 för en instanskonstruktor, en instansmetod, en instansåtkomstor (§12.2.1) eller en finalizer. Den har någon av följande betydelser:
- När
this
används i en primary_expression inom en instanskonstruktor för en klass klassificeras den som ett värde. Typen av värde ä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
this
används i en primary_expression inom 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 där användningen sker, och värdet är en referens till det objekt som metoden eller accessorn anropades för. - När
this
anvä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 som användningen sker inom, och variabeln representerar den struct som skapas.- Om konstruktordeklarationen inte har någon konstruktorinitierare fungerar variabeln
this
exakt samma som en utdataparameter av typen struct. Detta innebär särskilt att variabeln definitivt ska tilldelas i varje körningsväg för instanskonstruktorn. - Annars fungerar variabeln
this
exakt samma som enref
parameter 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
this
används i en primary_expression inom 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.14) eller asynkron funktion (§15.15)
this
representerar variabeln den struct för vilken metoden eller accessorn anropades.- Om structen är en
readonly struct
beter sig variabelnthis
exakt samma som en indataparameter av structtypen - Annars beter sig variabeln
this
exakt samma som enref
parameter av typen struct
- Om structen är en
- Om metoden eller accessorn är en iterator eller asynkron funktion representerar variabeln
this
en kopia av den struct för vilken metoden eller accessorn anropades och fungerar exakt samma som en värdeparameter av structtypen.
- Om metoden eller accessorn inte är en iterator (§15.14) eller asynkron funktion (§15.15)
Användning av this
i en primary_expression i en annan kontext än de som anges ovan är ett kompileringsfel. I synnerhet är det inte möjligt att referera till this
i en statisk metod, en statisk egenskapsåtkomstor eller i en variable_initializer av en fältdeklaration.
12.8.15 Grundläggande åtkomst
En base_access består av nyckelordsbasen följt av antingen en ".
" token och en identifierare och 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 brödtexten för en instanskonstruktor, en instansmetod, en instansåtkomstor (§12.2.1) eller en finalizer. När base.I
inträffar i en klass eller struct ska jag ange en medlem av basklassen för den klassen eller struct. På samma sätt ska en tillämplig indexerare finnas i basklassen när base[E]
den inträffar i en klass.
Vid bindningstid utvärderas base_access uttryck i formuläret 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 inträffar. base.I
base[E]
Och motsvarar this.I
alltså och this[E]
, förutom this
visas som en instans av basklassen.
När en base_access refererar till en virtuell funktionsmedlem (en metod, egenskap eller indexerare) ändras bestämningen 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) för funktionsmedlemmen med avseende B
på (i stället för med avseende på körningstypen this
för , vilket skulle vara vanligt i en icke-basåtkomst). Inom en åsidosättning 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.
Obs! Till skillnad från
this
,base
är inte 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 decrementåtgärd ska vara ett uttryck som klassificeras som en variabel, en egenskapsåtkomst eller en indexerares å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 kompileringstidstypen dynamic
och följande regler tillämpas vid körning med hjälp av körningstypen för primary_expression.
Om operand för en postfix-inkrements- eller decrementåtgärd är en egenskaps- eller indexerareåtkomst, ska egenskapen eller indexeraren ha både en get- och en uppsättningsåtkomst. Om så inte är fallet uppstår ett bindningstidsfel.
Unary operator overload resolution (§12.4.4) tillämpas för att välja en specifik operatorimplementering. Fördefinierade ++
operatorer och --
operatorer finns för följande typer: sbyte
, byte
, short
, ushort
, int
, uint
, long
, ulong
, char
, float
, double
, och decimal
alla uppräkningstyper. De fördefinierade ++
operatorerna returnerar det värde som produceras genom att lägga 1
till operanden, och de fördefinierade --
operatorerna returnerar det värde som produceras 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 integrerad typ eller uppräkningstyp, genereras en System.OverflowException
.
Det ska ske 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 dementeringsåtgärd i formuläret x++
eller x--
består av följande steg:
- Om
x
klassificeras som en variabel:x
utvärderas för att skapa variabeln.- Värdet
x
för sparas. - Det sparade värdet
x
för 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 av
x
och lagras på den plats som angavs i den tidigare utvärderingen avx
. - Det sparade värdet
x
för blir resultatet av åtgärden.
- Om
x
klassificeras som en egenskap eller indexerares åtkomst:- Instansuttrycket (om
x
intestatic
) och argumentlistan (omx
är en indexerareåtkomst) som är associerad medx
utvärderas och resultaten används i efterföljande get- och set-åtkomstanrop. - Get-accessorn
x
för anropas och det returnerade värdet sparas. - Det sparade värdet
x
för 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 av
x
och den angivna accessornx
för anropas med det här värdet som värdeargument. - Det sparade värdet
x
för blir resultatet av åtgärden.
- Instansuttrycket (om
Operatorerna ++
och --
stöder även prefix notation (§12.9.6). Resultatet av x++
eller är värdet x
för före åtgärden, medan resultatet av ++x
eller --x
är värdet x
för efter x--
åtgärden. I båda fallen x
har sig själv samma värde efter åtgärden.
En operator ++
- eller operatorimplementering --
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 och anonyma 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 ombud används för att hämta instanser av ombudstyper.
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.
Obs! Delegera skapandeuttryck 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 det leda till att en befintlig delegatinstans återanvänds. slutkommentar
12.8.17.2 Objektskapandeuttryck
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
;
Typen av object_creation_expression ska vara en class_type, en value_type eller en type_parameter. 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 typen ä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.3) eller insamlingsinitiator (§12.8.17.4).
Om något av argumenten i den valfria argument_list har kompileringstidstypen dynamic
är object_creation_expression dynamiskt bunden (§12.3.3) och följande regler tillämpas vid körning med hjälp av körningstypen för argumenten i argument_list som har kompileringstidstypen dynamic
. Objektskapandet genomgår dock en begränsad kompileringstidskontroll enligt beskrivningen i §12.6.5.
Bindningstiden för en object_creation_expression av formuläret ny 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 ochA
inte finns:- Object_creation_expression är en standardkonstruktoranrop. Resultatet av object_creation_expression är ett värde av typen
T
, nämligen standardvärdet förT
enligt 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 ochA
inte finns:- Om ingen begränsning av värdetyp eller konstruktor (§15.2.5) har angetts för
T
uppstå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_type uppstå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ör A (§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 ny 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 klassen
T
allokeras. Om det inte finns tillräckligt med minne tillgängligt för att allokera den nya instansen genereras enSystem.OutOfMemoryException
och 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 klassen
- Om
T
är en struct_type:- En instans av typen
T
skapas 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.3 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 insamlingsinitiator. Det är inte möjligt för uttryck i objektinitieraren att referera till det nyligen skapade objektet som initieras.
En medlemsinitierare som anger ett uttryck efter att likhetstecknet bearbetas på samma sätt som en tilldelning (§12.21.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.4.
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
Point
kan 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 skapande och initiering av en
Rectangle
instans:public class Rectangle { public Point P1 { get; set; } public Point P2 { get; set; } }
En instans av
Rectangle
kan 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
,__p1
och__p2
är tillfälliga variabler som annars är osynliga och otillgängliga.Om
Rectangle
konstruktorn allokerar de två inbäddadePoint
instanserna kan de användas för att initiera de inbäddadePoint
instanserna 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 de inbäddade
Point
instanserna 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.4 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_list ',' expression
;
En insamlingsinitierare består av en sekvens av elementinitierare, omgivna av {
och }
token och avgränsade 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 elementinitierare med ett enda uttryck kan skrivas utan klammerparenteser, men kan då inte vara ett tilldelningsuttryck, för att undvika tvetydighet med medlemsinitierare. Den non_assignment_expression produktionen definieras i §12.22.
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 överbelastningsmatchning 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
på varje elementinitierare.
Exempel: Följande visar en klass som representerar en kontakt med ett namn och en lista med telefonnummer samt skapandet och initieringen 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
,__c1
och__c2
är tillfälliga variabler som annars är osynliga och otillgängliga.slutexempel
12.8.17.5 Matrisskapande uttryck
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
new int[10,20]
matrisen genererar en matrisinstans av typenint[,]
, och det nyaint[10][,]
matrisskapande uttrycket genererar en matrisinstans av typenint[][,]
. slutexempel
Varje uttryck i uttryckslistan ska vara av typen int
, uint
, long
eller ulong
eller implicit konvertibelt 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 (§23.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, utan bestäms som den vanligaste typen (§12.6.3.15) av uppsättningen uttryck i matrisinitieraren. För en flerdimensionell matris, 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.
Matrisinitierare 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.OverflowException
och 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.OutOfMemoryException
och inga ytterligare steg körs. - Alla element i den nya matrisinstansen initieras till deras standardvärden (§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: Instruktionen
int[][] a = new int[100][];
skapar en endimensionell matris med 100 element av typen
int[]
. Det initiala värdet för varje element ärnull
. Det går inte att skapa samma matrisuttryck även genom att instansiera undermatriserna och -instruktionenint[][] a = new int[100][5]; // Error
resulterar 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, dvs. när alla undermatriser 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" }; // Error
Det sista uttrycket orsakar ett kompileringsfel eftersom varken
int
ellerstring
är implicit konvertibelt till det andra, så det finns ingen vanlig typ. Ett explicit skrivet matrisskapandeuttryck måste användas i det här fallet, till exempel ange vilken typ 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.7) 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.6 Delegera skapandeuttryck
En delegate_creation_expression används för att hämta en instans av en delegate_type.
delegate_creation_expression
: 'new' delegate_type '(' expression ')'
;
Argumentet för ett uttryck för skapande av ombud 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 metodtexten för ombudsmålet. Om argumentet är ett värde identifierar det en delegerad instans som du kan skapa en kopia av.
Om uttrycket har kompileringstidstypen dynamic
tillämpas delegate_creation_expression dynamiskt (§12.8.17.6) 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 uttrycket för att skapa ombud på samma sätt som en metodgruppkonvertering (§10.8) frånE
tillD
.Om
E
är en anonym funktion bearbetas uttrycket för att skapa ombud på samma sätt som en anonym funktionskonvertering (§10.7) frånE
tillD
.Om
E
är ett värde skaE
vara kompatibelt (§20.2) medD
, och resultatet är en referens till ett nyligen skapat ombud med en lista över anrop med en enda post som anroparE
.
Körningsbearbetningen 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 utvärderas uttrycket för att skapa ombud som en metodgruppkonvertering (§10.8) frånE
tillD
. - Om
E
är en anonym funktion utvärderas skapandet av ombudet som en anonym funktionskonvertering frånE
tillD
(§10.7). - Om
E
är ett värde för en delegate_type:E
utvärderas. Om den här utvärderingen orsakar ett undantag körs inga ytterligare steg.- Om värdet
E
för ärnull
genereras enSystem.NullReferenceException
och inga ytterligare steg körs. - En ny instans av ombudstypen
D
allokeras. Om det inte finns tillräckligt med minne tillgängligt för att allokera den nya instansen genereras enSystem.OutOfMemoryException
och inga ytterligare steg körs. - Den nya delegatinstansen initieras med en anropslista med en enda post 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, ett nytt delegatresultat. Inget befintligt ombud har ändrat sitt innehåll. slutkommentar
Det går inte att skapa ett ombud som refererar till en egenskap, indexerare, användardefinierad operator, instanskonstruktor, finalizer eller statisk konstruktor.
Exempel: Enligt beskrivningen 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.f
initieras med ett ombud som refererar till den andraSquare
metoden eftersom den metoden exakt matchar parameterlistan och returnerar typen avDoubleFunc
. Om den andraSquare
metoden inte hade funnits skulle ett kompileringsfel ha uppstått.slutexempel
12.8.17.7 Anonyma objektskapandeuttryck
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
. Medlemmar av en anonym typ är en sekvens med skrivskyddade egenskaper som härleds från den anonyma objektinitieraren som används 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 metoden Equals
genereras automatiskt av kompilatorn och kan inte refereras 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
p1
ochp2
är av samma anonyma typ.slutexempel
Metoderna Equals
och GetHashcode
för anonyma typer åsidosätter de metoder som ärvts från object
och definieras i termer av Equals
egenskaperna och GetHashcode
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 null villkorlig projektion initializer §12.8.8 eller en basåtkomst (§12.8.15). Detta kallas projektioninitierare 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:
«identifer» = «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.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 '::' identifier generic_dimension_specifier?
| unbound_type_name '.' 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 för det här formuläret ä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 alltid typeof(T) == typeof(T)
sant. Typen får inte vara dynamic
.
Den andra formen av typeof_expression består av ett typeof
nyckelord följt av en parenteserad unbound_type_name.
Obs! En unbound_type_name liknar en type_name (§7.8) förutom att en unbound_type_name innehåller generic_dimension_specifierdär en type_name innehåller type_argument_lists. slutkommentar
När operand för en typeof_expression är en sekvens av token som uppfyller grammatikerna i både unbound_type_name och type_name, nämligen när den varken innehåller en generic_dimension_specifier eller en type_argument_list, anses sekvensen av token vara en type_name. Innebörden av en unbound_type_name bestäms på följande sätt:
- Konvertera sekvensen med token till en type_name genom att ersätta varje generic_dimension_specifier med en type_argument_list med samma antal kommatecken och nyckelordet
object
som varje type_argument. - Utvärdera den resulterande type_name, samtidigt som du ignorerar alla typparameterbegränsningar.
- Den unbound_type_name matchar den obundna generiska typen som är associerad med den resulterande konstruerade typen (§8.4).
Det är ett fel när typnamnet ska vara en nullbar referenstyp.
Resultatet av typeof_expression är objektet System.Type
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 alla typer.
Obs! Det här specialobjektet
System.Type
är användbart i klassbibliotek som tillåter reflektion till metoder på 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 den körningstyp som var bunden till typparametern. Om körningstypen är en referenstyp som kan ogiltigförklaras blir resultatet motsvarande referenstyp som inte går att nolla. Operatorn typeof
kan också användas på en konstruerad typ eller en obunden allmän typ (§8.4.4). Objektet System.Type
för en obundna generisk typ är inte detsamma som System.Type
objektet av instanstypen (§15.3.2). Instanstypen är alltid en sluten konstruktionstyp vid körning, så objektet System.Type
beror på vilka argument av körningstyp 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
int
ochSystem.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 en operand till sizeof ska vara en unmanaged_type (§8.8).
sizeof_expression
: 'sizeof' '(' unmanaged_type ')'
;
För vissa fördefinierade typer ger operatorn sizeof
ett konstant int
värde enligt tabellen nedan:
Expression | 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 är lika med storleken på dess underliggande typ, enligt ovan. För alla andra operandtyper anges operatorn sizeof
i §23.6.9.
12.8.20 De markerade och okontrollerade operatorerna
Operatorerna checked
och unchecked
används för att styra överflödeskontrollkontexten för aritmetiska åtgärder av typen integraltyp och konverteringar.
checked_expression
: 'checked' '(' expression ')'
;
unchecked_expression
: 'unchecked' '(' expression ')'
;
Operatorn checked
utvärderar det inneslutna uttrycket i en kontrollerad kontext och operatorn unchecked
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 den angivna overflow-kontrollkontexten.
Kontrollkontexten för spill kan också styras via instruktionen checked
och unchecked
(§13.12).
Följande åtgärder påverkas av den överflödeskontrollkontext som har upprättats av de markerade och okontrollerade operatorerna och -instruktionerna:
- De fördefinierade
++
operatorerna och--
operatorerna (§12.8.16 och §12.9.6), 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.10), 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
float
ellerdouble
till 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
checked
sammanhang är ett konstant uttryck (§12.23) uppstår ett kompileringsfel. Annars genereras enSystem.OverflowException
när åtgärden utförs vid körning. - I ett
unchecked
sammanhang trunkeras resultatet genom att alla högordningsbitar som inte får plats i måltypen ignoreras.
För icke-konstanta uttryck (§12.23) (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.23) (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 kompileringstidsfel.
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
F
genererar metoden enSystem.OverflowException
, ochG
metoden returnerar –727379968 (de lägre 32 bitarna i resultatet som ligger utom intervallet). Metodens beteendeH
beror på standardkontexten för spillkontroll för kompilering, men den är antingen samma somF
eller 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 spill som uppstår när du utvärderar konstanta uttryck i
F
ochH
orsakar kompileringstidsfel som rapporteras eftersom uttrycken utvärderas i enchecked
kontext. Ett spill uppstår också när du utvärderar det konstanta uttrycket iG
, men eftersom utvärderingen sker i enunchecked
kontext rapporteras inte spillet.slutexempel
Operatorerna checked
och unchecked
påverkar endast overflow-kontrollkontexten för de åtgärder som finns i text i tokenerna "(
" 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
checked
i F påverkar inte utvärderingen avx * y
iMultiply
, såx * y
utvärderas i standardöverflödeskontrollkontexten.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örint
intervallet, utan operatornunchecked
, skulle gjutningarna tillint
generera kompileringsfel.slutexempel
Obs! Operatorerna
checked
ochunchecked
-instruktionerna gö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 multiplicerande av två flyttal aldrig i ett undantag vid spill även inom en explicit kontrollerad konstruktion. 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
: explictly_typed_default
| default_literal
;
explictly_typed_default
: 'default' '(' type ')'
;
default_literal
: 'default'
;
Ett 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 explictly_typed_default eller måltypen för konverteringen för en default_value_expression.
Ett default_value_expression är ett konstant uttryck (§12.23) 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: , , , , , , ,
long
,ulong
,char
,float
,double
, ,decimal
,bool,
ellerint
uint
ushort
short
byte
sbyte
- någon uppräkningstyp.
12.8.22 Stack-allokering
Ett stackallokeringsuttryck allokerar ett minnesblock från körningsstacken. 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.
De säkra kontextreglerna för ett stackallokeringsuttryck beskrivs i §16.4.12.7.
stackalloc_expression
: 'stackalloc' unmanaged_type '[' expression ']'
| 'stackalloc' unmanaged_type? '[' constant_expression? ']'
stackalloc_initializer
;
stackalloc_initializer
: '{' stackalloc_initializer_element_list '}'
;
stackalloc_initializer_element_list
: stackalloc_element_initializer (',' stackalloc_element_initializer)* ','?
;
stackalloc_element_initializer
: expression
;
En stackalloc_expression tillåts endast i två kontexter:
- Initieringsuttrycket ,
E
av en local_variable_declaration (§13.6.2); och - Det högra operanduttrycket, , av en enkel tilldelning (§12.21.2) som sig själv uppstår som en expression_statement (§13.7)
E
I båda sammanhangen tillåts stackalloc_expression endast ske som:
- Hela
E
; eller - Understödja och/eller tredje operander av en conditional_expression (§12.18) som är sig själv helheten av
E
.
Unmanaged_type (§8.8) anger vilken typ av objekt som ska lagras på den nyligen allokerade platsen, och uttrycket anger antalet objekt. Tillsammans anger dessa den nödvändiga allokeringsstorleken. Uttryckets typ 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.
Vid körningen 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 ett 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.15) för uppsättningen med stackalloc_element_initializers.
- Om constant_expression utelämnas härleds det till antalet stackalloc_element_initializers.
- Om constant_expression finns skall det vara lika med antalet stackalloc_element_initializers.
Varje stackalloc_element_initializer ska ha en implicit konvertering till unmanaged_type (§10.2). Stackalloc_element_initializer initierar 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.
Resultatet av en stackalloc_expression är en instans av typen Span<T>
, där T
är unmanaged_type:
Span<T>
(§C.3) är en referens struct typ (§16.2.3), som presenterar ett block av minne, här blocket allokeras av stackalloc_expression, som en indexerbar samling av typade (T
) objekt.- Resultatets
Length
egenskap returnerar antalet allokerade objekt. - Resultatets indexerare (§15.9) returnerar en variable_reference (§9.5) till ett objekt i det allokerade blocket och är intervallkontrollerat.
Obs! Vid inträffar i osäker kod kan resultatet av en stackalloc_expression vara av en annan typ, se (§23.9). slutkommentar
Stackallokeringsinitierare tillåts inte i catch
eller block (§13.11finally
).
Obs! Det finns inget sätt att uttryckligen frigöra minne som allokerats med .
stackalloc
slutkommentar
Alla stackallokerade minnesblock som skapades under körningen av en funktionsmedlem ignoreras automatiskt när funktionsmedlemmen returnerar.
Förutom operatorn stackalloc
innehå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
span8
,stackalloc
resulterar i enSpan<int>
, som konverteras av en implicit operator tillReadOnlySpan<int>
. På samma sätt konverteras resultatetSpan<double>
förspan9
för 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
det 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 av kompatibilitetsskäl behandlas uttrycket 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 där 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 för en named_entity_target att ha typen dynamic
.
Ett nameof_expression är ett konstant uttryck av typen string
och har ingen effekt vid körning. Mer specifikt utvärderas inte dess named_entity och ignoreras i syfte att få en bestämd 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 formatting_characters 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
nameof
uttryck, förutsatt att en allmän typList<T>
deklareras inomSystem.Collections.Generic
namnområdet: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
nameof(System.Collections.Generic)
på 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
Ett anonymous_method_expression är ett av två sätt att definiera en anonym funktion. Dessa beskrivs ytterligare i §12.19.
12.9 Unary operatorer
12.9.1 Allmänt
Operatorerna +
, , -
( !
logisk negation §12.9.4 ), ~
, ++
, --
, cast och await
operatorer kallas unary operatorer.
Obs! Postfixet null-forgiving-operatorn (§12.8.9),
!
, på grund av dess kompileringstid och icke-överbelastningsbara endast karaktär, undantas från ovanstående lista. slutkommentar
unary_expression
: primary_expression
| '+' unary_expression
| '-' unary_expression
| logical_negation_operator unary_expression
| '~' unary_expression
| pre_increment_expression
| pre_decrement_expression
| cast_expression
| await_expression
| pointer_indirection_expression // unsafe code support
| addressof_expression // unsafe code support
;
pointer_indirection_expression (§23.6.2) och addressof_expression (§23.6.5) är endast tillgängliga i osäker kod (§23).
Om operand för en unary_expression har kompileringstidstypen dynamic
är den dynamiskt bunden (§12.3.3). I det här fallet är dynamic
kompileringstidstypen för unary_expression , och lösningen som beskrivs nedan sker vid körning med hjälp av körningstypen för operand.
12.9.2 Unary plus operator
För en åtgärd i formuläret +x
tillämpas unary operator overload resolution (§12.4.4) för att välja en specifik operatorimplementering. Operand konverteras till parametertypen för den valda operatorn och typen av resultat är operatorns returtyp. De fördefinierade odefinierade 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.
Lyfts (§12.4.8) bildar av de unlifted fördefinierade unary plus operatörerna som definieras ovan, är också fördefinierade.
12.9.3 Unary minus operator
För en åtgärd i formuläret –x
tillämpas unary operator overload resolution (§12.4.4) för att välja en specifik operatorimplementering. Operand konverteras till parametertypen för den valda operatorn och typen av resultat är operatorns returtyp. De fördefinierade unary minus-operatorerna är:
Heltals negation:
int operator –(int x); long operator –(long x);
Resultatet beräknas genom att subtrahera
X
från noll. Om värdera avX
är det minsta representable värderar av operandtypen (−2³¹ förint
eller −2⁶³ förlong
), därefter är den matematiska negationen avX
inte representable inom operandtypen. Om detta inträffar inom enchecked
kontext genereras enSystem.OverflowException
. Om det inträffar inom enunchecked
kontext är resultatet operandvärdet och spillet rapporteras inte.Om negationsoperatorns operand är av typen
uint
konverteras den till typlong
och resultatets typ ärlong
. Ett undantag är regeln som tillåterint
att värdet−2147483648
(−2³¹) skrivs som en decimal heltalsliteral (§6.4.5.3).Om negationsoperatorns operand är av typen
ulong
uppstår ett kompileringsfel. Ett undantag är regeln som tillåterlong
att värdet−9223372036854775808
(−2⁶³) skrivs som en decimal heltal (§6.4.5.3)Flyttal negation:
float operator –(float x); double operator –(double x);
Resultatet är värdet för
X
med dess tecken inverterat. Omx
ärNaN
är resultatet ocksåNaN
.Decimal negation:
decimal operator –(decimal x);
Resultatet beräknas genom att subtrahera
X
från noll. Decimal negation motsvarar att använda den unary minus operatorn av typenSystem.Decimal
.
Lyfts (§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 åtgärd i formuläret !x
tillämpas unary operator overload resolution (§12.4.4) för att välja en specifik operatorimplementering. 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 den logiska negationen av operand: Om operand är true
är false
resultatet . Om operand är false
är true
resultatet .
Lyfts (§12.4.8) bildar av den unlifted fördefinierade logiska negationoperatorn som definieras ovan, är också fördefinierade.
Obs! Prefixet logisk negation och postfix null-förlåtande operatorer (§12.8.9), medan de representeras av samma lexikala token (!
), är distinkta. slutkommentar
12.9.5 Bitvis komplementoperator
För en åtgärd i formuläret ~x
tillämpas unary operator overload resolution (§12.4.4) för att välja en specifik operatorimplementering. 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 bitvis komplement av x
.
Varje uppräkningstyp E
tillhandahåller implicit följande bitvis komplementoperator:
E operator ~(E x);
Resultatet av utvärderingen , där X
är ett uttryck för en uppräkningstyp E
med en underliggande typ U
, är exakt detsamma som att (E)(~(U)x)
utvärdera , förutom att konverteringen till E
alltid utförs som i ett unchecked
sammanhang (§12.8.20).~x
Lyfts (§12.4.8) bildar av de unlifted fördefinierade bitwisekompletteringoperatorerna som definieras ovan, är också fördefinierade.
12.9.6 Inkrements- och minskningsoperatorer för prefix
pre_increment_expression
: '++' unary_expression
;
pre_decrement_expression
: '--' unary_expression
;
Operand för en prefix-inkrements- eller decrementåtgärd ska vara ett uttryck som klassificeras som en variabel, en egenskapsåtkomst eller en indexerares åtkomst. Resultatet av åtgärden är ett värde av samma typ som operanden.
Om operand för en prefix-inkrements- eller decrementåtgärd är en egenskaps- eller indexerareåtkomst, ska egenskapen eller indexeraren ha både en get- och en uppsättningsåtkomst. Om så inte är fallet uppstår ett bindningstidsfel.
Unary operator overload resolution (§12.4.4) tillämpas för att välja en specifik operatorimplementering. Fördefinierade ++
operatorer och --
operatorer finns för följande typer: sbyte
, byte
, short
, ushort
, int
, uint
, long
, ulong
, char
, float
, double
, och decimal
alla uppräkningstyper. De fördefinierade ++
operatorerna returnerar det värde som produceras genom att lägga 1
till operanden, och de fördefinierade --
operatorerna returnerar det värde som produceras genom att subtrahera 1
från operanden. I ett checked
sammanhang, 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 ske 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 ett prefix för inkrement eller minskning av formuläret ++x
eller --x
består av följande steg:
- Om
x
klassificeras som en variabel:x
utvärderas för att skapa variabeln.- Värdet
x
för 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
. Det resulterande värdet lagras på den plats som anges av utvärderingen avx
. - och blir resultatet av åtgärden.
- Om
x
klassificeras som en egenskap eller indexerares åtkomst:- Instansuttrycket (om
x
intestatic
) och argumentlistan (omx
är en indexerareåtkomst) som är associerad medx
utvärderas och resultaten används i efterföljande get- och set-åtkomstanrop. - Get-accessorn
x
för anropas. - 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-åtkomstornx
för anropas med det här värdet som värdeargument. - Det här värdet blir också resultatet av åtgärden.
- Instansuttrycket (om
Operatorerna ++
och --
stöder även postfix notation (§12.8.16). Resultatet av x++
eller är värdet x
för före åtgärden, medan resultatet av ++x
eller --x
är värdet x
x--
för efter åtgärden. I båda fallen x
har sig själv samma värde efter åtgärden.
En operator ++
- eller operatorimplementering --
kan anropas med antingen postfix eller prefix notation. Det går inte att ha separata operatorimplementeringar för de två notationerna.
Lyfts (§12.4.8) bildar av de unlifted predefined prefixet inkrement och decrementoperatorer som definieras ovan, är också fördefinierade.
12.9.7 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 formuläret (T)E
, där T
är en typ och E
är en unary_expression, utför en explicit konvertering (§10.3) av värdet E
för att skriva T
. Om det inte finns någon explicit konvertering från E
till T
inträ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
det anger en variabel.
Grammatiken för en cast_expression leder till vissa syntaktiska tvetydigheter.
Exempel: Uttrycket
(x)–y
kan antingen tolkas som en cast_expression (en uppsättning–y
av typenx
) 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 av token är korrekt grammatik för en typ, och token omedelbart efter de avslutande parenteserna är token "
~
", token "!
", token "(
", en identifierare (§6.4.3), en literal (§6.4.5) eller nyckelord (§6.4.4) förutomas
ochis
.
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
x
ochy
är identifierare är detx.y
korrekt grammatik för en typ, även omx.y
det faktiskt inte anger en typ. slutexempel
Obs! Från disambiguationsregeln följer det att om
x
ochy
är identifierare,(x)y
,(x)(y)
och(x)(-y)
är cast_expressions, men(x)-y
inte är det, även omx
identifierar en typ. Men omx
är ett nyckelord som identifierar en fördefinierad typ (till exempelint
), är alla fyra formulären cast_expressions (eftersom ett sådant nyckelord inte kan vara ett uttryck av sig självt). slutkommentar
12.9.8 Vänta på uttryck
12.9.8.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.15). Inom den närmaste omslutande asynkrona funktionen ska en await_expression inte ske på följande platser:
- Inuti en kapslad (icke-asynkron) anonym funktion
- Inuti blocket i en lock_statement
- I en anonym funktionskonvertering till en uttrycksträdstyp (§10.7.3)
- I ett osäkert sammanhang
Obs! En await_expression kan inte förekomma på de flesta platser inom en query_expression, eftersom de omvandlas syntaktiskt till att använda lambda-uttryck som inte är asynkrona. slutkommentar
I en asynkron funktion await
ska inte användas som en available_identifier även om den ordagranna identifieraren @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 await
fungerar som en normal identifierare.
Operand för en await_expression kallas uppgiften. Den representerar en asynkron åtgärd som kanske eller kanske inte är slutförd vid den tidpunkt då await_expression utvärderas. Syftet med operatorn await
är att avbryta körningen av den omslutande asynkrona funktionen tills den väntande uppgiften är klar och sedan få resultatet.
12.9.8.2 Väntande uttryck
Uppgiften för en await_expression måste vara väntande. Ett uttryck t
är väntande om något av följande gäller:
t
är av kompileringstidstypdynamic
t
har en tillgänglig instans eller tilläggsmetod som anropasGetAwaiter
utan parametrar och inga typparametrar och en returtypA
som alla följande undantag för:A
implementerar gränssnittetSystem.Runtime.CompilerServices.INotifyCompletion
(kallas därefterINotifyCompletion
för korthet)A
har en tillgänglig, läsbar instansegenskapIsCompleted
av typenbool
A
har en tillgänglig instansmetodGetResult
utan parametrar och inga typparametrar
Syftet med GetAwaiter
metoden är att hämta en awaiter för uppgiften. Typen A
kallas för awaiter-typen för await-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 INotifyCompletion.OnCompleted
metoden är att registrera en "fortsättning" för uppgiften, dvs. ett ombud (av typen System.Action
) som anropas när uppgiften är klar.
Syftet med GetResult
metoden är att få resultatet av uppgiften när den är klar. Det här resultatet kan slutföras, eventuellt med ett resultatvärde, eller så kan det vara ett undantag som genereras av GetResult
metoden.
12.9.8.3 Klassificering av inväntningsuttryck
Uttrycket await t
klassificeras på samma sätt som uttrycket (t).GetAwaiter().GetResult()
. Om returtypen är GetResult
void
klassificeras därför await_expression som ingenting. Om den har en typ som intevoid
returneras klassificeras await_expression som ett värde av typen T
.T
12.9.8.4 Körningsutvärdering av await-uttryck
Vid körning utvärderas uttrycket await t
enligt följande:
- En awaiter
a
hämtas genom att utvärdera uttrycket(t).GetAwaiter()
. - A
bool
b
erhålls genom att utvärdera uttrycket(a).IsCompleted
. - Om
b
ärfalse
beror utvärderingen på oma
implementerar gränssnittetSystem.Runtime.CompilerServices.ICriticalNotifyCompletion
(kallas därefterICriticalNotifyCompletion
för korthet). Den här kontrollen görs vid bindningstillfället. d.v.s. vid körning oma
har kompileringstidstypendynamic
, och vid kompileringstillfället annars. Låtr
oss ange återtagandedelegaten (§15.15):- Om
a
inte implementerarICriticalNotifyCompletion
utvärderas uttrycket((a) as INotifyCompletion).OnCompleted(r)
. - Om
a
implementerarICriticalNotifyCompletion
utvä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
var ), eller vid senare anrop av återtagandedelegaten (omb
varfalse
), utvärderas uttrycket(a).GetResult()
true
. Om det returnerar ett värde är det värdet resultatet av await_expression. Annars är resultatet ingenting.
En awaiter implementerar gränssnittsmetoderna INotifyCompletion.OnCompleted
och ICriticalNotifyCompletion.UnsafeOnCompleted
bör göra så att ombudet r
anropas högst en gång. I annat fall är beteendet för den omslutande asynkrona funktionen odefinierat.
12.10 Aritmetiska operatorer
12.10.1 Allmänt
Operatorerna *
, /
, %
, +
och -
kallas för aritmetiska operatorer.
multiplicative_expression
: unary_expression
| multiplicative_expression '*' unary_expression
| multiplicative_expression '/' unary_expression
| multiplicative_expression '%' unary_expression
;
additive_expression
: multiplicative_expression
| additive_expression '+' multiplicative_expression
| additive_expression '-' multiplicative_expression
;
Om en operand av en aritmetikoperator har kompileringstidstypen dynamic
är uttrycket dynamiskt bundet (§12.3.3). I det här fallet är dynamic
kompileringstidstypen för uttrycket , och den lösning som beskrivs nedan sker vid körning med hjälp av körningstypen för de operander som har kompileringstypen dynamic
.
12.10.2 Multiplikationsoperator
För en åtgärd i formuläret x * y
tillä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
.
Heltals multiplikation:
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 produkten i ett
checked
sammanhang ligger utanför resultattypens intervall genereras enSystem.OverflowException
. I ettunchecked
sammanhang rapporteras inte spill och några betydande högordningsbitar utanför resultattypens intervall ignoreras.Multiplikation med flyttals:
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
x
ochy
är positiva ändliga värden.z
är resultatet av , avrundatx * y
till närmaste representerande värde. Om resultatets omfattning är för stor för måltypenz
är oändligheten. På grund av avrundningz
kan vara noll även om varkenx
ellery
ä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.10.2–§12.10.6 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 typenSystem.Decimal
.
Lyfts (§12.4.8) bildar av de unlifted fördefinierade multiplikationsoperatorerna som definieras ovan, är också fördefinierade.
12.10.3 Divisionsoperatör
För en åtgärd i formuläret x / y
tillä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 x
för 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 den minsta representable
int
ellerlong
värde och den högra operand är–1
, uppstår ett spill. I ettchecked
sammanhang leder detta till att enSystem.ArithmeticException
(eller en underklass därav) genereras. I ettunchecked
sammanhang är det implementeringsdefinierat om huruvida enSystem.ArithmeticException
(eller en underklass därav) genereras eller om spillet inte rapporteras med det resulterande värdet som den vänstra operandens.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
x
ochy
är positiva ändliga värden.z
är resultatet av , avrundatx / y
till närmaste representerande värde.+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
Decimaldelning:
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 enSystem.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örx
mindre skalan föry
.Decimaldelning motsvarar att använda divisionsoperatorn av typen
System.Decimal
.
Lyfts (§12.4.8) bildar av de unlifted fördefinierade uppdelningoperatorerna som definieras ovan, är också fördefinierade.
12.10.4 Restoperator
För en åtgärd i formuläret x % y
tillä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 avx – (x / y) * y
. Omy
är noll utlöses enSystem.DivideByZeroException
.Om den vänstra operanden är det minsta
int
värdet ellerlong
värdet och den högra operanden är–1
, genereras enSystem.OverflowException
om och endast omx / y
skulle 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
x
ochy
är positiva ändliga värden.z
är resultatet avx % y
och beräknas somx – n * y
, där n är det största möjliga heltalet som är mindre än eller lika medx / y
. Den här metoden för att beräkna resten motsvarar den som används för heltalsoperor, men skiljer sig från IEC 60559-definitionen (därn
är heltalet närmastx / 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 enSystem.ArithmeticException
(eller en underklass därav) genereras. Ett genomförande i enlighet med detta får inte utlösa ett undantag förx % y
i vilket fall som helst närx / y
ett undantag inte utlöser något 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örx
.Decimaler motsvarar att använda restoperatorn av typen
System.Decimal
.Obs! Dessa regler säkerställer att resultatet för alla typer aldrig har motsatt tecken på den vänstra operanden. slutkommentar
Lyfts (§12.4.8) bildar av de unlifted fördefinierade restoperatorerna som definieras ovan, är också fördefinierade.
12.10.5 Additionsoperator
För en åtgärd i formuläret x + y
tillä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 string
sammanfogar 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
Om summan i ett
checked
sammanhang ligger utanför resultattypens intervall genereras enSystem.OverflowException
. I ettunchecked
sammanhang rapporteras inte spill och några betydande högordningsbitar utanför resultattypens intervall ignoreras.Flyttalstillägg:
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
x
, ochy
är ickezero ändliga värden, ochz
är resultatet avx + y
. Omx
ochy
har samma storlek men motsatta tecken,z
är positiv noll. Omx + y
är för stort för att representera i måltypen ärz
en oändlighet med samma tecken somx + 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, ochU
är den underliggande typen avE
: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 binäroperatorn
+
utför strängsammanfogning. Om en operand av strängsammanfogning ärnull
ersätts en tom sträng. Annars konverteras alla icke-operanderstring
till dess strängrepresentation genom att anropa den virtuellaToString
metoden som ärvts från typenobject
. OmToString
returnerarnull
ersä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 amerikansk-engelskt 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
string
som består av tecknen i den vänstra operanden följt av tecknen i den högra operanden. Strängsammanfogningsoperatorn returnerar aldrig ettnull
värde. EnSystem.OutOfMemoryException
kan genereras om det inte finns tillräckligt med minne tillgängligt för att allokera den resulterande strängen.Delegera kombination. Varje ombudstyp tillhandahåller implicit följande fördefinierade operator, där
D
är ombudstypen: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ånull
är ). Om den andra operanden annars ärnull
ä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.10.6 och §20.6. Eftersom
System.Delegate
inte är en ombudstyp definieras inte operatorn + för den. slutkommentar
Lyfts (§12.4.8) bildar av de unlifted fördefinierade tilläggoperatorerna som definieras ovan, är också fördefinierade.
12.10.6 Subtraktionsoperator
För en åtgärd i formuläret x – y
tillä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 y
alla från x
.
Heltalsundertraktion:
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 skillnaden i ett
checked
sammanhang ligger utanför resultattypens intervall genereras enSystem.OverflowException
. I ettunchecked
sammanhang rapporteras inte spill och några betydande högordningsbitar utanför resultattypens intervall ignoreras.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
x
, ochy
är ickezero ändliga värden, ochz
är resultatet avx – y
. Omx
ochy
är lika med,z
är positiv noll. Omx – y
är för stort för att representera i måltypen ärz
en oändlighet med samma tecken somx – 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 posterna
-y
negationen avy
, inte att värdet är negativt.)Decimal subtraktion:
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 uppräkningstyp tillhandahåller implicit följande fördefinierade operator, där
E
är uppräkningstypen, ochU
är den underliggande typen avE
: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ärdenax
för ochy
, och typen av resultat ä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.Delegera borttagning. Varje ombudstyp tillhandahåller implicit följande fördefinierade operator, där
D
är ombudstypen:D operator –(D x, D y);
Semantiken är följande:
- Om den första operanden är
null
ärnull
resultatet av åtgärden . - 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 (§20.2).
- Om listorna jämför lika, enligt vad som bestäms av ombudets likhetsoperator (§12.12.9), blir
null
resultatet 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 åtgärden värdet för den vänstra operanden.
- Om listorna jämför lika, enligt vad som bestäms av ombudets likhetsoperator (§12.12.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
Lyfts (§12.4.8) bildar av de unlifted fördefinierade subtraktionsoperatorerna som definieras ovan, är också fördefinierade.
12.11 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 dynamic
kompileringstidstypen för uttrycket , och den lösning som beskrivs nedan sker vid körning med hjälp av körningstypen för de operander som har kompileringstypen dynamic
.
För en åtgärd i formuläret x << count
eller x >> count
tillä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
<<
skiftarx
åt vänster av ett antal bitar som beräknas enligt beskrivningen nedan.Högordningsbitarna utanför resultattypens
x
intervall ignoreras, 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
>>
skiftarx
åt höger efter ett antal bitar som beräknas enligt beskrivningen nedan.När
x
är av typenint
ellerlong
ignorerasx
bitarna med låg ordning, de återstående bitarna flyttas åt höger och de tomma bitpositionerna i hög ordning är inställda på noll omx
de inte är negativa och ställs in på ett omx
det är negativt.När
x
är av typenuint
ellerulong
ignoreras de låga bitarna ix
, 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
ärint
elleruint
anges skiftantalet av fem bitar med låg ordning icount
. Med andra ord beräknas skiftantalet fråncount & 0x1F
. - När typen av
x
ärlong
ellerulong
anges skiftantalet av den låga ordningen sex bitar avcount
. Med andra ord beräknas skiftantalet fråncount & 0x3F
.
Om det resulterande skiftantalet är noll returnerar skiftoperatorerna helt enkelt värdet x
för .
Skiftåtgärder orsakar aldrig spill och ger samma resultat i markerade och avmarkerade kontexter.
När operatorns >>
vänstra operande är av en signerad integraltyp utför operatorn ett aritmetiskt skift till höger där värdet för den viktigaste biten (teckenbiten) för operanden sprids till de tomma bitpositionerna i hög ordning. När operatorns >>
vänstra operande är av en osignerad integraltyp utför operatorn ett logiskt skift 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 skifträtt till höger omx
. slutexempel
Lyfts (§12.4.8) bildar av de unlifted fördefinierade skiftoperatorerna som definieras ovan, är också fördefinierade.
12.12 Relations- och typtestningsoperatorer
12.12.1 Allmänt
Operatorerna ==
, !=
, <
, >
, >=
<=
, is
och as
kallas för relations- och typtestningsoperatorerna.
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
;
Obs! Sökning efter rätt operand för operatorn
is
måste först testas som en typ och sedan som ett uttryck som kan sträcka sig över flera token. Om operanden är en expreesion måste mönsteruttrycket ha företräde minst så högt som shift_expression. slutkommentar
Operatören beskrivs i §12.12.12 och operatören as
beskrivs i §12.12.13.is
Operatorerna ==
, !=
, <
, <=
>
och >=
är jämförelseoperatorer.
Om en default_literal (§12.8.21) används som operand för en <
, >
, <=
eller >=
operator uppstår ett kompileringsfel.
Om en default_literal används som båda operanderna för en ==
eller !=
-operatorn uppstår ett kompileringsfel. Om en default_literal används som vänster operand för is
operatorn eller as
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 dynamic
kompileringstidstypen för uttrycket , och den lösning som beskrivs nedan sker vid körning med hjälp av körningstypen för de operander som har kompileringstidstypen dynamic
.
För en åtgärd i formuläret x «op» y
, där «op» är en jämförelseoperator, tillämpas ö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. Om båda operanderna i en equality_expression är literalen null
utförs inte överlagringsupplösningen och uttrycket utvärderas till ett konstant värde av true
eller false
beroende på om operatorn är ==
eller !=
.
De fördefinierade jämförelseoperatorerna beskrivs i följande underklienter. Alla fördefinierade jämförelseoperatorer returnerar ett resultat av typen bool, enligt beskrivningen i följande tabell.
Åtgärd | Resultat |
---|---|
x == y |
true om x är lika med y , false annars |
x != y |
true om x inte är lika med y , false annars |
x < y |
true om x är mindre än y , false annars |
x > y |
true om x är större än y , false annars |
x <= y |
true om x är mindre än eller lika med y , false annars |
x >= y |
true om x är större än eller lika med y , false annars |
12.12.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) bildar av de unlifted fördefinierade heltaljämförelseoperatorerna som definieras ovan, är också fördefinierade.
12.12.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 false
resultatet för alla operatorer utom !=
, för vilka resultatet är true
. För två operander x != y
ger alltid samma resultat som !(x == y)
. Men när en eller båda operanderna är NaN, <
ger operatorerna , >
, <=
och inte >=
samma resultat som den logiska negationen för den motsatta operatorn.
Exempel: Om något av
x
ochy
är NaN ärfalse
detx < y
, 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.
Lyfts (§12.4.8) bildar av de unlifted fördefinierade flyttalsjämförelseoperatorerna som definieras ovan, är också fördefinierade.
12.12.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å decimalopernderna 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
.
Lyfts (§12.4.8) bildar av de unlifted fördefinierade decimaljämförelseoperatorerna som definieras ovan, är också fördefinierade.
12.12.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 false
resultatet .
Resultatet av !=
är false
om både x
och y
är true
eller om både x
och y
är false
. Annars blir true
resultatet . När operanderna är av typen bool
genererar operatorn !=
samma resultat som operatorn ^
.
Lyfts (§12.4.8) bildar av de unlifted fördefinierade booleska likhetoperatorerna som definieras ovan, är också fördefinierade.
12.12.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 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 ((U)x) «op» ((U)y)
utvärdera . Med andra ord jämför jämförelseoperatorerna för uppräkningstypen helt enkelt de underliggande integralvärdena för de två operanderna.
Lyfts (§12.4.8) bildar av de unlifted fördefinierade uppräkningjämförelseoperatorerna som definieras ovan, är också fördefinierade.
12.12.7 Likhetsoperatorer för referenstyp
Varje klasstyp C
tillhandahåller implicit följande fördefinierade likhetsoperatorer för 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 är båda null
, medan operator !=
returnerar true
om och endast om operator ==
med samma operander skulle returnera false
.
Förutom normala tillämplighetsregler (§12.6.4.2) kräver de fördefinierade likhetsoperatorerna av referenstyp 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 för att vara en reference_type eller literalen
null
. Dessutom finns det en identitet eller explicit referenskonvertering (§10.3.5) från antingen operande till den andra operandens typ. - Den ena operanden är literalen
null
, och den andra operanden är ett värde av typenT
därT
är en type_parameter som inte är känd för att vara en värdetyp och som inte har begränsningen för värdetyp.- Om vid körningen
T
är en värdetyp som inte kan nollas ärfalse
resultatet och==
resultatet av!=
ärtrue
. - Om vid körning
T
är en nullbar värdetyp beräknas resultatet frånHasValue
operandens egenskap, enligt beskrivningen i (§12.12.10). - Om vid körning
T
är en referenstyp blirtrue
resultatet om operand ärnull
, ochfalse
annars.
- Om vid körningen
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 operatorerna för referenstypjämlikhet 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 åtgärd av formuläret
x == y
ellerx != y
, om det finns någon tillämplig användardefinieradoperator ==
elleroperator !=
finns, väljer operatorns regler för överbelastningsmatchning (§12.4.5) den operatorn i stället för den fördefinierade likhetsoperatorn för referenstyp. Det är alltid möjligt att välja den fördefinierade referenstypjämlikhetsoperatorn genom att uttryckligen gjuta en eller båda operanderna för att skrivaobject
.slutkommentar
Exempel: I följande exempel kontrolleras om ett argument av en icke-tränad typparametertyp är
null
.class C<T> { void F(T x) { if (x == null) { throw new ArgumentNullException(); } ... } }
Konstruktionen
x == null
tillåts även omT
den kan representera en värdetyp som inte kan nollställas, och resultatet definieras helt enkelt somfalse
närT
är en värdetyp som inte kan nulleras.slutexempel
För en åtgärd av formuläret x == y
eller x != y
, om någon tillämplig operator ==
eller operator !=
finns, väljer operatörens överbelastningsmatchning (§12.4.5) den operatorn i stället för den fördefinierade referenstypjämlikhetsoperatorn.
Obs! Det är alltid möjligt att välja den fördefinierade referenstypsjämlikhetsoperatorn genom att uttryckligen gjuta båda operanderna för att skriva
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 False
Variablerna
s
ocht
refererar till två distinkta stränginstanser som innehåller samma tecken. Den första jämförelsen utdata eftersom den fördefinierade strängjämlikhetsoperatornTrue
(§12.12.8) väljs när båda operanderna är av typenstring
. Återstående jämförelser av alla utdataFalse
eftersom överbelastningenstring
avoperator ==
i typen inte är tillämplig när 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); } }
utdata
False
eftersom avgjutningarna skapar referenser till två separata instanser av boxadeint
värden.slutexempel
12.12.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-referenser
null
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.12.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.12.9 Delegera likhetsoperatorer
De fördefinierade operatorerna för ombudsjämlikhet är:
bool operator ==(System.Delegate x, System.Delegate y);
bool operator !=(System.Delegate x, System.Delegate y);
Två delegatinstanser anses vara lika med följande:
- Om någon av de delegerade instanserna är
null
är de lika om och endast om båda ärnull
. - Om ombuden har olika körningstyp är de aldrig lika.
- Om båda de delegerade instanserna har en anropslista (§20.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 tagits fram från utvärderingen av semantiskt identiska anonyma funktioner (§12.19) 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 §20 i stället System.Delegate
för , och det inte finns någon identitetskonvertering mellan operandtyperna av bindningstyp, uppstår ett bindningsfel.
Obs! Den här regeln förhindrar jämförelser som aldrig kan betrakta icke-värden
null
som lika på grund av referenser till instanser av olika typer av ombud. slutkommentar
12.12.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 literalen null
, även om det inte finns någon fördefinierad eller användardefinierad operator (i obelyftad eller hävd form) för åtgärden.
För en åtgärd av ett av formulären
x == null null == x x != null null != x
där x
är ett uttryck av en nullbar värdetyp, om operatorns överlagringsmatchning (§12.4.5) inte kan hitta en tillämplig operator, beräknas resultatet i stället från HasValue
egenskapen x
. Mer specifikt översätts de två första formulären till !x.HasValue
, och de två sista formulären översätts till x.HasValue
.
12.12.11 Tuppelns likhetsoperatorer
Tuppelns likhetsoperatorer tillämpas parvis på elementen i tuppelns operander i lexikal ordning.
Om varje operande x
och y
en eller !=
en ==
operator klassificeras antingen som en tuppeln eller som ett värde med tuppelns typ (§8.3.11), är operatorn en tuppelns likhetsoperator.
Om en operande e
klassificeras som en tupplar ska elementen e1...en
vara resultatet av utvärderingen av elementuttrycken i tuppelns uttryck. Om e
inte är ett värde av tuppelns typ ska elementen vara t.Item1...t.Itemn
där t
är resultatet av utvärderingen e
.
Operanderna och y
en tupplars likhetsoperator x
ska ha samma araitet, eller så uppstår ett kompileringstidsfel. För varje elementpar xi
och yi
ska samma likhetsoperator tillämpas och ge ett resultat av typen bool
, dynamic
, en typ som har en implicit konvertering till bool
eller en typ som definierar operatorerna true
och false
.
Operatorn x == y
för tuppelns likhet utvärderas på följande sätt:
- Den vänstra operanden
x
utvärderas. - Operand
y
på höger sida utvärderas. - För varje elementpar
xi
ochyi
i lexikal ordning:- Operatorn
xi == yi
utvärderas och ett resultat av typenbool
erhålls på följande sätt:- Om jämförelsen gav ett
bool
så är det resultatet. - Annars om jämförelsen gav en
dynamic
anropas operatornfalse
dynamiskt på den, och det resulterandebool
värdet negeras med den logiska negationsoperatorn (!
). - Om jämförelsetypen annars har en implicit konvertering till
bool
tillämpas konverteringen. - Om jämförelsetypen annars har en operator
false
anropas operatorn och det resulterandebool
värdet negeras med den logiska negationsoperatorn (!
).
- Om jämförelsen gav ett
- Om resultatet
bool
ärfalse
, sker ingen ytterligare utvärdering och resultatet av tuppelns likhetsoperator ärfalse
.
- Operatorn
- Om alla elementjämförelser ger
true
är resultatet av tuppelns likhetsoperatortrue
.
Operatorn x != y
för tuppelns likhet utvärderas på följande sätt:
- Den vänstra operanden
x
utvärderas. - Operand
y
på höger sida utvärderas. - För varje elementpar
xi
ochyi
i lexikal ordning:- Operatorn
xi != yi
utvärderas och ett resultat av typenbool
erhålls på följande sätt:- Om jämförelsen gav ett
bool
så är det resultatet. - Om jämförelsen annars gav en
dynamic
anropas operatorntrue
dynamiskt på den, och det resulterandebool
värdet är resultatet. - Om jämförelsetypen annars har en implicit konvertering till
bool
tillämpas konverteringen. - Om jämförelsetypen annars har en operator
true
anropas operatorn och det resulterandebool
värdet är resultatet.
- Om jämförelsen gav ett
- Om resultatet
bool
ärtrue
, sker ingen ytterligare utvärdering och resultatet av tuppelns likhetsoperator ärtrue
.
- Operatorn
- Om alla elementjämförelser ger
false
är resultatet av tuppelns likhetsoperatorfalse
.
12.12.12 Is-operatorn
Det finns två former av operatorn is
. Den ena är operatorn is-type, som har en typ till höger. Den andra är is-pattern-operatorn, som har ett mönster till höger.
12.12.12.1 Operatorn är av typen
Operatorn is-type används för att kontrollera om körningstypen för ett objekt ä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 annan typ än dynamic
, är ett booleskt värde som anger om E
är icke-null och kan konverteras till typ T
av en referenskonvertering, en boxningskonvertering, en konvertering som inte är null, en omslutningskonvertering eller en omskrivningskonvertering.
Åtgärden utvärderas på följande sätt:
- Om
E
är en anonym funktion eller metodgrupp uppstår ett kompileringsfel - Om
E
är literalennull
, eller om värdetE
för ärnull
, ärfalse
resultatet . - Annars:
- Låt vara
R
körningstypenE
. - Låt
D
oss härledas frånR
följande: - Om
R
är en nullbar värdetypD
är den underliggande typen avR
. - Annars
D
ärR
. - Resultatet beror på
D
ochT
på följande sätt: - Om
T
är en referenstyp blirtrue
resultatet om:- det finns en identitetskonvertering mellan
D
ochT
, D
är en referenstyp och en implicit referenskonvertering frånD
tillT
finns, eller- Antingen:
D
är en värdetyp och en boxningskonvertering frånD
tillT
finns.
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 blirtrue
resultatet omD
är den underliggande typen avT
. - Om
T
är en värdetyp som inte kan null-värdet ärtrue
resultatet omD
ochT
är av samma typ. - Annars blir
false
resultatet .
Användardefinierade konverteringar beaktas inte av operatorn is
.
Obs! Eftersom operatorn
is
utvärderas vid körning har alla typargument ersatts och det finns inga öppna typer (§8.4.3) att överväga. slutkommentar
Obs! Operatorn
is
kan förstås i termer av kompileringstidstyper och konverteringar på följande sätt, därC
är kompileringstidstypenE
för :
- Om kompileringstidstypen
e
är samma somT
, eller om en implicit referenskonvertering (§10.2.8), boxningskonvertering (§10.2.9), omslutningskonvertering (§10.6) eller en explicit omskrivningskonvertering (§10.6) finns från kompileringstidstypenE
tillT
:
- Om
C
är av en värdetyp som inte kan nullvärde ärtrue
resultatet av åtgärden .- Annars motsvarar resultatet av åtgärden att utvärdera
E != null
.- Om en explicit referenskonvertering (§10.3.5) eller omboxningskonvertering (§10.3.7) finns från
C
tillT
, eller omC
ellerT
är en öppen typ (§8.4.3), ska körningskontroller enligt ovan vara peformerade.- Annars är ingen referens, boxning, omslutning eller omskrivningskonvertering av
E
typenT
möjlig, och resultatet av åtgärden ärfalse
. En kompilator kan implementera optimeringar baserat på kompileringstidstypen.slutkommentar
12.12.12.2 Is-pattern-operatorn
Is-pattern-operatorn används för att kontrollera om värdet som beräknas av ett uttryck matchar ett givet 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 i formuläret E is P
, där E
är ett relationsuttryck av typen T
och P
är ett mönster, är det ett kompileringsfel om något av följande undantag:
E
anger inget värde eller har ingen typ.- Mönstret
P
är inte tillämpligt (§11.2) på typenT
.
12.12.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 cast-uttryck (§12.9.7) genererar operatorn as
aldrig ett undantag. Om den angivna konverteringen inte är möjlig är null
det resulterande värdet i stället .
I en åtgärd i formuläret E as T
E
ska vara ett uttryck och T
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 från
E
tillT
. - Typen av
E
ellerT
är en öppen typ. E
är literalennull
.
Om kompileringstidstypen E
inte dynamic
är genererar å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. Kompilatorn kan förväntas optimera E as T
för att utföra högst en körningstypkontroll i motsats till de två körningstypkontrollerna som antyds i expansionen ovan.
Om kompileringstidstypen E
är dynamic
, till skillnad från gjutningsoperatorn, är operatorn as
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
T
G
för är känd för att vara en referenstyp, eftersom den har klassbegränsningen. TypparameternU
H
för är dock inte tillåten. Därför är det inte tillåtet att använda operatornas
iH
.slutexempel
12.13 Logiska operatorer
12.13.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 dynamic
kompileringstidstypen för uttrycket , och den lösning som beskrivs nedan sker vid körning med hjälp av körningstypen för de operander som har kompileringstidstypen dynamic
.
För en åtgärd i formuläret x «op» y
, där «op» är en av de logiska operatorerna, tillämpas ö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 logiska operatorerna beskrivs i följande underklienter.
12.13.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);
Operatorn &
beräknar det bitvis logiska OCH för de två operanderna, operatorn |
beräknar den bitvis logiska OR:t för de två operanderna och ^
operatorn beräknar den bitvis logiska exklusiva OR för de två operanderna. Inga spill är möjliga från dessa åtgärder.
Hissade (§12.4.8) former av de olyftade fördefinierade heltals logiska operatorerna som definieras ovan är också fördefinierade.
12.13.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 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 de logiska operatorerna, är exakt samma som att (E)((U)x «op» (U)y)
utvärdera . Med andra ord utför uppräkningstypen logiska operatorer helt enkelt den logiska åtgärden på den underliggande typen av de två operanderna.
Lyfts (§12.4.8) bildar av de unlifted fördefinierade uppräkning logiska operatorerna som definieras ovan, är också fördefinierade.
12.13.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 false
resultatet .
Resultatet av x | y
är true
om antingen x
eller y
är true
. Annars blir false
resultatet .
Resultatet av är om är och är , eller x
är false
och y
är true
.false
true
y
x
true
x ^ y
Annars blir false
resultatet . När operanderna är av typen bool
beräknar operatorn ^
samma resultat som operatorn !=
.
12.13.5 Nullbar boolesk och | Operatörer
Den booleska typen bool?
som kan vara null kan representera tre värden, true
, false
och null
.
Precis som med de andra binära operatorerna är även de upplyfta formerna av de logiska operatorerna &
och |
(§12.13.4) fördefinierade:
bool? operator &(bool? x, bool? y);
bool? operator |(bool? x, bool? y);
Semantiken för de lyfte &
operatorerna och |
operatorerna 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 |
Obs! Typen
bool?
liknar den trevärdestyp som används för booleska uttryck i SQL. Tabellen ovan följer samma semantik som SQL, medan tillämpningen av reglerna i §12.4.8 på operatörerna&
och|
inte skulle göra det. Reglerna i §12.4.8 tillhandahåller redan SQL-liknande semantik för den lyftade^
operatorn. slutkommentar
12.14 Villkorsstyrda logiska operatorer
12.14.1 Allmänt
Operatorerna &&
och ||
kallas för villkorsstyrda logiska operatorer. De kallas också logiska operatorer för "kortslutning".
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 && y
motsvarar åtgärdenx & y
, förutom atty
endast utvärderas omx
intefalse
är . - Åtgärden
x || y
motsvarar åtgärdenx | y
, förutom atty
endast utvärderas omx
intetrue
är .
Obs! Anledningen till att kortslutning använder villkoren "inte sant" och "inte falskt" är att göra det möjligt för användardefinierade villkorsoperatorer att definiera när kortslutning gäller. Användardefinierade typer kan vara i ett tillstånd där
operator true
returnerarfalse
ochoperator false
returnerarfalse
. I dessa fall skulle varken&&
eller||
skulle kortslutning. 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 dynamic
kompileringstidstypen för uttrycket , och den lösning som beskrivs nedan sker vid körning med hjälp av körningstypen för de operander som har kompileringstidstypen dynamic
.
En åtgärd av formuläret x && y
eller x || y
bearbetas genom att tillämpa överbelastningsmatchning (§12.4.5) som om åtgärden var skriven x & y
eller x | y
. Sedan kan du
- Om det inte går att hitta en enda bästa operator, eller om överlagringsupplösningen väljer en av de fördefinierade heltals logiska operatorerna eller nullbara booleska logiska operatorer (§12.13.5), uppstår ett bindningstidsfel.
- Annars, om den valda operatorn är en av de fördefinierade booleska logiska operatorerna (§12.13.4), bearbetas åtgärden enligt beskrivningen i §12.14.2.
- I annat fall är den valda operatorn en användardefinierad operatör och åtgärden bearbetas enligt beskrivningen i §12.14.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.14.3.
12.14.2 Booleska villkorsstyrda logiska operatorer
När operanderna av &&
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 enligt följande:
- Åtgärden
x && y
utvärderas somx ? y : false
. Med andra ordx
utvärderas och konverteras först till typenbool
. Sedan, omx
ärtrue
,y
utvärderas och konverteras till typbool
, och detta blir resultatet av åtgärden. Annars ärfalse
resultatet av åtgärden . - Åtgärden
x || y
utvärderas somx ? true : y
. Med andra ordx
utvärderas och konverteras först till typenbool
.x
Om ärtrue
ärtrue
sedan resultatet av åtgärden . Annarsy
utvärderas och konverteras till typenbool
, och detta blir resultatet av åtgärden.
12.14.3 Användardefinierade villkorsstyrda logiska operatorer
När operander av &&
eller ||
är av typer som deklarerar en tillämplig användardefinierad operator &
eller operator |
, ska båda av 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 typenT
, och returnera ett resultat av typenT
. T
skall innehålla deklarationer avoperator true
ochoperator false
.
Ett bindningstidsfel uppstår om något av dessa krav inte uppfylls. Annars &&
utvärderas åtgärden eller ||
genom att kombinera den användardefinierade operator true
eller operator false
med den valda användardefinierade operatorn:
- Åtgärden utvärderas som
T.false(x) ? x : T.&(x, y)
, därT.false(x)
är ett anrop av denoperator false
deklarerade iT
, ochT.&(x, y)
är ett anrop för den valdaoperator &
.x && y
Med andra ordx
utvärderas först ochoperator false
anropas 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
. Annarsy
utvärderas och det valdaoperator &
anropas på det värde som tidigare beräknats förx
och värdet som beräknas för föry
att generera resultatet av åtgärden. - Åtgärden utvärderas som
T.true(x) ? x : T.|(x, y)
, därT.true(x)
är ett anrop av denoperator true
deklarerade iT
, ochT.|(x, y)
är ett anrop för den valdaoperator |
.x || y
Med andra ordx
utvärderas först ochoperator true
anropas på resultatet för att avgöra omx
är definitivt sant.x
Om är definitivt sant är resultatet av åtgärden sedan värdet som tidigare beräknats förx
. Annarsy
utvärderas och det valdaoperator |
anropas på det värde som tidigare beräknats förx
och värdet som beräknas för föry
att generera resultatet av åtgärden.
I någon av dessa åtgärder utvärderas uttrycket som ges av x
bara en gång, och uttrycket som anges av y
utvärderas eller utvärderas antingen inte exakt en gång.
12.15 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 i formuläret a ?? b
, om a
är icke-null
, är a
resultatet ; annars är b
resultatet . Åtgärden utvärderar b
endast 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 för formuläret
a ?? b ?? c
utvärderas som en?? (b ?? c)
. I allmänna termer returnerar ett uttryck för formuläretE1 ?? E2 ?? ... ?? EN
den första av operanderna som intenull
är ellernull
om alla operander ärnull
. slutexempel
Typen av uttryck a ?? b
beror på vilka implicita konverteringar som är tillgängliga på operanderna. I prioritetsordning är typen , , eller B
, där A
är typen av a
(tillhandahålls som a
har en typ), B
typen av b
(tillhandahålls som b
har en typ) och A₀
är den underliggande typen av A
om A
är en nullbar värdetyp, eller A
på annat sätt. A
a ?? b
A₀
a ?? b
Mer specifikt bearbetas följande:
- Om
A
det finns och inte är en nullbar värdetyp eller en referenstyp uppstår ett kompileringsfel. - Annars, om
A
det finns ochb
är ett dynamiskt uttryck, ärdynamic
resultattypen . Vid körninga
utvärderas först. Oma
intenull
konverteras ,a
konverteras tilldynamic
och detta blir resultatet. Annarsb
utvärderas och detta blir resultatet. - Annars, om
A
finns och är en nullbar värdetyp och en implicit konvertering finns frånb
tillA₀
, ärA₀
resultattypen . Vid körninga
utvärderas först. Oma
är intenull
,a
packas upp för att skrivaA₀
, och detta blir resultatet. Annarsb
utvärderas och konverteras till typA₀
, och detta blir resultatet. - Annars, om
A
finns och en implicit konvertering finns frånb
tillA
, ärA
resultattypen . Vid körning utvärderas en först. Om en inte är null blir ett resultat. Annarsb
utvärderas och konverteras till typA
, och detta blir resultatet. - Annars, om
A
finns och är en nullbar värdetyp,b
har en typB
och en implicit konvertering finns frånA₀
tillB
, ärB
resultattypen . Vid körninga
utvärderas först. Oma
är intenull
,a
packas upp för att skrivaA₀
och konverteras till typB
, och detta blir resultatet. Annarsb
utvärderas och blir resultatet. - Annars, om
b
har en typB
och en implicit konvertering finns fråna
tillB
, ärB
resultattypen . Vid körninga
utvärderas först. Oma
intenull
konverteras ,a
konverteras till typB
, och detta blir resultatet. Annarsb
utvärderas och blir resultatet.
Annars a
b
och är inkompatibla och a
kompileringsfel inträffar.
12.16 Operatorn för utkastsuttryck
throw_expression
: 'throw' null_coalescing_expression
;
En throw_expression genererar värdet som skapas genom att utvärdera null_coalescing_expression. Uttrycket ska implicit konverteras till System.Exception
, och resultatet av utvärderingen av uttrycket konverteras till System.Exception
innan det utlöses. Beteendet vid körning av utvärderingen av ett kastuttryck är detsamma som angetts för ett kastuttryck (§13.10.6).
En throw_expression har ingen typ. En throw_expression konverteras till varje typ av implicit kastkonvertering.
Ett utkastsuttryck ska endast förekomma i följande syntaktiska kontexter:
- Som den andra eller tredje operanden för en ternary-villkorsoperator (
?:
). - Som den andra operanden för en null-sammanskolningsoperator (
??
). - Som kroppen av en uttrycksfyllig lambda eller medlem.
12.17 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 det enkelt att 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
out
argument_value i en argument_list. - Som ett enkelt kasserande
_
som består av vänster sida av en enkel tilldelning (§12.21.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.
Obs! Det innebär att ett deklarationsuttryck inte kan parenteseras. slutkommentar
Det är ett fel för en implicit typvariabel som deklarerats med en declaration_expression som ska refereras inom argument_list där den deklareras.
Det är ett fel för en variabel som deklarerats med en declaration_expression som ska refereras i dekonstruktionstilldelningen där den inträffar.
Ett deklarationsuttryck som är ett enkelt ignorerande eller där local_variable_type är identifieraren var
klassificeras som en implicit typvariabel. 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 vänster sida av en enkel tilldelning är den härledda typen av variabel typen av den högra sidan av tilldelningen.
- I en tuple_expression till vänster om en enkel tilldelning är den härledda typen av variabel typen av motsvarande tuppeln på höger sida (efter dekonstruktion) av tilldelningen.
I annat fall klassificeras deklarationsuttrycket som en explicit typ av variabel, och uttryckets typ samt den deklarerade variabeln ska vara den som anges av local_variable_type.
Ett deklarationsuttryck med identifieraren _
är ett ignorerande (§9.2.9.1) och ger 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 visar
s1
både explicita och implicit inskrivna deklarationsuttryck. Den härledda typen avb1
berorbool
på att det är typen av motsvarande utdataparameter iM1
. Den efterföljandeWriteLine
kan komma åti1
ochb1
, som har introducerats i omfånget för omslutning.Deklarationen visar
s2
ett försök att användai2
i det kapslade anropet tillM
, som inte tillåts, eftersom referensen inträffar i argumentlistan däri2
deklarerades. Å andra sidan tillåts referensen tillb2
i det slutliga argumentet, eftersom den inträffar efter slutet av den kapslade argumentlista därb2
deklarerades.Deklarationen visar
s3
användningen av både implicita och explicit inskrivna deklarationsuttryck som ignoreras. Eftersom ignoreranden inte deklarerar en namngiven variabel tillåts flera förekomster av identifieraren_
.(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 _); }
I de här exemplen visas användningen av
var _
för att ange en implicit typad ignorera när_
den inte är tillgänglig, eftersom den anger en variabel i omfånget omslutande.slutexempel
12.18 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.16) tillåts inte i en villkorsoperator om ref
det finns.
Ett villkorsuttryck för formuläret b ? x : y
utvärderar först villkoret b
. Sedan, om b
är true
, x
utvärderas och blir resultatet av åtgärden. Annars y
utvärderas och blir resultatet av åtgärden. 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 för formuläret
a ? b : c ? d : e
utvärderas soma ? b : (c ? d : e)
. slutexempel
Operatorns ?:
första operande ska vara ett uttryck som implicit kan konverteras till bool
, eller 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_references, och typen av resultat kan vara någon av typerna. Om någon av typerna är
dynamic
föredrar typinferensdynamic
(§8.7). Om någon av typerna är en tupplar (§8.3.11) innehåller typinferens elementnamnen när elementnamnen i samma ordningsposition matchar i båda tupplar. - Resultatet är en variabelreferens som kan skrivas om båda variable_referenceär skrivbara.
Obs! När
ref
finns returnerar conditional_expression en variabelreferens som kan tilldelas till en referensvariabel med operatorn= ref
eller skickas som en referens-/indata-/utdataparameter. slutkommentar
Om ref
inte finns styr den andra och tredje operanderna och x
y
, för ?:
operatorn typen av villkorsuttryck:
- Om
x
har typX
ochy
har typY
då,- Om det finns en identitetskonvertering mellan
X
ochY
är resultatet den vanligaste typen av uttryck (§12.6.3.15). Om någon av typerna ärdynamic
föredrar typinferensdynamic
(§8.7). Om någon av typerna är en tupplar (§8.3.11) innehåller typinferens elementnamnen när elementnamnen i samma ordningsposition matchar i båda tupplar. - Annars, om en implicit konvertering (§10.2) finns från
X
tillY
, men inte frånY
tillX
,Y
så är typen av villkorsuttryck. - Om det annars finns en implicit uppräkningskonvertering (§10.2.4) från
X
tillY
är detY
typen av villkorsuttryck. - Om det annars finns en implicit uppräkningskonvertering (§10.2.4) från
Y
tillX
är detX
typen av villkorsuttryck. - Annars, om en implicit konvertering (§10.2) finns från
Y
tillX
, men inte frånX
tillY
,X
så är typen av villkorsuttryck. - Annars kan ingen uttryckstyp fastställas och ett kompileringsfel inträffar.
- Om det finns en identitetskonvertering mellan
- Om bara en av
x
ochy
har en typ, och bådex
ochy
implicit kan konverteras till den typen, så är det typen av villkorsuttryck. - Annars kan ingen uttryckstyp fastställas och ett kompileringsfel inträffar.
Körningsbearbetningen av ett ref-villkorsuttryck i formuläret b ? ref x : ref y
består av följande steg:
b
Först utvärderas ochbool
värdet för bestämsb
:- Om en implicit konvertering från typen till
b
bool
finns utförs den implicita konverteringen för att skapa ettbool
värde. - Annars anropas den
operator true
som definieras av typen förb
för att skapa ettbool
värde.
- Om en implicit konvertering från typen till
- Om värdet
bool
som skapas av steget ovan ärtrue
utvärderas ochx
den resulterande variabelreferensen blir resultatet av villkorsuttrycket. - Annars
y
utvärderas och den resulterande variabelreferensen blir resultatet av villkorsuttrycket.
Körningsbearbetningen av ett villkorsuttryck i formuläret b ? x : y
består av följande steg:
b
Först utvärderas ochbool
värdet för bestämsb
:- Om en implicit konvertering från typen till
b
bool
finns utförs den implicita konverteringen för att skapa ettbool
värde. - Annars anropas den
operator true
som definieras av typen förb
för att skapa ettbool
värde.
- Om en implicit konvertering från typen till
- Om värdet
bool
som skapas av steget ovan ärtrue
x
utvärderas och konverteras till typen av villkorsuttryck, och detta blir resultatet av villkorsuttrycket. - Annars
y
utvärderas och konverteras till typen av villkorsuttryck, och detta blir resultatet av villkorsuttrycket.
12.19 Anonyma funktionsuttryck
12.19.1 Allmänt
En anonym funktion är ett uttryck som representerar en "in-line"-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 ombudsvärde som refererar till den metod 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 varianter 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 uttrycksalternativ är tillämpliga, ska 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 uttryck är en syntaktisk form som
x?.M()
ett fel om resultattypenM
ärvoid
(§12.8.13). Men när den behandlas som en null_conditional_invocation_expression tillåts resultattypen varavoid
. slutkommentar
Exempel: Resultattypen
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.15.
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 ombudstyp 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 ett ombud med en parameterlista som inte innehåller utdataparametrar.
En blocktext av 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 omitted
slutexempel
Beteendet för lambda_expressions och anonymous_method_expressions är detsamma förutom följande punkter:
- anonymous_method_expression tillåter att parameterlistan utelämnas helt och hållet, vilket ger konverteringsbarhet för att delegera typer av valfri lista med värdeparametrar.
- lambda_expression s tillå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_expressionhar konverteringar till kompatibla uttrycksträdstyper (§8.6).
12.19.2 Anonyma funktionssignaturer
Anonymous_function_signature för en anonym funktion definierar namnen och eventuellt typerna av parametrarna för den anonyma funktionen. Omfånget för parametrarna för den anonyma funktionen är anonymous_function_body (§7.7). Tillsammans med parameterlistan (om den ges) utgör anonym-metod-brödtexten ett deklarationsutrymme (§7.3). Det är därför ett kompileringsfel för namnet på en parameter för den anonyma funktionen för att matcha namnet på en lokal variabel, lokal konstant eller parameter vars omfång omfattar anonymous_method_expression eller lambda_expression.
Om en anonym funktion har en explicit_anonymous_function_signature begränsas uppsättningen med kompatibla ombudstyper och uttrycksträdstyper till de 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_signature begrä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.19.3 Anonyma funktionskroppar
Brödtexten (uttrycket eller blocket) 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 brödtexten att komma åt en parameter av en
ref struct
typ. - När typen av
this
är en struct-typ är det ett kompileringsfel för brödtexten att komma åtthis
. Detta gäller om åtkomsten är explicit (som ithis.x
) eller implicit (som i varx
är en instansmedlem ix
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.19.6) för den anonyma funktionen. Åtkomsten till en yttre variabel refererar till instansen av variabeln som är aktiv vid den tidpunkt då lambda_expression eller anonymous_method_expression utvärderas (§12.19.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 för en innesluten anonym funktion. - En
return
instruktion i brödtexten returnerar kontroll från ett anrop av närmaste omslutande anonyma funktion, 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. Kompilatorn kan i synnerhet 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.19.4 Överbelastningsupplösning
Anonyma funktioner i en argumentlista deltar i typinferens och överlagringsmatchning. 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åSum
metoder. Var och en tar ettselector
argument som extraherar värdet som ska summeras från ett listobjekt. Det extraherade värdet kan vara antingen enint
eller endouble
och den resulterande summan är på samma sätt antingen enint
eller endouble
.Metoderna
Sum
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( ... ) { ... } }
I den första anropet av
orderDetails.Sum
gäller bådaSum
metoderna eftersom den anonyma funktionend => d.UnitCount
är kompatibel med bådeFunc<Detail,int>
ochFunc<Detail,double>
. Överlagringsmatchningen 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.Sum
gäller endast den andraSum
metoden eftersom den anonyma funktionend => d.UnitPrice * d.UnitCount
genererar ett värde av typendouble
. Överbelastningsupplösningen väljer därför den andraSum
metoden för det anropet.slutexempel
12.19.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.19.6 Yttre variabler
12.19.6.1 Allmänt
Alla lokala variabler, värdeparametrar eller parametermatriser vars omfång omfattar lambda_expression eller anonymous_method_expression kallas för en yttre variabel för 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.19.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). 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
x
registreras av den anonyma funktionen och livslängdenx
för utökas åtminstone tills ombudet som returneras frånF
blir berättigad till skräpinsamling. Eftersom varje anrop av den anonyma funktionen fungerar på samma instans avx
är utdata från exemplet:1 2 3
slutexempel
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 (§23.4), utan anses i stället vara en flyttbar variabel. Insamlade yttre variabler kan dock inte användas i en fixed
instruktion (§23.7), så adressen för en infångade yttre variabel kan inte tas.
Obs! Till skillnad från en inkapslade variabel kan en insamlad lokal variabel samtidigt exponeras för flera körningstrådar. slutkommentar
12.19.6.3 Instansiering av lokala variabler
En lokal variabel anses vara instansierad när körningen anger variabelns omfång.
Exempel: När följande metod anropas instansieras och initieras den lokala variabeln
x
tre 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
x
utanfö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 instansation 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 5
Men när deklarationen av
x
flyttas 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 5
Observera att kompilatorn 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 3
slutexempel
Det är möjligt för anonyma funktionsdelegater att dela vissa insamlade variabler men ändå ha separata instanser av andra.
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 ombuden samlar in samma instans av
x
men separata instanser avy
, och utdata är:1 1 2 1 3 1
slutexempel
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 10
slutexempel
12.19.7 Utvärdering av anonyma funktionsuttryck
En anonym funktion F
ska alltid konverteras till en ombudstyp D
eller en uttrycksträdstyp E
, antingen direkt eller genom körningen av ett uttryck new D(F)
för att skapa ombud . Denna konvertering avgör resultatet av den anonyma funktionen enligt beskrivningen i §10.7.
12.19.8 Implementeringsexempel
Den här underklienten är informativ.
Den här delklienten beskriver en möjlig implementering av anonyma funktionskonverteringar när det gäller 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 den här underklienten innehåller 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
av 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 this
i :
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.19.6.3) motsvarar sedan att skapa en instans av den kompilatorgenererade klassen och att komma åt 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ängder:
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
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 "lyfte" lokala variablerna kan delas mellan ombud och uttrycksträd.
Slut på informativ text.
12.20 Frågeuttryck
12.20.1 Allmänt
Frågeuttryck ger 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_clauses? select_or_group_clause query_continuation?
;
query_body_clauses
: query_body_clause
| query_body_clauses query_body_clause
;
query_body_clause
: from_clause
| let_clause
| where_clause
| join_clause
| join_into_clause
| orderby_clause
;
let_clause
: 'let' identifier '=' expression
;
where_clause
: 'where' boolean_expression
;
join_clause
: 'join' type? identifier 'in' expression 'on' expression
'equals' expression
;
join_into_clause
: 'join' type? identifier 'in' expression 'on' expression
'equals' expression 'into' identifier
;
orderby_clause
: 'orderby' orderings
;
orderings
: ordering (',' ordering)*
;
ordering
: expression ordering_direction?
;
ordering_direction
: 'ascending'
| 'descending'
;
select_or_group_clause
: select_clause
| group_clause
;
select_clause
: 'select' expression
;
group_clause
: 'group' expression 'by' expression
;
query_continuation
: 'into' identifier query_body
;
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
eller join
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 för en annan sekvens, vilket ger matchande par. Varje orderby
sats ordnar om objekt enligt angivna villkor. select
Slutsatsen eller group
-satsen anger resultatets form i termer av 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.20.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
och select
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. För detta ändamål är ett frågeuttryck alla uttryck som börjar med "from
identifierare" följt av alla token utom ";
", "=
" eller ",
".
12.20.3 Översättning av frågeuttryck
12.20.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.20.4). Mer specifikt översätts frågeuttryck till anrop av metoder med namnet Where
, , Select
, SelectMany
Join
, GroupJoin
, , OrderBy
, OrderByDescending
, ThenBy
ThenByDescending
, , GroupBy
och Cast
. Dessa metoder förväntas ha särskilda signaturer och returtyper enligt beskrivningen i §12.20.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 körningen 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 matar in intervallvariabler med transparenta identifierare som anges av *. Dessa beskrivs ytterligare i §12.20.3.8.
12.20.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 vars
customers. GroupBy(c => c.Country). Select(g => new { Country = g.Key, CustCount = g.Count() })
slutexempel
12.20.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 c
den 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.20.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.20.3.6 och §12.20.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
Select
källan. Det är sedan upp till implementerarna förSelect
och andra frågeoperatorer att se till att dessa metoder aldrig returnerar själva källobjektet. slutkommentar
12.20.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 sats from
:
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 sats from
:
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 customersh 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
x
ochy
är kompilatorgenererade identifierare som annars är osynliga och otillgängliga.slutexempel
En orderby
sats och dess föregående sats from
:
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 i stället ett anrop av OrderByDescending
eller ThenByDescending
.
Exempel: Exemplet
from o in orders orderby o.Customer.Name, o.Total descending select o
har 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
eller orderby
join
-satser och inte mer än en inledande from
sats i varje frågeuttryck.
12.20.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.20.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.20.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 identifierare inträffar som en parameter i en anonym funktion, är medlemmarna av den associerade anonyma typen automatiskt i omfånget i brödtexten i den anonyma funktionen.
- När en medlem med en transparent identifierare finns i omfånget finns även medlemmarna i den medlemmen i omfånget.
- När en transparent identifierare inträffar som en medlemsdeklarator i en anonym objektinitierare 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 översätts ytterligare 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 ytterligare reduceras 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
x
ochy
är kompilatorgenererade identifierare som annars är osynliga och otillgängliga. slutexempel
12.20.4 Frågeuttrycksmönstret
Frågeuttrycksmönstret etablerar ett mönster med metoder 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 av följande klassdefinition. Medlemmarna och tillgängliga extensonmetoder kallas för "form" av en generisk 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.
Obs! Den rekommenderade relationen mellan
C<T>
ochO<T>
som säkerställer attThenBy
metoderna ochThenByDescending
endast är tillgängliga på resultatet av enOrderBy
ellerOrderByDescending
. slutkommentar
Obs! Den rekommenderade formen på resultatet av
GroupBy
– en sekvens med sekvenser, där varje inre sekvens har ytterligare enKey
egenskap. slutkommentar
Obs! 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.Linq
tillhandahåller en implementering av frågeuttrycksmönstret för alla typer som implementerarSystem.Collections.Generic.IEnumerable<T>
gränssnittet. slutkommentar
12.21 Tilldelningsoperatorer
12.21.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
, en egenskapsåtkomst, en indexerares åtkomst, en händelseåtkomst eller en tuppeln. 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 för 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 enkla tilldelningsoperatörens vänstra operande får inte vara en händelseåtkomst (förutom enligt beskrivningen i §15.8.2). Den enkla tilldelningsoperatören beskrivs i §12.21.2.
Operatorn = ref
kallas referenstilldelningsoperatorn. Det gör den högra operanden, som ska vara en variable_reference (§9.5), referenten av hänvisa till variabeln som designeras av den lämnade operanden. Referenstilldelningsoperatören beskrivs i §12.21.3.
Andra tilldelningsoperatorer än operatorerna och kallas för sammansatta tilldelningsoperatorer.= ref
=
Dessa operatorer utför den angivna åtgärden på de två operanderna och tilldelar sedan det resulterande värdet till variabeln, egenskapen eller indexeraren som anges av den vänstra operanden. De sammansatta tilldelningsoperatörerna beskrivs i §12.21.4.
Operatorerna +=
och med ett uttryck för händelseåtkomst som den vänstra operanden kallas för händelsetilldelningsoperatorer.-=
Ingen annan tilldelningsoperator är giltig med en händelseåtkomst som den vänstra operanden. Händelsetilldelningsoperatörerna beskrivs i §12.21.5.
Tilldelningsoperatorerna är höger-associativa, vilket innebär att åtgärder grupperas från höger till vänster.
Exempel: Ett uttryck för formuläret
a = b = c
utvärderas soma = (b = c)
. slutexempel
12.21.2 Enkel tilldelning
Operatorn =
kallas för enkel tilldelningsoperator.
Om den vänstra operanden i en enkel tilldelning är av formuläret E.P
eller E[Ei]
där E
har kompileringstidstypen dynamic
, är tilldelningen dynamiskt bunden (§12.3.3). I det här fallet är dynamic
tilldelningsuttryckets kompileringstidstyp , och den lösning som beskrivs nedan sker vid körning baserat på körningstypen E
för . Om den vänstra operanden är av det formulär E[Ei]
där minst ett element av Ei
har kompileringstidstypen dynamic
, och kompileringstidstypen 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 tuppeln kallas också för en dekonstruktionstilldelning. Om något av tuppelns element i den vänstra operanden har ett elementnamn uppstår ett kompileringsfel. Om något av tuppelns element i den vänstra operanden är en declaration_expression och något annat element inte är en declaration_expression eller en enkel ignorera, uppstår ett kompileringsfel.
Typen av en enkel tilldelning x = y
är typen av en tilldelning till x
av y
, som rekursivt bestäms på följande sätt:
- Om
x
är ett tupppeluttryck(x1, ..., xn)
, ochy
kan dekonstrueras till ett tupppeluttryck(y1, ..., yn)
medn
element (§12.7), och varje tilldelning tillxi
yi
har typenTi
, har tilldelningen typen(T1, ..., Tn)
. - Annars, om
x
klassificeras som en variabel, är variabeln intereadonly
,x
har en typT
ochy
har en implicit konvertering tillT
, så har tilldelningen typenT
. - Annars, om
x
klassificeras som en implicit typvariabel (dvs. ett implicit skrivet deklarationsuttryck) ochy
har en typT
, ärT
variabelns härledda typ , och tilldelningen har typenT
. - Annars, om
x
klassificeras som en egenskap eller indexerare åtkomst, egenskapen eller indexeraren har en tillgänglig uppsättning accessor,x
har en typT
, ochy
har en implicit konvertering tillT
, då tilldelningen har typenT
. - Annars är tilldelningen inte giltig och ett bindningstidsfel inträffar.
Körningsbearbetningen av en enkel tilldelning av formuläret x = y
med typen T
utförs som en tilldelning till x
av y
med typen T
, som består av följande rekursiva steg:
x
utvärderas om det inte redan var det.- Om
x
klassificeras som en variabely
utvärderas och konverteras vid behov tillT
genom en implicit konvertering (§10.2).- Om variabeln som anges av
x
är ett matriselement i en reference_type utförs en körningskontroll för att säkerställa att värdet som beräknas föry
är kompatibelt med matrisinstansen somx
är ett element. Kontrollen lyckas omy
ärnull
, eller om det finns en implicit referenskonvertering (§10.2.8) från den typ av instans som refereras avy
till den faktiska elementtypen för matrisinstansen som innehållerx
. Annars kastas enSystem.ArrayTypeMismatchException
. - Värdet som är resultatet av utvärderingen och konverteringen av
y
lagras på den plats som anges av utvärderingen avx
och returneras som ett resultat av tilldelningen.
- Om variabeln som anges av
- Om
x
klassificeras som en egenskap eller indexerares åtkomst:y
utvärderas och konverteras vid behov tillT
genom en implicit konvertering (§10.2).- Set-accessorn
x
för anropas med värdet som är resultatet av utvärderingen och konverteringen avy
som värdeargument. - Värdet som är resultatet av utvärderingen och konverteringen av
y
returneras som ett resultat av tilldelningen.
- Om
x
klassificeras som en tuppeln(x1, ..., xn)
med adeln
:y
dekonstrueras medn
element till ett tupppeluttrycke
.- en resultattupppel
t
skapas genom atte
konvertera tillT
att använda en implicit tupppelkonvertering. - för varje
xi
i ordning från vänster till höger utförs en tilldelning tillxi
avt.Itemi
, förutom att dexi
inte utvärderas igen. t
returneras som ett resultat av tilldelningen.
Obs! Om kompileringstidstypen
x
ärdynamic
och det finns en implicit konvertering från kompileringstidstypeny
tilldynamic
krävs ingen körningsmatchning. slutkommentar
Obs! Matrisens samavvikelseregler (§17.6) tillåter att ett värde av en matristyp
A[]
är en referens till en instans av en matristypB[]
, förutsatt att det finns en implicit referenskonvertering frånB
tillA
. 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(); // ArrayTypeMismatchException
den senaste tilldelningen gör att en
System.ArrayTypeMismatchException
genereras eftersom en referens till enArrayList
inte 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.7 gä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.A
ochr.B
tillåts eftersomp
ochr
ä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.A
ochr.B
inte är variabler.slutexempel
12.21.3 Referenstilldelning
Operatorn = ref
kallas referenstilldelningsoperatorn.
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 kompileringstidsfel om ref-safe-context (§9.7.2) för 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 (dvs. 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 kan skrivas om den vänstra operanden kan skrivas.
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äsaref
delen 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= ref
operator ochb ? ref x : ref y
vara rätt operand:ref int a = ref (b ? ref x : ref y);
. Det är viktigt att uttrycketref b
inte är en del av den instruktionen, även om det kan visas så vid första anblicken. slutkommentar
12.21.4 Sammansatt tilldelning
Om den vänstra operanden för en sammansatt tilldelning är av formuläret E.P
eller E[Ei]
där E
har kompileringstidstypen dynamic
, är tilldelningen dynamiskt bunden (§12.3.3). I det här fallet är dynamic
tilldelningsuttryckets kompileringstidstyp , och den lösning som beskrivs nedan sker vid körning baserat på körningstypen E
för . Om den vänstra operanden är av det formulär E[Ei]
där minst ett element av Ei
har kompileringstidstypen dynamic
, och kompileringstidstypen E
inte är en matris, är den resulterande indexerarens åtkomst dynamiskt bunden, men med begränsad kompileringstidskontroll (§12.6.5).
En åtgärd av formuläret x «op»= y
bearbetas genom att tillämpa binär operatoröverbelastningsmatchning (§12.4.5) som om åtgärden skrevs x «op» y
. Sedan kan du
- Om returtypen för den valda operatorn implicit kan konverteras till typen av
x
utvärderas åtgärden somx = x «op» y
, förutom attx
endast utvärderas en gång. - Annars, om den valda operatorn är en fördefinierad operator, om returtypen för den valda operatorn uttryckligen kan konverteras till typen ,
x
och omy
är implicit konvertibel till typen avx
eller operatorn är en skiftoperator, utvärderas åtgärden somx = (T)(x «op» y)
, därT
är typen avx
, förutom attx
utvärderas endast 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» y
sparas x
resultatet av eventuella konstituerande uttryck 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[]
, ochC
B
är metoder som returnerarint
, anropas metoderna bara en gång, i ordningenA
,B
,C
. slutexempel
När den vänstra operanden för en sammansatt tilldelning är en egenskapsåtkomst eller indexerareåtkomst, ska egenskapen eller indexeraren ha både en get-accessor och en uppsättningsåtkomst. Om så inte är fallet uppstår ett bindningstidsfel.
Den andra regeln ovan tillåter x «op»= y
att 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
, ushort
eller 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 tillåten x «op»= y
om både och x «op» y
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; // OK
Den 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»= y
utvärderas som antingenx = x «op» y
ellerx = (T)(x «op» y)
, omfattar utvärderingsreglerna implicit borttagna operatorer. slutkommentar
12.21.5 Händelsetilldelning
Om operatorns vänstra operand a += or -=
klassificeras som en händelseåtkomst utvärderas uttrycket enligt följande:
- Instansuttrycket, om det finns någon, av händelseåtkomsten utvärderas.
- Den högra operanden för
+=
operatorn eller-=
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 uttryck för händelsetilldelning endast giltigt i samband med en statement_expression (§13.7).
12.22 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.23 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 literalen
null
). - Referenser till
const
medlemmar i klass- och struct-typer. - Referenser till medlemmar i uppräkningstyper.
- Referenser till lokala konstanter.
- Parentesiserade underuttryck, som i sig är konstanta uttryck.
- Gjutna uttryck.
checked
ochunchecked
uttryck.nameof
Uttryck.- Fördefinierade
+
operatorerna ,-
,!
(logisk negation) och~
unary. - Fördefinierade
+
operatorerna , ,-
*
,/
,%
,<<
,>>
,&
, ,|
,^
,&&
,||
,==
,!=
,<
,>
, ,<=
och>=
binära operatorer. - Villkorsoperatorn
?:
. - Operatorn
!
null-forgiving (§12.8.9). sizeof
uttryck, förutsatt att den ohanterade typen är en av de typer som anges i §23.6.9 för vilkasizeof
returnerar 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
null
värdet.
Obs! Andra konverteringar, inklusive boxning, unboxing och implicita referenskonverteringar av icke-värden
null
tillåts inte 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 icke-värdenull
krä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 inträffar 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.
- Konstanta deklarationer (§15.4)
- Uppräkningsmedlemsdeklarationer (§19.4)
- Standardargument för parameterlistor (§15.6.2)
case
etiketter för enswitch
instruktion (§13.8.3).goto case
instruktioner (§13.10.4)- Dimensionslängder i ett uttryck för matrisskapande (§12.8.17.5) som innehåller en initialiserare.
- Attribut (§22)
- 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
, uint
eller ulong
, förutsatt att värdet för konstantuttrycket ligger inom måltypens intervall.
12.24 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 kontexter som anges i 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.18) 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 implicit konverteras till
bool
tillämpas den implicita konverteringen vid körning. - Annars används unary operator overload resolution (§12.4.4) för att hitta en unik bästa implementering av
operator true
påE
, och den implementeringen tillämpas vid körning. - Om ingen sådan operator hittas uppstår ett bindningstidsfel.
ECMA C# draft specification