Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
12.1 Általános
A kifejezés operátorok és operandusok sorozata. Ez a záradék határozza meg az operandusok és operátorok kiértékelési sorrendjét, valamint a kifejezések jelentését.
12.2 Kifejezésbesorolások
12.2.1 Általános
Egy kifejezés eredménye az alábbiak egyikének minősül:
- Egy érték. Minden értékhez tartozik egy társított típus.
- Egy változó. Ha másként nincs megadva, a változó explicit módon van begépelve, és társított típussal rendelkezik, nevezetesen a változó deklarált típusával. Az implicit módon beírt változókhoz nincs társított típus.
- Null literál. Az ezzel a besorolással rendelkező kifejezések implicit módon átalakíthatók hivatkozástípussá vagy null értékűvé.
- Névtelen függvény. Az ezzel a besorolással rendelkező kifejezések implicit módon konvertálhatók kompatibilis delegálttípussá vagy kifejezésfatípussá.
- Egy tus. Minden rekordhoz rögzített számú elem tartozik, amelyek mindegyike kifejezéssel és opcionális rekordelemnévvel rendelkezik.
- Tulajdonsághozzáférés. Minden tulajdonsághozzáférésnek van egy társított típusa, nevezetesen a tulajdonság típusa. A tulajdonsághozzáférés emellett kapcsolódó példánykifejezéssel rendelkezhet. Egy példánytulajdonság-hozzáférés meghívásakor a példánykifejezés kiértékelésének eredménye a
this
által képviselt példány lesz (§12.8.14). - Indexelő funkció elérése. Minden indexelő-hozzáféréshez tartozik egy társított típus, nevezetesen az indexelő elemtípusa. Az indexelői hozzáférés emellett társított példánykifejezéssel és társított argumentumlistával is rendelkezik. Indexelő-hozzáférés meghívásakor a példánykifejezés kiértékelésének eredménye a
this
által képviselt példány lesz (§12.8.14), és az argumentumlista kiértékelésének eredménye lesz a meghívás paraméterlistája. - Semmi. Ez akkor fordul elő, ha a kifejezés egy metódus meghívása, amelynek visszatérési típusa
void
. A semmiként besorolt kifejezés csak egy statement_expression (§13.7) vagy egy lambda_expression törzsében (§12.19) érvényes.
A nagyobb kifejezések alexpressziójaként előforduló kifejezéseknél a feljegyzett korlátozások mellett az eredmény az alábbiak egyikének is besorolható:
- Névtér. Az ezzel a besorolással rendelkező kifejezések csak egy member_access bal oldalán jelenhetnek meg (§12.8.7). Bármely más környezetben a névtérnek minősített kifejezés fordítási hibát eredményez.
- Egy típus. Az ezzel a besorolással rendelkező kifejezések csak egy member_access bal oldalán jelenhetnek meg (§12.8.7). Bármely más környezetben a típusként besorolt kifejezés fordítási idejű hibát okoz.
- Egy metóduscsoport, amely egy tagkeresésből eredő túlterhelt metódusok halmaza (12.5. §). A metóduscsoportokhoz társított példánykifejezés és társított típusú argumentumlista tartozhat. Egy példánymetódus meghívásakor a példánykifejezés kiértékelésének eredménye a
this
által képviselt példány lesz (§12.8.14). Egy metóduscsoport a invocation_expression (§12.8.10) vagy a delegate_creation_expression (§12.8.17.5) használatakor engedélyezett, és implicit módon átalakítható egy kompatibilis delegátustípusra (§10.8). Bármely más környezetben a metóduscsoportként besorolt kifejezés fordítási hibát eredményez. - Egy eseményhez való hozzáférés. Minden eseményhozzáférésnek van egy társított típusa, nevezetesen az esemény típusa. Az eseményhozzáférés emellett társított példánykifejezéssel is rendelkezhet. Az eseményhozzáférés a
+=
és-=
operátor bal operandusaként jelenhet meg (§12.21.5). Bármely más környezetben az eseményhozzáférésként besorolt kifejezés fordítási idejű hibát okoz. Amikor egy példányesemény hozzáférés accessorja kerül meghívásra, a példánykifejezés kiértékelésének eredménye azthis
által képviselt példány lesz (§12.8.14). - Dobókifejezés, amely több kontextusban is használható egy kifejezés kivételének kidobásához. Egy throw kifejezés implicit átalakítással bármilyen típusra konvertálható.
A tulajdonsághozzáférés vagy az indexelőhozzáférés mindig értékként lesz újra osztályozva a getter vagy a setter meghívásával. Az adott tartozékot a tulajdonság- vagy indexelőhozzáférés kontextusa határozza meg: Ha a hozzáférés egy hozzárendelés célja, a rendszer meghívja a készlethez tartozó tartozékot egy új érték hozzárendelésére (12.21.2.. §). Ellenkező esetben a get kiegészítő meghívása az aktuális érték lekéréséhez történik (§12.2.2).
A példány hozzáférése történhet egy példány tulajdonságához, eseményéhez vagy indexelőjéhez való hozzáféréssel.
12.2.2 Kifejezések értékei
A kifejezéseket tartalmazó szerkezetek többsége végső soron megköveteli, hogy a kifejezés értéket jelöljön. Ilyen esetekben, ha a tényleges kifejezés névteret, típust, metóduscsoportot vagy semmit jelöl, fordítási időhiba lép fel. Ha azonban a kifejezés tulajdonsághozzáférést, indexelő-hozzáférést vagy változót jelöl, a tulajdonság, az indexelő vagy a változó értéke implicit módon helyettesíthető:
- A változó értéke egyszerűen a változó által azonosított tárolási helyen tárolt érték. A változót mindenképpen hozzárendeltnek kell tekinteni (§9.4) az értékének lekértése előtt, vagy ellenkező esetben fordítási időhiba lép fel.
- A tulajdonságelérési kifejezés értéke a tulajdonság lekérőjének meghívásával nyerhető ki. Ha a tulajdonság nem rendelkezik get hozzáférővel, fordítási idejű hiba lép fel. Ellenkező esetben a függvénytagok meghívása (§12.6.6) történik, és a meghívás eredménye a tulajdonsághozzáférési kifejezés értéke lesz.
- Az indexelő hozzáférési kifejezés értékét a getter meghívásával szerezheti be. Ha az indexelő nem rendelkezik beolvasási kiegészítővel, fordítási időhiba lép fel. Ellenkező esetben a függvénytagok meghívása (§12.6.6) az indexelő hozzáférési kifejezéséhez társított argumentumlistával történik, a meghívás eredménye pedig az indexelő hozzáférési kifejezés értéke lesz.
- A tuple kifejezés értékét úgy kapjuk meg, hogy alkalmazzuk az implicit tuple-átalakítást (§10.2.13) a tuple kifejezés típusára. Hiba egy olyan n-es kifejezés értékének lekérdezése, amelynek nincs típusa.
12.3 Statikus és dinamikus kötés
12.3.1 Általános
kötés az a folyamat, amely meghatározza, hogy egy művelet mire vonatkozik a kifejezések típusának vagy értékének (argumentumok, operandusok, fogadók) alapján. A metódushívás kötését például a fogadó típusa és az argumentumok határozzák meg. Az operátor kötését az operandusok típusa alapján határozzuk meg.
A C#-ban a művelet kötése általában fordítási időpontban van meghatározva, az alexpressziók fordítási idejének típusa alapján. Hasonlóképpen, ha egy kifejezés hibát tartalmaz, a rendszer a fordításkor észleli és jelenti a hibát. Ez a megközelítés statikus kötésinéven ismert.
Ha azonban egy kifejezés egy dinamikus kifejezési (azaz dynamic
típussal rendelkezik), az azt jelzi, hogy minden olyan kötésnek, amelyben részt vesz, a futásidejű típusán kell alapulnia, nem pedig a fordítási időpontban található típuson. Az ilyen művelet kötése ezért a program futtatása során a művelet végrehajtásának időpontjáig halasztható. Ezt dinamikus kötésnek nevezzük .
Ha egy művelet dinamikusan van kötve, a fordítási időben kevés vagy semmilyen ellenőrzés nem történik. Ehelyett, ha a futásidejű kötés meghiúsul, a hibák kivételként jelennek meg futásidőben.
A C#-ban a következő műveletek kötésre vonatkoznak:
- Taghozzáférés:
e.M
- Metódushívás:
e.M(e₁,...,eᵥ)
- Meghatalmazotti meghívás:
e(e₁,...,eᵥ)
- Elem hozzáférés:
e[e₁,...,eᵥ]
- Objektum létrehozása: új
C(e₁,...,eᵥ)
- Túlterhelt unáris operátorok:
+
,-
,!
(csak logikai negáció),~
,++
,--
,true
,false
- Túlterhelt bináris operátorok:
+
,-
,*
,/
,%
,&
,&&
,|
,||
,??
,^
,<<
,>>
,==
,!=
,>
,<
,>=
,<=
- Hozzárendelési operátorok:
=
,= ref
,+=
,-=
,*=
,/=
,%=
,&=
,|=
,^=
,<<=
,>>=
,??=
- Implicit és explicit konverziók
Ha nincsenek dinamikus kifejezések, a C# alapértelmezés szerint statikus kötést alkalmaz, ami azt jelenti, hogy a kijelölési folyamatban a fordítási idő típusú alkifejezések lesznek használatban. Ha azonban a fenti műveletek egyik alkifejezése dinamikus kifejezés, a művelet dinamikusan kötött.
Fordítási időbeli hiba lép fel, ha egy metódushívás dinamikusan kötődik, és a paraméterek bármelyike, beleértve a fogadót is, bemeneti paraméter.
12.3.2 Kötési idő
A statikus kötés fordítási időben történik, míg a dinamikus kötés futásidőben történik. A következő albekezdésekben a kötési idő kifejezés fordítási vagy futási időre vonatkozik attól függően, hogy mikor történik a kötés.
példa: Az alábbiak a statikus és dinamikus kötés, valamint a kötési idő fogalmait szemléltetik:
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)
Az első két hívás statikusan kötött: a
Console.WriteLine
túlterhelését a rendszer az argumentum fordítási idő típusa alapján választja ki. Így a kötési idő fordítási idő.A harmadik hívás dinamikusan kötött: a
Console.WriteLine
túlterhelését a rendszer az argumentum futásidejének típusa alapján választja ki. Ez azért történik, mert az argumentum egy dinamikus kifejezés – a fordítási idő típusa dinamikus. Így a harmadik hívás kötési ideje futásidő.példa vége
12.3.3 Dinamikus kötés
Ez az alfejezet informatív.
A dinamikus kötés lehetővé teszi a C#-programok számára a dinamikus objektumokkal való interakciót, azaz olyan objektumokat, amelyek nem követik a C# típusú rendszer normál szabályait. A dinamikus objektumok lehetnek más, különböző típusú rendszerekkel rendelkező programozási nyelvek objektumai, vagy lehetnek olyan objektumok, amelyek programozott módon vannak beállítva, hogy saját kötési szemantikáját implementálják a különböző műveletekhez.
A dinamikus objektum saját szemantikáját megvalósító mechanizmus implementálási definícióval rendelkezik. A dinamikus objektumok egy adott interfészt implementálnak, amely jelzi a C# futásidejének, hogy speciális szemantikával rendelkeznek. Így amikor egy dinamikus objektumon végzett műveletek dinamikusan vannak megkötve, a saját kötési szemantikájuk, és nem az ebben a specifikációban megadott C#-értékeket veszi át.
Bár a dinamikus kötés célja, hogy lehetővé tegye a dinamikus objektumokkal való együttműködést, a C# lehetővé teszi a dinamikus kötést minden objektumon, függetlenül attól, hogy dinamikusak-e vagy sem. Ez lehetővé teszi a dinamikus objektumok zökkenőmentesebb integrációját, mivel a rajtuk végzett műveletek eredményei nem mindig dinamikus objektumok, de fordításkor még mindig ismeretlenek a programozó számára. Emellett a dinamikus kötés segít kiküszöbölni a hibákra hajlamos tükröződésalapú kódot, még akkor is, ha az érintett objektumok nem dinamikus objektumok.
12.3.4 Az alkifejezések típusai
Ha egy művelet statikusan van kötve, az alexpresszió típusa (például fogadó és argumentum, index vagy operandus) mindig a kifejezés fordítási idejének típusa.
Ha egy művelet dinamikusan van kötve, az alexpresszió típusa a szubexpresszió fordítási idejének típusától függően különböző módokon lesz meghatározva:
- A fordítási idejű dinamikus részexpresszió úgy tekinthető, hogy a kifejezés ténylegesen futásidőben értékeli ki a tényleges értékének típusát.
- Az a részexpresszió, amelynek fordítási idejű típusa típusparaméter, úgy tekinthető, hogy az futásidőben ahhoz a típushoz van kötve, amelyhez a típusparaméter kötődik.
- Ellenkező esetben az alexpresszió fordítási idő típusúnak minősül.
12.4 Operátorok
12.4.1 Általános
A kifejezések operandusokból és operátorokból állnak. Egy kifejezés operátorai jelzik, hogy mely műveletek vonatkozzanak az operandusokra.
Példa: Az operátorok közé tartoznak például a
+
,-
,*
,/
ésnew
. Az operandusok közé tartoznak például a literálok, a mezők, a helyi változók és a kifejezések. példa vége
Háromféle operátor létezik:
- Unary operátorok. A unary operátorok egy operandust használnak, és vagy előtag-jelölést (például
–x
), vagy utótag-jelölést (példáulx++
) alkalmaznak. - Bináris operátorok. A bináris operátorok két operandust használnak, és mind infix jelölést használnak (például
x + y
). - Ternáris operátor. Csak egy ternáris operátor létezik,
?:
; három operandusra van szükség, és az infix jelölést (c ? x : y
) használja.
A kifejezésben szereplő operátorok kiértékelési sorrendjét a operátorok precedenciája és asszociativitása határozzák meg (12.4.2.).
Egy kifejezés operandusait balról jobbra értékeli a rendszer.
Példa: A
F(i) + G(i++) * H(i)
aF
metódust ai
régi értékével hívjuk meg, majd aG
metódust ai
régi értékével hívjuk meg, végül pedig aH
metódust az új i értékkel. Ez független az operátorok precedenciájától, és nem kapcsolódik hozzá. példa vége
Bizonyos operátorok túlterhelhetők. Az operátorok túlterhelése (§12.4.3) lehetővé teszi a felhasználó által definiált operátor-implementációk megadását olyan műveletekhez, amelyeknél az egyik vagy mindkét operandus felhasználó által definiált osztály vagy szerkezet típusú.
12.4.2 Operátorok elsőbbsége és asszociativitása
Ha egy kifejezés több operátort tartalmaz, az operátorok elsőbbsége az egyes operátorok kiértékelésének sorrendjét vezérli.
Megjegyzés: A
x + y * z
kifejezés példáulx + (y * z)
ként van kiértékelve, mert a*
operátor nagyobb elsőbbséget élvez, mint a bináris+
operátor. végjegyzet
Az operátorok elsőbbséget a hozzá tartozó nyelvtani termelés definíciója határozza meg.
Megjegyzés: Egy additive_expression például vagy
+
operátorok által elválasztott-
sorozatból áll, így a+
és-
operátorok alacsonyabb elsőbbséget élveznek, mint a*
,/
és%
operátorok. végjegyzet
Megjegyzés: Az alábbi táblázat az összes operátort a legmagasabbtól a legalacsonyabbig prioritási sorrendben összegzi:
Alcikkely kategória operátorok §12.8 Elsődleges x.y
x?.y
f(x)
a[x]
a?[x]
x++
x--
x!
new
typeof
default
checked
unchecked
delegate
stackalloc
§12.9 Unáris +
-
!x
~
++x
--x
(T)x
await x
§12.10 Többtényezős *
/
%
§12.10 Adalékanyag +
-
§12.11 Műszak <<
>>
§12.12 Relációs és típustesztelés <
>
<=
>=
is
as
§12.12 Egyenlőség ==
!=
§12.13 Logikai és &
§12.13 Logikai XOR ^
§12.13 Logikai vagy \|
§12.14 Feltételes ÉS &&
§12.14 Feltételes vagy \|\|
§12.15 és §12.16 Null egyesítés és kivételdobó kifejezés ??
throw x
§12.18 Feltételes ?:
12.21. §- és 12.19. §- Hozzárendelés és lambda kifejezés =
= ref
*=
/=
%=
+=
-=
<<=
>>=
&=
^=
\|=
=>
??=
végjegyzet
Ha egy operandus két azonos elsőbbséget élvező operátor között fordul elő, az operátorok asszociativitási a műveletek végrehajtásának sorrendjét vezérli:
- A hozzárendelési operátorok és a null szenesítő operátor kivételével minden bináris operátor bal asszociatív
, ami azt jelenti, hogy a műveletek balról jobbra haladnak. Példa:
x + y + z
értékelve van mint(x + y) + z
. példa vége - A hozzárendelési operátorok, a null egyesítő operátor és a feltételes operátor (
?:
) jobb-asszociatívak, ami azt jelenti, hogy a műveletek jobbról balra hajtódnak végre.Példa:
x = y = z
értékelve van mintx = (y = z)
. példa vége
Az elsőbbség és az asszociativitás zárójelekkel szabályozható.
Példa:
x + y * z
először megszorozzay
-atz
-gyel, majd hozzáadja az eredménytx
-höz, de(x + y) * z
először összeadjax
-et ésy
-at, majd megszorozza az eredménytz
-cel. példa vége
12.4.3 Operátor túlterhelése
Minden nem szereplő és bináris operátor előre meghatározott implementációkkal rendelkezik. Emellett a felhasználó által definiált implementációk úgy is bevezethetők, hogy operátori deklarációkat (§15.10) belevesznek az osztályokba és a szerkezetekbe. A felhasználó által definiált operátor-implementációk mindig elsőbbséget élveznek az előre definiált operátor-implementációkkal szemben: Az előre definiált operátor-implementációk csak abban az esetben kerülnek figyelembevételre, ha nem találhatók vonatkozó, felhasználó által definiált operátor-implementációk, amint azt a §12.4.4 és a §12.4.5meghatározza.
A túlterhelhető unáris operátorok a következők:
+ - !
(csak logikai tagadás) ~ ++ -- true false
Megjegyzés: Bár a
true
és afalse
nincsenek explicit módon használva a kifejezésekben (és ezért nem szerepelnek a 12.4.2 §precedenciatáblázatában), operátoroknak minősülnek, mivel több kifejezéskörnyezetben is hivatkoznak rájuk: logikai kifejezésekben (§12.24), valamint a feltételes (§12.18) és a feltételes logikai operátorokkal (§12.14) kapcsolatos kifejezésekben is. végjegyzet
Megjegyzés: A null-megbocsátó operátor (
!
, §12.8.9) nem egy túlterhelhető operátor. végjegyzet
A túlterhelhető bináris operátorok a következők:
+ - * / % & | ^ << >> == != > < <= >=
Csak a fent felsorolt operátorok terhelhetők túl. Különösen nem lehet túlterhelni a tagok hozzáférését, a metódushívást vagy a =
, &&
, ||
, ??
, ?:
, =>
, checked
, unchecked
, new
, typeof
, default
, as
és is
operátorokat.
Ha egy bináris operátor túlterhelt, a megfelelő összetett hozzárendelési operátor is implicit módon túlterhelődik.
példa: Az operátor
*
túlterhelése egyben az operátor*=
túlterhelése is. Ez részletesebben kifejtésre kerül a 12.21. §-ban. példa vége
Maga a hozzárendelési operátor (=)
nem terhelhető túl. A hozzárendelések mindig egy érték egyszerű tárolását hajtják végre egy változóban (§12.21.2).
Az olyan típuskonverziós műveletek, mint a (T)x
, a felhasználó által definiált átalakítások (§10.5) révén vannak túlterhelve.
Megjegyzés: A felhasználó által definiált átalakítások nem befolyásolják a
is
vagyas
operátorok viselkedését. végjegyzet
Az elem hozzáférése (például a[x]
) nem tekinthető túlterhelhető operátornak. Ehelyett a felhasználó által definiált indexelést az indexelők támogatják (§15.9).
A kifejezésekben az operátorokra operátorjelöléssel, a deklarációkban pedig függvényjelöléssel hivatkoznak. Az alábbi táblázat az operátorok és a funkcionális jelölések közötti kapcsolatot mutatja be a nemáris és a bináris operátorok esetében. Az első bejegyzésben az "op" bármelyik túlterhelhető unáris előjeles operátort jelöl. A második bejegyzésben az "op" az unáris postfix ++
és --
operátorokat jelöli. A harmadik bejegyzésben az "op" minden túlterhelhető bináris operátort jelöl.
Megjegyzés: A
++
és--
operátorok túlterhelésére példaként lásd 15.10.2. végjegyzet
operátor jelölése | funkcionális jelölés |
---|---|
«op» x |
operator «op»(x) |
x «op» |
operator «op»(x) |
x «op» y |
operator «op»(x, y) |
A felhasználó által definiált operátor-deklarációkhoz mindig legalább az egyik paraméternek az operátordeklarációt tartalmazó osztály- vagy szerkezettípusnak kell lennie.
Megjegyzés: Így nem lehetséges, hogy egy felhasználó által definiált operátor ugyanazzal az aláírással rendelkezzen, mint egy előre definiált operátor. végjegyzet
A felhasználó által definiált operátor-deklarációk nem módosíthatják az operátorok szintaxisát, elsőbbségét vagy asszociativitását.
Példa : Aoperátor mindig bináris operátor, mindig a 12.4.2. §- megadott elsőbbségi szinttel rendelkezik, és mindig bal asszociatív. példa vége
Megjegyzés: Bár a felhasználó által definiált operátorok bármilyen általa kívánt számítást végrehajthatnak, az intuitíven várt eredményektől eltérő eredményeket produkáló implementációkat határozottan nem javasoljuk. Az operátori
==
implementációjának például össze kell hasonlítania az egyenlőség két operandusát, és megfelelőbool
eredményt kell visszaadnia. végjegyzet
A §12.9-től a §12.21-ig az egyes operátorok leírásai meghatározzák az operátorok előre definiált implementációit és az egyes operátorokra vonatkozó további szabályokat. A leírások a unáris operátortúlterhelés feloldását, bináris operátortúlterhelés feloldását, valamint a numerikus előléptetést, és a speciális operátordefiníciókat használják, amelyek az alábbi alfejezetekben találhatók.
12.4.4. Egyváltozós operátor túlterhelés feloldása
A «op» x
vagy x «op»
alakú művelet, ahol az «op» túlterhelhető egyes művelet, és x
egy X
típusú kifejezés, a következő módon kerül feldolgozásra:
- A
X
által biztosított, aoperator «op»(x)
művelethez tartozó felhasználói operátorok készletét a §12.4.6szabályai alapján határozzák meg. - Ha a felhasználó által definiált jelölt operátorok készlete nem üres, akkor ez lesz a művelet jelölt operátorainak halmaza. Ellenkező esetben az előre definiált bináris
operator «op»
-implementációk, beleértve a kiterjesztett változatokat is, a művelet jelölt operátorainak készletévé válnak. Az adott operátor előre definiált implementációit az operátor leírása határozza meg. Az enum vagy delegált típus által biztosított előre definiált operátorok csak akkor szerepelnek ebben a készletben, ha a kötési idő típusa – vagy a mögöttes típus, ha nullázható típus – bármelyik operandus enum vagy delegált típusa. - A §12.6.4 túlterhelésfeloldási szabályait alkalmazzuk a jelölt operátorok halmazára, hogy az argumentumlista
(x)
szerint kiválasszuk a legjobb operátort, ami a túlterhelésfeloldási folyamat eredménye lesz. Ha a túlterhelés feloldása nem tud egyetlen legjobb operátort kiválasztani, bindolási idő hiba érkezik létre.
12.4.5 Bináris operátor túlterhelésének feloldása
Az x «op» y
űrlap egy művelete, ahol az «op» egy túlterhelhető bináris operátor, x
X
típusú kifejezés, y
pedig Y
típusú kifejezés, az alábbiak szerint lesz feldolgozva:
- A
X
és aY
által aoperator «op»(x, y)
művelethez megadott, felhasználó által megadott jelölt operátorok készlete lesz meghatározva. A halmaz aX
által biztosított jelölt operátorok és aY
által biztosított jelölt operátorok egyesüléséből áll, amelyek mindegyike a §12.4.6szabályai alapján van meghatározva. Az egyesített készlet esetében a jelöltek az alábbiak szerint egyesülnek:- Ha
X
ésY
identitáskonvertáltak, vagy ha aX
és aY
egy közös alaptípusból származik, akkor a megosztott jelölt operátorok csak egyszer fordulnak elő az egyesített csoportban. - Ha az
X
és aY
között identitásátalakítás történik, az«op»Y
által biztosítottY
operátor visszatérési típusa megegyezik a«op»X
által biztosítottX
visszatérési típusával, és az«op»Y
operandus típusai identitásátalakítással bírnak a megfelelő«op»X
operandus típusokhoz, akkor csak«op»X
van jelen a halmazban.
- Ha
- Ha a felhasználó által definiált jelölt operátorok készlete nem üres, akkor ez lesz a művelet jelölt operátorainak halmaza. Ellenkező esetben az előre definiált bináris
operator «op»
-implementációk, beleértve a kiterjesztett változatokat is, a művelet jelölt operátorainak készletévé válnak. Az adott operátor előre definiált implementációit az operátor leírása határozza meg. Az előre definiált felsorolási és delegálási operátorok esetében csak azokat az operátorokat veszik figyelembe, amelyeket az egyik operandus kötési idejének típusaként szolgáló felsorolási vagy delegálási típus biztosít. - A §12.6.4 túlterhelésfeloldási szabályait alkalmazzuk a jelölt operátorok halmazára, hogy az argumentumlista
(x, y)
szerint kiválasszuk a legjobb operátort, ami a túlterhelésfeloldási folyamat eredménye lesz. Ha a túlterhelés feloldása nem tud egyetlen legjobb operátort kiválasztani, bindolási idő hiba érkezik létre.
12.4.6 Jelölt felhasználó által definiált operátorok
Adott egy típus T
és egy művelet operator «op»(A)
, ahol az «op» egy túlterhelhető operátor és A
egy argumentumlista, a T
által biztosított felhasználó által megadott jelölt operátorok halmaza az operátor «op»(A)
esetében az alábbiak szerint van meghatározva:
- Határozza meg a
T₀
típust. HaT
null értékű,T₀
a mögöttes típus; ellenkező esetbenT₀
egyenlőT
. - Ha az
operator «op»
T₀
összes deklarációja és az ilyen operátorok összes emelt formája esetében legalább egy operátor alkalmazható (12.6.4.2. §-) az argumentumlistaA
tekintetében, akkor a jelölt operátorok halmaza aT₀
tartalmazza az összes ilyen alkalmazható operátort. - Ellenkező esetben, ha
T₀
object
, a jelölt operátorok készlete üres. - Ellenkező esetben a
T₀
által biztosított jelölt operátorok halmaza aT₀
közvetlen alaposztálya által biztosított jelölt operátorok készlete, vagy aT₀
tényleges alaposztálya, haT₀
típusparaméter.
12.4.7 Numerikus promóciók
12.4.7.1 Általános
Ez az alfejezet informatív.
§12.4.7 és annak alpontjai az alábbiak együttes hatásának összefoglalása:
- az implicit numerikus konverziók szabályai (§10.2.3);
- Az átalakítás fejlesztésének szabályai (§12.6.4.7);
- a rendelkezésre álló számtani (§ 12.10), relációs (§ 12.12), és az integrált logikai (§12.13.2) operátorok.
A numerikus előléptetés az előre definiált nemary és bináris numerikus operátorok operandusainak bizonyos implicit konverzióit automatikusan végrehajtja. A numerikus előléptetés nem egy különálló mechanizmus, hanem a túlterhelés feloldásának hatása az előre definiált operátorokra. A numerikus előléptetés kifejezetten nem befolyásolja a felhasználó által definiált operátorok kiértékelését, bár a felhasználó által definiált operátorok hasonló hatást mutatnak.
A numerikus előléptetés példájaként vegye figyelembe a bináris *
operátor előre definiált implementációit:
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);
Ha a túlterhelésfeloldási szabályok (§12.6.4) vannak alkalmazva erre az operátorkészletre, a hatás az, hogy kiválasztja az első operátort, amelyhez implicit átalakítások léteznek az operandustípusokból.
Példa: A
b * s
műveletnél, ahol ab
egybyte
és as
egyshort
, a túlterhelés feloldási eljárása aoperator *(int, int)
-t választja ki a legjobb operátornak. Így ab
és as
int
-re lesz átalakítva, és az eredmény típusaint
. Hasonlóképpen, ai * d
művelet esetében , aholi
int
, ésd
double
,overload
felbontás a legjobb operátorkéntoperator *(double, double)
választja ki. példa vége
Informatív szöveg vége.
12.4.7.2 Nem számszerű promóciók
Ez az alfejezet informatív.
Az unáris numerikus átalakítás az előre definiált +
, -
és ~
unáris operátorok operandusai esetében történik. Az unáris numerikus előléptetés egyszerűen sbyte
, byte
, short
, ushort
vagy char
típusú operandusok int
típusba való konvertálásából áll. Emellett az unáris – operátor esetében az unáris numerikus előléptetés a uint
típusú operandusokat long
típussá alakítja.
Informatív szöveg vége.
12.4.7.3 Bináris numerikus promóciók
Ez az alfejezet informatív.
A bináris numerikus előléptetés az előre definiált +
, -
, *
, /
, %
, &
, |
, ^
, ==
, !=
, >
, <
, >=
és <=
bináris operátorok operandusai esetében történik. A bináris numerikus előléptetés implicit módon konvertálja mindkét operandust egy közös típussá, amely a nem relációs operátorok esetén a művelet eredménytípusává is válik. A bináris numerikus előléptetés a következő szabályok alkalmazásából áll, az itt megjelenő sorrendben:
- Ha bármelyik operandus
decimal
típusú, a másik operandusdecimal
típussá alakul, vagy kötési idő hibát tapasztal, ha a másik operandusfloat
vagydouble
típusú. - Ellenkező esetben, ha bármelyik operandus
double
típusú, a másik operandusdouble
típussá alakul. - Ellenkező esetben, ha bármelyik operandus
float
típusú, a másik operandusfloat
típussá alakul. - Ellenkező esetben, ha az operandus
ulong
típusú, a másik operandusulong
típussá alakul, vagy kötési idő hiba történik, ha a másik operandustype sbyte
,short
,int
vagylong
. - Ellenkező esetben, ha bármelyik operandus
long
típusú, a másik operanduslong
típussá alakul. - Ellenkező esetben, ha bármelyik operandus
uint
típusú, a másik operandus pedigsbyte
,short
vagyint
típusú, akkor a rendszer mindkét operanduslong
típussá alakul. - Ellenkező esetben, ha bármelyik operandus
uint
típusú, a másik operandusuint
típussá alakul. - Ellenkező esetben mindkét operandus
int
típussá lesz konvertálva.
Megjegyzés: Az első szabály letilt minden olyan műveletet, amely a
decimal
típust adouble
ésfloat
típussal keveri. A szabály abból a tényből következik, hogy adecimal
típus, valamint adouble
ésfloat
típus között nincsenek implicit konverziók. végjegyzet
Megjegyzés: Azt is vegye figyelembe, hogy az operandus nem lehet
ulong
típusú, ha a másik operandus aláírt integrál típusú. Ennek az az oka, hogy nincs olyan integráltípus, amely aulong
teljes tartományát, valamint az aláírt integráltípusokat képviselheti. végjegyzet
A fenti esetekben egy öntött kifejezéssel explicit módon alakítható át az egyik operandus olyan típussá, amely kompatibilis a másik operandussal.
példa: Az alábbi kódban
decimal AddPercent(decimal x, double percent) => x * (1.0 + percent / 100.0);
A kötési idejű hiba azért fordul elő, mert egy
decimal
nem szorozható egydouble
-gyel. A hiba úgy oldható meg, hogy a második operandust explicit módondecimal
-ra konvertáljuk, az alábbiak szerint:decimal AddPercent(decimal x, double percent) => x * (decimal)(1.0 + percent / 100.0);
példa vége
Informatív szöveg vége.
12.4.8 Emelt operátorok
emelt operátorok lehetővé teszik, hogy a nem null értékű értéktípusokon működő előre definiált és felhasználó által definiált operátorok is használhatók legyenek ezen típusok null értékű formáival. A felemelt operátorok előre meghatározott és felhasználó által meghatározott operátorokból épülnek fel, amelyek megfelelnek bizonyos követelményeknek, az alábbiak szerint:
- A
+
,++
,-
,--
,!
(logikai negálás) és~
operátorok esetében az operátor emelt alakja akkor létezik, ha az operandus és az eredménytípus egyaránt nem-null értékűek. A felemelt forma úgy jön létre, hogy egyetlen?
módosítót ad hozzá az operandus és az eredménytípusok számára. Az emelt operátornull
értéket állít elő, ha az operandusnull
. Ellenkező esetben a felemelt operátor kibontja az operandust, alkalmazza az alap operátort, és becsomagolja az eredményt. - A bináris operátorok
+
,-
,*
,/
,%
,&
,|
,^
,<<
és>>
esetében létezik egy operátor felemelt formája, ha az operandus és az eredménytípus mind nem null értékű. A felemelt forma úgy készül, hogy minden operandushoz és eredmény típushoz hozzáadunk egy?
módosítót. A felemelt operátornull
értéket állít elő, ha az egyik vagy mindkét operandusnull
(kivételt képeznek a&
típusú|
ésbool?
operátorok, ahogy azt a §12.13.5leírja). Ellenkező esetben az emelt operátor kibontja az operandusokat, alkalmazza az alapul szolgáló operátort, és becsomagolja az eredményt. - Az egyenlőségi operátorok
==
és!=
esetében az operátorok emelt formája akkor létezik, ha az operandusok nem null értékűek, és ha az eredménytípusbool
. A megemelt alak úgy jön létre, hogy egyetlen?
módosítót adunk hozzá minden operandustípushoz. A felemelt operátor kétnull
értéket egyenlőnek, és anull
értéket pedig nem egyenlőnek tekinti a nem-null
értékekkel. Ha mindkét operandus értéke más, mintnull
, a kibővített operátor lebontja az operandusokat, majd a mögöttes operátort alkalmazza, hogy előállítsa abool
eredményt. - A relációs operátorok
<
,>
,<=
és>=
esetében az operátorok emelt formája akkor létezik, ha az operandusok nem null értékűek, és az eredménytípusbool
. A megemelt alak úgy jön létre, hogy egyetlen?
módosítót adunk hozzá minden operandustípushoz. Azfalse
értéket állítja elő a felemelt operátor, ha az egyik vagy mindkét operandusnull
. Ellenkező esetben a felemelt operátor kibontja az operandusokat, és az alapértelmezett operátort alkalmazza abool
eredmény létrehozásához.
12.5 Tagkeresés
12.5.1 Általános
A tagkeresés az a folyamat, amelynek során a rendszer meghatározza egy név jelentését egy típus kontextusában. A tagkeresés egy kifejezés simple_name (§12.8.4) vagy egy member_access (§12.8.7) kiértékelésének részeként következhet be. Ha a simple_name vagy a member_access egy invocation_expressionprimary_expression (§12.8.10.2), akkor azt mondják, hogy a tag meg van hívva.
Ha egy tag egy metódus vagy esemény, vagy ha egy állandó, mező vagy tulajdonság akár egy delegált típus (§20), akár a dynamic
típusú (§8.2.4), akkor a tagot meghívhatónak minősítjük.
A tagkeresés nemcsak a tag nevét, hanem a tag típusparamétereinek számát is figyelembe veszi, valamint azt is, hogy a tag elérhető-e. A tagkeresés céljából az általános metódusok és a beágyazott általános típusok a megfelelő deklarációkban megadott típusparaméterek számával rendelkeznek, és az összes többi tag nulla típusparaméterekkel rendelkezik.
A N
típusú tagkeresés K
névvel és T
típusargumentumokkal a következőképpen van feldolgozva:
- Először is
N
nevű akadálymentes tagok halmazát határozzuk meg:- Ha a
T
egy típusparaméter, akkor a halmaz aN
-hez§15.2.5-ban elsődleges vagy másodlagos megkötésként meghatározott minden egyes típusban szereplőT
nevű elérhető tagok halmazainak uniója, valamint aN
-ban lévőobject
nevű elérhető tagok halmaza. - Egyébként a halmaz az §7.5 nevű elérhető tagokból áll a
N
-ben, beleértve az örökölt tagokat és aT
nevű elérhető tagokat aN
-ben. Ha aT
egy konstrukciós típus, akkor a tagok halmazát a típusargumentumok helyettesítésével kapjuk meg, ahogy azt a § 15.3.3leírja. Azokat a tagokat, amelyekoverride
módosítót tartalmaznak, kizárjuk a csoportból.
- Ha a
- Ezután, ha
K
nulla, a program eltávolítja az összes olyan beágyazott típust, amelynek deklarációi típusparamétereket tartalmaznak. HaK
nem nulla, a rendszer eltávolítja a különböző számú típusparaméterrel rendelkező tagokat. HaK
nulla, a típusparaméterekkel rendelkező metódusok nem törlődnek, mivel a típuskövető folyamat (§12.6.3) képes lehet a típusargumentumokra következtetni. - Ezután, ha a rendszer meghívja a tagot, a rendszer eltávolítja az összes nem meghívható tagot a halmazból.
- Ezután a többi tag által elrejtett tagok törlődnek a csoportból. A halmaz minden tagja
S.M
, aholS
az a típus, amelyben a tagM
deklarálva van, a következő szabályok érvényesek:- Ha
M
állandó, mező, tulajdonság, esemény vagy számbavételi tag, akkor aS
alaptípusában deklarált összes tag törlődik a készletből. - Ha
M
típusdeklaráció, akkor aS
alaptípusában deklarált összes nem típus el lesz távolítva a készletből, és aM
alaptípusában deklaráltS
azonos számú típusparaméterrel rendelkező típusdeklarációk törlődnek a készletből. - Ha
M
egy metódus, akkor aS
alaptípusában deklarált nem metódustagok törlődnek a készletből.
- Ha
- Ezután az osztálytagok által elrejtett felülettagok törlődnek a készletből. Ennek a lépésnek csak akkor van hatása, ha
T
típusparaméter, ésT
-nek van egyobject
-től eltérő effektív alaposztálya, valamint egy nem üres effektív interfészhalmaza (§15.2.5). A halmaz minden tagjaS.M
, aholS
a tagM
deklarálásának típusa, a következő szabályokat kell alkalmazni, haS
nemobject
osztálydeklaráció:- Ha
M
állandó, mező, tulajdonság, esemény, számbavételi tag vagy típusdeklaráció, akkor az interfészdeklarációban deklarált összes tag törlődik a készletből. - Ha
M
egy metódus, akkor az illesztődeklarációban deklarált összes nem metódustag el lesz távolítva a készletből, és az illesztődeklarációban deklaráltM
aláírással rendelkező összes metódus el lesz távolítva a készletből.
- Ha
- Végül, miután eltávolította a rejtett tagokat, a keresés eredménye a következő lesz:
- Ha a halmaz egyetlen olyan tagból áll, amely nem metódus, akkor ez a tag a keresés eredménye.
- Ellenkező esetben, ha a készlet csak metódusokat tartalmaz, akkor ez a metóduscsoport a keresés eredménye.
- Ellenkező esetben a keresés nem egyértelmű, és kötés ideje alatt hiba történik.
A típusparamétereken és interfészeken kívül más típusú tagkeresések, valamint a szigorúan egyöröklésű felületeken lévő tagkeresések esetében (az öröklési lánc minden felülete pontosan nulla vagy egy közvetlen alapfelülettel rendelkezik), a keresési szabályok hatása egyszerűen az, hogy a származtatott tagok azonos nevű vagy aláírású alaptagokat rejtenek el. Az ilyen egyes öröklődéses keresések soha nem kétértelműek. A többszörös örökléssel rendelkező felületekben történő tagkereséseknél esetlegesen felmerülő kétértelműségek a §18.4.6részben vannak leírva.
Megjegyzés: Ez a fázis csak egyfajta kétértelműséget ad. Ha a tagkeresés egy metóduscsoportot eredményez, a metóduscsoport további használata kétértelműség miatt meghiúsulhat, például 12.6.4.1 és §12.6.6.2. végjegyzet
12.5.2 Alaptípusok
Tagkeresés céljából egy T
típus a következő alaptípusokkal rendelkezik:
- Ha
T
object
vagydynamic
, akkorT
nem rendelkezik alaptípussal. - Ha
T
egy enum_type, aT
alaptípusai aSystem.Enum
,System.ValueType
ésobject
osztálytípusok . - Ha
T
egy struct_type, aT
alaptípusa aSystem.ValueType
és aobject
osztálytípus.Megjegyzés: Egy nullable_érték_típus egy struktúra_típus (§8.3.1). végjegyzet
- Ha
T
egy class_type, akkor aT
alaptípusai aT
alaposztályai , beleértve aobject
osztálytípust is . - Ha
T
interface_type, akkor aT
alaptípusai azT
alaptípusai és az osztálytípusobject
alaptípusai. - Ha
T
egy array_type, aT
alaptípusai aSystem.Array
és aobject
osztálytípusok. - Ha
T
egy delegate_type, akkor aT
alaptípusai aSystem.Delegate
és aobject
osztálytípusok.
12.6 Függvénytagok
12.6.1 Általános
A függvénytagok végrehajtható utasításokat tartalmazó tagok. A függvénytagok mindig típusok tagjai, és nem lehetnek névterek tagjai. A C# a függvénytagok következő kategóriáit határozza meg:
- Módszerek
- Tulajdonságok
- Események
- Indexelők
- Felhasználó által definiált operátorok
- Példánykonstruktorok
- Statikus konstruktorok
- Véglegesítők
A véglegesítők és a statikus konstruktorok kivételével (amelyek explicit módon nem hívhatók meg) a függvénytagokban található utasítások végrehajtása függvénytag-meghívásokkal történik. A függvénytag-meghívások írásának tényleges szintaxisa az adott függvénytag-kategóriától függ.
A függvénytagok meghívásának argumentumlistája (12.6.2) a függvénytag paramétereinek tényleges értékeit vagy változóhivatkozásait tartalmazza.
Az általános metódusok meghívása típuskövetkezést alkalmazhat a metódusnak átadott típusargumentumok halmazának meghatározásához. Ez a folyamat a §12.6.3le van írva.
A metódusok, indexelők, operátorok és példánykonstruktorok meghívása túlterhelés-feloldást alkalmaz annak meghatározásához, hogy melyik jelölt függvénytag-csoportot kell meghívni. Ez a folyamat a §12.6.4pontban van leírva.
Miután egy adott függvénytagot kötési időpontban azonosítottak, esetleg a túlterhelés feloldásával, a függvénytag meghívásának tényleges futásidejű folyamatát a 12.6.6rész tárgyalja.
Megjegyzés: Az alábbi táblázat a kifejezetten meghívható hat függvénytag-kategóriát tartalmazó szerkezetekben zajló feldolgozást foglalja össze. A táblázatban
e
,x
,y
ésvalue
változóként vagy értékként besorolt kifejezéseket,T
egy típusként besorolt kifejezést,F
egy metódus egyszerű neve,P
pedig egy tulajdonság egyszerű neve.
Épít Példa Leírás Metódushívás F(x, y)
A túlterhelés feloldásával kiválasztjuk a legjobb metódust F
a tartalmazó osztályban vagy struktúrában. A metódust a(x, y)
paraméterlistával hívják meg. Ha a módszer nemstatic
, a példánykifejezésthis
.T.F(x, y)
A túlterhelés feloldása a legjobb módszer kiválasztásához történik az osztályban vagy a struktúrában F
T
. Kötési idejű hiba akkor fordul elő, ha a metódus nemstatic
. A metódust a(x, y)
paraméterlistával hívják meg.e.F(x, y)
A túlterhelés feloldása a F
típus által megadott osztályban, szerkezetben vagy interfészbene
legjobb módszer kiválasztására történik. Kötési idejű hiba akkor fordul elő, ha a metódusstatic
. A metódus meghívása a példánykifejezéssele
és az argumentumlistával(x, y)
.Tulajdonsághozzáférés P
A P
tulajdonság lekérési metódusa meghívásra kerül az azt tartalmazó osztályban vagy struktúrában. Fordítási időhiba akkor fordul elő, haP
csak írható. HaP
nemstatic
, a példánykifejezésthis
.P = value
Az osztály vagy szerkezet P
tulajdonságának beállító hozzáférőjét meghívják a(z)(value)
argumentumlistával. Fordításkori hiba történik, haP
csak olvasható. HaP
nemstatic
, a példánykifejezésthis
.T.P
Az osztály vagy a struktúra P
T
tulajdonságának megkapó metódusa meghívásra kerül. Fordítási időhiba akkor fordul elő, haP
nemstatic
vagy haP
csak írható.T.P = value
Az P
osztály vagy structT
tulajdonság beállító metódusa az argumentumlistával van meghívva(value)
. Fordítási időhiba akkor fordul elő, haP
nemstatic
, vagy haP
írásvédett.e.P
A P
típusa által megadott osztályban, struktúrában vagy felületen aE
tulajdonság lekérési hozzáférőjét ae
példánykifejezéssel hívják meg. Kötési idejű hiba akkor fordul elő, haP
megegyezikstatic
-gyel, vagy haP
csak írásra alkalmas.e.P = value
A P
típus szerinti osztályban, struktúrában vagy interfészben aE
tulajdonság 'set' metódusa a példánykifejezéssele
és az argumentumlistával(value)
van meghívva. Kötési idejű hiba akkor fordul elő, haP
megegyezikstatic
-gyel, vagy haP
írásvédett.Eseményhozzáférés E += value
Az esemény hozzáadó hozzáférője, E
, meghívásra kerül az azt tartalmazó osztályban vagy szerkezetben. HaE
nemstatic
, a példánykifejezésthis
.E -= value
A(z) E
esemény eltávolító hozzáférőjét az azt tartalmazó osztályban vagy struktúrában meghívják. HaE
nemstatic
, a példánykifejezésthis
.T.E += value
Az esemény E
hozzáadó metódusa a(z)T
osztályban vagy struktúrában kerül meghívásra. Kötési idejű hiba akkor fordul elő, haE
nemstatic
.T.E -= value
A E
esemény eltávolító tartozéka a(z)T
osztályban vagy struktúrában van meghívva. Kötési idejű hiba akkor fordul elő, haE
nemstatic
.e.E += value
Az E
típus által megadott osztályban, szerkezetben vagy felületen azE
esemény add hozzáférője ae
példánykifejezéssel meg van hívva. Kötési idejű hiba akkor fordul elő, haE
static
.e.E -= value
Az esemény eltávolító metódusa a(z) E
osztályban, szerkezetben vagy interfészben, amelyet a(z)E
típus ad meg, a példánykifejezése
használatával van meghívva. Kötési idejű hiba akkor fordul elő, haE
static
.Indexelői hozzáférés e[x, y]
A túlterhelés feloldása a e
típusa által megadott osztály, szerkezet vagy felület legjobb indexelőjének kiválasztására van alkalmazva. Az indexelő lekérő eljárása/elhívása a példánykifejezése
és az argumentumlista(x, y)
használatával kerül meghívásra. Kötési idejű hiba akkor fordul elő, ha az indexelő csak írható.e[x, y] = value
A túlterhelés feloldása a e
típusa által megadott osztály, szerkezet vagy felület legjobb indexelőjének kiválasztására van alkalmazva. Az indexelő készletkiegészítőjének meghívása a példánykifejezéssele
és az argumentumlistával(x, y, value)
. Kötési idejű hiba akkor fordul elő, ha az indexelő írásvédett.Operátor-meghívás -x
A túlterhelés feloldása a x
típusának megfelelő osztály vagy szerkezet legjobb unary operátorának kiválasztására vonatkozik. A kijelölt operátor meghívása az argumentumlistával(x)
.x + y
A túlterhelés feloldása a legjobb bináris operátor kiválasztására vonatkozik a x
ésy
típusai által megadott osztályokban vagy szerkezetekben. A kijelölt operátor meghívása az argumentumlistával(x, y)
.Példánykonstruktor meghívása new T(x, y)
A túlterhelés feloldási folyamatát alkalmazzák a legjobb példánykonstruktor kiválasztásához az osztályban vagy a struktúrában T
. A példánykonstruktor az(x, y)
argumentumlistával kerül meghívásra.végjegyzet
12.6.2 Argumentumlisták
12.6.2.1 Általános
Minden függvénytag és delegált meghívás tartalmaz egy argumentumlistát, amely tényleges értékeket vagy változóhivatkozásokat biztosít a függvénytag paramétereihez. A függvénytagok meghívásának argumentumlistájának megadására szolgáló szintaxis a függvénytagok kategóriájától függ:
- Konstruktorok, metódusok, indexelők és meghatalmazottak esetében az argumentumok argument_listként vannak megadva az alábbiak szerint. Indexelők esetén a beállított tartozék meghívásakor az argumentumlista a hozzárendelési operátor megfelelő operandusaként megadott kifejezést is tartalmazza.
Megjegyzés: Ez a további argumentum nem használt túlterhelésfeloldáshoz, csak a set hozzáférő meghívása során. végjegyzet
- Tulajdonságok esetén az argumentumlista üres a get kiegészítő meghívásakor, és a hozzárendelési operátor megfelelő operandusaként megadott kifejezésből áll a beállított tartozék meghívásakor.
- Események esetén az argumentumlista a
+=
vagy-=
operátor jobb operandusaként megadott kifejezésből áll. - A felhasználó által definiált operátorok esetében az argumentumlista a nem szereplő operátor egyetlen operandusából vagy a bináris operátor két operandusából áll.
A tulajdonságok (§15.7) és az események (§15.8) argumentumai mindig értékparaméterként vannak átadva (§15.6.2.2). A felhasználó által definiált operátorok (§15.10) argumentumai mindig értékparaméterként (§15.6.2.2) vagy bemeneti paraméterekként (§9.2.8) lesznek átadva. Az indexelők (§15.9) argumentumai mindig értékparaméterekként (§15.6.2.2), bemeneti paraméterekként (§9.2.8) vagy paramétertömbökként (§15.6.2.4) lesznek átadva. A kimeneti és referenciaparaméterek nem támogatottak a függvénytagok ezen kategóriáiban.
A példánykonstruktor, metódus, indexelő vagy delegált meghívás argumentumai argument_listként vannak megadva:
argument_list
: argument (',' argument)*
;
argument
: argument_name? argument_value
;
argument_name
: identifier ':'
;
argument_value
: expression
| 'in' variable_reference
| 'ref' variable_reference
| 'out' variable_reference
;
A argument_list egy vagy több argumentumbóls-ből áll, vesszővel elválasztva. Minden argumentum egy választható argument_name, amelyet egy argument_valuekövet. A argument_nameargumentumotelnevezett argumentumnak nevezzük, míg a argument_name nélküli argumentumpozícióargumentum.
A argument_value a következő formák egyikét veheti fel:
- Egy kifejezés, amely azt jelzi, hogy az argumentumot értékparaméterként adják át, vagy bemeneti paraméterré alakítják át, majd ennek megfelelően továbbítják, amint azt a (§12.6.4.2 és leírva a §12.6.2.3) határozzák meg.
- A kulcsszó
in
után egy variable_reference (§9.5), amely azt jelzi, hogy az argumentum bemeneti paraméterként van átadva (§15.6.2.3.2). A változót mindenképpen hozzá kell rendelni (§9.4) ahhoz, hogy bemeneti paraméterként átadható legyen. - A kulcsszó
ref
után egy variable_reference (§9.5), amely azt jelzi, hogy az argumentum hivatkozási paraméterként van átadva (§15.6.2.3.3). A változót mindenképpen hozzárendelni (§9.4) ahhoz, hogy referenciaparaméterként átadható legyen. - A kulcsszó
out
után egy variable_reference (§9.5), amely azt jelzi, hogy az argumentum kimeneti paraméterként van átadva (§15.6.2.3.4). A függvénytagok meghívását követően a változót mindenképpen hozzárendeltnek tekintjük (§9.4), amelyben a változó kimeneti paraméterként van átadva.
Az űrlap az argumentum paraméterátadási módjának határozza meg: érték, bemeneti, hivatkozásivagy kimeneti. Azonban, mint fentebb említettük, egy értékátadási móddal rendelkező argumentum átalakítható bemeneti továbbítási móddal rendelkező argumentummá.
Ha egy illékony mezőt (15.5.4. §) bemenetként, kimenetként vagy referenciaparaméterként ad át, figyelmeztetést okoz, mivel a meghívott metódus nem tudja volatilisként kezelni a mezőt.
12.6.2.2 Megfelelő paraméterek
Az argumentumlista minden argumentumához meg kell adni egy megfelelő paramétert a meghívandó függvénytagban vagy meghatalmazottban.
Az alábbiakban használt paraméterlista a következőképpen van meghatározva:
- Az osztályokban definiált virtuális metódusok és indexelők esetében a paraméterlista a függvénytag első deklarációjából vagy felülbírálásából lesz kiválasztva, amikor a fogadó statikus típusával kezdődik, és az alaposztályokon keresztül keres.
- Részleges metódusok esetén a rendszer a definiált részleges metódus deklarációjának paraméterlistáját használja.
- Az összes többi függvénytag és meghatalmazott esetében csak egyetlen paraméterlista van, amely a használt.
Az argumentumok vagy paraméterek pozícióját az argumentumlistában vagy paraméterlistában az azt megelőző argumentumok vagy paraméterek száma határozza meg.
A függvénytag-argumentumok megfelelő paraméterei a következők:
- A példánykonstruktorok, metódusok, indexelők és delegáltak argumentumai az argument_list-ben:
- Az a pozícióargumentum, amelyben egy paraméter a paraméterlistában ugyanazon a helyen történik, megfelel ennek a paraméternek, kivéve, ha a paraméter paramétertömb, és a függvény tagját kibontott formában hívja meg a függvény.
- A függvénytagok kibővített formában meghívott paramétertömbjének pozícióargumentuma, amely a paraméterlista paramétertömbjének pozíciójánál vagy után fordul elő, a paramétertömb egyik elemének felel meg.
- A névvel ellátott argumentum a paraméterlistában szereplő azonos nevű paraméternek felel meg.
- Indexelők esetén a készlet tartozékának meghívásakor a hozzárendelési operátor jobb operandusaként megadott kifejezés megfelel a készlet tartozékdeklarációjának implicit
value
paraméterének.
- Tulajdonságok esetén a getter meghívásakor nincsenek argumentumok. A készlet tartozékának meghívásakor a hozzárendelési operátor jobb operandusaként megadott kifejezés megfelel a készlet tartozékdeklarációjának implicit értékparaméterének.
- A felhasználó által definiált nem kötelező operátorok (beleértve a konverziókat) esetében az egyetlen operandus az operátor-deklaráció egyetlen paraméterének felel meg.
- Felhasználó által definiált bináris operátorok esetében a bal operandus az első paraméternek, a jobb operandus pedig az operátordeklaráció második paraméterének felel meg.
- A névtelen argumentumok egyetlen paraméternek sem felelnek meg, ha egy pozíción kívüli nevű argumentumot vagy egy paramétertömbnek megfelelő elnevezett argumentumot követnek.
Megjegyzés: Ez megakadályozza, hogy a
void M(bool a = true, bool b = true, bool c = true);
meghívjaM(c: false, valueB);
. Az első argumentumot pozíción kívül használja (az argumentumot az első pozícióban használja a rendszer, de ac
nevű paraméter harmadik pozícióban van), ezért az alábbi argumentumokat kell elnevezni. Más szavakkal, az elnevezett argumentumok, amelyek nem a mondat végén találhatóak, csak akkor engedélyezettek, ha a nevük és pozíciójuk ugyanazon megfelelő paraméterhez vezetnek. végjegyzet
12.6.2.3 Argumentumlisták futásidejű kiértékelése
A függvénytagok meghívásának futásidejű feldolgozása során (§12.6.6) az argumentumlista kifejezéseit vagy változóhivatkozásait balról jobbra sorrendben értékeli ki a rendszer az alábbiak szerint:
Értékargumentum esetén, ha a paraméter átadási módja érték
az argumentumkifejezés kiértékelése és implicit átalakítás (§10.2) végrehajtása a megfelelő paramétertípusra. Az eredményül kapott érték lesz az értékparaméter kezdeti értéke a függvénytag-meghívásban.
ellenkező esetben a paraméter továbbítási módja inputként van megadva. Ha az argumentum változóhivatkozás, és az argumentum típusa és a paraméter típusa között identitáskonvertálás (§10.2.2) áll fenn, az eredményül kapott tárolóhely lesz a függvénytag meghívásában szereplő paraméter által képviselt tárolási hely. Ellenkező esetben a megfelelő paraméter típusával megegyező típusú tárolóhely jön létre. Az argumentumkifejezés kiértékelése és implicit átalakítás (§10.2) végrehajtása a megfelelő paramétertípusra. Az eredményként kapott érték ezen a tárolóhelyen belül lesz tárolva. Ezt a tárolási helyet a függvénytag-meghívás bemeneti paramétere jelöli.
Példa: A következő deklarációk és metódushívások alapján:
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
A
M1(i)
metódushívásban ai
bemeneti argumentumként lesz átadva, mivel változóként van besorolva, és ugyanazzal a típussalint
, mint a bemeneti paraméter. AM1(i + 5)
metódushívásban létrejön egy névtelenint
változó, inicializálva az argumentum értékével, majd adja át bemeneti argumentumként. Lásd 12.6.4.2. és 12.6.4.4.példa vége
Bemenet, kimenet vagy referenciaargumentum esetén a változóhivatkozás kiértékelése után az eredményül kapott tárolóhely lesz a függvénytag meghívásában szereplő paraméter által képviselt tárolási hely. Bemeneti vagy referenciaargumentum esetén a változót mindenképpen a metódushívás helyén kell hozzárendelni. Ha a változóhivatkozás kimeneti argumentumként van megadva, vagy egy reference_typetömbeleme, a rendszer futásidejű ellenőrzést végez annak ellenőrzésére, hogy a tömb elemtípusa megegyezik-e a paraméter típusával. Ha ez az ellenőrzés sikertelen, egy
System.ArrayTypeMismatchException
kivételt dobunk.
Megjegyzés: ez a futásidejű ellenőrzés a tömb kovariancia miatt szükséges (§17.6). végjegyzet
példa: Az alábbi kódban
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 } }
a
F
második meghívásaSystem.ArrayTypeMismatchException
-t dob, mert ab
tényleges elemtípusastring
, és nemobject
.példa vége
A metódusok, indexelők és példánykonstruktorok a legkedvezőbb paraméterüket paramétertömbnek deklarálhatják (§15.6.2.4). Az ilyen függvénytagok meghívása normál formájukban vagy kiterjesztett formájukban történik attól függően, hogy melyik alkalmazható (§12.6.4.2):
- Ha egy paramétertömböt tartalmazó függvénytagot a rendszer normál formában hív meg, a paramétertömbhöz megadott argumentumnak egyetlen kifejezésnek kell lennie, amely implicit módon átalakítható (§10.2) a paramétertömb típusára. Ebben az esetben a paramétertömb pontosan úgy működik, mint egy értékparaméter.
- Ha egy paramétertömböt tartalmazó függvénytagot kibontott formában hív meg, a meghívásnak nulla vagy több pozícióargumentumot kell megadnia a paramétertömbhöz, ahol minden argumentum egy implicit módon átalakítható kifejezés (§10.2) a paramétertömb elemtípusára. Ebben az esetben a meghívás létrehozza a paramétertömbtípus egy példányát, amelynek hossza megfelel az argumentumok számának, inicializálja a tömbpéldány elemeit a megadott argumentumértékekkel, és az újonnan létrehozott tömbpéldányt használja tényleges argumentumként.
Az argumentumlista kifejezéseit a rendszer mindig szöveges sorrendben értékeli ki.
példa: A példa tehát
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++); } }
a kimenetet hozza létre
x = 0, y = 1, z = 2 x = 4, y = -1, z = 3
példa vége
Ha egy paramétertömböt tartalmazó függvénytagot kibontott formában, legalább egy kibontott argumentummal hívnak meg, a meghívást úgy dolgozza fel a rendszer, mintha egy tömbinicializálóval ellátott tömblétrehozási kifejezést illesztettek volna be a kibontott argumentumok köré (12.8.17.4). A rendszer üres tömböt ad át, ha a paramétertömbhöz nincsenek argumentumok; nem határozza meg, hogy az átadott hivatkozás egy újonnan lefoglalt vagy meglévő üres tömbre utal-e.
Példa: Adott a deklaráció
void F(int x, int y, params object[] args);
a metódus kibontott formájának alábbi meghívásai
F(10, 20, 30, 40); F(10, 20, 1, "hello", 3.0);
pontosan megfelel
F(10, 20, new object[] { 30, 40 }); F(10, 20, new object[] { 1, "hello", 3.0 });
példa vége
Ha egy függvénytag választható paraméterei el vannak hagyva, a függvénytag deklaráció alapértelmezett argumentumai automatikusan átadásra kerülnek. (Ez magában foglalhatja egy tárolóhely létrehozását a fent leírtak szerint.)
Megjegyzés: Mivel ezek mindig állandóak, a kiértékelésük nem befolyásolja a fennmaradó argumentumok kiértékelését. végjegyzet
12.6.3 Típuskövetkozó
12.6.3.1 Általános
Ha egy általános metódust típusargumentumok megadása nélkül hív meg, egy típusú következtetés folyamat megpróbál típusargumentumokat következtetni a híváshoz. A típuskövetkeztetés jelenléte kényelmesebb szintaxist tesz lehetővé egy általános metódus meghívásához, és lehetővé teszi a programozó számára, hogy elkerülje a redundáns típusinformációk megadását.
Példa:
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> } }
A típuskövetkeztetés révén a
int
ésstring
típusargumentumok az argumentumoktól a metódusig vannak meghatározva.példa vége
A típusmegkövetkezés egy metódushívás kötésidejű feldolgozásának részeként történik (§12.8.10.2), és a meghívás túlterhelésfeloldási lépése előtt történik. Ha egy metódushívásban egy adott metóduscsoport van megadva, és a metódushívás részeként nem ad meg típusargumentumokat, a rendszer típuskövető következtetést alkalmaz a metóduscsoport minden általános metódusára. Ha a típuskövetkeztetés sikeres, akkor a rendszer a következő típusargumentumokkal határozza meg a későbbi túlterhelés-feloldás argumentumtípusait. Ha a túlterhelés feloldása egy általános metódust választ a meghíváshoz, akkor a rendszer a kikövetkeztetett típusargumentumokat használja a meghívás típusargumentumaként. Ha egy adott metódus típuskövetkeztetése meghiúsul, az a metódus nem vesz részt a túlterhelés feloldásában. A típuskövetkeztetés hibája önmagában nem okoz kötés-idő hibát. Ez azonban gyakran kötési idejű hibához vezet, amikor a túlterhelés feloldása során nem találhatóak megfelelő módszerek.
Ha az egyes megadott argumentumok nem felelnek meg pontosan egy paraméternek a metódusban (§12.6.2.2), vagy van egy nem választható paraméter megfelelő argumentum nélkül, akkor a következtetés azonnal meghiúsul. Ellenkező esetben tegyük fel, hogy az általános metódus a következő aláírást tartalmazza:
Tₑ M<X₁...Xᵥ>(T₁ p₁ ... Tₓ pₓ)
Az űrlap metódushívásával M(E₁ ...Eₓ)
a típuskövetkezés feladata az egyes típusparaméterekhez S₁...Sᵥ
egyedi típusargumentumok keresése X₁...Xᵥ
, hogy a hívás M<S₁...Sᵥ>(E₁...Eₓ)
érvényessé váljon.
A típuskövetkezési folyamatot az alábbiakban algoritmusként ismertetjük. A megfelelő fordító alternatív megközelítéssel is implementálható, feltéve, hogy minden esetben ugyanazt az eredményt éri el.
A következtetési folyamat során az egyes típusparaméterek Xᵢ
vagy hozzárendelve vannak egy adott típusra Sᵢ
, vagy nincs hozzárendelve, és egy kapcsolódó korlátok halmazával rendelkezik . Minden egyes korlát valamilyen típus T
. Kezdetben minden Xᵢ
típusváltozó nincs rögzítve, és üres határértékekkel rendelkezik.
A típuskövetkeztetés fázisokban történik. Minden fázis megpróbál típusargumentumokat kikövetkezni több típusváltozóra az előző fázis eredményei alapján. Az első fázis a korlátok néhány kezdeti következtetését teszi lehetővé, míg a második fázis a típusváltozókat konkrét típusokra javítja, és további korlátokat is kikövetkeztet. Előfordulhat, hogy a második fázist többször is meg kell ismételni.
Megjegyzés: A típuskövetkeztetést más környezetekben is használják, beleértve a metóduscsoportok átalakítását is (12.6.3.14.) és a kifejezéskészletek legjobb gyakori típusát (§12.6.3.15). végjegyzet
12.6.3.2 Az első fázis
Az egyes metódusargumentumok esetében Eᵢ
:
- Ha
Eᵢ
névtelen függvény, akkor explicit paramétertípus-következtetés történik aEᵢ
. - Ellenkező esetben, ha
típus , és a megfelelő paraméter egy értékparaméter ( §15.6.2.2 ), akkor történik egyalsó határú következtetés (§12.6.3.10 )-ról -ra. - Ellenkező esetben, ha
Eᵢ
típusU
, és a megfelelő paraméter egy referenciaparaméter (§15.6.2.3.3), vagy kimeneti paraméter (§15.6.2.3.4), akkor pontos következtetési (§12.6.3.9) történik innenU
odaTᵢ
. - Ellenkező esetben, ha a
Eᵢ
típusúU
, és a megfelelő paraméter egy bemeneti paraméter (§15.6.2.3.2), valamintEᵢ
egy bemeneti argumentum, akkor pontos következtetést (exact inference) vonnak le §12.6.3.9) aU
-ról aTᵢ
-ra. - Ellenkező esetben, ha
típusú van, és a megfelelő paraméter egy bemeneti paraméter ( §15.6.2.3.2 ), akkor alsó határú következtetés történik a-től a -ba ( §12.6.3.10 ). - Ellenkező esetben nincs következtetés az argumentumra vonatkozóan.
12.6.3.3 A második fázis
A második fázis a következőképpen halad:
- Minden olyan nem rögzített típusváltozó, amely nem
Xᵢ
(§12.6.3.6) semmilyen másiktól, rögzítve van (Xₑ
). - Ha ilyen típusváltozók nem léteznek, minden nem rögzített típusváltozó
Xᵢ
rögzített, amelyekre az alábbi feltételek mindegyike igaz:- Legalább egy típusváltozó
Xₑ
van, amely attól függXᵢ
-
Xᵢ
nem üres határok halmazával rendelkezik
- Legalább egy típusváltozó
- Ha nem léteznek ilyen típusváltozók, és továbbra is van meg nem oldott típusváltozó, a típuskövetkeztetés meghiúsul.
- Ha azonban nincsenek további nem rögzített típusváltozók, a típuskövetkeztetés sikeres lesz.
- Ellenkező esetben, minden olyan argumentumra
, amelynek a megfelelő paramétertípusa , ahol a kimeneti típusok (§12.6.3.5 ) fixálatlan típusváltozókat tartalmaznak, de a bemeneti típusok (§12.6.3.4 ) nem, egy kimeneti típusra vonatkozó következtetés( §12.6.3.7 ) készül ettől erre. Ezután a második fázis ismétlődik.
12.6.3.4 Bemeneti típusok
Ha a
12.6.3.5 Kimeneti típusok
Ha
12.6.3.6 Függőség
A nem rögzített típusváltozó Xᵢ
közvetlenül függ egy másik nem rögzített típusváltozótól Xₑ
, ha egy bizonyos argumentum Eᵥ
, amelynek típusa Tᵥ
, megjelenik egy Xₑ
, amelynek típusa Eᵥ
, és megjelenik egy Tᵥ
Xᵢ
, amelynek típusa .
Xₑ
Xᵢ
attól függ, hogy Xₑ
közvetlenülXᵢ
függ-e, vagy attól, hogy Xᵢ
közvetlenülXᵥ
függ és hogy Xᵥ
attól függXₑ
. Így az “függ”-hoz képest a “közvetlenül függ”-tól való tranzitív, de nem reflexív lezárás.
12.6.3.7 Kimeneti típus következtetései
A kimeneti típusának következtetése történik egy kifejezésből E
egy T típusra a következő módon:
- Ha a
E
egy névtelen függvény, amelynek visszatérési típusaU
(§12.6.3.13), ésT
egy delegált típus vagy kifejezésfa típus a visszatérési típussalTₓ
esetén, akkor egy alsó határú következtetést (§12.6.3.10) készítenek azU
-ból azTₓ
felé. - Ellenkező esetben, ha a
E
metóduscsoport, és aT
delegált típusú vagy kifejezésfa típusú,T₁...Tᵥ
paramétertípusokkal ésTₓ
visszatérési típussal rendelkezik, és ha aE
típusúT₁...Tᵥ
túlterhelés feloldásával egyetlen,U
visszatérési típussal rendelkező metódust kapunk, akkor egy alsó határú következtetéstkell elvégezniU
ésTₓ
között. - Ellenkező esetben, ha
egy típusú kifejezés, akkor alsó határi következtetés a alapján történik a -től -ig. - Ellenkező esetben nem történik következtetés.
12.6.3.8 Kifejezett paramétertípus-levezetés
A
- Ha
E
egy explicit módon megadott névtelen függvényU₁...Uᵥ
paramétertípusokkal, ésT
egy delegált vagy kifejezésfa típusúV₁...Vᵥ
paramétertípusokkal, akkor mindenUᵢ
esetén egy pontos következtetési (§12.6.3.9) történikUᵢ
a megfelelőVᵢ
-ra.
12.6.3.9 Pontos következtetések
A pontos következtetésU
típusbólV
típusba az alábbiak szerint történik:
- Ha
V
az egyik nem rögzítettXᵢ
, akkorU
hozzáadódik aXᵢ
pontos határainak készletéhez. - Ellenkező esetben a
V₁...Vₑ
és aU₁...Uₑ
a következő esetek bármelyikének ellenőrzésével határozható meg:-
V
tömbtípusúV₁[...]
ésU
egy azonos rangú tömbtípusU₁[...]
-
V
aV₁?
típus,U
pedig aU₁
-
V
egy konstruált típusC<V₁...Vₑ>
, ésU
is egy konstruált típusC<U₁...Uₑ>
.
Ha ezen esetek bármelyike fennáll, akkor a minden esetéből pontos következtetés történik a megfelelőUᵢ
-raVᵢ
.
-
- Ellenkező esetben nem történik következtetés.
12.6.3.10 Alsó határú következtetés
A alsó határa aU
-ből aV
típusra történő következtetésnél az alábbiak szerint van megállapítva:
- Ha
V
az egyik nem rögzítettXᵢ
, akkorU
hozzáadódik aXᵢ
alsó határainak halmazához. - Ellenkező esetben, ha
V
a típusV₁?
, ésU
a típusU₁?
, akkor alsó korlát következtetés vezethető leU₁
-bőlV₁
-re. - Ellenkező esetben a
U₁...Uₑ
és aV₁...Vₑ
a következő esetek bármelyikének ellenőrzésével határozható meg:-
V
egy tömbtípusV₁[...]
, ésU
egy azonos rangú tömbtípusU₁[...]
-
V
IEnumerable<V₁>
,ICollection<V₁>
,IReadOnlyList<V₁>>
,IReadOnlyCollection<V₁>
vagyIList<V₁>
egyike, ésU
egydimenziós tömbtípusU₁[]
-
V
egyclass
,struct
,interface
vagydelegate
típusúC<V₁...Vₑ>
, és létezik egy olyan egyedi típusC<U₁...Uₑ>
, amelyre igaz, hogyU
(vagy haU
parameter
típusú, akkor a tényleges alaposztálya, vagy a tényleges interfészkészlet bármely tagja) megegyezik vele,inherits
belőle származik (közvetlenül vagy közvetve), vagy (közvetlenül vagy közvetve) implementáljaC<U₁...Uₑ>
-t. - (Az "egyediség" korlátozás azt jelenti, hogy az interfész
C<T>{} class U: C<X>, C<Y>{}
esetén nem történik következtetés aU
és aC<T>
közötti következtetéskor, mertU₁
lehetX
vagyY
.)
Ha a fentiek bármelyike érvényes, akkor következtetést vonunk le az egyesUᵢ
és a megfelelőVᵢ
között az alábbiak szerint. - Ha
Uᵢ
nem ismert referenciatípusként, akkor pontos következtetés készül. - Ellenkező esetben, ha a
U
egy tömbtípus, akkor egy alsó határon alapuló következtetés történik. - Ellenkező esetben, ha
V
C<V₁...Vₑ>
akkor a következtetés ai-th
C
típusparaméterétől függ:- Ha kovariáns, akkor egy alsó határú következtetés jön létre.
- Ha ez contravariant, akkor egy felső határú következtetési jön létre.
- Ha az adott elem invariáns, akkor egy pontos következtetés történik.
-
- Ellenkező esetben nem történik következtetés.
12.6.3.11 Felső határú következtetés
Egy típusból a típus U
típusúV
felső korlátú következtetése az alábbiak szerint történik:
- Ha
V
az egyik nem rögzítettXᵢ
, akkorU
lesz hozzáadva aXᵢ
felső határainak halmazához. - Ellenkező esetben a
V₁...Vₑ
és aU₁...Uₑ
a következő esetek bármelyikének ellenőrzésével határozható meg:-
U
egy tömbtípusU₁[...]
, ésV
egy azonos rangú tömbtípusV₁[...]
-
U
IEnumerable<Uₑ>
,ICollection<Uₑ>
,IReadOnlyList<Uₑ>
,IReadOnlyCollection<Uₑ>
vagyIList<Uₑ>
egyike, ésV
egydimenziós tömbtípusVₑ[]
-
U
aU1?
típus,V
pedig aV1?
-
U
olyan osztály, struktúra, interfész vagy delegált típus, amelyC<U₁...Uₑ>
, ésV
olyanclass, struct, interface
vagydelegate
típus, amely közvetlenül vagy közvetettenidentical
-ra vonatkozik,inherits
-ről származik, vagy közvetlenül vagy közvetetten egyedi típustC<V₁...Vₑ>
megvalósít. - (Az "egyediség" korlátozás azt jelenti, hogy ha adott egy
C<T>{} class V<Z>: C<X<Z>>, C<Y<Z>>{}
interfész, akkor nem történik következtetés aC<U₁>
-ről aV<Q>
-re. Nem történik következtetés aU₁
-ról sem aX<Q>
-re, sem aY<Q>
-re.)
Ha a fentiek bármelyike érvényes, akkor következtetést vonunk le az egyesUᵢ
és a megfelelőVᵢ
között az alábbiak szerint. - Ha
Uᵢ
nem ismert referenciatípusként, akkor pontos következtetés készül. - Ellenkező esetben, ha
V
tömbtípus, akkor felső határú következtetési jön létre - Ellenkező esetben, ha
U
C<U₁...Uₑ>
akkor a következtetés ai-th
C
típusparaméterétől függ:- Ha kovarianciáns, akkor felső határú következtetési jön létre.
- Ha ez contravariant, akkor egy alsó határú következtetési jön létre.
- Ha az adott elem invariáns, akkor egy pontos következtetés történik.
-
- Ellenkező esetben nem történik következtetés.
12.6.3.12 Rögzítés
Az nem rögzített típusú változót Xᵢ
a következőképpen rögzítették egy halmaz kötéseivel:
- A jelölttípusok
Uₑ
halmaza aXᵢ
korlátkészletének összes típusának halmazaként kezdődik. - A
Xᵢ
minden egyes kötését egymás után vizsgáljuk meg: AXᵢ
minden pontos korlát U értékére az összes olyanUₑ
típust eltávolítjuk a jelöltkészletből, amely nem azonos aU
típussal. A jelöltkészletből eltávolításra kerül aU
minden olyan típusXᵢ
, amelyhez nincsUₑ
alól implicit átváltás minden egyes alsó határ miatt. Minden felső határa UXᵢ
esetén minden olyan típusUₑ
eltávolításra kerül a jelöltkészletből, amelyhez nincs implicit konverzióU
. - Ha a fennmaradó jelölttípusok között
Uₑ
van egy egyedi típusV
, amelyhez implicit konverzió lehetséges az összes többi jelölttípusból, akkorXᵢ
rögzítve vanV
. - Ellenkező esetben a típus következtetés meghiúsul.
12.6.3.13 A következtetett visszatérési típus
A névtelen függvények F
kikövetkeztethető visszatérési típusa a típuskövetkeztetés és a túlterhelés feloldása során használatos. A kikövetkeztetett visszatérési típus csak olyan névtelen függvény esetében határozható meg, amelyben az összes paramétertípus ismert, akár azért, mert explicit módon vannak megadva, névtelen függvénykonvertálással, vagy a típuskövetkeztetés során következtetve egy általános metódus meghívására.
A tényleges visszatérési típus a következőképpen határozható meg:
- Ha a
F
törzse egy kifejezés, ami típusú, akkor aF
tényleges visszatérési típusa az adott kifejezés típusa. - Ha a
F
törzse egy blokk, és a blokkreturn
utasításainak kifejezéskészlete a leggyakrabban használtT
(§12.6.3.15), akkor aF
kikövetkzett tényleges visszatérési típusaT
. - Ellenkező esetben a hatékony visszatérési típus nem következtethető ki
F
.
A -inferált visszatérési típus a következőképpen van meghatározva:
- Ha
F
aszinkron, és a törzseF
vagy egy semmiként besorolt kifejezés (12.2. §), vagy olyan blokk, amelyben nincsenekreturn
kifejezések, a következtetett visszatérési típus«TaskType»
(15.14.1. §). - Ha
F
aszinkron, és tényleges eredménytípusaT
van, a kikövetkeztetett visszatérési típus«TaskType»<T>»
(15.14.1.1. §). - Ha
F
nem aszinkron, ésT
kikövetkeztetett tényleges visszatérési típussal rendelkezik, akkor a kikövetkeztetett visszatérési típusT
. - Máskülönben nem lehet következtetni a
F
visszatérési típusára.
Példa: A névtelen függvényeket érintő típuskövetkezmények példájaként vegye figyelembe a
Select
osztályban deklaráltSystem.Linq.Enumerable
bővítménymetódust: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); } } } }
Feltéve, hogy a
System.Linq
névtér egyusing namespace
irányelvvel lett importálva, és aCustomer
osztály rendelkezik egyName
típusústring
tulajdonsággal, aSelect
módszer használható az ügyfelek listájából a nevek kiválasztására.List<Customer> customers = GetCustomerList(); IEnumerable<string> names = customers.Select(c => c.Name);
A (bővítménymetódus-meghívása (
Select
)) a meghívás statikus metódusmeghívásra való átírásával történik.IEnumerable<string> names = Enumerable.Select(customers, c => c.Name);
Mivel a típusargumentumok nincsenek explicit módon megadva, a rendszer a típusargumentumok következtetésére használja a típusargumentumokat. Először is az ügyfél érvelése a forrásparaméterhez kapcsolódik, amely arra következtet, hogy
TSource
Customer
lesz. Ezután a fent ismertetett névtelen függvénytípus-következtetési folyamat során ac
aCustomer
típust kapja, és ac.Name
kifejezés a választóparaméter visszatérési típusához kapcsolódvaTResult
string
jelölését kapja meg. Így a meghívás egyenértékű aSequence.Select<Customer,string>(customers, (Customer c) => c.Name)
és az eredmény
IEnumerable<string>
típusú.Az alábbi példa bemutatja, hogy a névtelen függvénytípus-következtetés hogyan teszi lehetővé a típusadatok "áramlását" egy általános metódushívás argumentumai között. Tekintettel a következő módszerre és meghívásra:
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); } }
A típuslevezetés a hívás esetében az alábbiak szerint történik: Először is az "1:15:30" argumentum az értékparaméterhez kapcsolódik, amiből az következik, hogy
X
leszstring
. Ezután az első névtelen függvény paramétere (s
) megkapja astring
típusú besorolást, és aTimeSpan.Parse(s)
kifejezés af1
visszatérési típusához kapcsolódik, ami alapján aY
értéketSystem.TimeSpan
típusúnak következtetjük. Végül a második névtelen függvényt
paraméterének megadott típusa azSystem.TimeSpan
, és at.TotalHours
kifejezés kapcsolódik af2
visszatérési típusához, így aZ
típusátdouble
-ként származtatjuk. Így a meghívás eredményedouble
típusú.példa vége
12.6.3.14 Metóduscsoportok konvertálásának típuskövetkeztetése
Általános metódusok hívásaihoz hasonlóan a típuskövetkeztetést is alkalmazni kell, ha egy általános metódust tartalmazó metóduscsoportot M
átalakítanak egy adott delegált típus D
-re (§10.8). Adott egy metódus
Tₑ M<X₁...Xᵥ>(T₁ x₁ ... Tₑ xₑ)
és a metóduscsoport M
a D
delegált típushoz rendelve, a típuskövetkeztetés feladata meg kell találni a típusargumentumokat S₁...Sᵥ
annak érdekében, hogy a kifejezés:
M<S₁...Sᵥ>
összeegyeztethetővé válik (§20.2) a D
-vel.
Az általános metódushívások típuskövető algoritmusával ellentétben ebben az esetben csak az argumentumok típusai jelennek meg, nincsenek argumentum kifejezések. Különösen nincsenek névtelen függvények, ezért nincs szükség több következtetési fázisra.
Ehelyett minden Xᵢ
-ra nincs meghatározva határ, a típuskövetkeztetés sikertelen. Ellenkező esetben az összes Xᵢ
rögzített a megfelelő Sᵢ
, amely a típuslevezetés eredménye.
12.6.3.15 Kifejezéskészletek leggyakoribb típusának megkeresése
Bizonyos esetekben gyakori típust kell kikövetkeztetni egy kifejezéskészletre. Az implicit típusú tömbök elemtípusai és a blokk testtel rendelkező névtelen függvények visszatérési típusai ilyen módon határozhatók meg.
A kifejezéskészletek E₁...Eᵥ
leggyakrabban használt típusa az alábbiak szerint van meghatározva:
- Új nem rögzített típusváltozó
X
kerül bevezetésre. - Minden egyes kifejezésnél
Ei
egy kimeneti típus következtetés (§12.6.3.7) hajtódik végre, aX
felé. -
X
rögzített (§12.6.3.12), ha lehetséges, és az eredményként kapott típus a legjobb közös típus. - Ellenkező esetben a következtetés meghiúsul.
Megjegyzés: Intuitív módon ez a következtetés egyenértékű egy metódus meghívásával,
void M<X>(X x₁ ... X xᵥ)
aEᵢ
argumentumként és aX
következtetésével. végjegyzet
12.6.4 Túlterhelés feloldás
12.6.4.1 Általános
A túlterhelés feloldása egy kötési idejű mechanizmus, amely az argumentumlista és a jelölt függvénytagok alapján kiválasztja a legjobb meghívandó függvényt. A túlterhelés feloldása kiválasztja azt a függvénytagot, amelyet a C#-ban a következő különböző környezetekben szeretne meghívni:
- Egy invocation_expression nevesített metódus meghívása (§12.8.10).
- Egy objektum_létrehozó_kifejezésben megnevezett példánykonstruktor meghívása (§12.8.17.2).
- Indexelő tartozék meghívása element_access (§12.8.12).
- Egy kifejezésben hivatkozott előre definiált vagy felhasználó által definiált operátor meghívása (§12.4.4 és §12.4.5).
Ezen környezetek mindegyike egyedi módon határozza meg a jelölt függvénytagok halmazát és az argumentumok listáját. A metódus hívására szolgáló jelöltek listája például nem tartalmazza a felülírással jelölt metódusokat (§12.5), és az ősi osztályban található metódusok nem számítanak jelöltnek, ha a származtatott osztály valamelyik metódusa alkalmazható (§12.8.10.2).
A jelölt függvénytagok és az argumentumlista azonosítása után a legjobb függvénytag kiválasztása minden esetben ugyanaz:
- Először is a jelölt függvénytagok halmaza azokra a függvénytagokra csökken, amelyek az adott argumentumlistára vonatkoznak (12.6.4.2.). Ha ez a csökkentett készlet üres, fordítási időhiba lép fel.
- Ezután a megfelelő jelölt függvénytagok közül a legjobb tag található. Ha a készlet csak egy függvénytagot tartalmaz, akkor ez a függvénytag a legjobb függvénytag. Ellenkező esetben a legjobb függvény az az egyetlen függvény, amely jobb, mint az összes többi az adott argumentumlistára tekintettel, feltéve, hogy minden függvényt összehasonlítanak az összes többivel a szabályok szerint a §12.6.4.3-ben. Ha nincs pontosan egy olyan függvénytag, amely jobb, mint az összes többi függvénytag, akkor a függvénytagok meghívása nem egyértelmű, és kötési idejű hiba történik.
Az alábbi alklámok határozzák meg az alkalmazandó függvénytag
12.6.4.2 Alkalmazható függvénytag
Egy függvénytagot akkor nevezünk alkalmazható függvénytagnak egy argumentumlistával A
kapcsolatban, ha az alábbiak mindegyike igaz:
- A
A
minden argumentuma megfelel a függvénytag-deklaráció egyik paraméterének, ahogyan a 12.6.2.2szakaszban le van írva. Legfeljebb egy argumentum felelhet meg egy paraméternek, és bármely paraméter, amelyhez nem tartozik argumentum, opcionális paraméter. - A
A
minden argumentuma esetében az argumentum paraméterátadási módja megegyezik a megfelelő paraméter paraméterátadási módjával, és- értékparaméter vagy paramétertömb esetén implicit átalakítás (§10.2) létezik az argumentumkifejezésből a megfelelő paraméter típusára, vagy
- hivatkozási vagy kimeneti paraméter esetén az argumentumkifejezés típusa (ha van ilyen) és a megfelelő paraméter típusa között identitáskonvertálás történik, vagy
- bemeneti paraméter esetén, ha a megfelelő argumentum rendelkezik a
in
módosítóval, az argumentumkifejezés típusa (ha van ilyen) és a megfelelő paraméter típusa között identitáskonvertálás történik, vagy - bemeneti paraméter esetén, ha a megfelelő argumentum kihagyja a
in
módosító értékét, az argumentumkifejezésből implicit átalakítás (§10.2) létezik az argumentumkifejezésből a megfelelő paraméter típusára.
Paramétertömböt tartalmazó függvénytagok esetén, ha a függvénytagra a fenti szabályok érvényesek, akkor azt mondjuk, hogy a normál formájábanalkalmazható. Ha egy paramétertömböt tartalmazó függvénytag nem alkalmazható a normál formájában, akkor a függvénytag alkalmazható lehet a bővített űrlapon:
- A kibontott űrlap úgy jön létre, hogy a függvénytag-deklarációban szereplő paramétertömbet a paramétertömb elemtípusának nulla vagy több értékparaméterére cseréli, így az argumentumlistában szereplő argumentumok száma
A
megegyezik a paraméterek teljes számával. HaA
kevesebb argumentummal rendelkezik, mint a függvénytag-deklaráció rögzített paramétereinek száma, a függvénytag kibontott formája nem hozható létre, ezért nem alkalmazható. - Ellenkező esetben a kibontott űrlap akkor alkalmazható, ha a
A
minden argumentuma esetében az alábbiak egyike igaz:- az argumentum paraméterátadási módja megegyezik a megfelelő paraméter paraméterátadási módjával, és:
- rögzített értékparaméter vagy a bővítés által létrehozott értékparaméter esetében implicit átalakítás (§10.2) létezik az argumentumkifejezésből a megfelelő paraméter típusára; vagy
- by-reference paraméter esetén az argumentumkifejezés típusa megegyezik a megfelelő paraméter típusával.
- az argumentum paraméterátadási módja az érték, a megfelelő paraméter paraméterátadási módja pedig bemenet, és implicit konverzió (10.2) létezik az argumentumkifejezésből a megfelelő paraméter típusára.
- az argumentum paraméterátadási módja megegyezik a megfelelő paraméter paraméterátadási módjával, és:
Ha az argumentumtípusból a bemeneti paraméter paramétertípusára történő implicit átalakítás dinamikus implicit átalakítás (§10.2.10), az eredmények nem lesznek meghatározva.
Példa: A következő deklarációk és metódushívások alapján:
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 }
példa vége
- Statikus módszer csak akkor alkalmazható, ha a metóduscsoport egy simple_name vagy member_access egy típuson keresztül származik.
- A példánymetódus csak akkor alkalmazható, ha a metóduscsoport egy simple_name-ből, egy változón vagy értéken keresztüli member_access-ből vagy base_access-ből ered.
- Ha a metóduscsoport egy simple_nameeredménye, a példánymetódus csak akkor alkalmazható, ha
this
hozzáférés engedélyezett §12.8.14.
- Ha a metóduscsoport egy simple_nameeredménye, a példánymetódus csak akkor alkalmazható, ha
- Ha a metóduscsoport egy olyan member_access eredménye, amely vagy egy példány vagy egy típus révén történhet, ahogyan azt a 12.8.7.2leírja, mind a példánymetódusok, mind a statikus metódusok alkalmazhatók.
- Nem alkalmazható az az általános metódus, amelynek típusargumentumai (explicit módon megadva vagy kikövetkeztetve) nem felelnek meg a kényszereknek.
- A metóduscsoport-átalakítás kontextusában identitásátalakítást (§10.2.2) vagy implicit referenciaátalakítást (§10.2.8) kell létrehozni a metódus visszatérési típusától a meghatalmazott visszatérési típusához. Ellenkező esetben a jelölt módszer nem alkalmazható.
12.6.4.3 Kiválóbb függvénytag
A jobb függvénytag meghatározásához egy lecsupaszított argumentumlista A
jön létre, amely csak az argumentumkifejezéseket tartalmazza az eredeti argumentumlistában megjelenő sorrendben, és kihagy minden out
vagy ref
argumentumot.
Az egyes jelölt függvénytagok paraméterlistái a következő módon jönnek létre:
- A kibontott űrlapot akkor használja a rendszer, ha a függvénytag csak a kibontott űrlapon volt alkalmazható.
- A megfelelő argumentumok nélküli választható paraméterek törlődnek a paraméterlistából
- A referencia- és kimeneti paraméterek törlődnek a paraméterlistából
- A paraméterek átrendezése úgy történik, hogy az argumentumlista megfelelő argumentumával azonos helyen történjenek.
Egy argumentumlista A
egy halmaz argumentumkifejezéssel {E₁, E₂, ..., Eᵥ}
, valamint két alkalmazható függvénytaggal Mᵥ
és Mₓ
, amelyek paramétertípusai {P₁, P₂, ..., Pᵥ}
és {Q₁, Q₂, ..., Qᵥ}
, úgy van definiálva, hogy Mᵥ
egy jobb függvénytag legyen, mint Mₓ
, ha
- minden argumentum esetében az
Eᵥ
-rőlQᵥ
-re történő implicit átalakítás nem jobb, mint aEᵥ
-rőlPᵥ
-re történő implicit átalakítás, és - legalább egy argumentum esetében a
Eᵥ
-rólPᵥ
-ra való átalakítás jobb, mint aEᵥ
-rólQᵥ
- ra történő átalakítás.
Ha a paramétertípus-sorozatok {P₁, P₂, ..., Pᵥ}
és {Q₁, Q₂, ..., Qᵥ}
egyenértékűek (azaz minden Pᵢ
identitásátalakítással rendelkezik a megfelelő Qᵢ
-ra), a jobb függvényjellemző meghatározásához a következő kötéstörő szabályokat kell sorrendben alkalmazni.
- Ha
Mᵢ
nem általános módszer, ésMₑ
általános módszer, akkorMᵢ
jobb, mintMₑ
. - Ellenkező esetben, ha a
Mᵢ
a normál formájában alkalmazható, ésMₑ
params tömbje van, és csak kibontott formában alkalmazható, akkorMᵢ
jobb, mintMₑ
. - Ellenkező esetben, ha mindkét metódus paramtömbökből áll, és csak a kibontott formájukban alkalmazható, és ha a
Mᵢ
params tömbje kevesebb elemből áll, mint aMₑ
params tömbje, akkorMᵢ
jobb, mintMₑ
. - Ellenkező esetben, ha
Mᵥ
több paramétertípust tartalmaz, mintMₓ
, akkorMᵥ
jobb, mintMₓ
. Legyen{R1, R2, ..., Rn}
és{S1, S2, ..., Sn}
aMᵥ
ésMₓ
nem példányosított és nem kibontott paramétertípusainak jelölése.Mᵥ
paramétertípusokMₓ
s-nél pontosabbak, ha minden paraméter esetébenRx
nem kevésbé specifikus, mintSx
, és legalább egy paraméter esetébenRx
pontosabb, mintSx
:- A típusparaméter kevésbé specifikus, mint egy nem típusparaméter.
- Rekurzív módon a létrehozott típus pontosabb, mint egy másik (azonos számú típusargumentummal rendelkező) típus, ha legalább egy típusargumentum pontosabb, és egyetlen típusargumentum sem kevésbé specifikus, mint a másik típusargumentum.
- Egy tömbtípus pontosabb, mint egy másik tömbtípus (azonos számú dimenzióval), ha az első elemtípusa pontosabb, mint a második elemtípus.
- Ellenkező esetben, ha az egyik elem nem felülemelt operátor, a másik pedig felülemelt operátor, akkor a nem felülemelt operátor jobb.
- Ha egyik függvénytag sem bizonyult jobbnak, és az
Mᵥ
összes paramétere rendelkezik megfelelő argumentumokkal, míg az alapértelmezett argumentumokat legalább egy választható paraméterre kell cserélni aMₓ
, akkorMᵥ
jobb, mintMₓ
. - Ha legalább egy paraméter esetében
Mᵥ
a jobb paraméterátadási módot (§12.6.4.4) használja, mint aMₓ
megfelelő paramétere, ésMₓ
paraméterei közül egyik sem használ jobb paraméterátadási módot, mintMᵥ
, akkorMᵥ
jobb, mintMₓ
. - Ellenkező esetben egyetlen függvénytag sem jobb.
12.6.4.4 Jobb paraméterátadási mód
Két túlterhelt metódus megfelelő paraméterei csak paraméterátadási módban térhetnek el, feltéve, hogy a két paraméter egyike értékátadási móddal rendelkezik, az alábbiak szerint:
public static void M1(int p1) { ... }
public static void M1(in int p1) { ... }
A int i = 10;
alapján, a 12.6.4.2szerint, a M1(i)
és M1(i + 5)
hívások esetén mindkét túlterhelés alkalmazható. Ilyen esetekben a módszer, amely a paraméterátadás érték szerinti módját használja, a jobb paraméterátadási mód választása.
Megjegyzés: Nincs szükség ilyen választásra a bemeneti, kimeneti vagy referenciaátadási módok argumentumaihoz, mivel ezek az argumentumok csak pontosan ugyanazoknak a paraméterátadási módoknak felelnek meg. végjegyzet
12.6.4.5 Jobb átalakítás kifejezésből
Egy kifejezést C₁
típussá E
konvertáló implicit átalakítás, valamint egy kifejezést T₁
típussá C₂
konvertáló implicit átalakítás esetén, E
egy T₂
, mint C₁
, ha az alábbiak valamelyike fennáll.
-
E
pontosan megegyezikT₁
-gyel, ésE
nem pontosan egyezik megT₂
-mal (§12.6.4.6) -
E
pontosan egyezik mindkettővel vagy egyikkel sem a következőek közül:T₁
ésT₂
, illetveT₁
jobb konverziós cél, mintT₂
(§12.6.4.7) -
E
egy metóduscsoport (§12.2),T₁
kompatibilis (§20.4) a metóduscsoport egyetlen legjobb metódusával az átalakításhozC₁
, ésT₂
nem kompatibilis a metóduscsoport egyetlen legjobb metódusával az átalakításhozC₂
.
12.6.4.6 Pontosan egyező kifejezés
Ha egy kifejezés E
és egy T
típust tartalmaz, E
pontosan egyezikT
, ha az alábbiak egyikét tartalmazza:
-
E
S
típussal rendelkezik, és azonosítási átalakítás létezikS
-rólT
-ra. -
E
egy névtelen függvény,T
vagy delegált típusD
, vagy kifejezésfa típusExpression<D>
, és az egyik a következő feltételek közül teljesül:- A
X
paraméterlistájában létezik egyE
következtetett visszatérési típusD
számára (§12.6.3.12), és létezik egy identitásátalakításX
-ről aD
visszatérési típusára. -
E
egyasync
lambda, amelynek nincs visszatérési értéke, ésD
olyan visszatérési típussal rendelkezik, amely nem általános«TaskType»
- Vagy
E
nem aszinkron, ésD
rendelkezikY
visszatérési típussal, vagyE
aszinkron, ésD
rendelkezik«TaskType»<Y>
visszatérési típussal (15.14.1. §), és az alábbiak egyike igaz:- A(z)
E
törzse az a kifejezés, amely pontosan megegyezikY
-jel. - A
E
törzse egy blokk, ahol minden visszatérési utasítás egy pontosan egyező kifejezést ad visszaY
- A(z)
- A
12.6.4.7 Jobb konverziós cél
Adott két típus, T₁
és T₂
, T₁
egy jobb konverziós célpont, mint T₂
, ha az alábbi feltételek egyike fennáll:
- Az
T₁
-rőlT₂
-ra történő implicit átalakítás létezik, és nem létezik implicit átalakításT₂
-rólT₁
-ra -
T₁
is«TaskType»<S₁>
(§15.14.1),T₂
is«TaskType»<S₂>
, ésS₁
egy jobb konverziós cél, mintS₂
-
T₁
is«TaskType»<S₁>
(§15.14.1),T₂
is«TaskType»<S₂>
, ésT₁
specializáltabb, mintT₂
-
T₁
S₁
vagyS₁?
, ahol aS₁
aláírt integráltípus,T₂
pedigS₂
vagyS₂?
, ahol aS₂
aláírás nélküli integráltípus. Kifejezetten:-
S₁
sbyte
ésS₂
byte
,ushort
,uint
vagyulong
-
S₁
short
ésS₂
ushort
,uint
vagyulong
-
S₁
int
, ésS₂
uint
, vagyulong
-
S₁
long
ésS₂
ulong
-
12.6.4.8 Túlterhelés általános osztályokban
megjegyzés: Bár a deklarált aláírásoknak egyedinek kell lenniük (8.6. §), lehetséges, hogy a típusargumentumok helyettesítése azonos aláírásokat eredményez. Ilyen esetben a túlterhelés feloldása kiválasztja az eredeti aláírások legspecifikusabb (12.6.4.3) pontját (a típusargumentumok helyettesítése előtt), ha létezik, és egyéb esetben hibát jelez. végjegyzet
példa: Az alábbi példák a szabálynak megfelelően érvényes és érvénytelen túlterheléseket mutatnak:
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); }
példa vége
12.6.5 A dinamikus taghívás fordítási idő alatti ellenőrzése
Annak ellenére, hogy egy dinamikusan kötött művelet túlterhelésfeloldása futásidőben történik, néha fordítási időben is lehet tudni azoknak a függvénytagoknak a listáját, amelyek közül túlterhelést választunk:
- Delegált hívás esetén (§12.8.10.4) a lista egyetlen függvénytagból áll, amely ugyanazzal a paraméterlistával rendelkezik, mint a meghívás delegált_típusa .
- A metódushívás (§12.8.10.2) esetében egy típuson vagy olyan értéken, amelynek statikus típusa nem dinamikus, a metóduscsoportban elérhető metódusok halmaza fordítási időben ismert.
- Objektumlétrehozó kifejezés (§12.8.17.2) esetében a típushoz tartozó akadálymentes konstruktorkészlet fordítási időben ismert.
- Az indexelő hozzáférés (§12.8.12.3) esetében a fogadóban elérhető indexelők készlete fordítási időben ismert.
Ezekben az esetekben az ismert függvénytagok halmazához tartozó minden egyes tagon korlátozott fordítási időben végzett ellenőrzést hajtanak végre annak megállapítására, hogy egyértelműen kizárható-e, hogy soha nem kerül meghívásra futásidőben. Minden egyes függvénytag F
esetén összeállításra kerül egy módosított paraméter- és argumentumlista.
- Először is, ha a
F
egy általános metódus, és a típusargumentumok meg lettek adva, akkor azokat a paraméterlistában szereplő típusparaméterek helyettesítik. Ha azonban nem adtak meg típusargumentumokat, nem történik ilyen helyettesítés. - Ezután minden olyan paraméter, amelynek a típusa nyitott (azaz tartalmaz egy típusparamétert, lásd a 8.4.3. szakaszt) a megfelelő argumentumokkal együtt elhagyásra kerül.
Ahhoz, hogy F
megfeleljen az ellenőrzésnek, az alábbiak mindegyikének teljesülnie kell:
- A
F
módosított paraméterlistája a §12.6.4.2szerinti módosított argumentumlistára vonatkozik. - A módosított paraméterlistában szereplő összes létrehozott típus megfelel a korlátozásainak (§8.4.5).
- Ha a fenti lépésben a
F
típusparamétereit helyettesítették, a korlátozások teljesülnek. - Ha a
F
statikus metódus, a metóduscsoport nem származhat olyan member_access-ból, amelynek fogadója fordítási időben ismert változóként vagy értékként. - Ha a
F
egy példánymetódus, akkor a metóduscsoport nem származhat olyan tag_elérésből, amelynek fogadója fordítási időpontban ismert típus.
Ha egy jelölt sem teljesíti ezt a tesztet, fordítási időhiba lép fel.
12.6.6 Függvénytagok meghívása
12.6.6.1 Általános
Ez az alklám egy adott függvénytag meghívásának futásidejű folyamatát ismerteti. Feltételezzük, hogy egy kötési idejű folyamat már meghatározta az adott tag meghívását, esetleg túlterhelésfeloldást alkalmazva a jelölt függvénytagokra.
A meghívási folyamat leírásához a függvénytagok két kategóriába sorolhatók:
- Statikus függvénytagok. Ezek statikus metódusok, statikus tulajdonságkiegészítők és felhasználó által definiált operátorok. A statikus függvények tagjai mindig nem virtuálisak.
- Példányfüggvény-tagok. Ezek a példánymetódusok, példánykonstruktorok, példánytulajdonság-elérők és az indexelő-elérők. A példányfüggvény tagjai vagy nem virtuálisak, vagy virtuálisak, és mindig egy adott példányon hívják meg őket. A példányt egy példánykifejezés számítja ki, és a függvénytagon belül
this
(§12.8.14) válik elérhetővé. Példánykonstruktor esetén a példánykifejezés az újonnan lefoglalt objektumra vonatkozik.
A függvénytagok meghívásának futásidejű feldolgozása a következő lépésekből áll, ahol M
a függvénytag, és ha M
példánytag, E
a példánykifejezés:
Ha
M
statikus függvénytag:- Az argumentumlista kiértékelése a 12.6.2 részben leírtak szerint történik.
-
M
hívódik meg.
Máskülönben, ha a
E
típusa értéktípusúV
, ésM
deklarálva van vagy felülbírálva van aV
:-
E
-t értékelik. Ha ez a kiértékelés kivételt okoz, a rendszer nem hajt végre további lépéseket. Egy példánykonstruktor esetében ez az értékelés az új objektum tárolására szolgáló memória (amely általában a végrehajtási veremből származik) kiosztásából áll. Ebben az esetbenE
változóként van besorolva. - Ha
E
nincs változóként besorolva, vagy haV
nem egy könnyen strukturált típus (§16.2.2), ésE
a következők egyike:- bemeneti paraméter (§15.6.2.3.2), vagy
- egy
readonly
mező (§15.5.3), vagy - egy
readonly
referenciaváltozó vagy visszatérési érték (§9.7)
ezután létrejön
E
típusának ideiglenes helyi változója, és aE
értéke hozzá lesz rendelve ehhez a változóhoz.E
ezután át lesz sorolva az ideiglenes helyi változóra mutató hivatkozásként. Az ideiglenes változóthis
-ként érhető elM
-en belül, de más módon nem. Csak akkor képes a hívó megfigyelni a változtatásokat, amelyeketE
végez aM
-en, hathis
írható.- Az argumentumlista kiértékelése a 12.6.2 részben leírtak szerint történik.
-
M
hívódik meg. AzE
által hivatkozott változó lesz azthis
által hivatkozott változó.
-
Egyébként:
-
E
-t értékelik. Ha ez a kiértékelés kivételt okoz, a rendszer nem hajt végre további lépéseket. - Az argumentumlista kiértékelése a 12.6.2 részben leírtak szerint történik.
- Ha a
E
típusa egy value_type, egy boxing átalakítást (§10.2.9) hajtunk végreE
class_typekonvertálásához, ésE
-at az alábbi lépésekben az adott class_type-ként tekintjük. Ha a value_type egy enum_type, akkor a class_type; különbenSystem.Enum;
, aSystem.ValueType
. - A
E
értékét érvényesnek ellenőrzik. Ha aE
értéke null, a rendszerSystem.NullReferenceException
dob, és nem hajt végre további lépéseket. - A meghívásra kerülő függvénytag implementációját a következőképpen határozzuk meg:
- Ha a
E
kötési idejének típusa egy interfész, a meghívandó függvénytag aM
által hivatkozott példány futásidejű típusa által biztosítottE
megvalósítása. Ezt a függvénytagot az interfészleképezési szabályok (18.6.5) alkalmazásával határozzuk meg aM
által hivatkozott példány futásidejű típusa által biztosítottE
megvalósításának meghatározásához. - Ellenkező esetben, ha
M
egy virtuális függvény tagja, a meghívandó függvénytag aM
által hivatkozott példány futásidejű típusa által biztosítottE
megvalósítása. Ezt a függvénytagot úgy határozzuk meg, hogy a leglevezetettebb implementációjának (M
) meghatározására vonatkozó szabályokat alkalmazzuk aE
által hivatkozott példány futásidejű típusára vonatkozóan. - Ellenkező esetben
M
nem virtuális függvénytag, és a meghívni kívánt függvénytag magaM
.
- Ha a
- A rendszer meghívja a fenti lépésben meghatározott függvénytag-implementációt. A
E
által hivatkozott objektum az ez által hivatkozott objektummá válik.
-
A példánykonstruktor meghívásának eredménye (§12.8.17.2) a létrehozott érték. Bármely más függvénytag meghívásának eredménye a törzséből visszaadott (13.10.5. §) érték, ha van ilyen.
12.6.6.2 Meghívások dobozos példányokon
A value_type implementált függvénytagok az adott value_type egy dobozos példányán keresztül hívhatók meg a következő helyzetekben:
- Ha a függvénytag a class_type típustól örökölt metódus felülbírálása, és azt egy példánykifejezésen keresztül hívják meg az adott class_typeesetén.
Megjegyzés: A class_type mindig a
System.Object
,System.ValueType
vagySystem.Enum
egyike lesz. végjegyzet - Ha a függvénytag egy interfész függvénytag implementációja, és egy interface_typepéldány kifejezéssel hívják meg.
- Amikor a függvény tagját egy meghatalmazott hívja meg.
Ezekben az esetekben a dobozos példány úgy tekintendő, hogy a value_typetípusú változót tartalmaz, és ez a változó lesz az, amelyre a függvénytag-meghívás során hivatkoznak.
Megjegyzés: Ez különösen azt jelenti, hogy ha egy függvénytagot egy dobozos példányon hív meg, a függvénytag módosíthatja a dobozos példányban található értéket. végjegyzet
12.7 Dekonstruálás
A dekonstruálás egy olyan folyamat, amely során egy kifejezést egyéni kifejezések tömbjévé alakítják át. A dekonstruálás akkor használatos, amikor egy egyszerű hozzárendelés célpontja egy tupelkifejezés, hogy értékeket rendeljen annak mindegyik eleméhez.
A E
kifejezés formában van átalakítva egy n
elemet tartalmazó többszörös kifejezésre a következő módon:
- Ha a
E
egyn
elemet tartalmazó tömbkifejezés, akkor a dekonstruálás eredménye maga aE
kifejezés. - Ellenkező esetben, ha a
E
egy(T1, ..., Tn)
elemű,n
típusú tétel, akkor aE
egy ideiglenes változóként__v
lesz kiértékelve, és a lebontás eredménye a(__v.Item1, ..., __v.Itemn)
kifejezés. - Ellenkező esetben, ha a kifejezés
E.Deconstruct(out var __v1, ..., out var __vn)
fordítási időpontban egy egyedi példányra vagy kiterjesztési metódusra oldódik fel, akkor a kifejezést kiértékelik, és a dekonstruálás eredménye a kifejezés(__v1, ..., __vn)
lesz. Az ilyen módszert dekonstruktornaknevezik. - Ellenkező esetben
E
nem bontható fel.
Itt __v
és __v1, ..., __vn
az egyébként láthatatlan és elérhetetlen ideiglenes változókra hivatkoznak.
Megjegyzés: A
dynamic
típusú kifejezések nem bonthatók fel. végjegyzet
12.8 Elsődleges kifejezések
12.8.1 Általános
Az elsődleges kifejezések közé tartoznak a kifejezések legegyszerűbb formái.
primary_expression
: literal
| interpolated_string_expression
| simple_name
| parenthesized_expression
| tuple_expression
| member_access
| null_conditional_member_access
| invocation_expression
| element_access
| null_conditional_element_access
| this_access
| base_access
| post_increment_expression
| post_decrement_expression
| null_forgiving_expression
| array_creation_expression
| object_creation_expression
| delegate_creation_expression
| anonymous_object_creation_expression
| typeof_expression
| sizeof_expression
| checked_expression
| unchecked_expression
| default_value_expression
| nameof_expression
| anonymous_method_expression
| pointer_member_access // unsafe code support
| pointer_element_access // unsafe code support
| stackalloc_expression
;
Megjegyzés: Ez a nyelvtani szabály nem ANTLR-kész, mivel olyan kölcsönösen bal rekurzív szabályok (
primary_expression
,member_access
,invocation_expression
,element_access
,post_increment_expression
,post_decrement_expression
,null_forgiving_expression
,pointer_member_access
éspointer_element_access
) egy csoportjának része, amelyeket az ANTLR nem kezel. Standard technikákkal átalakíthatja a nyelvtant, hogy eltávolítsa a balrekurziót. Ez a standard nem teszi ezt meg, mivel nem minden elemzési stratégia igényli (például egy LALR-elemző nem), és ez elhomályosítaná a struktúrát és a leírást. végjegyzet
pointer_member_access (§23.6.3) és pointer_element_access (§23.6.4) csak nem biztonságos kódban érhető el (§23).
12.8.2 Literálok
A primary_expression, amely literális (§6.4.5) áll, értékként van besorolva.
12.8.3 Interpolált sztringkifejezések
Egy interpolated_string_expression$
-ből, $@
-ből, vagy @$
-ből áll, amelyet "
karaktereken belüli szöveg azonnal követ. Az idézett szövegben nulla vagy több interpoláció{
és }
karakterekkel tagolva, amelyek mindegyike kifejezési és választható formázási specifikációkat tartalmaz.
Az interpolált sztringkifejezések két alakot öltenek: reguláris (interpolated_regular_string_expression) és szó szerinti (interpolated_verbatim_string_expression), amelyek lexikálisan hasonlítanak, de szemantikailag eltérnek a sztringkonstansok két formájától (§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
: '}}'
;
A fent definiált lexikális szabályok közül hat környezetfüggő az alábbiak szerint:
szabály | Kontextuális követelmények |
---|---|
Interpolated_Regular_String_Mid | Csak egy Interpolated_Regular_String_Start után, az alábbi interpolációk között, és a megfelelő Interpolated_Regular_String_End előtt ismerhető fel. |
Regular_Interpolation_Format | Csak a regular_interpolation belül ismerhető fel, és ha a kezdő kettőspont (:) nincs beágyazva semmilyen zárójelbe (kerek, kapcsos vagy szögletes zárójel). |
Interpolated_Regular_String_End | Csak akkor ismerhető fel, ha egy Interpolated_Regular_String_Start után következik, és csak abban az esetben, ha a közbeékelkedő tokenek vagy Interpolated_Regular_String_Mids vagy olyan tokenek, amelyek a regular_interpolations részét képezhetik, beleértve az ilyen interpolációkban található interpolated_regular_string_expressions tokeneket is. |
Interpolált_Szó_szerinti_Sztring_KözépenSzó_szerinti_Beszúrási_FormátumInterpolált_Szó_szerinti_Sztring_Végén | E három szabály felismerése a fenti megfelelő szabályokhoz hasonlóan történik, ahol minden említett rendszeres nyelvtani szabályt a megfelelő szó szerinti szabály vált fel. |
Megjegyzés: A fenti szabályok környezetérzékenyek, mivel definícióik átfedésben vannak a nyelv más elemeivel. végjegyzet
Megjegyzés: A fenti nyelvtan a környezetfüggő lexikális szabályok miatt nem alkalmas az ANTLR-rel való használatra. A többi lexergenerátorhoz hasonlóan az ANTLR támogatja a környezetfüggő lexikális szabályokat, például a lexikális módjainakhasználatát, de ez egy implementálási részlet, ezért nem része ennek a specifikációnak. végjegyzet
A interpolated_string_expression értékként van besorolva. Ha azonnal átalakul System.IFormattable
vagy System.FormattableString
implicit interpolált sztring konverzióval (§10.2.5), az interpolált sztringkifejezés ilyen típussá válik. Ellenkező esetben a string
típussal rendelkezik.
Megjegyzés: Az interpolated_string_expression lehetséges típusai közötti különbségek a
System.String
(§C.2) és aSystem.FormattableString
(§C.3) dokumentációjából állapíthatók meg. végjegyzet
Az interpoláció jelentése, mind a regular_interpolation, mind a verbatim_interpolation esetében, az, hogy az kifejezés értékét vagy a string
, vagy a Verbatim_Interpolation_Format által megadott formátum szerint formázza, vagy a kifejezéstípus alapértelmezett formátuma szerint. A formázott karakterláncot ezután a interpolation_minimum_widthmódosítja, ha van ilyen, hogy a végső string
az interpolated_string_expressioninterpolálásra kerülhessen.
Megjegyzés: A típus alapértelmezett formátumának meghatározásának módját a
System.String
(§C.2) ésSystem.FormattableString
(§C.3) dokumentációja ismerteti. Az Regular_Interpolation_Format és a Verbatim_Interpolation_Formatszabványformátumok leírásai megtalálhatók aSystem.IFormattable
dokumentációjában (§C.4), valamint a szabványos könyvtár más típusaiban (§C). végjegyzet
A interpolation_minimum_width esetén a constant_expression implicit módon konvertálódik int
. Legyen a mezőszélesség a constant_expression abszolút értéke, a igazítási legyen a constant_expressionértékének jele (pozitív vagy negatív):
- Ha a mezőszélesség értéke kisebb vagy egyenlő a formázott sztring hosszával, a formázott sztring nem módosul.
- Ellenkező esetben a formázott sztring üres szóköz karakterekkel van kipárnázva, így a hossza megegyezik a mező szélességével:
- Ha az igazítás pozitív, a formázott sztring jobbra igazított a kitöltés előtagolásával,
- Egyéb esetben a kitöltést hozzáadva balra igazítja.
Egy interpolated_string_expressionáltalános jelentését – beleértve a fenti formázást és az interpolációk kitöltését – a kifejezés metódushívásra való konvertálásával határozható meg: ha a kifejezés típusa System.IFormattable
vagy System.FormattableString
ez a metódus System.Runtime.CompilerServices.FormattableStringFactory.Create
(§C.3), amely System.FormattableString
típusú értéket ad vissza; ellenkező esetben a típusnak string
kell lennie, és a módszer string.Format
(§C.2), amely string
típusú értéket ad vissza.
Mindkét esetben a hívás argumentumlistája egy formátum sztring literálból, valamint az interpolációkhoz tartozó formázási specifikációkból és az egyes kifejezésekhez tartozó argumentumokból áll, amelyek megfelelnek a formázási specifikációknak.
A formátum karakterlánc literál a következőképpen jön létre, ahol N
az interpolációk száma a interpolált karakterlánc kifejezésben. A sztringkonstans formátuma a következő sorrendből áll:
- A Interpolated_Regular_String_Start vagy Interpolated_Verbatim_String_Start karakterei
- A Interpolated_Regular_String_Mid vagy Interpolated_Verbatim_String_Midkarakterei, ha vannak ilyenek
- Akkor ha
N ≥ 1
minden számraI
0
ésN-1
között:- Helykitöltő specifikáció
- Bal oldali zárójel (
{
) karakter - A
I
decimális ábrázolása - Ezután, ha a megfelelő regular_interpolation vagy verbatim_interpolation esetén van interpolation_minimum_width, akkor vessző (
,
), majd a constant_expression értékének tizedes számábrázolása következik. - A Regular_Interpolation_Format vagy Verbatim_Interpolation_Formatkarakterei, ha vannak ilyenek, a megfelelő regular_interpolation vagy verbatim_interpolation közül.
- Jobb oldali kapcsos zárójel (
}
) karakter
- Bal oldali zárójel (
- A Interpolated_Regular_String_Mid vagy Interpolated_Verbatim_String_Mid karakterei közvetlenül a megfelelő interpolációt követően, ha vannak ilyenek
- Helykitöltő specifikáció
- Végül a Interpolated_Regular_String_End vagy a Interpolated_Verbatim_String_Endkarakterei.
A következő argumentumok az interpolációkból származó kifejezések, ha vannak ilyenek, sorrendben.
Ha egy interpolated_string_expression több interpolációt tartalmaz, az ezekben az interpolációkban szereplő kifejezéseket a rendszer szöveges sorrendben értékeli ki balról jobbra.
Példa:
Ez a példa a következő formátumspecifikációs funkciókat használja:
- a
X
formátumspecifikációja, amely az egész számokat nagybetűs hexadecimálisként formázza, - egy
string
érték alapértelmezett formátuma maga az érték, - pozitív igazítási értékek, amelyek a megadott minimális mezőszélességen belül jobbra igazítják magukat,
- negatív igazítási értékek, amelyek a megadott minimális mezőszélességen belül balra igazítanak
- a interpolation_minimum_widthmeghatározott állandói, és
-
{{
és}}
{
és}
formátumban vannak formázva.
Adott:
string text = "red";
int number = 14;
const int width = -4;
Akkor:
Interpolált karakterlánc kifejezés |
Azonos jelentésű, mint string |
Érték |
---|---|---|
$"{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" |
példa vége
12.8.4 Egyszerű nevek
A simple_name egy azonosítóból állnak, amelyet opcionálisan egy típusargumentumlista követ:
simple_name
: identifier type_argument_list?
;
A simple_name az I
formájú vagy az I<A₁, ..., Aₑ>
formájú, ahol a I
egyetlen azonosító, a I<A₁, ..., Aₑ>
pedig választható típus_argumentum_lista. Ha nincs megadva type_argument_list, vegye figyelembe, hogy e
nulla. A simple_name kiértékelése és besorolása az alábbiak szerint történik:
- Ha
e
nulla, és a simple_name egy helyi változó deklarációs területén (§7.3) belül jelenik meg, amely közvetlenül tartalmaz egy helyi változót, paramétert vagy állandótI
néven, akkor a simple_name arra a helyi változóra, paraméterre vagy állandóra hivatkozik, és változóként vagy értékként van besorolva. - Ha
nulla, és a egyszerű_név egy általános metódusdeklarációban jelenik meg, de ametódus_deklaráció attribútumain kívül, és ha a deklaráció tartalmaz egy nevű típusparamétert, akkor a egyszerű_név erre a típusparaméterre hivatkozik. - Ellenkező esetben minden példánytípus esetében
T
(§15.3.2), kezdve az azonnal belefoglalt típusdeklaráció példánytípusával, és folytatva az egyes belefoglaló osztályok vagy szerkezetdeklarációk példánytípusát (ha van):- Ha
e
nulla, és aT
deklarációja tartalmaz egyI
nevű típusparamétert, akkor a simple_name erre a típusparaméterre hivatkozik. - Ellenkező esetben, ha a taglekérdezés (
I
) aT
-bene
típusú argumentumokkal egyezést eredményez:- Ha
T
az azonnal belefoglalt osztály vagy struktúratípus példánytípusa, és a keresés egy vagy több metódust azonosít, az eredmény egy metóduscsoport, amely athis
társított példánykifejezésével rendelkezik. Ha típusargumentumlistát adott meg, az általános metódus meghívására szolgál (§12.8.10.2). - Ellenkező esetben, ha a
az azonnal belefoglalt osztály vagy struktúratípus példánytípusa, ha a keresés azonosít egy példánytagot, és ha a hivatkozás egy példánykonstruktor, példánymetódus vagy példánykiegészítő blokkban történik ( §12.2.1 ), az eredmény megegyezik az űrlaptaghozzáférésével ( 12.8.7 . Ez csak akkor fordulhat elő, hae
nulla. - Ellenkező esetben az eredmény megegyezik az űrlap vagy
T.I
taghozzáférésével (T.I<A₁, ..., Aₑ>
).
- Ha
- Ha
- Ellenkező esetben minden névtér
N
, kezdve azzal a névtérrel, amelyben a simple_name bekövetkezik, az egyes beágyazó névtérekkel folytatva (ha van ilyen), és a globális névtérrel végződik, a rendszer a következő lépéseket értékeli ki, amíg egy entitás nem található:- Ha
e
nulla, ésI
egy névtér neveN
-ben, akkor következik:- Ha a simple_name helye egy
N
névtér deklarációja által van körbezárva, és a névtér-deklaráció tartalmaz egy extern_alias_directive vagy using_alias_directive-t, amely azI
nevet egy névtérrel vagy típussal társítja, akkor az simple_name nem egyértelmű, és fordítási időhiba lép fel. - Ellenkező esetben a simple_name a
I
N
nevű névtérre hivatkozik.
- Ha a simple_name helye egy
- Ellenkező esetben, ha
N
olyan akadálymentes típust tartalmaz, amelynek neveI
ése
típusparaméterek, akkor:- Ha
e
nulla, és a simple_name előfordulása egyN
névtér deklarációval van körülzárva, és ez a névtér-deklaráció tartalmaz egy extern_alias_directive vagy using_alias_directive, amely a nevetI
egy névterrel vagy típussal társítja, akkor a simple_name nem egyértelmű, és fordítási időhiba lép fel. - Ellenkező esetben a namespace_or_type_name az adott típusargumentumokkal létrehozott típusra hivatkozik.
- Ha
- Ellenkező esetben, ha a egyszerű_név helyét egy
N
névtér deklarációja határozza meg:- Ha
e
nulla, és a névtér-deklaráció tartalmaz egy extern_alias_directive vagy using_alias_directive, amely a nevetI
egy importált névtérhez vagy típushoz társítja, akkor a simple_name erre a névtérre vagy típusra hivatkozik. - Ellenkező esetben, ha a névtér-deklaráció using_namespace_directives-jei által importált névterek pontosan egy típust tartalmaznak, amely név
I
ése
típusparaméterekkel rendelkezik, akkor a simple_name az adott típusargumentumokkal létrehozott típusra hivatkozik. - Ellenkező esetben, ha a névtér-deklaráció using_namespace_directives által importált névterek több típust tartalmaznak, amelyek név
I
ése
típusparamétereket tartalmaznak, akkor a simple_name nem egyértelmű, és fordítási időhiba lép fel.
- Ha
Megjegyzés: Ez a teljes lépés pontosan párhuzamos a namespace_or_type_name feldolgozásának megfelelő lépésével (7.8. §). végjegyzet
- Ha
- Ellenkező esetben, ha
e
nulla, ésI
az azonosító_
, a egyszerű_név egy egyszerű eldobás, amely a deklarációs kifejezés egyik formája (§12.17). - Ellenkező esetben a simple_name nincs meghatározva, és fordítási időhiba lép fel.
12.8.5 Zárójeles kifejezések
A
parenthesized_expression
: '(' expression ')'
;
A
12.8.6 Tuple-kifejezések
A tuple_expression egy tömböt jelöl, és két vagy több vesszővel elválasztott, opcionálisan elnevezett kifejezésekbőláll, zárójelekbe foglalva. A deconstruction_expression egy implicit módon beírt deklarációs kifejezéseket tartalmazó tuple rövidített szintaxisa.
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
;
A tuple_expression tuppelnek minősül.
A deconstruction_expressionvar (e1, ..., en)
a tuple_expression(var e1, ..., var en)
rövidített formája, és ugyanazt a viselkedést mutatja. Ez rekurzív módon vonatkozik bármely deconstruction_expressionbeágyazott deconstruction_tuple-re. A deconstruction_expression beágyazott azonosítók így egy deklarációs kifejezést vezetnek be (§12.17). Ennek eredményeképpen egy deconstruction_expression csak egy egyszerű feladat bal oldalán fordulhat elő.
A tömbkifejezésnek akkor és csak akkor van típusa, ha minden egyes elemkifejezésének Ei
típusa van Ti
. A típusnak ugyanolyan aritású tuple típusnak kell lennie, mint a tuple kifejezés, ahol az egyes elemeket a következők adják meg:
- Ha a megfelelő pozícióban lévő tömbelem neve
Ni
, akkor a tömb típusú elemTi Ni
legyen. - Ellenkező esetben, ha
Ei
Ni
,E.Ni
vagyE?.Ni
formájú, akkor a tuple típusú elemnekTi Ni
-nak, -nek kell lennie, kivéve, ha az alábbiak valamelyike fennáll:- A tömb egy másik elemének a neve "
Ni
", vagy - Egy név nélküli másik tuppel elem az űrlap
Ni
,E.Ni
vagyE?.Ni
, vagy -
Ni
aItemX
formátumú, ahol aX
egy nem0
által kezdett tizedesjegy-sorozat, amely egy tuple elem pozícióját jelölheti, ésX
nem az elem pozícióját jelöli.
- A tömb egy másik elemének a neve "
- Ellenkező esetben a tuple típusú elemnek
Ti
kell lennie.
Egy tömb kifejezés úgy kerül kiértékelésre, hogy minden elemkifejezését balról jobbra kiértékelik.
A tuple érték kinyerhető egy tuple kifejezésből úgy, hogy tuple típussá konvertálják (10.2.13 §), értékként átsorolják (12.2.2 §), vagy egy dekonstruáló hozzárendelés céljává teszik (12.21.2 §).
Példa:
(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
Ebben a példában mind a négy tömbkifejezés érvényes. Az első kettő,
t1
ést2
, nem a tuple kifejezés típusát használja, hanem implicit tuple konverziót alkalmaz. At2
esetében az implicit tuple konverzió az2
-rőllong
- ésnull
-rőlstring
-ra történő implicit átalakításokra támaszkodik . A harmadik tupel kifejezésnek(int i, string)
a típusa, ezért átsorolható ilyen típusú értékként. At4
deklarációja viszont hiba: A tuppelkifejezésnek nincs típusa, mert a második elemnek nincs típusa.if ((x, y).Equals((1, 2))) { ... };
Ez a példa azt mutatja, hogy a rekordok néha több zárójelréteghez is vezethetnek, különösen akkor, ha a rekordkifejezés az egyetlen argumentum egy metódus meghívásához.
példa vége
12.8.7 Tagok hozzáférése
12.8.7.1 Általános
A member_access egy primary_expression, egy predefined_typevagy egy qualified_alias_member, amelyet egy ".
" token követ, majd egy azonosító, opcionálisan egy type_argument_listkövethet.
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'
;
A qualified_alias_member gyártása a 14.8 §alatt van meghatározva.
A member_access vagy az E.I
alakja, vagy az E.I<A₁, ..., Aₑ>
alakja, ahol E
egy primary_expression, predefined_type, vagy qualified_alias_member,I
pedig egyedüli azonosító, és <A₁, ..., Aₑ>
egy választható type_argument_list. Ha nincs megadva type_argument_list, vegye figyelembe, hogy e
nulla.
A member_access egy típusú dynamic
esetében dinamikusan kötött (§12.3.3). Ebben az esetben a fordító a taghozzáférést dynamic
típusú tulajdonsághozzáférésként sorolja be. Az alábbi szabályokat a member_access jelentésének meghatározására futtatási időben alkalmazzák, a primary_expression fordítási idő típusa helyett a futtatási idő típusa alapján. Ha ez a futásidejű besorolás egy metóduscsoporthoz vezet, akkor a taghozzáférés a hívási_kifejezéselsődleges_kifejezése lesz.
A member_access kiértékelése és besorolása az alábbiak szerint történik:
- Ha
e
nulla, ésE
egy névtér, ésE
egy beágyazott névteret tartalmaz, amelynek neveI
, akkor az eredmény a névtér lesz. - Ellenkező esetben, ha
E
névtér, ésE
tartalmaz egy elérhető típust, amely névI
ésK
típusparamétereket tartalmaz, akkor az eredmény az adott típusargumentumokkal létrehozott típus. - Ha
E
típusként van besorolva, haE
nem típusparaméter, és ha aI
E
típusparaméterekkel rendelkező tagkeresése (K
) egyezést eredményez, akkor aE.I
kiértékelése és besorolása a következőképpen történik:Megjegyzés: Ha egy ilyen tagkeresés eredménye metóduscsoport, és
K
nulla, a metóduscsoport tartalmazhat típusparaméterekkel rendelkező metódusokat. Ez lehetővé teszi, hogy az ilyen metódusok figyelembe legyenek véve a típusargumentumok következtetéséhez. végjegyzet- Ha
I
azonosít egy típust, akkor az eredmény az adott típusargumentumokkal létrehozott típus. - Ha
I
azonosít egy vagy több metódust, akkor az eredmény egy metóduscsoport, amelyhez nincs társítva példánykifejezés. - Ha
I
azonosít egy statikus tulajdonságot, akkor az eredmény egy társított példánykifejezés nélküli tulajdonsághozzáférés. - Ha
I
statikus mezőt azonosít:- Ha a mező csak olvasható, és a hivatkozás az osztálynak vagy struktúrának a statikus konstruktorán kívül történik, amelyben a mező deklarálva van, akkor az eredmény egy érték, nevezetesen a statikus mező
I
értéke aE
-ben. - Ellenkező esetben az eredmény egy változó, nevezetesen a statikus mező
I
E
.
- Ha a mező csak olvasható, és a hivatkozás az osztálynak vagy struktúrának a statikus konstruktorán kívül történik, amelyben a mező deklarálva van, akkor az eredmény egy érték, nevezetesen a statikus mező
- Ha
I
azonosít egy statikus eseményt:- Ha a hivatkozás abban az osztályban vagy szerkezetben történik, amelyben az esemény deklarálva van, és az eseményt event_accessor_declarations nélkül deklarálták (§15.8.1), akkor a
E.I
feldolgozása pontosan úgy történik, minthaI
statikus mező volna. - Ellenkező esetben az eredmény egy eseményhozzáférés, amelyhez nincs társított példánykifejezés.
- Ha a hivatkozás abban az osztályban vagy szerkezetben történik, amelyben az esemény deklarálva van, és az eseményt event_accessor_declarations nélkül deklarálták (§15.8.1), akkor a
- Ha
I
azonosít egy állandót, akkor az eredmény egy érték, nevezetesen az állandó értéke. - Ha
I
enumerálási tagot azonosít, akkor az eredmény egy érték, nevezetesen az enumerálási tag értéke. - Ellenkező esetben a
E.I
érvénytelen taghivatkozás, és fordítási időhiba lép fel.
- Ha
- Ha a
E
tulajdonsághozzáférés, indexelőhozzáférés, változó vagy érték típusaT
, és a 12.5. § szerinti tagkeresés során aI
T
-benK
típusargumentumokkal egyezést talál, akkor aE.I
a következőképpen kerül kiértékelésre és besorolásra:- Először is, ha a
E
egy tulajdonság vagy indexelő hozzáférés, akkor a rendszer megszerzi a tulajdonság vagy indexelő hozzáférés értékét (§12.2.2), és az E-t átsorolják értékként. - Ha
I
egy vagy több metódust azonosít, akkor az eredmény egy metóduscsoport, amely aE
társított példánykifejezésével rendelkezik. - Ha
I
azonosít egy példánytulajdonságot, akkor az eredmény egy tulajdonsághozzáférés aE
társított példánykifejezésével és egy társított típussal, amely a tulajdonság típusa. Ha aT
osztálytípus, akkor a társított típust a tulajdonság első deklarációjából vagy felülbírálásából választják ki úgy, hogy aT
elemmel kezdve az alaposztályokon keresztül keresnek. - Ha
T
egy osztálytípus, ésI
az adott osztálytípuspéldánymezőjét azonosítja:- Ha a
E
értékenull
, akkor egySystem.NullReferenceException
-t dobnak. - Ellenkező esetben, ha a mező csak olvasható, és a hivatkozás az osztály példánykonstruktorán kívül van, amelyben a mező deklarálva van, akkor az eredmény egy érték, nevezetesen a mező
I
értéke aE
által hivatkozott objektumban. - Ellenkező esetben az eredmény egy változó, nevezetesen a
I
által hivatkozott objektumbanE
mező.
- Ha a
- Ha
T
egy struct_type, ésI
az adott struct_typepéldánymezőjét azonosítja:- Ha
E
érték, vagy ha a mező olvasható, és a hivatkozás azon szerkezet egy példánykonstruktorán kívül történik, amelyben a mezőt deklarálják, akkor az eredmény egy érték, nevezetesen aI
által megadott szerkezetpéldánybanE
mező értéke. - Ellenkező esetben az eredmény egy változó, nevezetesen a
I
által megadott szerkezetpéldánybanE
mező.
- Ha
- Ha
I
azonosít egy példányeseményt:- Ha a hivatkozás abban az osztályban vagy szerkezetben történik, amelyben az esemény deklarálva van, és az eseményt event_accessor_declarations nélkül deklarálták (§15.8.1), és a hivatkozás nem a
a +=
vagy-=
operátor bal oldalán fordul elő, akkor aE.I
feldolgozása pontosan úgy történik, minthaI
példánymező lett volna. - Ellenkező esetben az eredmény egy eseményhozzáférés, amely
E
társított példánykifejezéssel rendelkezik.
- Ha a hivatkozás abban az osztályban vagy szerkezetben történik, amelyben az esemény deklarálva van, és az eseményt event_accessor_declarations nélkül deklarálták (§15.8.1), és a hivatkozás nem a
- Először is, ha a
- Ellenkező esetben megkíséreljük a
E.I
-t bővítmény-metódus meghívásként feldolgozni (§12.8.10.3). Ha ez nem sikerül, akkorE.I
érvénytelen taghivatkozás, és kötési idejű hiba következik be.
12.8.7.2 Azonos egyszerű nevek és típusnevek
Egy E.I
formátumú taghozzáférés esetén, ha E
egyetlen azonosítóként szerepel, és ha a E
simple_name jelentése (§12.8.4) egy állandó, mező, tulajdonság, helyi változó vagy paraméter, amely ugyanolyan típusú, mint a E
jelentése a type_name (§7.8.1), akkor a E
mindkét lehetséges jelentése megengedett. A E.I
tagkeresése soha nem egyértelmű, mivel I
mindkét esetben feltétlenül az E
típusú tagnak kell lennie. Más szóval a szabály egyszerűen engedélyezi a hozzáférést a statikus tagokhoz és a beágyazott E
típusokhoz, ahol egyébként fordítási időhiba történt volna.
Példa:
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 } }
A
A
osztályon belül azonColor
azonosító előfordulásait, amelyek aColor
típusra hivatkoznak,«...»
határolja el, és azok nem, amelyek aColor
mezőre hivatkoznak.példa vége
12.8.8 Null feltételes taghozzáférés
A null_conditional_member_access a member_access feltételes verziója (§12.8.7), és kötési időhiba, ha az eredménytípus void
. Null feltételes kifejezés esetén, ahol az eredménytípus void
lehet, lásd (§12.8.11).
A null_conditional_member_access egy primary_expression-ből áll, amelyet a két "?
" és ".
" token követ, majd egy azonosító opcionális type_argument_list-tel, ezt követően pedig nulla vagy több dependent_access, amelyek bármelyikét egy null_forgiving_operator előzheti meg.
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?
;
A null_conditional_member_access kifejezés E
a következő formában van: P?.A
. A E
jelentése a következőképpen határozható meg:
Ha a
P
típusa null értékű:Legyen
T
aP.Value.A
típusa.Ha
T
olyan típusparaméter, amely nem ismert referenciatípus vagy nem null értékű értéktípus, fordítási időhiba lép fel.Ha
T
nem null értékű érték, akkor aE
típusaT?
, és aE
jelentése megegyezik a következő jelentéssel:((object)P == null) ? (T?)null : P.Value.A
Kivéve, hogy a
P
csak egyszer lesz kiértékelve.Ellenkező esetben a
E
típusaT
, és aE
jelentése megegyezik a következők jelentésével:((object)P == null) ? (T)null : P.Value.A
Kivéve, hogy a
P
csak egyszer lesz kiértékelve.
Egyébként:
Legyen
T
aP.A
kifejezés típusa.Ha
T
olyan típusparaméter, amely nem ismert referenciatípus vagy nem null értékű értéktípus, fordítási időhiba lép fel.Ha
T
nem null értékű érték, akkor aE
típusaT?
, és aE
jelentése megegyezik a következő jelentéssel:((object)P == null) ? (T?)null : P.A
Kivéve, hogy a
P
csak egyszer lesz kiértékelve.Ellenkező esetben a
E
típusaT
, és aE
jelentése megegyezik a következők jelentésével:((object)P == null) ? (T)null : P.A
Kivéve, hogy a
P
csak egyszer lesz kiértékelve.
Megjegyzés: Egy kifejezés alakjában:
P?.A₀?.A₁
Ha
P
null
-re van kiértékelve, akkor semA₀
, sem pedigA₁
nincs kiértékelve. Ugyanez igaz, ha egy kifejezés null_conditional_member_access vagy null_conditional_element_access§12.8.13 műveletek sorozata.végjegyzet
A null_conditional_projection_initializer a null_conditional_member_access korlátozása, és azonos szemantikával rendelkezik. Ez csak egy névtelen objektumlétrehozó kifejezésben (§12.8.17.3) vetületi inicializálóként fordul elő.
12.8.9 Null-megbocsátó kifejezések
12.8.9.1 Általános
A null-megbocsátó kifejezés értéke, típusa, besorolása (12.2. §) és biztonságos kontextusa (16.4.15. §) a primary_expression értéke, típusa, besorolása és biztonságos kontextusa.
null_forgiving_expression
: primary_expression null_forgiving_operator
;
null_forgiving_operator
: '!'
;
Megjegyzés: A null-megbocsátó és a logikai negáció előtagú operátorok (§12.9.4), bár ugyanazon lexikai tokennel (!
) vannak jelölve, különbözőek. Csak az utóbbi felülbírálható (§15.10), a null-megbocsátó operátor definíciója rögzített.
végjegyzet
Fordítási hiba, ha a null-megbocsátó operátort többször alkalmazzuk ugyanarra a kifejezésre, a zárójelek jelenléte ellenére.
példa: a következők érvénytelenek:
var p = q!!; // error: applying null_forgiving_operator more than once var s = ( ( m(t) ! ) )! // error: null_forgiving_operator applied twice to m(t)
példa vége
Az alszakasz fennmaradó része és a következő testvér alszakaszok feltételesen normatív jellegűek.
A statikus nullállapot-elemzést (§8.9.5) végző fordítónak meg kell felelnie az alábbi specifikációnak.
A null-megbocsátó operátor egy fordítási időben végrehajtott látszatművelet, amely a fordító statikus nullállapot-elemzésének segítésére szolgál. Két funkciója van: a fordító megállapításának felülírása, hogy egy kifejezés talán nulllehet; és felülírni egy fordítót, amely figyelmeztetést ad ki a null értékűséggel kapcsolatban.
A null-elbocsátó operátor olyan kifejezésre való alkalmazása, amelyhez a fordító statikus nullállapot-elemzése nem eredményez figyelmeztetést, nem hiba.
12.8.9.2 A "talán null" meghatározás felülírása
Bizonyos körülmények között a fordító statikus nullállapot-elemzése megállapíthatja, hogy egy kifejezés null állapotú, lehet, hogy null, és diagnosztikai figyelmeztetést ad ki, ha más információk azt jelzik, hogy a kifejezés nem lehet null értékű. A null-megbocsátó operátor ilyen kifejezésre való alkalmazása tájékoztatja a fordító statikus nullérték állapot-elemzését arról, hogy a nullérték állapot nem null, amely megakadályozza a diagnosztikai figyelmeztetést, és befolyásolhatja a folyamatban lévő elemzéseket.
példa: Vegye figyelembe a következőket:
#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;
Ha a
IsValid
true
ad vissza, ap
biztonságosan meg lehet hivatkozni aName
tulajdonság eléréséhez, és az "esetleg null értékű hivatkozás feloldása" figyelmeztetés a!
használatával elnyomható.példa vége
Példa: A null-megbocsátó operátort körültekintően kell használni, fontolja meg a következő szempontokat:
#nullable enable int B(int? x) { int y = (int)x!; // quash warning, throw at runtime if x is null return y; }
Itt a null-elbocsátó operátor egy értéktípusra lesz alkalmazva, és minden figyelmeztetést felold
x
. Ha azonban a futásidőbenx
null
, akkor kivételt kapunk, mivelnull
nem alakítható átint
-vá.példa vége
12.8.9.3 Egyéb nullelemzési figyelmeztetések felülírása
A fenti esetleges null megállapításának felülírása mellett előfordulhatnak olyan helyzetek is, amikor szükség van a fordító statikus nullállapot-analízis meghatározásának felülbírálására, amely szerint egy kifejezéshez egy vagy több figyelmeztetés szükséges. A null-elbocsátó operátor alkalmazása olyan kifejezéskérelemekre, amelyek esetében a fordító nem ad ki figyelmeztetést a kifejezésre. Válaszul a fordító dönthet úgy, hogy nem ad ki figyelmeztetéseket, és módosíthatja annak további elemzését is.
példa: Vegye figyelembe a következőket:
#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; }
Az
Assign
metódus paramétereinek típusai ,lv
&rv
,string?
,lv
kimeneti paraméter, és egyszerű hozzárendelést hajt végre.A
M
metódus as
típusústring
változót, mintAssign
kimeneti paramétert adja át, a fordító figyelmeztetést ad, mivels
nem nullázható változó. MivelAssign
második argumentuma nem lehet null értékű, a null-elbocsátó operátor a figyelmeztetés feloldására szolgál.példa vége
feltételesen normatív szöveg vége.
12.8.10 Hívási kifejezések
12.8.10.1 Általános
Metódus meghívására egy invocation_expression szolgál.
invocation_expression
: primary_expression '(' argument_list? ')'
;
A primary_expression lehet null_forgiving_expression, kizárólag akkor, ha delegate_type.
Egy invocation_expression dinamikusan kötött (§12.3.3), ha az alábbiak közül legalább az egyik igaz:
- A primary_expression fordítási időbeli típusa
dynamic
. - Az opcionális argument_list közül legalább egy argumentum fordítási idejű típusú
dynamic
.
Ebben az esetben a fordító a invocation_expression-t dynamic
típusú értékként osztályozza. Az alábbi szabályokat a invocation_expression jelentésének meghatározására futásidőben alkalmazva, a primary_expression és az argumentumok futási idő típusát használjuk a fordítási idő típusa helyett. Ha a primary_expression nem rendelkezik a fordítási időben meghatározott típusú dynamic
, akkor a metódus meghívása korlátozott fordítási idejű ellenőrzésen megy keresztül, ahogy az a §12.6.5részben le van írva.
Az primary_expression egy invocation_expression esetén metóduscsoportnak vagy egy delegate_typeértékének kell lennie. Ha a primary_expression metóduscsoport, akkor a invocation_expression metódushívás (§12.8.10.2). Ha a primary_expression egy delegate_typeértéke, akkor a invocation_expression egy delegált meghívás (§12.8.10.4). Ha a primary_expression sem metóduscsoport, sem a delegate_typeértéke, akkor kötési idejű hiba lép fel.
Az opcionális argument_list (12.6.2. § ) értékeket vagy változóhivatkozásokat biztosít a metódus paramétereihez.
A invocation_expression kiértékelésének eredménye a következőképpen van besorolva:
- Ha a invocation_expression egy eredmény nélküli metódust hív meg (§15.6.1) vagy egy visszatérési érték nélküli delegáltat, az eredmény semmi. A semmiként besorolt kifejezés csak egy statement_expression (13.7. §) vagy lambda_expression törzse (§12.19) kontextusában engedélyezett. Ellenkező esetben kötési időhiba lép fel.
- Ellenkező esetben, ha a invocation_expression egy referencia által visszatérő metódust hív meg (§15.6.1) vagy egy referencia által visszatérő delegáltat, az eredmény egy olyan változó, amely a metódus vagy a delegált visszatérési típusával összefüggő típussal rendelkezik. Ha a meghívás egy példány metódusra vonatkozik, és a fogadó
T
osztálytípusba tartozik, a társított típust az első deklarációból vagy a metódus felülbírálásából választják ki, amikor azT
-ből indul, és végigkeresi az alaposztályokat. - Ellenkező esetben a invocation_expression egy visszatérési értékkel rendelkező metódust (§15.6.1) vagy deleáltat hív meg, és az eredmény egy érték, amelynek társított típusa a metódus vagy delegált visszatérési típusa. Ha a meghívás egy példány metódusra vonatkozik, és a fogadó
T
osztálytípusba tartozik, a társított típust az első deklarációból vagy a metódus felülbírálásából választják ki, amikor azT
-ből indul, és végigkeresi az alaposztályokat.
12.8.10.2 Metódushívások
Módszerhívás esetén a primary_expression a híváskifejezés metóduscsoport kell, hogy legyen. A metóduscsoport azonosítja az egyetlen meghívandó metódust, vagy azt a túlterhelt metóduskészletet, amelyből kiválaszthat egy adott metódust. Az utóbbi esetben a meghívandó konkrét módszer meghatározása a argument_listargumentumtípusai által biztosított kontextuson alapul.
A M(A)
alakú metódushívás kötési idejének feldolgozása, ahol a M
egy metóduscsoport (esetleg egy típusa_argumentum_listis), és a A
egy választható argumentum_lista, a következő lépésekből áll:
- A metódushíváshoz jelölt metódusok készlete létre van hozva. A
F
metóduscsoporthoz társítottM
metódusok esetében:- Ha
F
nem általános, akkorF
a következő esetekben jelölt:-
M
nem tartalmaz típusargumentumlistát, és -
F
aA
(§12.6.4.2) vonatkozásában alkalmazható.
-
- Ha
F
általános, ésM
nem tartalmaz típusargumentumlistát,F
a következő esetekben jelölt:- A típusinferencia sikeres (§12.6.3), és következtetés eredményeként kapott egy típusargumentumok listáját a híváshoz.
- Miután a kikövetkeztetett típusargumentumokat helyettesítik a megfelelő metódustípus-paraméterekkel, a
F
paraméterlistájában szereplő összes típus megfelel a korlátaiknak (§8.4.5), és aF
paraméterlistája alkalmazható aA
vonatkozásában (§12.6.4.2).
- Ha a
F
általános, ésM
tartalmaz egy típusargumentumlistát,F
akkor jelölt, ha:- A
F
ugyanannyi metódustípus-paraméterrel rendelkezik, mint amennyit a típusargumentum-listában megadtak. - Miután a típusargumentumokat behelyettesítették a megfelelő metódustípus-paraméterek helyére, a
F
paraméterlistájában szereplő összes létrehozott típus megfelel a feltételeiknek (§8.4.5), és aF
paraméterlistája helyénvaló aA
tekintetében (§12.6.4.2).
- A
- Ha
- A jelölt metódusok készlete csak a leg származtatottabb típusok metódusait tartalmazza: A készletben
C.F
metódusok esetében, aholC
az a típus, amelyben a metódusF
deklarálva van, aC
alaptípusában deklarált összes metódus törlődik a készletből. Továbbá, haC
nemobject
osztálytípus, a rendszer eltávolítja a készletből az illesztőtípusban deklarált összes metódust.Megjegyzés: Ez utóbbi szabály csak akkor van hatással, ha a metóduscsoport egy olyan típusparaméter tagkeresésének eredménye volt, amelynek hatékony alaposztálya nem
object
, és tényleges illesztőkészlete nem üres. végjegyzet - Ha a jelölt metódusok eredményül kapott készlete üres, akkor a következő lépések további feldolgozását abbahagyják, és ehelyett megkísérlik a meghívást bővítménymetódus-meghívásként feldolgozni (§12.8.10.3). Ha ez nem sikerül, akkor nem léteznek alkalmazható metódusok, és kötési időhiba lép fel.
- A jelölt módszerek csoportjának legjobb módszerét a §12.6.4-beli túlterhelés-feloldási szabályok alkalmazásával azonosítják. Ha egyetlen legjobb módszer nem azonosítható, a metódushívás nem egyértelmű, és kötési idő hibát jelez. Túlterhelésfeloldás esetén az általános metódus paramétereit a megfelelő metódustípus-paraméterek típusargumentumainak (megadott vagy kikövetkeztetett) helyettesítése után veszi figyelembe a rendszer.
Miután a fenti lépések során kiválasztották és érvényesítették a metódust a kötési időben, a tényleges futásidejű meghívás végrehajtása a függvénytagok meghívásának szabályai szerint történik, ahogyan az a §12.6.6részben le van írva.
Megjegyzés: A fent leírt feloldási szabályok intuitív hatása a következő: A metódushívás által meghívott metódus megkereséséhez kezdje a metódushívás által jelzett típussal, és folytassa az öröklési láncot, amíg legalább egy alkalmazható, akadálymentes, felülbírálás nélküli metódus-deklaráció nem található. Ezután végezzen típuskövetkeztetést és túlterhelésfeloldást az adott típusban deklarált alkalmazható, akadálymentes, felülbírálás nélküli metódusok halmazán, és hívja meg az így kiválasztott metódust. Ha nem található metódus, akkor a meghívást próbálja inkább bővítménymetódus-meghívásként feldolgozni. végjegyzet
12.8.10.3 Bővítménymetódus-meghívások
Egy metódushívásban (§12.6.6.2) az egyik formák közül
«expr» . «identifier» ( )
«expr» . «identifier» ( «args» )
«expr» . «identifier» < «typeargs» > ( )
«expr» . «identifier» < «typeargs» > ( «args» )
ha a hívás normál feldolgozása nem talál alkalmazható metódusokat, a rendszer megkísérli a szerkezetet bővítménymetódus-meghívásként feldolgozni. Ha a «expr» vagy bármely «args» fordítási idő típusú dynamic
, a bővítménymetelyek nem lesznek érvényesek.
A cél a legjobb type_nameC
megkeresése, hogy a megfelelő statikus metódus meghívása a következő módon valósuljon meg:
C . «identifier» ( «expr» )
C . «identifier» ( «expr» , «args» )
C . «identifier» < «typeargs» > ( «expr» )
C . «identifier» < «typeargs» > ( «expr» , «args» )
A bővítménymetódus Cᵢ.Mₑ
jogosult, ha:
- Az
Cᵢ
egy nem generikus és nem beágyazott osztály -
Mₑ
azonosítójának neve -
Mₑ
elérhető és alkalmazható, ha az argumentumokra statikus módszerként alkalmazzák a fent látható módon - Implicit identitás-, hivatkozás- vagy dobozolás-konverzió létezik, amely az expr kifejezést az
Mₑ
első paraméterének típusára alakítja át.
A C
keresése a következőképpen folytatódik:
- A legközelebbi befoglaló névtér-deklarációtól kezdve, az egyes befoglaló névtér-deklarációkkal folytatva és az azt tartalmazó fordítási egységgel végződve egymást követő kísérleteket tesznek a kiterjesztési metódusok jelöltkészletének megkeresésére.
- Ha a megadott névtér vagy fordítási egység közvetlenül nem generikus típusú deklarációkat tartalmaz
Cᵢ
, érvényes bővítő metódusokkalMₑ
, akkor a bővítő metódusok halmaza a jelöltkészlet. - Ha az adott névtérben vagy fordítási egységben névtér-irányelvek használatával importált névterek közvetlenül nem általános típusú deklarációkat tartalmaznak a jogosult kiterjesztési metódusokkal, akkor ezeknek a kiterjesztési metódusoknak a halmaza lesz a jelöltkészlet.
- Ha a megadott névtér vagy fordítási egység közvetlenül nem generikus típusú deklarációkat tartalmaz
- Ha a névtér-deklarációban vagy fordítási egységben nem található jelöltkészlet, fordítási időhiba lép fel.
- Ellenkező esetben a túlterhelés feloldása az 12.6.4szakaszban leírtak szerint történik. Ha nem található egyetlen legjobb módszer sem, fordítási időhiba lép fel.
-
C
az a típus, amelyen belül a legjobb módszer bővítménymetódusként van deklarálva.
A C
célként való használatával a metódushívás statikus metódushívásként lesz feldolgozva (§12.6.6).
Megjegyzés: A példánymetódus meghívásával ellentétben, ha a kifejezést null értékű hivatkozásra értékelik, nem dob kivételt. Ehelyett ez a
null
érték a bővítménymetódusnak lesz átadva, mivel az egy normál statikus metódushíváson keresztül történik. A bővítménymetódus implementálásán múlik annak eldöntése, hogyan kell válaszolni egy ilyen hívásra. végjegyzet
Az előző szabályok azt jelentik, hogy a példánymetóciók elsőbbséget élveznek a kiterjesztési módszerekkel szemben, hogy a belső névtér-deklarációkban elérhető bővítménymetelyek elsőbbséget élveznek a külső névtér-deklarációkban elérhető kiterjesztési módszerekkel szemben, és hogy a névtérben közvetlenül deklarált kiterjesztési módszerek elsőbbséget élveznek az ugyanabba a névtérbe egy névtér-irányelvvel importált bővítménymetelyekkel szemben.
Példa:
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) } }
A példában
B
metódusa elsőbbséget élvez az első bővítménymetódusnál,C
metódusa pedig mindkét bővítménymetódusnál elsőbbséget élvez.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(); } } }
A példa kimenete a következő:
E.F(1) D.G(2) C.H(3)
D.G
elsőbbséget élvezC.G
-el szemben, ésE.F
elsőbbséget élvez mindD.F
, mindC.F
felett.példa vége
12.8.10.4 Meghatalmazotti meghívások
Delegált meghívás esetén a primary_expression a invocation_expression részeként legyen a delegate_typeegy értéke. Továbbá , figyelembe véve , hogy a delegate_type a delegate_typeparaméterlistával megegyező paraméterlistával rendelkező függvénytagnak minősülnek , a delegate_type az invocation_expressionargument_list tekintetében alkalmazandó (12.6.4.2.
Az D(A)
alakú delegált meghívás futásidejű feldolgozása, ahol a D
egy primary_expression egy delegate_type-ból, és a A
egy választható argument_list, a következő lépésekből áll:
-
D
-t értékelik. Ha ez a kiértékelés kivételt okoz, a rendszer nem hajt végre további lépéseket. - Az argumentumlistát
A
kiértékeljük. Ha ez a kiértékelés kivételt okoz, a rendszer nem hajt végre további lépéseket. - A
D
értékét érvényesnek ellenőrzik. Ha aD
értékenull
, a rendszerSystem.NullReferenceException
dob, és nem hajt végre további lépéseket. - Ellenkező esetben a
D
egy delegált példányra mutató hivatkozás. A függvénytag-meghívások (§12.6.6) a meghatalmazott meghívási listájában szereplő összes meghívható entitáson végrehajthatók. Egy példányból és példány metódusából álló meghívható entitások esetében a hívási példány a meghívható entitásban található példány.
A paraméterek nélküli több meghívási lista részleteiért lásd 20.6.
12.8.11 Nulla feltételes meghívási kifejezés
A null_conditional_invocation_expression szintaktikailag egy null_conditional_member_access (§12.8.8) vagy null_conditional_element_access (§12.8.13), ahol a végső dependent_access egy meghívási kifejezés (§12.8.10).
A null_conditional_invocation_expression egy statement_expression (§13.7), anonymous_function_body (§12.19.1) vagy method_body (§15.6.1) környezetében történik.
A szintaktikailag egyenértékű null_conditional_member_access vagy null_conditional_element_accessellentétben a null_conditional_invocation_expression semmiként besorolható.
null_conditional_invocation_expression
: null_conditional_member_access null_forgiving_operator? '(' argument_list? ')'
| null_conditional_element_access null_forgiving_operator? '(' argument_list? ')'
;
A választható null_forgiving_operator csakis akkor vehető fel, ha a null_conditional_member_access vagy null_conditional_element_access rendelkezik egy delegate_type.
A null_conditional_invocation_expression kifejezés E
P?A
; ahol A
a szintaktikailag egyenértékű null_conditional_member_access vagy null_conditional_element_access fennmaradó része, ezért A
.
vagy [
kezdetű lesz. Legyen PA
a P
és A
összefűzése.
Amikor a E
statement_expression fordul elő, a E
jelentése megegyezik a utasítás jelentésével.
if ((object)P != null) PA
kivéve, hogy a P
csak egyszer lesz kiértékelve.
Amikor a E
anonymous_function_body vagy method_body szerepel mint ilyen, akkor a E
jelentése a besorolásától függ.
Ha
E
semmiként van besorolva, akkor a jelentése megegyezik a blokkjelentésével:{ if ((object)P != null) PA; }
kivéve, hogy a
P
csak egyszer lesz kiértékelve.Ellenkező esetben a
E
jelentése megegyezik a blokkjelentésével:{ return E; }
és a blokk jelentése attól függ, hogy a
E
szintaktikailag egyenértékű-e egy null_conditional_member_access (§12.8.8) vagy null_conditional_element_access (§12.8.13).
12.8.12 Elem-hozzáférés
12.8.12.1 Általános
A element_access egy primary_expression-ből, majd egy "[
" tokenből, majd egy argument_list-ből, majd egy "]
" tokenből áll. A argument_list egy vagy több argumentumbóls-ből áll, vesszővel elválasztva.
element_access
: primary_expression '[' argument_list ']'
;
A primary_expression felismerésekor, ha a element_access és a pointer_element_access (23.6.4. §) is alkalmazható, akkor az utóbbit kell választani, ha a beágyazott primary_expression mutatótípusú (23.3. §).
A element_accessargument_list nem tartalmazhat out
vagy ref
argumentumokat.
A element_access primary_expression nem lehet array_creation_expression, kivéve, ha array_initializer vagy stackalloc_expression tartalmaz, kivéve, ha stackalloc_initializer tartalmaz.
Megjegyzés: Ez a korlátozás arra vonatkozik, hogy letiltsa a potenciálisan zavaró kódot, például:
var a = new int[3][1];
amely egyébként a következőképpen értelmezendő:
var a = (new int[3])[1];
Hasonló korlátozás vonatkozik null_conditional_element_access (12.8.13. §). végjegyzet
Egy element_access dinamikusan lekötött (§12.3.3), ha az alábbiak közül legalább az egyik igaz:
- A primary_expression fordítási időbeli típusa
dynamic
. - A argument_list legalább egy kifejezése fordítási idő típusú
dynamic
, és a primary_expression nem rendelkezik tömbtípussal.
Ebben az esetben a fordító a element_accessdynamic
típusú értékként sorolja be. Az alábbi szabályok a element_access jelentésének meghatározására futásidőben lesznek alkalmazva, a primary_expression és argument_list kifejezések fordítási időtípusa helyett a futásidő típusának dynamic
használatával. Ha a primary_expression nem rendelkezik fordítási idő típussal dynamic
, akkor az elemhozzáférés átesik egy korlátozott fordítási idő ellenőrzésen, ahogy azt a §12.6.5 leírja.
Ha az element_accessprimary_expression egy array_type értéke, akkor a element_access tömbelérés (12.8.12.2. §). Ellenkező esetben a primary_expression egy egy vagy több indexelőtaggal rendelkező osztály, szerkezet vagy interfésztípus változója vagy értéke, amely esetben a element_access indexelő hozzáférés (12.8.12.3. §).
12.8.12.2 Tömb hozzáférés
Tömbhozzáférés esetén az primary_expression az element_access egy array_type értékének kell lennie. Ezenkívül a tömbhozzáférés argument_list nem tartalmazhat nevesített argumentumokat. A argument_list kifejezéseinek száma megegyezik a array_typerangjának számával, és minden kifejezésnek int
, uint
, long
vagy ulong,
kell lennie, vagy implicit módon átalakíthatónak kell lennie egy vagy több ilyen típusra.
A tömbhozzáférés kiértékelésének eredménye a tömb elemtípusának változója, nevezetesen a argument_list kifejezéseinek értékei által kiválasztott tömbelem.
Az űrlap P[A]
tömbhozzáférésének futásidejű feldolgozása , amely P
egy array_type primary_expression és A
egy argument_list, a következő lépésekből áll:
-
P
-t értékelik. Ha ez a kiértékelés kivételt okoz, a rendszer nem hajt végre további lépéseket. - A argument_list indexkifejezéseinek kiértékelése sorrendben történik, balról jobbra. Az egyes indexkifejezések kiértékelését követően implicit átalakítást (§10.2) hajtunk végre a következő típusok egyikére:
int
,uint
,long
,ulong
. A lista első olyan típusát választja ki, amelyhez implicit konverzió tartozik. Ha például az indexkifejezésshort
típusú, akkor implicit átalakítást hajt végreint
, mivel ashort
-rőlint
- ésshort
-róllong
implicit átalakításra is lehetőség van. Ha egy indexkifejezés vagy az azt követő implicit átalakítás kiértékelése kivételt okoz, akkor a rendszer nem értékel ki további indexkifejezéseket, és nem hajt végre további lépéseket. - A
P
értékét érvényesnek ellenőrzik. Ha aP
értékenull
, a rendszerSystem.NullReferenceException
dob, és nem hajt végre további lépéseket. - A argument_list minden egyes kifejezésének értéke a
P
által hivatkozott tömbpéldány egyes dimenzióinak tényleges határaival van összevetve. Ha egy vagy több érték kívül esik a tartományon, a rendszerSystem.IndexOutOfRangeException
dob, és nem hajt végre további lépéseket. - A rendszer kiszámítja az indexkifejezések által megadott tömbelem helyét, és ez a hely lesz a tömbhozzáférés eredménye.
12.8.12.3 Indexelői hozzáférés
Indexelő hozzáférés esetén a element_access primary_expression egy osztály, szerkezet vagy interfésztípus változója vagy értéke, és ennek a típusnak egy vagy több indexelőt kell implementálnia, amelyek a element_access argument_list tekintetében alkalmazhatók.
Az P[A]
formájú indexelői hozzáférés kötésidejű feldolgozása, ahol P
egy osztály, struktúra vagy interfész típusú primary_expression, és T
egy A
, a következő lépésekből áll:
- A
T
által biztosított indexelők készlete létre van állítva. A készlet aT
-ban deklarált összes indexből vagy aT
alaptípusú összes indexből áll, amelyek nem felüldeklarációk, és hozzáférhetőek a jelenlegi környezetben (§7.5). - A készlet azokra az indexelőkre lesz csökkentve, amelyek alkalmazhatók, és nem rejtik el más indexelők. A rendszer a következő szabályokat alkalmazza a készletben lévő összes indexelőre
S.I
, aholS
az indexelőI
deklarált típusa:- Ha
I
nem alkalmazható aA
-hez (§12.6.4.2), akkorI
kikerül a halmazból. - Ha
I
aA
(§12.6.4.2) vonatkozásában alkalmazható, akkor minden aS
egy alaptípusában deklarált indexelő törlődik a készletből. - Ha
I
A
vonatkozásában alkalmazható (§12.6.4.2), és haS
nemobject
osztálytípus, akkor az interfészben deklarált összes indexelő el lesz távolítva az indexrendszerből.
- Ha
- Ha az eredményként kapott jelölt indexelők készlete üres, akkor nem léteznek megfelelő indexelők, és kötési idő hiba lép fel.
- A jelölt indexelők halmazának legjobb indexelője a túlterhelési feloldási szabályok alkalmazásával azonosítható a 12.6.4szerint. Ha egyetlen legjobb indexelő nem azonosítható, az indexelő hozzáférése nem egyértelmű, és kötési idejű hiba lép fel.
- A argument_list indexkifejezéseinek kiértékelése sorrendben történik, balról jobbra. Az indexelői hozzáférés feldolgozásának eredménye egy indexelő hozzáférésként besorolt kifejezés. Az indexelő hozzáférési kifejezés a fenti lépésben meghatározott indexelőre hivatkozik, és rendelkezik a
P
társított példánykifejezésével, aA
társított argumentumlistával, valamint annak típusával, ami az indexelő típusa. HaT
egy osztály típus, akkor a társított típust az indexelő első deklarációjából vagy felülírásából választják ki, amit úgy találnak meg, hogy a kereséstT
-től kezdik, és végighaladnak az alaposztályokon.
Attól függően, hogy milyen környezetben használják, az indexelő hozzáférése az indexelő lekéréses tartozékának vagy a beállított tartozékának meghívását okozza. Ha az indexelő hozzáférés egy hozzárendelés célja, a beállító metódus kerül meghívásra új érték hozzárendeléséhez (12.21.2). Minden más esetben a get kiegészítő meghívása történik az aktuális érték lekéréséhez (§12.2.2).
12.8.13 Null conditionális elem elérés
A null_conditional_element_access egy primary_expression-ből áll, amelyet a „?
” és „[
” tokenek követnek, majd egy argument_list, egy „]
” token, végül pedig nullás vagy több dependent_access, amelyek közül bármelyik null_forgiving_operator-rel előzhető meg.
null_conditional_element_access
: primary_expression '?' '[' argument_list ']'
(null_forgiving_operator? dependent_access)*
;
A null_conditional_element_access argumentumlista nem tartalmazhat és out
argumentumokat.
A primary_expression egy null_conditional_element_access esetén nem lehet egy array_creation_expression, hacsak nem tartalmaz egy array_initializer, vagy egy stackalloc_expression, hacsak nem tartalmaz egy stackalloc_initializer.
Megjegyzés: Ez a korlátozás a potenciálisan zavaró kód letiltására vonatkozik. Hasonló korlátozás vonatkozik a element_access (12.8.12. §), ahol a kizárt adatokra lehet példa. végjegyzet
A null_conditional_element_access a element_access feltételes verziója (§12.8.12), és kötési időhiba, ha az eredménytípus void
. Null feltételes kifejezés esetén, ahol az eredménytípus void
lehet, lásd (§12.8.11).
A null_conditional_element_access kifejezés a következő formában van E
P?[A]B
; ahol B
van a dependent_access, ha van ilyen. A E
jelentése a következőképpen határozható meg:
Ha a
P
típusa null értékű:Legyen
T
aP.Value[A]B
kifejezés típusa.Ha
T
olyan típusparaméter, amely nem ismert referenciatípus vagy nem null értékű értéktípus, fordítási időhiba lép fel.Ha
T
nem null értékű érték, akkor aE
típusaT?
, és aE
jelentése megegyezik a következő jelentéssel:((object)P == null) ? (T?)null : P.Value[A]B
Kivéve, hogy a
P
csak egyszer lesz kiértékelve.Ellenkező esetben a
E
típusaT
, és aE
jelentése megegyezik a következők jelentésével:((object)P == null) ? null : P.Value[A]B
Kivéve, hogy a
P
csak egyszer lesz kiértékelve.
Egyébként:
Legyen
T
aP[A]B
kifejezés típusa.Ha
T
olyan típusparaméter, amely nem ismert referenciatípus vagy nem null értékű értéktípus, fordítási időhiba lép fel.Ha
T
nem null értékű érték, akkor aE
típusaT?
, és aE
jelentése megegyezik a következő jelentéssel:((object)P == null) ? (T?)null : P[A]B
Kivéve, hogy a
P
csak egyszer lesz kiértékelve.Ellenkező esetben a
E
típusaT
, és aE
jelentése megegyezik a következők jelentésével:((object)P == null) ? null : P[A]B
Kivéve, hogy a
P
csak egyszer lesz kiértékelve.
Megjegyzés: Egy kifejezés alakjában:
P?[A₀]?[A₁]
Ha
P
kiértékelésenull
, akkor semA₀
, semA₁
nem lesz kiértékelve. Ugyanez igaz, ha egy kifejezés egy sorozat a null_conditional_element_access vagy null_conditional_member_access§12.8.8 műveletekből áll.végjegyzet
12.8.14 Ez a hozzáférés
A this_access a this
kulcsszóból áll.
this_access
: 'this'
;
A this_access csak egy példánykonstruktor, példánymetódus, példány-hozzáférő (§12.2.1) vagy véglegesítő blokkjában engedélyezett. A következő jelentéssel rendelkezik:
- Ha a
this
egy osztály példánykonstruktorán belül elsődleges_kifejezés formájában van használva, akkor értékként lesz besorolva. Az érték típusa annak az osztálynak a példánytípusa (§15.3.2), amelyben a használat történik, és az érték a létrehozandó objektumra mutató hivatkozás. - Ha a
this
-t egy osztály példány metódusában vagy példány hozzáférőjében lévő primary_expression-ben használják, az értékként lesz besorolva. Az érték típusa annak az osztálynak a példánytípusa (§15.3.2), amelyen belül a használat történik, és az érték annak az objektumnak a hivatkozása, amelyre a metódust vagy tartozékot meghívták. - Ha a
this
egy struktúra példánykonstruktorában lévő elsődleges_kifejezés használva van, akkor változóként kerül besorolásra. A változó típusa annak a szerkezetnek a példánytípusa (§15.3.2), amelyen belül a használat történik, és a változó a felépítendő szerkezetet jelöli.- Ha a konstruktor deklarációja nem rendelkezik konstruktor inicializálóval, a
this
változó pontosan ugyanúgy viselkedik, mint a szerkezettípus kimeneti paramétere. Ez különösen azt jelenti, hogy a változót mindenképpen hozzá kell rendelni a példánykonstruktor minden végrehajtási útvonalához. - Ellenkező esetben a
this
változó pontosan ugyanúgy viselkedik, mint a struktúratípusref
paramétere. Ez különösen azt jelenti, hogy a változót kezdetben hozzárendeltnek tekintik.
- Ha a konstruktor deklarációja nem rendelkezik konstruktor inicializálóval, a
- Ha a
this
-t egy struktúra példánymódszerén vagy példányhozzáférőjén belüli elsődleges_kifejezés-ként használják, azt változóként sorolják be. A változó típusa annak a szerkezetnek a példánytípusa (§15.3.2), amelyen belül a használat történik.- Ha a metódus vagy tartozék nem iterátor (15.15.§) vagy aszinkron függvény (15.14.§), a
this
változó azt a szerkezetet jelöli, amelyre a metódust vagy tartozékot meghívták.- Ha a szerkezet egy
readonly struct
, athis
változó pontosan ugyanúgy viselkedik, mint a struktúratípus bemeneti paramétere. - Ellenkező esetben a
this
változó pontosan ugyanúgy viselkedik, mint a struktúratípusref
paramétere
- Ha a szerkezet egy
- Ha a metódus vagy tartozék egy iterátor vagy aszinkron függvény, a
this
változó annak a szerkezetnek a másolatát jelöli, amelyhez a metódust vagy tartozékot meghívták, és pontosan ugyanúgy viselkedik, mint egy érték szerkezettípus paramétere.
- Ha a metódus vagy tartozék nem iterátor (15.15.§) vagy aszinkron függvény (15.14.§), a
A this
használata primary_expression formában a fent felsoroltaktól eltérő környezetben fordítási időhiba. Nem lehet különösen a this
-re hivatkozni statikus módszerben, statikus tulajdonság-elérésekor, vagy egy mező deklarációjának variable_initializer-jében.
12.8.15 Alaphozzáférés
A base_access a base
kulcsszóból áll, amelyet egy „.
” token követ, valamint egy azonosító és opcionális type_argument_list, vagy egy szögletes zárójelek közé zárt argument_list.
base_access
: 'base' '.' identifier type_argument_list?
| 'base' '[' argument_list ']'
;
A base_access olyan alaposztálytagok elérésére szolgál, amelyeket az aktuális osztály vagy struktúra hasonló nevű tagjai rejtenek el. A base_access csak a példánykonstruktor törzsében, egy példánymetódusban, egy példánykiegészítőben (12.2.1) vagy egy véglegesítőben engedélyezett. Ha base.I
osztályban vagy szerkezetben fordul elő, I
az adott osztály vagy szerkezet alaposztályának egy tagját kell jelölnie. Hasonlóképpen, ha base[E]
egy osztályban fordul elő, az alaposztályban egy alkalmazható indexelőnek kell léteznie.
Kötési időben a és base.I
formájú base[E]
kifejezéseket pontosan úgy értékeli ki a rendszer, mintha ((B)this).I
-ként és ((B)this)[E]
-ként lettek volna írva, ahol B
annak az osztálynak vagy struktúrának, amelyben a konstrukció előfordul, az alaposztálya. Így base.I
és base[E]
hozzájuk tartoznak this.I
-höz és this[E]
-hoz, kivételével, hogy this
az alaposztály egy példányaként tekinthető.
Ha egy base_access egy virtuális függvény tagjára (metódusra, tulajdonságra vagy indexelőre) hivatkozik, annak meghatározása, hogy melyik függvénytagot hívja meg futásidőben (12.6.6. §) megváltozik. A meghívott függvénytagot a függvénytag legjobban levezetett implementációjának (§15.6.4) megtalálásával határozzuk meg a B
szempontjából, az alapszintű hozzáférés szokásos módjával ellentétben, amely a this
futásidejű típusát venné figyelembe. Így egy virtuális függvénytag felülbírálásán belül egy base_access használható a függvénytag öröklött implementációjának meghívására. Ha egy base_access által hivatkozott függvénytag absztrakt, kötési idejű hiba lép fel.
Megjegyzés: A
this
ellentétben abase
önmagában nem kifejezés. Ez a kulcsszó csak egy base_access vagy constructor_initializer kontextusában használatos (§15.11.2). végjegyzet
12.8.16 Postfix növekményes és csökkentő operátorok
post_increment_expression
: primary_expression '++'
;
post_decrement_expression
: primary_expression '--'
;
Egy postfix növekmény vagy csökkentés művelet operandusa olyan kifejezés kell legyen, amely változóként, tulajdonsághozzáférésként vagy indexelőként van besorolva. A művelet eredménye az operandussal azonos típusú érték.
Ha a primary_expression fordítási idejű típusú dynamic
, akkor az operátor dinamikusan kötött (§12.3.3), a post_increment_expression vagy post_decrement_expression fordítási idejű típusú dynamic
, és a következő szabályokat alkalmazzák futásidőben a primary_expressionfutásidejű típusának használatával.
Ha a postfix inkrementálási vagy dekrementálási művelet operandusa egy tulajdonság vagy indexelő hozzáférés, akkor a tulajdonságnak vagy az indexelőnek rendelkeznie kell mind get, mind set hozzáférőkkel. Ha nem ez a helyzet, kötési idő hiba lép fel.
Az egyoperandusú operátor túlterhelésének feloldását (§12.4.4) alkalmazzák egy adott operátor-megvalósítás kiválasztására. Az előre definiált ++
és --
operátorok a következő típusokhoz léteznek: sbyte
, byte
, short
, ushort
, int
, uint
, long
, ulong
, char
, float
, double
, decimal
és bármely számtípus. Az előre definiált ++
operátorok az operandushoz 1
hozzáadásával előállított értéket, az előre definiált --
operátorok pedig az operandusból 1
kivonásával előállított értéket adja vissza. Ha az összeadás vagy kivonás eredménye az eredménytípus tartományán kívül esik, és az eredménytípus egy integráltípus vagy számtípus, a rendszer System.OverflowException
dob.
Implicit átalakítást kell alkalmazni a kiválasztott unáris operátor visszatérési típusáról a primary_expressiontípusára, különben fordítási időben hiba lép fel.
Az x++
vagy x--
alakú postfix növekmény vagy csökkentés művelet futásidejű feldolgozása a következő lépésekből áll:
- Ha
x
változóként van besorolva:-
x
kiértékelése révén jön létre a változó. - A
x
értéke mentésre kerül. - A
x
mentett értéke a kiválasztott operátor operandustípusára lesz konvertálva, és az operátor argumentumként ezzel az értékkel lesz meghívva. - Az operátor által visszaadott érték a
x
típusára lesz konvertálva, és ax
korábbi kiértékelése által megadott helyen lesz tárolva. - A
x
mentett értéke lesz a művelet eredménye.
-
- Ha
x
tulajdonságként vagy indexelői hozzáférésként van besorolva:- A példánykifejezést (ha
x
nemstatic
) és ax
-hoz társított argumentumlistát (hax
egy indexelői hozzáférés) kiértékeli a rendszer, és az eredmények a következő lekérés és beállítás kiegészítő meghívásaiban kerülnek felhasználásra. - A
x
lekérdező hívása megtörténik, és a visszaadott érték mentésre kerül. - A
x
mentett értéke a kiválasztott operátor operandustípusára lesz konvertálva, és az operátor argumentumként ezzel az értékkel lesz meghívva. - Az operátor által visszaadott érték a
x
típusára lesz konvertálva, és ax
halmaz tartozéka ezzel az értékkel lesz meghívva értékargumentumként. - A
x
mentett értéke lesz a művelet eredménye.
- A példánykifejezést (ha
A ++
és --
operátorok is támogatják az előtag jelölését (§12.9.6). A x
ugyanazzal az értékkel rendelkezik a művelet után.
Az operátor ++
vagy operátor --
implementációja postfix vagy prefix jelöléssel hívható meg. A két jelöléshez nem lehet külön operátor-implementációt létrehozni.
12.8.17 Az új operátor
12.8.17.1 Általános
A new
operátor új típusú példányok létrehozására szolgál.
Az új kifejezéseknek három formája létezik:
- Az objektumlétrehozó kifejezések az osztálytípusok és értéktípusok új példányainak létrehozására szolgálnak.
- A tömblétrehozó kifejezések a tömbtípusok új példányainak létrehozására szolgálnak.
- A delegált létrehozási kifejezések delegált példányok beszerzésére szolgálnak.
A new
operátor egy típuspéldány létrehozását jelenti, de nem feltétlenül jelenti a memória lefoglalását. Az értéktípusok példányai nem igényelnek további memóriát azon változókon túl, amelyekben találhatók, és nem történik foglalás, ha new
használnak értéktípusok példányainak létrehozásához.
Megjegyzés: A delegált létrehozási kifejezések nem mindig hoznak létre új példányokat. Ha a kifejezés feldolgozása ugyanúgy történik, mint egy metóduscsoport-átalakítás (§10.8) vagy névtelen függvényátalakítás (§10.7), az egy meglévő delegált példány újbóli felhasználását eredményezheti. végjegyzet
12.8.17.2 Objektumlétrehozó kifejezések
12.8.17.2.1 Általános
A object_creation_expression egy class_type vagy value_typeúj példányának létrehozására szolgál.
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
;
Egy objektum_létrehozó_kifejezéstípusának egy osztály_típus, egy érték_típusvagy egy típus_paraméterkell lennie. A típus nem lehet tuple_típus vagy absztrakt vagy statikus osztálytípus.
A választható argument_list (§12.6.2) csak akkor engedélyezett, ha a típus egy class_type vagy egy struct_type.
Az objektumlétrehozási kifejezések kihagyhatják a konstruktor argumentumlistáját, és zárójeleket tartalmazhatnak, feltéve, hogy tartalmaz egy objektum inicializálót vagy gyűjtemény inicializálót. A konstruktor argumentumlistájának kihagyása és zárójelekbe helyezése egyenértékű egy üres argumentumlista megadásával.
Az objektum-inicializálót vagy gyűjtemény inicializálót tartalmazó objektumlétrehozási kifejezés feldolgozásához először a példánykonstruktort kell feldolgozni, majd feldolgozni az objektum inicializáló által meghatározott tag- vagy elemi inicializálásokat (§12.8.17.2.2) vagy gyűjtemény inicializálóját (§12.8.17.2.3).
Ha az opcionális argument_list bármelyik argumentuma fordítási idő alatt rendelkezik a dynamic
típussal, akkor a object_creation_expression dinamikusan kötött lesz (§12.3.3), és a következő szabályokat alkalmazzák futásidőben azokra az argumentumokra az argument_list-ból, amelyek fordítási idő alatt a dynamic
típussal rendelkeznek. Az objektumlétrehozás azonban egy korlátozott fordítási idejű ellenőrzésen megy keresztül, ahogy azt a §12.6.5írja le.
A new T(A)
kötési idejének feldolgozása, ahol T
egy class_typevagy value_type, és A
egy választható argument_list, a következő lépésekből áll:
- Ha
T
egy value_type, ésA
nincs jelen:- A object_creation_expression egy alapértelmezett konstruktorhívás. A object_creation_expression eredménye egy
T
típusú érték, nevezetesen aT
alapértelmezett értéke a §8.3.3.
- A object_creation_expression egy alapértelmezett konstruktorhívás. A object_creation_expression eredménye egy
- Ellenkező esetben, ha
T
egy type_parameter, ésA
nincs jelen:- Ha nincs megadva értéktípus-korlátozás vagy konstruktorkorlát (§15.2.5) a
T
esetében, kötési időhiba lép fel. - A object_creation_expression eredménye annak a futásidejű típusnak az értéke, amelyhez a típusparaméter hozzá lett kötve, vagyis az adott típus alapértelmezett konstruktorának meghívásának eredménye. A futtatási idő típusa lehet hivatkozástípus vagy értéktípus.
- Ha nincs megadva értéktípus-korlátozás vagy konstruktorkorlát (§15.2.5) a
- Ellenkező esetben, ha az
T
class_type vagy struct_type:- Ha
T
absztrakt vagy statikus osztálytípus, fordítási időhiba lép fel. - A meghívandó példánykonstruktort a túlterhelés feloldási szabályai határozzák meg a §12.6.4alapján. A jelölt példánykonstruktorok halmaza minden
T
-ban deklarált elérhető példánykonstruktorból áll, amelyek alkalmazhatók aA
tekintetében (§12.6.4.2). Ha a jelölt példánykonstruktorok készlete üres, vagy ha egyetlen legjobb példánykonstruktor nem azonosítható, kötési idejű hiba lép fel. - A object_creation_expression eredménye egy
T
típusú érték, nevezetesen a fenti lépésben meghatározott példánykonstruktor meghívásával előállított érték. - Ellenkező esetben a object_creation_expression érvénytelen, és kötési időhiba lép fel.
- Ha
Még ha a object_creation_expression dinamikusan kötött is, a fordítási idő típusa továbbra is T
.
Az object_creation_expression futásidejű feldolgozása új T(A)
formájában, ahol T
class_type vagy struct_type, és A
egy opcionális argument_list, a következő lépésekből áll:
- Ha
T
egy class_type:- Az osztály
T
új példánya allokálódik. Ha nincs elegendő memória az új példány lefoglalásához, a rendszerSystem.OutOfMemoryException
dob, és nem hajt végre további lépéseket. - Az új példány minden mezője inicializálva lesz az alapértelmezett értékre (§9.3).
- A példánykonstruktor meghívása a függvénytagok meghívásának szabályai szerint történik (§12.6.6). A rendszer automatikusan átadja az újonnan lefoglalt példányra mutató hivatkozást a példánykonstruktornak, és a példány a konstruktoron belülről is elérhető.
- Az osztály
- Ha
T
egy struct_type, akkor...- Egy ideiglenes helyi változó kiosztásával
T
típusú példány jön létre. Mivel egy struct_type példánykonstruktorának feltétlenül hozzá kell rendelnie egy értéket a létrehozott példány egyes mezőihez, nem szükséges inicializálni az ideiglenes változót. - A példánykonstruktor meghívása a függvénytagok meghívásának szabályai szerint történik (§12.6.6). A rendszer automatikusan átadja az újonnan lefoglalt példányra mutató hivatkozást a példánykonstruktornak, és a példány a konstruktoron belülről is elérhető.
- Egy ideiglenes helyi változó kiosztásával
12.8.17.2.2 Objektum-inicializálók
Egy objektum inicializáló egy objektum nulla vagy több mező, tulajdonság vagy indexelt elem értékeit adja meg.
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
;
Egy objektum-inicializáló tagok inicializálóinak sorozatából áll, amelyeket a {
és }
tokenek közé teszünk, és vesszőkkel választunk el. Minden member_initializer meg kell jelölnie az inicializálás célját. Az azonosító az inicializálandó objektum egy akadálymentes mezőjét vagy tulajdonságát, míg a szögletes zárójelek közé zárt argument_list argumentumokat kell megadnia az inicializálandó objektum akadálymentes indexelője számára. Hiba, hogy egy objektum inicializálója több tag inicializálót is tartalmaz ugyanahhoz a mezőhöz vagy tulajdonsághoz.
Megjegyzés: Bár egy objektum inicializálója nem állíthatja be többször ugyanazt a mezőt vagy tulajdonságot, az indexelőkre nincsenek ilyen korlátozások. Az objektum-inicializálók több inicializáló célt is tartalmazhatnak, amelyek indexelőkre hivatkoznak, és akár többször is ugyanazt az indexelő argumentumot használhatják. végjegyzet
Minden initializer_target után egyenlőségjel, valamint egy kifejezés, objektum inicializáló vagy gyűjtemény inicializáló következik. Az objektum inicializálójának kifejezései nem hivatkozhatnak arra az újonnan létrehozott objektumra, amelyet inicializál.
A tag inicializálója, amely az egyenlőségjel utáni kifejezést adja meg, ugyanúgy lesz feldolgozva, mint egy hozzárendelés (§12.21.2) a célhoz.
Egy olyan taginicializáló, amely az egyenlőségjel után objektuminicializálót ad meg, egy beágyazott objektuminicializáló, azaz egy beágyazott objektum inicializálása. Ahelyett, hogy új értéket rendel a mezőhöz vagy tulajdonsághoz, a beágyazott objektum inicializálójában lévő hozzárendeléseket a rendszer a mező vagy tulajdonság tagjaihoz való hozzárendelésként kezeli. A beágyazott objektum-inicializálók nem alkalmazhatók értéktípusú tulajdonságokra vagy értéktípusú, írásvédett mezőkre.
Egy tag inicializáló, amely az egyenlőségjel után egy gyűjtemény inicializálóját határozza meg, egy beágyazott gyűjtemény inicializálása. Ahelyett, hogy új gyűjteményt rendel a célmezőhöz, tulajdonsághoz vagy indexelőhöz, az inicializálóban megadott elemek a cél által hivatkozott gyűjteményhez lesznek hozzáadva. A célnak olyan gyűjteménytípusnak kell lennie, amely megfelel a §12.8.17.2.3által megadott követelményeknek.
Ha egy inicializáló cél indexelőre hivatkozik, az indexelő argumentumait mindig pontosan egyszer kell kiértékelni. Így még akkor is, ha az argumentumok végül soha nem lesznek használatban (például egy üres beágyazott inicializáló miatt), a rendszer kiértékeli a mellékhatásokat.
példa: Az alábbi osztály két koordinátával rendelkező pontot jelöl:
public class Point { public int X { get; set; } public int Y { get; set; } }
A
Point
egy példánya a következőképpen hozható létre és inicializálható:Point a = new Point { X = 0, Y = 1 };
Ennek ugyanaz a hatása, mint a
Point __a = new Point(); __a.X = 0; __a.Y = 1; Point a = __a;
ahol a
__a
egy egyébként láthatatlan és elérhetetlen ideiglenes változó.Az alábbi osztály egy két pontból létrehozott téglalapot, valamint egy
Rectangle
-példány létrehozását és inicializálását mutatja be:public class Rectangle { public Point P1 { get; set; } public Point P2 { get; set; } }
A
Rectangle
egy példánya a következőképpen hozható létre és inicializálható:Rectangle r = new Rectangle { P1 = new Point { X = 0, Y = 1 }, P2 = new Point { X = 2, Y = 3 } };
Ennek ugyanaz a hatása, mint a
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;
ahol
__r
,__p1
és__p2
ideiglenes változók, amelyek egyébként láthatatlanok és elérhetetlenek.Ha
Rectangle
konstruktora lefoglalja a két beágyazottPoint
példányt, az új példányok hozzárendelése helyett a beágyazottPoint
példányok inicializálására használhatók:public class Rectangle { public Point P1 { get; } = new Point(); public Point P2 { get; } = new Point(); }
a következő szerkezet használható a beágyazott
Point
példányok inicializálására új példányok hozzárendelése helyett:Rectangle r = new Rectangle { P1 = { X = 0, Y = 1 }, P2 = { X = 2, Y = 3 } };
Ennek ugyanaz a hatása, mint a
Rectangle __r = new Rectangle(); __r.P1.X = 0; __r.P1.Y = 1; __r.P2.X = 2; __r.P2.Y = 3; Rectangle r = __r;
példa vége
12.8.17.2.3 Gyűjtemény inicializálók
A gyűjtemény inicializálója a gyűjtemény elemeit határozza meg.
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)*
;
A gyűjteményinicializáló egy olyan sorozatból áll, amely elem-inicializálókat tartalmaz, és ezeket a {
és }
tokenek fogják közre, vesszővel elválasztva. Minden elem inicializálója megad egy elemet, amelyet hozzá kell adni az inicializálandó gyűjteményobjektumhoz, és {
és }
jogkivonatok által, vesszővel elválasztott kifejezésekből áll. Az egykifejezéses elem-inicializáló kapcsos zárójelek nélkül is írható, de nem lehet hozzárendelési kifejezés, így elkerülhető a tag inicializálókkal való kétértelműség. A non_assignment_expression meghatározása a 12.22. §szakaszban található.
példa: Az alábbi példa egy gyűjtemény inicializálót tartalmazó objektumlétrehozó kifejezésre:
List<int> digits = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
példa vége
A gyűjtemény-inicializálót alkalmazó gyűjteményobjektumnak olyan típusúnak kell lennie, amely megvalósítja a System.Collections.IEnumerable
-t, különben fordítási időhiba lép fel. Az egyes megadott elemeknél balról jobbra haladva a rendszer a normál tagkeresést alkalmazza a Add
nevű tag megkeresésére. Ha a tagkeresés eredménye nem metóduscsoport, fordítási időhiba lép fel. Ellenkező esetben a túlterhelés feloldása argumentumlistaként az elem inicializálójának kifejezéslistájával történik, és a gyűjtemény inicializálója meghívja az eredményként kapott metódust. Így a gyűjteményobjektumnak tartalmaznia kell egy alkalmazható példányt vagy bővítménymetódust, amelynek neve Add
az egyes elem-inicializálókhoz.
Példa: Az alábbiakban egy osztály látható, amely egy nevet és egy telefonszámok listáját tartalmazó kontaktot képvisel, valamint a
List<Contact>
létrehozását és inicializálását.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" } } }; } }
amelynek ugyanaz a hatása, mint a
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;
ahol
__clist
,__c1
és__c2
ideiglenes változók, amelyek egyébként láthatatlanok és elérhetetlenek.példa vége
12.8.17.3 Névtelen objektumlétrehozó kifejezések
A anonymous_object_creation_expression egy névtelen típusú objektum létrehozására szolgál.
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
;
A névtelen objektum inicializálója névtelen típust deklarál, és visszaad egy ilyen típusú példányt. A névtelen típus egy névtelen osztálytípus, amely közvetlenül object
örökli. A névtelen típus tagjai olyan írásvédett tulajdonságok sorozata, amely a névtelen objektum inicializálásának eredményeként jön létre, amikor a típuspéldányt készítik el. Pontosabban egy névtelen objektum-inicializáló az alábbi formában
new {
p₁=
e₁,
p₂=
e₂,
...
pv=
ev}
az űrlap névtelen típusát deklarálja
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() { ... }
}
ahol minden «Tx» a megfelelő «ex» kifejezés típusa. A member_declarator használt kifejezésnek típussal kell rendelkeznie. Jelentkezik fordítási időhiba, ha a member_declarator egy kifejezés null
vagy névtelen függvény.
A névtelen típus és annak Equals
metódusához tartozó paraméter neveit a fordító automatikusan generálja, és a program szövegében nem lehet rájuk hivatkozni.
Ugyanazon a programon belül két névtelen objektum inicializálója, amelyek azonos nevek és fordítási idő típusú tulajdonságok sorozatát adják meg ugyanabban a sorrendben, azonos névtelen típusú példányokat hoznak létre.
példa: A példában
var p1 = new { Name = "Lawnmower", Price = 495.00 }; var p2 = new { Name = "Shovel", Price = 26.95 }; p1 = p2;
az utolsó sor hozzárendelése engedélyezett, mert a
p1
és ap2
azonos névtelen típusúak.példa vége
A névtelen típusok Equals
és GetHashcode
metódusai felülbírálják a object
örökölt metódusokat, és a tulajdonságok Equals
és GetHashcode
szempontjából vannak definiálva, így az azonos névtelen típus két példánya egyenlő, ha és csak akkor, ha minden tulajdonságuk egyenlő.
A tag deklarátor rövidítése lehet egy egyszerű név (§12.8.4), egy taghozzáférés (§12.8.7), egy null feltételes kivetítési inicializáló (§12.8.8) vagy egy alaphozzáférés (§12.8.15). Ezt előrejelzési inicializáló nevezik, és rövidítése egy azonos nevű tulajdonság deklarációjának és hozzárendelésének. Konkrétan az űrlapok tagdeklarátorai
«identifier»
, «expr» . «identifier»
és «expr» ? . «identifier»
pontosan egyenértékűek az alábbiakval:
«identifer» = «identifier»
, «identifier» = «expr» . «identifier»
és «identifier» = «expr» ? . «identifier»
Így egy vetületi inicializálóban az azonosító kiválasztja azt az értéket és mezőt vagy tulajdonságot is, amelyhez az érték hozzá van rendelve. Intuitív módon a kivetítési inicializáló nem csak egy értéket, hanem az érték nevét is kivetíti.
12.8.17.4 Tömblétrehozás kifejezései
A array_creation_expression egy array_typeúj példányának létrehozására szolgál.
array_creation_expression
: 'new' non_array_type '[' expression_list ']' rank_specifier*
array_initializer?
| 'new' array_type array_initializer
| 'new' rank_specifier array_initializer
;
Az első űrlap tömblétrehozási kifejezése egy olyan típusú tömbpéldányt foglal le, amely az egyes kifejezések törlését eredményezi a kifejezéslistából.
Példa: A tömblétrehozó kifejezés
new int[10,20]
int[,]
típusú tömbpéldányt hoz létre, az újint[10][,]
pedigint[][,]
típusú tömbpéldányt hoz létre. példa vége
A kifejezéslistában szereplő kifejezéseknek int
, uint
, long
vagy ulong
típusúnak kell lenniük, vagy implicit módon átalakíthatók egy vagy több ilyen típusra. Az egyes kifejezések értéke határozza meg a megfelelő dimenzió hosszát az újonnan lefoglalt tömbpéldányban. Mivel a tömbdimenzió hossza nem lehet negatív, fordítási idejű hiba, ha a kifejezéslistában egy negatív értékkel rendelkező állandó kifejezés szerepel.
A nem biztonságos környezet (§23.2) kivételével a tömbök elrendezése meghatározatlan.
Ha az első űrlap tömblétrehozó kifejezése tömbinicializálót tartalmaz, a kifejezéslistában szereplő kifejezéseknek állandónak kell lenniük, és a kifejezéslista által megadott rang- és dimenzióhosszoknak meg kell egyeznie a tömb inicializálóéval.
A második vagy harmadik űrlap tömblétrehozó kifejezésében a megadott tömbtípus vagy rangválasztó rangjának meg kell egyeznie a tömb inicializálójának rangsorával. Az egyes dimenziók hosszát a tömb inicializálójának megfelelő beágyazási szintek elemeinek száma alapján határozzuk meg. Így az inicializáló kifejezés az alábbi deklarációban
var a = new int[,] {{0, 1}, {2, 3}, {4, 5}};
pontosan megfelel a
var a = new int[3, 2] {{0, 1}, {2, 3}, {4, 5}};
A harmadik formájú tömblétrehozó kifejezést implicit módon típusos tömblétrehozó kifejezésnek nevezzük. Hasonló a második űrlaphoz, azzal a kivételével, hogy a tömb elemtípusa nem explicit módon van megadva, hanem a tömb inicializálójának kifejezéskészletének legjobb közös típusaként (§12.6.3.15) határozható meg. Többdimenziós tömb esetén, azaz olyan tömbnél, ahol a rank_specifier legalább egy vesszőt tartalmaz, ez az összeállítás a beágyazott array_initializers-ekben található összes kifejezéstmagában foglalja.
A tömb inicializálóit részletesebben ismertetjük a §17.7-ben.
A tömblétrehozási kifejezés kiértékelésének eredménye értékként van besorolva, nevezetesen az újonnan lefoglalt tömbpéldányra mutató hivatkozásként. A tömblétrehozási kifejezés futásidejű feldolgozása a következő lépésekből áll:
- A expression_list dimenzióhossz-kifejezéseinek kiértékelése sorrendben történik, balról jobbra. Az egyes kifejezések kiértékelését követően implicit átalakítást (§10.2) hajtunk végre a következő típusok egyikére:
int
,uint
,long
,ulong
. A lista első olyan típusát választja ki, amelyhez implicit konverzió tartozik. Ha egy kifejezés kiértékelése vagy az azt követő implicit átalakítás kivételt okoz, akkor a rendszer nem értékel ki további kifejezéseket, és nem hajt végre további lépéseket. - A dimenzióhosszok számított értékei az alábbiak szerint lesznek érvényesítve: Ha egy vagy több érték nullánál kisebb, a rendszer
System.OverflowException
dob, és nem hajt végre további lépéseket. - A megadott dimenzióhosszúságokkal rendelkező tömbpéldány lesz lefoglalva. Ha nincs elegendő memória az új példány lefoglalásához, a rendszer
System.OutOfMemoryException
dob, és nem hajt végre további lépéseket. - Az új tömbpéldány minden eleme inicializálva lesz az alapértelmezett értékre (§9.3).
- Ha a tömblétrehozó kifejezés tömb inicializálót tartalmaz, a rendszer kiértékeli és hozzárendeli a tömb inicializálójának minden egyes kifejezését a megfelelő tömbelemhez. A kiértékelések és hozzárendelések abban a sorrendben lesznek végrehajtva, amelyben a kifejezések meg vannak írva a tömb inicializálójában– vagyis az elemek inicializálása növekvő index sorrendben történik, és a jobb szélső dimenzió növekszik először. Ha egy adott kifejezés kiértékelése vagy a megfelelő tömbelemhez való későbbi hozzárendelés kivételt okoz, akkor a rendszer nem inicializál további elemeket (és a többi elem így az alapértelmezett értékekkel fog rendelkezni).
A tömblétrehozási kifejezés lehetővé teszi egy tömb példányosítását egy tömbtípus elemeivel, de az ilyen tömb elemeit manuálisan kell inicializálni.
példa: Az utasítás
int[][] a = new int[100][];
egydimenziós tömböt hoz létre 100
int[]
típusú elemből. Az egyes elemek kezdeti értékenull
. Nem lehetséges, hogy ugyanaz a tömblétrehozó kifejezés is példányosíthassa az altömböket és az utasítástint[][] a = new int[100][5]; // Error
fordítási idejű hibát eredményez. Az altömbök példányosítása manuálisan is elvégezhető, ahogyan a
int[][] a = new int[100][]; for (int i = 0; i < 100; i++) { a[i] = new int[5]; }
példa vége
Megjegyzés: Ha tömbök tömbjei "téglalap alakúak", azaz az altömbök azonos hosszúságúak, hatékonyabb a többdimenziós tömbök használata. A fenti példában a tömbök tömbjének példányosítása 101 objektumot hoz létre – egy külső tömböt és 100 altömböt. Ezzel szemben
int[,] a = new int[100, 5];
csak egyetlen objektumot, kétdimenziós tömböt hoz létre, és egyetlen utasításban végzi el a lefoglalást.
végjegyzet
Példa: Az alábbiakban példákat mutatunk be az implicit módon típusozott tömblétrehozó kifejezésekre:
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
Az utolsó kifejezés fordítási időben hibát okoz, mert sem
int
, semstring
nem konvertálható implicit módon a másikra, ezért nincs legjobb közös típus. Ebben az esetben explicit módon beírt tömblétrehozási kifejezést kell használni, például meg kell adni aobject[]
típust. Másik lehetőségként az egyik elem átvehető egy közös alaptípusra, amely ezután a következtetett elemtípussá válik.példa vége
Az implicit módon beírt tömblétrehozó kifejezések névtelen objektum-inicializálókkal (§12.8.17.3) kombinálhatók névtelenül beírt adatstruktúrák létrehozásához.
Példa:
var contacts = new[] { new { Name = "Chris Smith", PhoneNumbers = new[] { "206-555-0101", "425-882-8080" } }, new { Name = "Bob Harris", PhoneNumbers = new[] { "650-555-0199" } } };
példa vége
12.8.17.5 Delegált létrehozási kifejezések
A delegate_creation_expression egy delegate_typepéldányának lekérésére szolgál.
delegate_creation_expression
: 'new' delegate_type '(' expression ')'
;
A delegált létrehozási kifejezés argumentuma egy metóduscsoport, egy névtelen függvény, vagy a fordítási idejű típus dynamic
vagy delegate_type értéke. Ha az argumentum metóduscsoport, akkor azonosítja a metódust, és egy példánymetódus esetében azt az objektumot, amelyhez delegáltat szeretne létrehozni. Ha az argumentum névtelen függvény, az közvetlenül meghatározza a delegált cél paramétereit és metódustörzsét. Ha az argumentum egy érték, akkor az egy olyan meghatalmazott példányt azonosít, amelynek másolatát létre kell hozni.
Ha a kifejezés fordítási idő típusú dynamic
rendelkezik, a delegate_creation_expression dinamikusan kötött (§12.8.17.5), és az alábbi szabályok futásidőben lesznek alkalmazva a kifejezésfutásidejű típusával. Ellenkező esetben a szabályokat fordításkor alkalmazzák.
A delegate_creation_expression kötési idejű feldolgozása az új D(E)
formájában – ahol a D
egy delegált típus és E
egy kifejezés– a következő lépésekből áll:
Ha
E
metóduscsoport, a delegált létrehozási kifejezés ugyanúgy lesz feldolgozva, mint a metóduscsoport-átalakítás (§10.8)E
-rólD
.Ha
E
névtelen függvény, a delegált létrehozási kifejezés ugyanúgy lesz feldolgozva, mint a névtelen függvények konvertálása (10.7. §-)E
-rólD
.Ha
E
érték, akkorE
-nek kompatibilisnek kell lennie (§20.2) aD
-gyel, és az eredmény egy újonnan létrehozott delegáltra mutató hivatkozás, amelyE
-öt hív meg.
Egy új D(E)
futásidejű feldolgozása – melyben a D
egy delegate_type és E
egy kifejezés– a következő lépésekből áll:
- Ha
E
metóduscsoport, a delegált létrehozási kifejezés metóduscsoport-átalakításként (10.8. §-) lesz kiértékelveE
-rólD
- ra. - Ha a
E
névtelen függvény, akkor a delegált létrehozását olyan névtelen függvénykonverzióként értékelik, amelyE
-rőlD
-re történik (§10.7). - Ha
E
egy delegate_typeértéke:-
E
-t értékelik. Ha ez a kiértékelés kivételt okoz, a rendszer nem hajt végre további lépéseket. - Ha a
E
értékenull
, a rendszerSystem.NullReferenceException
dob, és nem hajt végre további lépéseket. - A delegált típusú
D
új példánya lesz lefoglalva. Ha nincs elegendő memória az új példány lefoglalásához, a rendszerSystem.OutOfMemoryException
dob, és nem hajt végre további lépéseket. - Az új delegált példány inicializálva van egy egyszeri meghívási listával, amely meghívja
E
.
-
A delegált meghívási listája akkor kerül meghatározásra, amikor a delegáltat példányosítják, majd az teljes élettartama alatt állandó marad. Más szóval, egy delegátum célul szolgáló hívható entitásait a létrehozása után nem lehet módosítani.
Megjegyzés: Ne feledje, hogy ha két delegátumot egyesítenek, vagy egyiküket eltávolítják a másikból, új delegátum jön létre; egy meglévő delegátum sem változtatja meg a tartalmát. végjegyzet
Nem hozható létre olyan delegált, amely tulajdonságra, indexelőre, felhasználó által definiált operátorra, példánykonstruktorra, véglegesítőre vagy statikus konstruktorra hivatkozik.
Példa: A fentiekben leírtak szerint, amikor egy delegált egy metóduscsoportból jön létre, a delegált paraméterlistája és visszatérési típusa határozza meg, hogy melyik túlterhelt metódust válassza ki. A példában
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; }
a
A.f
mező inicializálva van egy delegálttal, amely a másodikSquare
metódusra hivatkozik, mivel ez a metódus pontosan megfelel a paraméterlistának, és visszaadja aDoubleFunc
típusát. Ha a másodikSquare
metódus nem jelenik meg, fordítási időhiba történt volna.példa vége
12.8.18 Az operátor típusa
A typeof
operátor a típus System.Type
objektumának lekérésére szolgál.
typeof_expression
: 'typeof' '(' type ')'
| 'typeof' '(' unbound_type_name ')'
| 'typeof' '(' 'void' ')'
;
unbound_type_name
: identifier generic_dimension_specifier? ('.' identifier generic_dimension_specifier?)*
| unbound_qualified_alias_member ('.' identifier generic_dimension_specifier?)*
;
unbound_qualified_alias_member
: identifier '::' identifier generic_dimension_specifier?
;
generic_dimension_specifier
: '<' comma* '>'
;
comma
: ','
;
A typeof_expression első formája egy typeof
kulcsszóból és egy zárójeles típusból áll. Az űrlap kifejezésének eredménye a megadott típus System.Type
objektuma. Egy adott típushoz csak egy System.Type
objektum tartozik. Ez azt jelenti, hogy egy T
típus esetében a typeof(T) == typeof(T)
mindig igaz. A típus nem lehet dynamic
.
A typeof_expression második formája egy typeof
kulcsszóból áll, amelyet zárójeles unbound_type_namekövet.
Megjegyzés: A unbound_type_name és a unbound_qualified_alias_member nyelvtana a type_name (§7.8) és a qualified_alias_member (§14.8.1) nyelvtanát követik, kivéve, hogy a generic_dimension_specifierek a type_argument_listek helyébe lépnek. végjegyzet
A typeof_expression operandusának felismerésekor, ha unbound_type_name és type_name is alkalmazható, nevezetesen ha sem generic_dimension_specifier , sem type_argument_list nem tartalmaz, akkor type_name kell választani.
Megjegyzés: Az ANTLR automatikusan választja a megadott választást a typeof_expression alternatíváinak sorrendje miatt. végjegyzet
A unbound_type_name jelentése a következőképpen határozható meg:
- A tokenek sorozata egy type_name-né alakul, mégpedig úgy, hogy minden egyes generic_dimension_specifier lecserélődik egy type_argument_list-re, amelynek ugyanannyi vesszője van és tartalmazza a
object
kulcsszót, mint az egyes type_argument. - Az eredményként kapott type_name fel van oldva egy létrehozott típusra (7.8.§).
- A unbound_type_name a feloldott generált típushoz társított kötetlen általános típus (8.4. §).
Megjegyzés: Nincs szükség a tokenek sorozatának átalakítására vagy a közvetítő típus előállítására, csak a kötetlen általános típus meghatározására, mintha ezt a folyamatot követné. végjegyzet
Hiba, ha a típus neve nullázható hivatkozástípus.
A typeof_expression eredménye az eredményül kapott kötetlen általános típus System.Type
objektuma.
A typeof_expression harmadik formája egy typeof
kulcsszó, majd egy zárójeles void
kulcsszó. Az űrlap kifejezésének eredménye az a System.Type
objektum, amely egy típus hiányát jelzi. A typeof(void)
által visszaadott típusobjektum különbözik a bármely típushoz visszaadott típusobjektumtól.
Megjegyzés: Ez a speciális
System.Type
objektum olyan osztálykódtárakban hasznos, amelyek lehetővé teszik a nyelv metódusainak tükrözését, ahol ezek a metódusok bármilyen metódus visszatérési típusát szeretnék ábrázolni, beleértvevoid
metódusokat is,System.Type
egy példányával. végjegyzet
A typeof
operátor egy típusparaméteren használható. Fordítás közbeni hiba, ha a típus neve ismerten nullázható hivatkozástípus. Az eredmény a típusparaméterhez kötött futásidejű típus System.Type
objektuma. Ha a futásidejű típus null értékű hivatkozástípus, az eredmény a megfelelő nem null értékű hivatkozástípus. A typeof
operátor konstruktált vagy kötetlen általános típuson is használható (§8.4.4). A kötetlen általános típus System.Type
objektuma nem azonos a példánytípus System.Type
objektumával (§15.3.2). A példánytípus futásidőben mindig zárt, konstruktált típus, ezért System.Type
objektuma a használatban lévő futásidejű típusargumentumoktól függ. A kötetlen általános típus viszont nem rendelkezik típusargumentumokkal, és ugyanazt a System.Type
objektumot adja eredményül, függetlenül a futásidejű típus argumentumaitól.
Példa: A példa
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(); } }
a következő kimenetet állítja elő:
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]
Vegye figyelembe, hogy
int
ésSystem.Int32
azonos típusúak. Atypeof(X<>)
eredménye nem függ a típusargumentumtól, de atypeof(X<T>)
eredménye igen.példa vége
12.8.19 Az operátor mérete
A sizeof
operátor egy adott típusú változó által elfoglalt 8 bites bájtok számát adja vissza. Az operandusként megadott típusnak unmanaged_type (§8.8) kell lennie.
sizeof_expression
: 'sizeof' '(' unmanaged_type ')'
;
Bizonyos előre definiált típusok esetében a sizeof
operátor állandó int
értéket ad az alábbi táblázatban látható módon:
Kifejezés | Eredmény |
---|---|
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 |
Enum típusú T
esetén a sizeof(T)
kifejezés eredménye az alapul szolgáló típus méretével megegyező állandó érték, a fentieknek megfelelően. Az összes többi operandustípus esetében a sizeof
operátor a §23.6.9-ban van meghatározva.
12.8.20 Az ellenőrzött és a nem ellenőrzött operátorok
A checked
és unchecked
operátorokkal szabályozható az integrált típusú aritmetikai műveletek és átalakítások túlcsordulás-ellenőrzési környezete.
checked_expression
: 'checked' '(' expression ')'
;
unchecked_expression
: 'unchecked' '(' expression ')'
;
A checked
operátor egy ellenőrzött környezetben értékeli ki a tartalmazott kifejezést, a unchecked
operátor pedig egy nem ellenőrzött környezetben értékeli ki a tartalmazott kifejezést. Egy checked_expression vagy unchecked_expression pontosan egy parenthesized_expression (§12.8.5) felel meg, azzal a kivétellel, hogy a tartalmazott kifejezés kiértékelése a megadott túlcsordulás-ellenőrzési környezetben történik.
A túlcsordulás-ellenőrzési környezet a checked
és unchecked
utasításokon keresztül is szabályozható (13.12. §-).
Az ellenőrzött és nem ellenőrzött operátorok és utasítások által létrehozott túlcsordulás-ellenőrzési környezet a következő műveleteket érinti:
- Az előre definiált
++
és--
operátorok (§12.8.16 és §12.9.6), ha az operandus integrál vagy szám típusú. - Az előre definiált
-
egyváltozós operátor (§12.9.3), amikor az operandus egész típusú. - Az előre definiált
+
,-
,*
és/
bináris operátorok (§12.10), ha mindkét operandus integrál- vagy számtípusú. - Explicit numerikus átalakítások (§10.3.2) egy egész vagy enumerációs típusból egy másik egész vagy enumerációs típusba, vagy
float
vagydouble
egész vagy enumerációs típusra.
Ha a fenti műveletek egyike olyan eredményt hoz létre, amely túl nagy ahhoz, hogy a céltípusban szerepeljen, a művelet végrehajtásának környezete szabályozza az eredményként kapott viselkedést:
- Ha egy
checked
környezetben a művelet állandó kifejezés (§12.23), fordítási idejű hiba következik be. Ellenkező esetben, ha a műveletet futásidőben hajtják végre, a rendszerSystem.OverflowException
dob. - Egy
unchecked
környezetben az eredmény úgy csonkítódik, hogy elveti a céltípusba nem illő, magas helyiértékű biteket.
A nem állandó kifejezések (§12.23) (futtatáskor kiértékelt kifejezések) esetében, amelyek nincsenek checked
vagy unchecked
operátorok vagy utasítások által elzárva, az alapértelmezett túlcsordulás-ellenőrzési környezet nincs bejelölve, kivéve, ha külső tényezők (például fordítókapcsolók és végrehajtási környezet konfigurációja) nem kérnek ellenőrzött értékelést.
Állandó kifejezések (§12.23) (fordításkor teljes mértékben kiértékelhető kifejezések) esetén a rendszer mindig ellenőrzi az alapértelmezett túlcsordulás-ellenőrzési környezetet. Ha egy állandó kifejezés nincs kifejezetten elhelyezve egy unchecked
környezetben, a kifejezés fordítási idő alatti kiértékelése során fellépő túlcsordulások mindig fordítási idő hibákat okoznak.
A névtelen függvények törzsét nem érintik checked
vagy unchecked
környezetek, amelyekben a névtelen függvény előfordul.
példa: Az alábbi kódban
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 }
Nem jelentkeznek fordítási idejű hibák, mivel egyik kifejezés sem értékelhető ki fordítási idő során. Futásidőben a
F
metódus egySystem.OverflowException
ad vissza, aG
metódus pedig 727379968 (a tartományon kívüli eredmény alsó 32 bitje). AH
metódus viselkedése a fordítás alapértelmezett túlcsordulás-ellenőrzési környezetétől függ, de vagy ugyanaz, mintF
, vagy ugyanaz, mintG
.példa vége
példa: Az alábbi kódban
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 }
A
F
ésH
állandó kifejezéseinek kiértékelésekor fellépő túlcsordulások fordítási időhibákat okoznak, mert a kifejezések kiértékelésechecked
környezetben történik. AG
állandó kifejezés értékelésekor is túlcsordulás történik, de mivel az értékelésunchecked
környezetben történik, a túlcsordulást nem jelzik.példa vége
A checked
és unchecked
operátorok csak a "(
" és ")
" jogkivonatokban szövegesen tárolt műveletek túlcsordulás-ellenőrzési környezetét befolyásolják. Az operátorok nem befolyásolják a függvény azon tagjait, amelyeket a tartalmazott kifejezés kiértékelése eredményeként hívnak meg.
példa: Az alábbi kódban
class Test { static int Multiply(int x, int y) => x * y; static int F() => checked(Multiply(1000000, 1000000)); }
checked
használata F-ben nem befolyásolja ax * y
kiértékelésétMultiply
-ben, ezért ax * y
alapértelmezett túlcsordulás-ellenőrzési környezetben értékelődik ki.példa vége
Az unchecked
operátor akkor kényelmes, ha hexadecimális jelölésben írja az aláírt integráltípusok állandóit.
Példa:
class Test { public const int AllBits = unchecked((int)0xFFFFFFFF); public const int HighBit = unchecked((int)0x80000000); }
A fenti hexadecimális állandók
uint
típusúak. Mivel az állandók aint
tartományon kívül vannak, aunchecked
operátor nélkül aint
áttípusításai fordítási időhibát eredményeznének.példa vége
Megjegyzés: A
checked
ésunchecked
operátorok és utasítások lehetővé teszik a programozók számára, hogy bizonyos numerikus számítások bizonyos aspektusait szabályozják. Egyes numerikus operátorok viselkedése azonban az operandusok adattípusától függ. A két tizedesjegy szorzása például mindig kivételt eredményez a túlcsordulás esetén, még egy explicit módon nem ellenőrzött szerkezeten belül is. Hasonlóképpen, két lebegőpontos szám szorzása soha nem eredményez kivételt túlcsordulás esetén, még egy explicit módon ellenőrzött szerkezeten belül sem. Emellett a többi operátort sem érinti az ellenőrzés módja, akár alapértelmezett, akár explicit. végjegyzet
12.8.21 Alapértelmezett értékkifejezések
A rendszer egy alapértelmezett értékkifejezést használ egy típus alapértelmezett értékének (§9.3) lekéréséhez.
default_value_expression
: explictly_typed_default
| default_literal
;
explictly_typed_default
: 'default' '(' type ')'
;
default_literal
: 'default'
;
A default_literal alapértelmezett értéket jelölnek (§9.3). Nem rendelkezik típussal, de bármilyen típussá konvertálható egy alapértelmezett konstans átalakítással (§10.2.16).
A alapértelmezett_értékkifejezés eredménye egy kifejezetten_tipizált_alapértelmezésexplicit típusának alapértelmezettje (§9.3), vagy az átalakítás céltípusa egy alapértelmezett_értékkifejezésesetén.
A default_value_expression állandó kifejezés (§12.23), ha a típus az alábbiak egyike:
- referenciatípus
- olyan típusparaméter, amely referenciatípusként ismert (§8.2);
- az alábbi értéktípusok egyike:
sbyte
,byte
,short
,ushort
,int
,uint
,long
,ulong
,char
,float
,double
,decimal
,bool,
; vagy - bármilyen enumerálási típus.
12.8.22 Veremfoglalás
A veremlefoglalási kifejezés egy memóriablokkot foglal le a végrehajtási veremből. A végrehajtási verem egy memóriaterület, ahol a helyi változók tárolódnak. A végrehajtási verem nem része a felügyelt halomnak. A helyi változó tárterülethez használt memória automatikusan helyreáll, amikor az aktuális függvény visszatér.
A veremfoglalási kifejezés biztonságos környezeti szabályai a 16.4.15.7. szakaszban leírásra kerülnek.
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
;
A unmanaged_type (§8.8) az újonnan lefoglalt helyen tárolt elemek típusát jelzi, a kifejezés pedig az elemek számát jelzi. Ezek együttesen határozzák meg a szükséges allokációs méretet. A kifejezés típusa implicit módon átalakítható int
típusra.
Mivel a veremfoglalás mérete nem lehet negatív, fordítási idejű hibát jelent, ha az elemek számát olyan constant_expression határozza meg, amely negatív értéket ad.
Futásidőben, ha a lefoglalni kívánt elemek száma negatív érték, akkor a viselkedés nincs meghatározva. Ha az érték nulla, akkor nem történik foglalás, és a visszaadott érték implementáció-függő. Ha nincs elegendő memória az elemek lefoglalásához, a rendszer System.StackOverflowException
dob.
Ha stackalloc_initializer van jelen:
- Ha kihagyják a unmanaged_type megadását, a rendszer a legjobb általános típusra vonatkozó szabályokat (§12.6.3.15) követi a stackalloc_element_initializer-ek halmazához.
- Ha a constant_expression nincs meghatározva, azt a stackalloc_element_initializer-ek számának tekintjük.
- Ha constant_expression van jelen, akkor annak az stackalloc_element_initializerszámával egyenlőnek kell lennie.
Minden stackalloc_element_initializer implicit módon átalakítandó unmanaged_type típusúvá (§10.2). A stackalloc_element_initializernövekvő sorrendben inicializálja a lefoglalt memória elemeit, kezdve a nulla indexnél lévő elemet. stackalloc_initializerhiányában az újonnan lefoglalt memória tartalma nincs meghatározva.
Ha egy stackalloc_expression közvetlenül egy local_variable_declaration inicializáló kifejezésként használják (§13.6.2), ahol a local_variable_type mutatótípus (23.3. §) vagy kiderített (var
), akkor a stackalloc_expression eredménye egy T*
típusú mutató (23.9. §). Ebben az esetben a stackalloc_expression nem biztonságos kódban kell megjelennie. Ellenkező esetben a stackalloc_expression eredménye egy a(z) Span<T>
típusú példány, ahol a(z) T
a unmanaged_type:
- A
Span<T>
(§C.3) egy referencia struct típus (§16.2.3), amely egy memóriablokkot mutat be, itt a stackalloc_expression által lefoglalt blokkot, mint indexelhető gyűjteményt típusosított (T
) elemekből. - Az eredmény
Length
tulajdonsága a lefoglalt elemek számát adja vissza. - Az eredmény indexelője (§15.9) visszaad egy változó_hivatkozás (§9.5) a lefoglalt blokk egy eleméhez, és tartományellenőrzés történik.
A veremfoglalás inicializálói nem engedélyezettek catch
vagy finally
blokkokban (§13.11).
Megjegyzés: Nincs mód a
stackalloc
használatával lefoglalt memória explicit felszabadítására. végjegyzet
A függvénytagok végrehajtása során létrehozott összes halmozott memóriablokk automatikusan el lesz vetve, amikor a függvénytag visszatér.
A stackalloc
operátor kivételével a C# nem biztosít előre definiált szerkezeteket a nem szemétből gyűjtött memória kezeléséhez. Ezeket a szolgáltatásokat általában az osztálykódtárak támogatásával vagy közvetlenül az alapul szolgáló operációs rendszerből importálják.
Példa:
// 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; } }
A
span8
esetében astackalloc
egySpan<int>
eredményez , amelyet implicit operátor konvertálReadOnlySpan<int>
. Hasonlóképpen azspan9
esetében az eredményül kapottSpan<double>
a rendszer a konvertálássalWidget<double>
felhasználó által definiált típussá alakítja, ahogyan az látható. példa vége
12.8.23 Az operátor neve
Egy nameof_expression egy programentitás nevének lekérésére szolgál, mint egy állandó karakterlánc.
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
;
Mivel nameof
nem kulcsszó, a nameof_expression mindig szintaktikailag nem egyértelmű az egyszerű név nameof
meghívásával. Kompatibilitási okokból, ha egy névkeresés (§12.8.4) sikeres nameof
, a kifejezés invocation_expression-ként lesz kezelve, függetlenül attól, hogy a hívás érvényes-e. Ellenkező esetben ez egy nameof_expression.
Egyszerű név- és taghozzáférési kereséseket hajtanak végre a named_entity esetében fordítási időben, a 12.8.4. § és 12.8.7. § leírt szabályokat követve. Ha azonban a 12.8.4 és §12.8.7 című cikkben leírt keresés hibát eredményez, mert egy példánytag statikus környezetben található, a nameof_expression nem okoz ilyen hibát.
Fordítási hiba, ha egy metóduscsoportot kijelölő named_entity-nek type_argument_list-ja van. Fordítási idejű hiba, ha egy named_entity_targetdynamic
típussal rendelkezik.
A nameof_expression a string
típusú állandó kifejezés, és futásidőben nincs hatása. Pontosabban, a named_entity nem kerül kiértékelésre, és nem kerül figyelembe vételre a határozott hozzárendelés elemzése céljából (§9.4.4.22). Értéke az utolsó azonosító a named_entity számára, mielőtt a választható végső type_argument_listmegjelenne, az alábbi módon átalakítva:
- A "
@
" előtagot a rendszer eltávolítja, ha használják. - Minden unicode_escape_sequence a megfelelő Unicode-karakterré alakul át.
- Minden formatting_characters el lesz távolítva.
Ugyanezek az átalakítások vannak alkalmazva a §6.4.3 szakaszban, amikor az azonosítók közötti egyenlőséget teszteljük.
Példa: Az alábbi ábra különböző
nameof
kifejezések eredményeit mutatja be, feltételezve, hogy egy általános típusúList<T>
aSystem.Collections.Generic
névtérben van deklarálva.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 { } }
A példa talán meglepő része, hogy a
nameof(System.Collections.Generic)
a teljes névtér helyett csak "Generic"-re, anameof(TestAlias)
pedig "Sztring" helyett "TestAlias"-ra van feloldva. példa vége
12.8.24 Névtelen metóduskifejezések
Az anonymous_method_expression a névtelen függvények meghatározásának két módja. Ezek a §12.19részletezve vannak.
12.9 Unary operátorok
12.9.1 Általános
A +
, -
, !
(logikai tagadás csak a 12.9.4. §-ban), , ~
, ++
, casting és --
operátorokat unáris operátoroknak nevezzük.
Megjegyzés: A null-elágaztatási operátor (12.8.9. §)
!
fordítási ideje és nem túlterhelhető jellege miatt nem szerepel a fenti listán. végjegyzet
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) és addressof_expression (§23.6.5) csak nem biztonságos kódban érhető el (§23).
Ha egy unary_expression operandusa fordítási időben meghatározott típusú, akkor dinamikusan kötött (dynamic
). Ebben az esetben a unary_expression fordítási idejének típusa dynamic
, és az alább ismertetett felbontás futásidőben történik az operandus futásidejű típusával.
12.9.2 Unáris plus operátor
A(z) +x
formájú művelet esetén az egyváltozós operátor túlterhelési feloldást (§12.4.4) alkalmazzák a kiválasztott operátor-megvalósítás érdekében. Az operandus a kiválasztott operátor paramétertípusára lesz konvertálva, az eredmény típusa pedig az operátor visszatérési típusa. Az előre definiált plusz operátorok a következők:
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);
Mindegyik operátor esetében az eredmény egyszerűen az operandus értéke.
Az emelt (§ 12.4.8) formái a fent meghatározott és nem emelt, előre definiált pozitív előjelű egyoperandusú operátoroknak szintén előre definiáltak.
12.9.3 Egynemű mínusz operátor
A(z) –x
formájú művelet esetén az egyváltozós operátor túlterhelési feloldást (§12.4.4) alkalmazzák a kiválasztott operátor-megvalósítás érdekében. Az operandus a kiválasztott operátor paramétertípusára lesz konvertálva, az eredmény típusa pedig az operátor visszatérési típusa. Az előre definiált egyoperandusú mínusz operátorok a következők:
Egész szám tagadása:
int operator –(int x); long operator –(long x);
Az eredmény kiszámítása a
X
nullából való kivonásával történik. Ha aX
értéke az operandus típus legkisebb ábrázolható értéke (int
esetén −2³¹,long
esetén −2⁶³), akkor aX
matematikai tagadása nem ábrázolható az operandus típusán belül. Ha ez egychecked
környezetben történik, a rendszerSystem.OverflowException
dob; ha egyunchecked
környezetben fordul elő, az eredmény az operandus értéke, és a túlcsordulás nem lesz jelentve.Ha a negation operátor operandusa
uint
típusú, akkor a rendszerlong
típussá alakítja, és az eredmény típusalong
. Kivételt képez az a szabály, amely lehetővé teszi, hogy aint
érték−2147483648
(−2³¹) decimális egész számként legyen megírva (§6.4.5.3).Ha a negation operátor operandusa
ulong
típusú, fordítási időhiba lép fel. Kivételt képez az a szabály, amely lehetővé teszi, hogy along
érték−9223372036854775808
(−2⁶³) decimális egész szám literálként legyen megírva (§6.4.5.3)Lebegőpontos negáció:
float operator –(float x); double operator –(double x);
Az eredmény az
X
értéke fordított előjellel. Hax
NaN
, akkor az eredmény isNaN
.Decimális tagadás:
decimal operator –(decimal x);
Az eredmény kiszámítása a
X
nullából való kivonásával történik. A decimális tagadás egyenértékű aSystem.Decimal
típusú unáris mínusz operátor használatával.
Az emelt (§12.4.8) formák a fent meghatározott, nem emelt előre definiált egyváltozós mínusz operátorokhoz is előre definiálva vannak.
12.9.4 Logikai tagadás operátor
A(z) !x
formájú művelet esetén az egyváltozós operátor túlterhelési feloldást (§12.4.4) alkalmazzák a kiválasztott operátor-megvalósítás érdekében. Az operandus a kiválasztott operátor paramétertípusára lesz konvertálva, az eredmény típusa pedig az operátor visszatérési típusa. Csak egy előre definiált logikai negation operátor létezik:
bool operator !(bool x);
Ez az operátor kiszámítja az operandus logikai negációját: Ha az operandus true
, az eredmény false
. Ha az operandus false
, az eredmény true
.
A fent meghatározott előre definiált logikai negációs operátor emelt formái (§12.4.8) szintén előre definiálva vannak.
Megjegyzés: A logikai negáció és a postfix null-megbocsátó operátorok (§12.8.9) bár ugyanazzal a lexikai tokennal (!
) vannak ábrázolva, egymástól különbözőek.
végjegyzet
12.9.5 Bitenkénti komplementer operátor
A(z) ~x
formájú művelet esetén az egyváltozós operátor túlterhelési feloldást (§12.4.4) alkalmazzák a kiválasztott operátor-megvalósítás érdekében. Az operandus a kiválasztott operátor paramétertípusára lesz konvertálva, az eredmény típusa pedig az operátor visszatérési típusa. Az előre definiált bitenkénti komplementer operátorok a következők:
int operator ~(int x);
uint operator ~(uint x);
long operator ~(long x);
ulong operator ~(ulong x);
Mindegyik operátor esetében a művelet eredménye a x
bitenkénti kiegészítése.
Minden enumerálási típus E
implicit módon a következő bitenkénti kiegészítő operátort biztosítja:
E operator ~(E x);
A ~x
kiértékelésének eredménye – ahol a X
egy olyan enumerációs típusú kifejezés, amelynek alapja a E
típus U
– pontosan megegyezik a (E)(~(U)x)
kiértékelésével, kivéve, hogy a E
konvertálása mindig úgy történik, mintha egy unchecked
kontextusban lenne (§12.8.20).
Az itt meghatározott, nem emelt formájú előre definiált bitkiegészítő operátorok emelt (§12.4.8) formái szintén előre definiálva vannak.
12.9.6 Előtagos növekmény- és csökkenés operátorok
pre_increment_expression
: '++' unary_expression
;
pre_decrement_expression
: '--' unary_expression
;
Az előjel növekményes vagy csökkentő műveletének operandusa olyan kifejezés legyen, amely változóként, tulajdonsághozzáférésként vagy indexelőhozzáférésként van besorolva. A művelet eredménye az operandussal azonos típusú érték.
Ha egy prefix növekményes vagy csökkentő művelet operandusa egy tulajdonsághoz vagy indexelőhöz való hozzáférés, a tulajdonságnak vagy indexelőnek rendelkeznie kell mind get, mind set tartozékkal. Ha nem ez a helyzet, kötési idő hiba lép fel.
Az egyoperandusú operátor túlterhelésének feloldását (§12.4.4) alkalmazzák egy adott operátor-megvalósítás kiválasztására. Az előre definiált ++
és --
operátorok a következő típusokhoz léteznek: sbyte
, byte
, short
, ushort
, int
, uint
, long
, ulong
, char
, float
, double
, decimal
és bármely számtípus. Az előre definiált ++
operátorok az operandushoz 1
hozzáadásával előállított értéket, az előre definiált --
operátorok pedig az operandusból 1
kivonásával előállított értéket adja vissza. Ha egy checked
környezetben az összeadás vagy kivonás eredménye kívül esik az eredménytípus tartományán, és az eredménytípus egy integráltípus vagy számtípus, a rendszer System.OverflowException
dob.
Implicit átalakítást kell alkalmazni a kiválasztott egyváltozós operátor visszatérési típusáról a unary_expressiontípusára, ellenkező esetben fordítási időben jelentkező hiba lép fel.
Az űrlap ++x
vagy --x
előtag-növekményének vagy dekrementálási műveletének futásideje a következő lépésekből áll:
- Ha
x
változóként van besorolva:-
x
kiértékelése révén jön létre a változó. - A
x
értékét a rendszer a kiválasztott operátor operandustípusára konvertálja, és az operátort ezzel az értékkel hívja meg argumentumként. - Az operátor által visszaadott érték
x
típusra lesz konvertálva. Az eredményül kapott érték ax
kiértékelése által megadott helyen lesz tárolva, és a művelet eredményévé válik.
-
- Ha
x
tulajdonságként vagy indexelői hozzáférésként van besorolva:- A példánykifejezést (ha
x
nemstatic
) és ax
-hoz társított argumentumlistát (hax
egy indexelői hozzáférés) kiértékeli a rendszer, és az eredmények a következő lekérés és beállítás kiegészítő meghívásaiban kerülnek felhasználásra. - A
x
lekérési metódusa meghívásra kerül. - A beolvasási tartozék által visszaadott érték a kiválasztott operátor operandustípusára lesz konvertálva, és az operátor argumentumként ezzel az értékkel lesz meghívva.
- Az operátor által visszaadott érték
x
típusra lesz konvertálva. Ax
beállító hozzáférője ezzel az értékkel hívódik meg értékargumentumként. - Ez az érték a művelet eredménye is lesz.
- A példánykifejezést (ha
A ++
és --
operátorok a postfix jelölést is támogatják (§12.8.16). A x++
vagy x--
eredménye a művelet előtti x
értéke, míg a ++x
vagy --x
eredménye a művelet utáni x
értéke. Mindkét esetben x
ugyanazzal az értékkel rendelkezik a művelet után.
Az operátor ++
vagy operátor --
implementációja postfix vagy prefix jelöléssel hívható meg. A két jelöléshez nem lehet külön operátor-implementációt létrehozni.
A fenti, emelt formájú (§12.4.8) előtag-növekedés és csökkentés operátorok szintén előre definiáltak.
12.9.7 Öntött kifejezések
A cast_expression egy kifejezés adott típussá alakítására szolgál.
cast_expression
: '(' type ')' unary_expression
;
Az cast_expression alak (T)E
formájában, ahol a T
egy típus, és E
egy unáris_kifejezés, explicit konverziót hajt végre a értékéről a E
típusra (T
). Ha nincs explicit átalakítás E
-ról T
-ra, kötési időhiba lép fel. Ellenkező esetben az eredmény az explicit átalakítás által előállított érték. Az eredmény mindig értékként van besorolva, még akkor is, ha E
egy változót jelöl.
A cast_expression nyelvtana bizonyos szintaktikai kétértelműségeket eredményez.
példa: A
(x)–y
kifejezés akár cast_expression értelmezhető, mint a(z)–y
típusként valóx
, vagy akár additive_expression, parenthesized_expression kombinálva (amely a(z)x – y
érték kiszámítására szolgál). példa vége
A cast_expression kétértelműségek megoldásához a következő szabály létezik: A zárójelek közé zárt egy vagy több jogkivonat (6.4..) sorozata csak akkor tekinthető cast_expression kezdetének, ha az alábbiak közül legalább az egyik igaz:
- A tokenek sorrendje egy típushoz helyes nyelvtan, de nem egy kifejezéshez.
- A tokenek sorrendje helyes nyelvtani szerkezet egy típus számára, és a záró zárójelet közvetlenül követő token a "
~
" token, a "!
" token, a "(
" token, egy azonosító (§6.4.3), egy literál (§6.4.5), vagy bármely kulcsszó (§6.4.4) lehet, kivéveas
ésis
.
A fenti "helyes nyelvtan" kifejezés csak azt jelenti, hogy a szóelemek sorozata meg kell, hogy feleljen az adott nyelvtani szabályrendszernek. Kifejezetten nem veszi figyelembe a rendszerelemek azonosítóinak tényleges jelentését.
Példa: Ha
x
ésy
azonosítók, akkorx.y
helyes nyelvhelyesség egy típushoz, még akkor is, hax.y
valójában nem jelöl típust. példa vége
Megjegyzés: A egyértelműsítési szabályból az következik, hogy ha
x
ésy
azonosítók, akkor(x)y
,(x)(y)
és(x)(-y)
cast_expression, de(x)-y
nem, még akkor sem, hax
azonosít egy típust. Ha azonbanx
egy előre definiált típust (példáulint
) azonosító kulcsszó, akkor mind a négy űrlap cast_expressions (mivel az ilyen kulcsszó önmagában nem lehet kifejezés). végjegyzet
12.9.8 Kifejezések várva
12.9.8.1 Általános
A await
operátor arra szolgál, hogy felfüggessze a környező aszinkron függvény kiértékelését, amíg az operandus által képviselt aszinkron művelet be nem fejeződik.
await_expression
: 'await' unary_expression
;
A await_expression csak az aszinkron függvény törzsében engedélyezett (15.14. §). A legközelebbi aszinkron függvényen belül egy await_expression nem fordulhat elő az alábbi helyeken:
- Beágyazott (nem aszinkron) névtelen függvényen belül
- Egy lock_statement blokkjának belsejében
- Névtelen függvény kifejezésfatípusra konvertálása (§10.7.3)
- Nem biztonságos környezetben
Megjegyzés: A await_expression nem fordulhat elő a legtöbb helyen egy query_expressionbelül, mert ezek szintaktikailag nem aszinkron lambda kifejezések használatára vannak átalakítva. végjegyzet
Az aszinkron függvényben a await
nem használható available_identifier-ként, bár a betű szerinti azonosító @await
használható. Ezért nem áll fenn szintaktikai kétértelműség az await_expressionés az azonosítókat tartalmazó különböző kifejezések között. Az aszinkron függvényen kívül await
normál azonosítóként szolgál.
Egy await_expression operandusát feladatnak nevezzük. Olyan aszinkron műveletet jelöl, amely a await_expression kiértékelésének időpontjában esetleg nem fejeződik be. A await
operátor célja, hogy felfüggesztse a beágyazó aszinkron függvény végrehajtását, amíg a várt tevékenység be nem fejeződik, majd megkapja annak eredményét.
12.9.8.2 Várakozó kifejezések
Egy await_expression feladatának várhatókell lennie. Egy kifejezés t
akkor várakoztatható, ha az alábbiak valamelyike fennáll:
-
t
fordítási idejű típusdynamic
-
t
rendelkezik egy elérhető,GetAwaiter
nevű példány- vagy bővítménymetódussal, amely nem rendelkezik paraméterekkel és típusparaméterekkel, és visszatérési típusaA
, amelyre az alábbi feltételek mindegyike érvényes.-
A
implementálja a felületetSystem.Runtime.CompilerServices.INotifyCompletion
(a továbbiakban rövidenINotifyCompletion
) -
A
-nak van egy elérhető, olvasható tulajdonságaIsCompleted
típusúbool
formájában. - A(z)
A
rendelkezik egy hozzáférhető példánymetódussalGetResult
, amely nem tartalmaz paramétereket és típusparamétereket.
-
A GetAwaiter
metódus célja egy várakozó megszerzése a feladathoz. A A
típus az "await kifejezés" esetén a várakozási típus néven ismert.
A IsCompleted
tulajdonság célja annak megállapítása, hogy a tevékenység már befejeződött-e. Ha igen, nincs szükség az értékelés felfüggesztésére.
A INotifyCompletion.OnCompleted
metódus célja, hogy "folytatást" regisztráljon a feladatra; vagyis egy (System.Action
típusú) delegált, amelyet a feladat befejezése után hív meg a rendszer.
A GetResult
módszer célja, hogy a feladat eredményét a befejezés után megkapja. Ez az eredmény lehet sikeres befejezés, esetleg eredményértékkel, vagy kivétel lehet, amelyet a GetResult
metódus dob.
12.9.8.3 Váró kifejezések besorolása
A await t
kifejezés ugyanúgy van besorolva, mint a (t).GetAwaiter().GetResult()
kifejezés. Így ha a GetResult
visszatérési típusa void
, a await_expression semmiként lesz besorolva. Ha a visszatérési típus nem avoid
T
, akkor a await_expressionT
típusú értékként van besorolva.
12.9.8.4 Várakozási kifejezések futásidejű kiértékelése
Futásidőben a await t
kifejezés kiértékelése a következőképpen történik:
- A váró
a
a(t).GetAwaiter()
kifejezés kiértékelésével szerezhető be. - A
bool
b
a(a).IsCompleted
kifejezés kiértékelésével kapott eredmény. - Ha
b
false
, akkor a kiértékelés attól függ, hogya
implementálja-e az interfésztSystem.Runtime.CompilerServices.ICriticalNotifyCompletion
(továbbiakbanICriticalNotifyCompletion
). Ez az ellenőrzés kötéskor történik; azaz futásidőben, haa
fordítási idő típusúdynamic
, és fordítva van. Jelöljükr
meg az újrakezdési meghatalmazottat (15.14.§):- Ha
a
nem implementálja aICriticalNotifyCompletion
, akkor a((a) as INotifyCompletion).OnCompleted(r)
kifejezés lesz kiértékelve. - Ha
a
implementálja aICriticalNotifyCompletion
, akkor a((a) as ICriticalNotifyCompletion).UnsafeOnCompleted(r)
kifejezés lesz kiértékelve. - A kiértékelés ezután fel van függesztve, és a rendszer visszaadja a vezérlést az aszinkron függvény aktuális hívójának.
- Ha
- Ha
b
true
, akkor közvetlenül utána, vagy az újrakezdési delegált későbbi meghívása esetén (hab
false
), a rendszer kiértékeli a(a).GetResult()
kifejezést. Ha értéket ad vissza, az az érték a await_expressioneredménye. Ellenkező esetben az eredmény semmi.
Az interfész metódusai INotifyCompletion.OnCompleted
és ICriticalNotifyCompletion.UnsafeOnCompleted
várakozó általi implementálása esetén a delegált r
legfeljebb egyszer hívható meg. Ellenkező esetben a beágyazó aszinkron függvény viselkedése nincs meghatározva.
12.10 Aritmetikai operátorok
12.10.1 Általános
A *
, /
, %
, +
és -
operátorokat számtani operátoroknak nevezzük.
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
;
Ha egy aritmetikai operátor operandusa dynamic
fordítási idő típusú, akkor a kifejezés dinamikusan kötött (§12.3.3). Ebben az esetben a kifejezés fordítási idejű típusa dynamic
, és az alábbiakban ismertetett feloldás futásidőben történik azoknak az operandusoknak a futásidejű típusával, amelyek fordítási idejű típusa dynamic
.
12.10.2 Szorzási operátor
Az űrlap x * y
művelete esetén a bináris operátorok túlterhelésének feloldása (§12.4.5) egy adott operátor implementációjának kiválasztására lesz alkalmazva. Az operandusok a kiválasztott operátor paramétertípusaiká lesznek konvertálva, az eredmény típusa pedig az operátor visszatérési típusa.
Az előre definiált szorzási operátorok az alábbiakban láthatók. Az operátorok az x
és a y
szorzatát számítják ki.
Egész szám szorzása:
int operator *(int x, int y); uint operator *(uint x, uint y); long operator *(long x, long y); ulong operator *(ulong x, ulong y);
Ha
checked
környezetben a termék kívül esik az eredménytípus tartományán, a rendszerSystem.OverflowException
dob. Egyunchecked
környezetben a túlcsordulások nem kerülnek jelentésre, és az eredménytípus tartományán kívül eső jelentős magasabb helyiértékű bitek elvetésre kerülnek.Lebegőpontos szorzás:
float operator *(float x, float y); double operator *(double x, double y);
A termék kiszámítása az IEC 60559 aritmetikai szabályok szerint történik. Az alábbi táblázat a nem véges értékek, nullák, végtelenségek és NaN-értékek összes lehetséges kombinációjának eredményeit sorolja fel. A táblázatban a
x
és ay
pozitív véges értékek.z
ax * y
eredménye, a legközelebbi ábrázolható értékre kerekítve. Ha az eredmény nagysága túl nagy a céltípushoz,z
végtelen. A kerekítés miatt az
lehet nulla, még akkor is, ha semx
, semy
nem nulla.+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
(Eltérő rendelkezés hiányában a 12.10.2. §–12.10.6. §- lebegőpontos tábláiban a "
+
" használata azt jelenti, hogy az érték pozitív; a "-
" használata azt jelenti, hogy az érték negatív; és a jel hiánya azt jelenti, hogy az érték pozitív vagy negatív lehet, vagy nincs jele (NaN).)Decimális szorzás:
decimal operator *(decimal x, decimal y);
Ha az eredményül kapott érték nagysága túl nagy ahhoz, hogy decimális formátumban legyen ábrázolva, egy
System.OverflowException
hiba keletkezik. A kerekítés miatt az eredmény nulla lehet, annak ellenére, hogy egyik operandus sem nulla. Az eredmény skálája a kerekítés előtt a két operandus skáláinak összege. A decimális szorzás egyenértékű aSystem.Decimal
típusú szorzási operátor használatával.
Az itt fent meghatározott nem emelt (§12.4.8) előre definiált szorzó operátorok emelt alakjai szintén előre definiálva vannak.
12.10.3 Osztó operátora
Az űrlap x / y
művelete esetén a bináris operátorok túlterhelésének feloldása (§12.4.5) egy adott operátor implementációjának kiválasztására lesz alkalmazva. Az operandusok a kiválasztott operátor paramétertípusaiká lesznek konvertálva, az eredmény típusa pedig az operátor visszatérési típusa.
Az előre definiált osztási operátorokat az alábbiakban találja. Az operátorok mindegyike kiszámítja a x
és y
hányadosát.
Egész szám osztás:
int operator /(int x, int y); uint operator /(uint x, uint y); long operator /(long x, long y); ulong operator /(ulong x, ulong y);
Ha a megfelelő operandus értéke nulla, egy
System.DivideByZeroException
dob.A számtani művelet nullára kerekíti az eredményt. Így az eredmény abszolút értéke a legnagyobb lehetséges egész szám, amely kisebb vagy egyenlő a két operandus hányadosának abszolút értékével. Az eredmény nulla vagy pozitív, ha a két operandus ugyanazzal a jellel rendelkezik, és nulla vagy negatív, ha a két operandus ellentétes előjelekkel rendelkezik.
Ha a bal operandus a legkisebb ábrázolható
int
vagylong
érték, és a jobb operandus–1
, akkor túlcsordulás történik. Egychecked
kontextusban ez egySystem.ArithmeticException
(vagy annak alosztálya) kivétel dobását okozza. Egyunchecked
kontextusban az implementáció által meghatározott, hogy egySystem.ArithmeticException
(vagy annak alosztálya) eldobódik-e, vagy a túlcsordulást nem jelentik be, illetve az így kapott érték a bal operandus értéke.Lebegőpontos osztás:
float operator /(float x, float y); double operator /(double x, double y);
A hányados kiszámítása az IEC 60559 aritmetikai szabályai szerint történik. Az alábbi táblázat a nem véges értékek, nullák, végtelenségek és NaN-értékek összes lehetséges kombinációjának eredményeit sorolja fel. A táblázatban a
x
és ay
pozitív véges értékek.z
ax / y
eredménye, a legközelebbi ábrázolható értékre kerekítve.+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
Decimális osztás:
decimal operator /(decimal x, decimal y);
Ha a megfelelő operandus értéke nulla, egy
System.DivideByZeroException
dob. Ha az eredményül kapott érték nagysága túl nagy ahhoz, hogy decimális formátumban legyen ábrázolva, egySystem.OverflowException
hiba keletkezik. A kerekítés miatt az eredmény nulla lehet, annak ellenére, hogy az első operandus nem nulla. Az eredmény skálája, a kerekítés előtt, az előnyben részesített skálához legközelebb eső skála, amely megegyezik a pontos eredménnyel. Az előnyben részesített skála ax
skálája mínusz ay
skálája.A decimális osztás egyenértékű a
System.Decimal
típusú osztás operátor használatával.
A fent meghatározott, emelt és nem emelt előre definiált osztási operátorok formái szintén előre meg vannak határozva (§12.4.8).
12.10.4 Fennmaradó operátor
Az űrlap x % y
művelete esetén a bináris operátorok túlterhelésének feloldása (§12.4.5) egy adott operátor implementációjának kiválasztására lesz alkalmazva. Az operandusok a kiválasztott operátor paramétertípusaiká lesznek konvertálva, az eredmény típusa pedig az operátor visszatérési típusa.
Az előre definiált fennmaradó operátorokat az alábbiakban találja. Az operátorok mind kiszámítják a x
és y
közötti osztás fennmaradó részét.
Egész szám hátralévő része:
int operator %(int x, int y); uint operator %(uint x, uint y); long operator %(long x, long y); ulong operator %(ulong x, ulong y);
A
x % y
eredménye ax – (x / y) * y
által megtermelt érték. Hay
nulla, egyetSystem.DivideByZeroException
-et dob.Ha a bal oldali operandus a legkisebb
int
vagylong
érték, és a jobb oldali operandus–1
, akkorSystem.OverflowException
csak akkor kerül dobásra, hax / y
kivételt dobna.Lebegőpontos maradék:
float operator %(float x, float y); double operator %(double x, double y);
Az alábbi táblázat a nem véges értékek, nullák, végtelenségek és NaN-értékek összes lehetséges kombinációjának eredményeit sorolja fel. A táblázatban a
x
és ay
pozitív véges értékek.z
ax % y
eredménye, ésx – n * y
ként van kiszámítva , ahol n a legnagyobb lehetséges egész szám , amely kisebb vagy egyenlőx / y
. Ez a maradékszámítási módszer analóg az egész szám operandusok esetében használt módszerrel, de eltér az IEC 60559 meghatározásától (amelybenn
azx / y
-hez legközelebbi egész szám).+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
Decimális maradék:
decimal operator %(decimal x, decimal y);
Ha a megfelelő operandus értéke nulla, egy
System.DivideByZeroException
dob. A függvényimplementáció szabja meg, mikor dobásra kerül egySystem.ArithmeticException
(vagy annak alosztálya). A megfelelő implementációnak semmilyen esetben sem szabad kivételt dobniax % y
esetén, hax / y
nem dob kivételt. Az eredmény skálája a kerekítés előtt a két operandus skálái közül a nagyobb, és az eredmény jele , ha nem nulla, akkor ugyanaz, mintx
.A decimális maradék egyenértékű a
System.Decimal
típusú maradék operátor használatával.Megjegyzés: Ezek a szabályok biztosítják, hogy minden típus esetében az eredmény soha ne vegye fel a bal operandus ellentétes előjelét. végjegyzet
A fent meghatározott, nem emelt előre definiált maradék operátorok emelt (§12.4.8) alakjai is szintén előre definiáltak.
12.10.5 Összeadás operátor
Az űrlap x + y
művelete esetén a bináris operátorok túlterhelésének feloldása (§12.4.5) egy adott operátor implementációjának kiválasztására lesz alkalmazva. Az operandusok a kiválasztott operátor paramétertípusaiká lesznek konvertálva, az eredmény típusa pedig az operátor visszatérési típusa.
Az előre definiált összeadási operátorokat az alábbiakban találja. Numerikus és enumerálási típusok esetén az előre definiált összeadási operátorok kiszámítják a két operandus összegét. Ha egy vagy mindkét operandus string
típusú, az előre definiált összeadási operátorok összefűzik az operandusok sztringképét.
Egész szám hozzáadása:
int operator +(int x, int y); uint operator +(uint x, uint y); long operator +(long x, long y); ulong operator +(ulong x, ulong y
Ha egy
checked
környezetben az összeg kívül esik az eredménytípus tartományán, a rendszerSystem.OverflowException
dob. Egyunchecked
környezetben a túlcsordulások nem kerülnek jelentésre, és az eredménytípus tartományán kívül eső jelentős magasabb helyiértékű bitek elvetésre kerülnek.Lebegőpontos hozzáadás
float operator +(float x, float y); double operator +(double x, double y);
Az összeg kiszámítása az IEC 60559 aritmetikai szabályai szerint történik. Az alábbi táblázat a nem véges értékek, nullák, végtelenségek és NaN-értékek összes lehetséges kombinációjának eredményeit sorolja fel. A táblázatban a
x
és ay
nem véges értékek,z
pedigx + y
eredménye. Hax
ésy
azonos nagyságrendű, de ellentétes előjelekkel rendelkeznek,z
pozitív nulla. Hax + y
túl nagy ahhoz, hogy a céltípusban szerepeljen,z
ax + y
jellel megegyező végtelen.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
Decimális összeadás:
decimal operator +(decimal x, decimal y);
Ha az eredményül kapott érték nagysága túl nagy ahhoz, hogy decimális formátumban legyen ábrázolva, egy
System.OverflowException
hiba keletkezik. Az eredmény skálája a kerekítés előtt a két operandus skálái közül a nagyobb.A decimális összeadás egyenértékű a
System.Decimal
típusú összeadási operátor használatával.Felsorolás hozzáadása. Minden enumerálási típus implicit módon a következő előre definiált operátorokat biztosítja, ahol
E
az enumerálás típusa,U
pedig aE
mögöttes típusa:E operator +(E x, U y); E operator +(U x, E y);
Futtatáskor ezeket az operátorokat a rendszer pontosan ugyanúgy értékeli ki, mint ahogy a
(E)((U)x + (U)y
esetében.Sztringösszefűzés:
string operator +(string x, string y); string operator +(string x, object y); string operator +(object x, string y);
A bináris
+
operátor túlterhelései sztringösszefűzést végeznek. Ha a sztringösszefűzés egyik operandusanull
, akkor egy üres karakterlánc helyettesíti azt. Ellenkező esetben minden olyan operandus, amely nemstring
, aToString
típustól örökölt virtuálisobject
metódus meghívásával kerül átalakításra a szöveges ábrázolására. Ha aToString
anull
-et adja vissza, akkor egy üres karakterlánc kerül a helyére.Példa:
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 } }
A megjegyzésekben látható kimenet egy US-English rendszer tipikus eredménye. A pontos kimenet a végrehajtási környezet területi beállításaitól függhet. Maga a sztringösszefűzési operátor minden esetben ugyanúgy viselkedik, de a végrehajtás során implicit módon meghívott
ToString
metódusokat érinthetik a területi beállítások.példa vége
A sztringösszefűzési operátor eredménye egy
string
, amely a bal operandus karaktereiből és a jobb operandus karaktereiből áll. A sztringösszefűzési operátor soha nem ad visszanull
értéket. Előfordulhat, hogySystem.OutOfMemoryException
dobódik, ha nincs elegendő memória a létrehozandó karakterlánc lefoglalásához.Delegálási kombináció. Minden delegálttípus implicit módon a következő előre definiált operátort biztosítja, ahol
D
a meghatalmazott típusa:D operator +(D x, D y);
Ha az első operandus
null
, a művelet eredménye a második operandus értéke (még akkor is, ha az isnull
). Ellenkező esetben, ha a második operandusnull
, akkor a művelet eredménye az első operandus értéke. Ellenkező esetben a művelet eredménye egy új delegált példány, amelynek meghívási listája az első operandus meghívási listájának elemeiből, majd a második operandus meghívási listájának elemeiből áll. Ez azt jelenti, hogy az eredményül kapott delegált meghívási listája a két operandus meghívási listájának összefűzése.Megjegyzés: A delegálási kombinációk példáiért lásd 12.10.6. §- és 20.6. Mivel
System.Delegate
nem delegált típus, az operátor + nincs definiálva. végjegyzet
A fent meghatározott emelt (§12.4.8) formák a nem emelt előre definiált összeadási operátorok esetén is előre definiálva vannak.
12.10.6 Kivonási operátor
Az űrlap x – y
művelete esetén a bináris operátorok túlterhelésének feloldása (§12.4.5) egy adott operátor implementációjának kiválasztására lesz alkalmazva. Az operandusok a kiválasztott operátor paramétertípusaiká lesznek konvertálva, az eredmény típusa pedig az operátor visszatérési típusa.
Az előre definiált kivonási operátorok az alábbiakban láthatók. Az operátorok mind kivonják y
-t x
-ből.
Egész szám kivonása:
int operator –(int x, int y); uint operator –(uint x, uint y); long operator –(long x, long y); ulong operator –(ulong x, ulong y
Egy
checked
kontextusban, ha a különbség kívül esik az eredménytípus tartományán, kivételkéntSystem.OverflowException
dobódik. Egyunchecked
környezetben a túlcsordulások nem kerülnek jelentésre, és az eredménytípus tartományán kívül eső jelentős magasabb helyiértékű bitek elvetésre kerülnek.Lebegőpontos kivonás:
float operator –(float x, float y); double operator –(double x, double y);
A különbség kiszámítása az IEC 60559 aritmetikai szabályok szerint történik. Az alábbi táblázat a nem véges értékek, nullák, végtelenségek és NaN-értékek összes lehetséges kombinációjának eredményeit sorolja fel. A táblázatban a
x
és ay
nem véges értékek,z
pedigx – y
eredménye. Hax
ésy
egyenlőek,z
pozitív nulla. Hax – y
túl nagy ahhoz, hogy a céltípusban szerepeljen,z
ax – y
jellel megegyező végtelen.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
(A fenti táblázatban a
-y
bejegyzések ay
jelölik, nem pedig azt, hogy az érték negatív.)Decimális kivonás:
decimal operator –(decimal x, decimal y);
Ha az eredményül kapott érték nagysága túl nagy ahhoz, hogy decimális formátumban legyen ábrázolva, egy
System.OverflowException
hiba keletkezik. Az eredmény skálája a kerekítés előtt a két operandus skálái közül a nagyobb.A decimális kivonás egyenértékű a
System.Decimal
típusú kivonási operátor használatával.Felsorolás kivonása. Minden enumerálási típus implicit módon a következő előre definiált operátort biztosítja, ahol
E
az enumerálás típusa,U
pedig aE
mögöttes típusa:U operator –(E x, E y);
Ez az operátor pontosan úgy van kiértékelve, mint
(U)((U)x – (U)y)
. Más szóval az operátor kiszámítja ax
és ay
sorszámértékeinek különbségét, és az eredmény típusa az enumerálás alapjául szolgáló típus.E operator –(E x, U y);
Ez az operátor pontosan úgy van kiértékelve, mint
(E)((U)x – y)
. Más szóval az operátor kivon egy értéket az enumerálás alapjául szolgáló típusból, ami az enumerálás értékét eredményezi.Delegált eltávolítása. Minden delegálttípus implicit módon a következő előre definiált operátort biztosítja, ahol
D
a meghatalmazott típusa:D operator –(D x, D y);
A szemantikák a következők:
- Ha az első operandus
null
, a művelet eredményenull
. - Ellenkező esetben, ha a második operandus
null
, akkor a művelet eredménye az első operandus értéke. - Ellenkező esetben mindkét operandus nem üres meghívási listákat jelöl (§20.2).
- Ha a listák egyenlőek, a delegált egyenlőségi operátor (§12.12.9) meghatározása szerint a művelet eredménye
null
. - Ellenkező esetben a művelet eredménye egy új meghívási lista, amely az első operandus listájából áll, és a második operandus bejegyzései törlődnek belőle, feltéve, hogy a második operandus listája az elsők allistája. (Az allista egyenlőségének meghatározásához a megfelelő bejegyzések összehasonlítása a meghatalmazott egyenlőségi operátorhoz képest.) Ha a második operandus listája megegyezik az első operandus listában szereplő összefüggő bejegyzések több allistájával, az egybefüggő bejegyzések utolsó egyező allistája el lesz távolítva.
- Ellenkező esetben a művelet eredménye a bal operandus értéke.
- Ha a listák egyenlőek, a delegált egyenlőségi operátor (§12.12.9) meghatározása szerint a művelet eredménye
Az operanduslisták egyike sem változik a folyamat során (ha egyáltalán vannak ilyenek).
Példa:
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 } }
példa vége
- Ha az első operandus
Az előbbi bekezdésben meghatározott nem emelt előre definiált kivonási operátorok emelt formái (§12.4.8) szintén előre definiáltak.
12.11 Műszak operátorok
A <<
és >>
operátorok bitváltó műveletek végrehajtására szolgálnak.
shift_expression
: additive_expression
| shift_expression '<<' additive_expression
| shift_expression right_shift additive_expression
;
Ha egy shift_expression operandusának kompilációs idejű típusa dynamic
, akkor a kifejezés dinamikusan kötött (§12.3.3). Ebben az esetben a kifejezés fordítási idejű típusa dynamic
, és az alábbiakban ismertetett feloldás futásidőben történik azoknak az operandusoknak a futásidejű típusával, amelyek fordítási idejű típusa dynamic
.
Az űrlap x << count
vagy x >> count
művelete esetén a bináris operátor túlterhelésének feloldása (§12.4.5) egy adott operátor implementációjának kiválasztására lesz alkalmazva. Az operandusok a kiválasztott operátor paramétertípusaiká lesznek konvertálva, az eredmény típusa pedig az operátor visszatérési típusa.
Túlterhelt műszakkezelő deklarálásakor az első operandus típusa mindig az operátori deklarációt tartalmazó osztály vagy szerkezet, a második operandus típusa pedig mindig int
.
Az előre definiált műszak operátorai az alábbiakban láthatók.
Balra váltás:
int operator <<(int x, int count); uint operator <<(uint x, int count); long operator <<(long x, int count); ulong operator <<(ulong x, int count);
A
<<
operátorx
-et az alábbiakban meghatározott módon kiszámított bitek számával balra tolja.A
x
eredménytípusának tartományán kívül eső nagy értékű biteket a rendszer elveti, a fennmaradó biteket balra, az alacsony sorrendű üres biteket pedig nullára állítja.Jobbra váltás:
int operator >>(int x, int count); uint operator >>(uint x, int count); long operator >>(long x, int count); ulong operator >>(ulong x, int count);
A
>>
operátor jobbra eltoljax
egy olyan bitek száma szerint, amit az alábbiakban leírnak.Amikor a
x
típusint
vagylong
, ax
alacsony rendű bitjei elvetésre kerülnek, a fennmaradó bitek jobbra tolódnak, és a magas rendű üres bitpozíciók nullára vannak állítva, hax
nem negatív, illetve egyre vannak állítva, hax
negatív.Ha a
x
uint
vagyulong
típusú, ax
alacsonyrendű bitjeit elvetik, majd a megmaradó biteket jobbra tolják, és a magas sorba helyezett üres bithézagokat nulla értékre állítják.
Az előre definiált operátorok esetében az eltolandó bitek száma a következőképpen lesz kiszámítva:
- Ha a
x
típusaint
vagyuint
, a műszakok számát az alacsony sorrendű öt bitcount
adja meg . Más szóval, a műszakok számát acount & 0x1F
alapján számítják ki. - Ha a
x
típusalong
vagyulong
, a műszakok számát acount
kisrendű hat bitje adja meg. Más szóval, a műszakok számát acount & 0x3F
alapján számítják ki.
Ha az eredményül kapott műszakszám nulla, a műszak operátorai egyszerűen visszaadják a x
értékét.
A műszakműveletek soha nem okoznak túlcsordulást, és ugyanazokat az eredményeket eredményezik a bejelölt és a nem ellenőrzött környezetekben.
Ha a >>
operátor bal operandusa aláírt egész típusú, az operátor egy számtani eltolást hajt végre, ahol az operandus legjelentősebb bitjének (a jelbitnek) az értéke kerül továbbvitelre a magasabb rendű üres bitpozíciókba. Ha a >>
operátor bal operandusa egy aláíratlan integráltípusú, az operátor egy logikai eltolódást hajt végre jobbra, ahol a nagy sorrendű üres bitpozíciók mindig nullára vannak állítva. Az operandus típusából kikövetkeztetett művelet ellenkező műveletének végrehajtásához explicit leadások használhatók.
Példa: Ha
x
egyint
típusú változó, a műveletunchecked ((int)((uint)x >> y))
logikai jobbra eltolást hajt végrex
. példa vége
A fentebb meghatározott emelt (§12.4.8) típusú, nem emelt, előre definiált váltó operátorok is előre definiálva vannak.
12.12 Relációs és típustesztelő operátorok
12.12.1 Általános
A ==
, !=
, <
, >
, <=
, >=
, is
és as
operátorokat relációs és típustesztelő operátoroknak nevezzük.
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
;
Megjegyzés: A
is
operátor jobb oldali operandusát először típusú-ként kell tesztelni, majd kifejezésként, amely több tokenre is kiterjedhet. Abban az esetben, ha az operandus egy kifejezés, a mintakifejezésnek legalább olyan prioritással kell rendelkeznie, mint a eltolás_kifejezés. végjegyzet
A is
operátort a 12.12.12. §, a as
operátort pedig a 12.12.13. §írja le.
A ==
, !=
, <
, >
, <=
és >=
operátorok összehasonlító operátorok.
Ha egy default_literal (§12.8.21) egy <
, >
, <=
vagy >=
operátor operandusaként használatos, fordítási időhiba lép fel.
Ha egy default_literal mindkét operandusként van használva egy ==
vagy !=
operátor esetén, akkor fordítási idejű hiba lép fel. Ha egy default_literal van használva a is
vagy as
operátor bal oldali operandusaként, fordítási időben hiba lép fel.
Ha egy összehasonlító operátor operandusa dynamic
fordítási idő típusú, akkor a kifejezés dinamikusan kötött (§12.3.3). Ebben az esetben a kifejezés fordításidőbeli típusa dynamic
, és az alább ismertetett feloldás futásidőben történik azon operandusok futásidejű típusa alapján, amelyek fordításidőbeli típusúak dynamic
.
Az x «op» y
alakú művelet esetén, ahol az "op" egy összehasonlító operátor, a túlterhelés feloldása (12.4.5. §) egy adott operátor implementációjának kiválasztására lesz alkalmazva. Az operandusok a kiválasztott operátor paramétertípusaiká lesznek konvertálva, az eredmény típusa pedig az operátor visszatérési típusa. Ha egy equality_expression mindkét operandusa a null
literál, akkor nincs túlterhelési feloldás, és a kifejezés a true
vagy a false
állandó értékre értékelődik attól függően, hogy az operátor ==
vagy !=
.
Az előre definiált összehasonlító operátorokat az alábbi alklámokban ismertetjük. Minden előre definiált összehasonlító operátor a bool típusú eredményt adja vissza az alábbi táblázatban leírtak szerint.
művelet | Eredmény |
---|---|
x == y |
true ha x egyenlő y , false ellenkező esetben |
x != y |
true ha x nem egyenlő y , false ellenkező esetben |
x < y |
true ha x kisebb, mint y , false ellenkező esetben |
x > y |
true ha x nagyobb, mint y , false ellenkező esetben |
x <= y |
true ha x kisebb vagy egyenlő y , false ellenkező esetben |
x >= y |
true ha x nagyobb vagy egyenlő y , false ellenkező esetben |
12.12.2 Egész szám összehasonlító operátorai
Az előre definiált egész számok összehasonlító operátorai a következők:
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);
Mindegyik operátor összehasonlítja a két egész szám operandus numerikus értékeit, és egy bool
értéket ad vissza, amely jelzi, hogy az adott reláció true
vagy false
.
Az előre definiált (nem emelt) egész számok összehasonlító operátorainak fent meghatározott emelt formái szintén előre definiáltak.
12.12.3 Lebegőpontos összehasonlító operátorok
Az előre definiált lebegőpontos összehasonlító operátorok a következők:
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);
Az operátorok az operandusokat az IEC 60559 szabvány szabályai szerint hasonlítják össze:
Ha bármelyik operandus NaN, az eredmény false
az összes operátor esetében, kivéve !=
, amelyeknél az eredmény true
. Minden két operandus esetében x != y
mindig ugyanazt az eredményt adja, mint !(x == y)
. Ha azonban az egyik vagy mindkét operandus NaN, akkor a <
, >
, <=
, és >=
operátorok nem a logikai tagadásával azonos eredményt eredményeznek az ellenkező operátor esetén.
Példa: Ha valamelyik
x
ésy
NaN, akkorx < y
false
, de!(x >= y)
true
. példa vége
Ha egyik operandus sem NaN, az operátorok összehasonlítják a két lebegőpontos operandus értékét a rendezés szempontjából
–∞ < –max < ... < –min < –0.0 == +0.0 < +min < ... < +max < +∞
ahol min
és max
a legkisebb és legnagyobb pozitív véges érték, amely az adott lebegőpontos formátumban ábrázolható. A rendezés jelentős hatásai a következők:
- A negatív és a pozitív nullák egyenlőnek minősülnek.
- A negatív végtelen kevesebb, mint az összes többi érték, de egyenlő egy másik negatív végtelenséggel.
- A pozitív végtelen nagyobb, mint az összes többi érték, de egyenlő egy másik pozitív végtelenséggel.
Az fent meghatározott emelt (§12.4.8) és a nem emelt, előre definiált lebegőpontos összehasonlító operátorok formái szintén előre definiálva vannak.
12.12.4 Decimális összehasonlító operátorok
Az előre definiált decimális összehasonlító operátorok a következők:
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);
Ezek az operátorok összehasonlítják a két tizedes operandus numerikus értékeit, és egy bool
értéket ad vissza, amely jelzi, hogy az adott reláció true
vagy false
. Minden decimális összehasonlítás egyenértékű a megfelelő System.Decimal
típusú relációs vagy egyenlőségi operátor használatával.
Az (§12.4.8) formában szereplő, fent meghatározott emelt előre definiált tizedes összehasonlító operátorok szintén előre definiálva vannak.
12.12.5 Logikai egyenlőségi operátorok
Az előre definiált logikai egyenlőség operátorai a következők:
bool operator ==(bool x, bool y);
bool operator !=(bool x, bool y);
A ==
eredménye true
, ha x
és y
is true
, vagy x
és y
is false
. Ellenkező esetben az eredmény false
.
A !=
eredménye false
, ha x
és y
is true
, vagy x
és y
is false
. Ellenkező esetben az eredmény true
. Ha az operandusok bool
típusúak, a !=
operátor ugyanazt az eredményt adja, mint a ^
operátor.
A fent meghatározott, nem emelt (§12.4.8) logikai egyenlőségi operátorok emelt formái szintén előre definiáltak.
12.12.6 Enumerálási összehasonlító operátorok
Minden enumerálási típus implicit módon a következő előre definiált összehasonlító operátorokat biztosítja
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);
Az x «op» y
kiértékelésének eredménye, ahol az x és y a E
felsorolt típusának kifejezései, amelynek mögöttes típusa a U
, és az «op» az összehasonlító operátorok egyike, pontosan megegyezik a ((U)x) «op» ((U)y)
kiértékelésének eredményével. Más szóval az enumerálási típusú összehasonlító operátorok egyszerűen összehasonlítják a két operandus mögöttes integrálértékeit.
Az emelt (§12.4.8) formái a fent meghatározott, nem emelt előre definiált enumerálású összehasonlító operátoroknak szintén előre definiálva vannak.
12.12.7 Referenciatípus egyenlőségi operátorai
Minden osztálytípus C
implicit módon a következő előre definiált referenciatípus-egyenlőségi operátorokat biztosítja:
bool operator ==(C x, C y);
bool operator !=(C x, C y);
kivéve, ha az C
-hoz nem léteznek előre meghatározott egyenlőségi operátorok (például, amikor C
is string
vagy System.Delegate
).
Az operátorok az egyenlőségre vagy nem egyenlőségre vonatkozó két hivatkozás összehasonlításának eredményét adják vissza.
operator ==
akkor és csak akkor ad vissza true
, ha x
és y
ugyanarra a példányra hivatkoznak, vagy mindkettő null
, míg operator !=
akkor és csak akkor ad vissza true
, ha az azonos operandusokkal rendelkező operator ==
false
adna.
A normál alkalmazhatósági szabályokon (12.6.4.2.) kívül az előre definiált referenciatípus egyenlőségi operátorai a következők egyikét igénylik az alkalmazhatóság érdekében:
- Mindkét operandus egy olyan típusú érték, amely ismeretes, hogy 'reference_type' vagy a literális
null
. Ezenkívül létezik egy identitás- vagy explicit referenciaátalakítás (§10.3.5), amely az egyik operandusból a másik operandus típusába történik. - Az egyik operandus a konstans
null
, a másik operandus pedig egyT
típusú érték, ahol aT
egy olyan type_parameter, amelyről ismert, hogy nem értéktípus, és nincs megkötése az értéktípusra.- Ha futásidőben
T
nem null értékű értéktípus, a==
eredményefalse
, a!=
eredménye pedigtrue
. - Ha futásidőben a
T
null értékű értéktípus, az eredményt az operandusHasValue
tulajdonságából számítja ki, amint az a (§12.12.10) szakaszban le van írva. - Ha futásidőben
T
egy referenciatípus, az eredménytrue
, ha az operandusnull
, ellenkező esetbenfalse
.
- Ha futásidőben
Ha az egyik feltétel nem igaz, kötési idejű hiba lép fel.
Megjegyzés: A szabályok figyelemre méltó következményei a következők:
- Kötési idejű hiba az előre definiált referenciatípus egyenlőségi operátorainak használata két olyan hivatkozás összehasonlítására, amelyekről ismert, hogy kötési időpontban eltérőek. Ha például az operandusok megkötési idejéhez tartozó típusok két külön osztálytípus, és egyik sem származik a másikból, akkor lehetetlen lenne, hogy a két operandus ugyanarra az objektumra hivatkozzon. Így a művelet kötési idejű hibának minősül.
- Az előre definiált referenciatípus-egyenlőségi operátorok nem teszik lehetővé az értéktípus operandusainak összehasonlítását (kivéve, ha a típusparamétereket a speciálisan kezelt
null
hasonlítják össze).- Az előre definiált referenciatípusú egyenlőségi operátorok operandusai soha nem lesznek bekeretezettek. Az ilyen csomagolási műveletek végrehajtása értelmetlen lenne, mivel az újonnan kiosztott csomagolt példányokra való hivatkozások szükségszerűen eltérnének minden más hivatkozástól.
Az űrlap
x == y
vagyx != y
művelete esetén , ha létezik a felhasználó által definiáltoperator ==
vagyoperator !=
, az operátor túlterhelés-feloldási szabályai (12.4.5.) az előre definiált hivatkozástípus egyenlőségi operátora helyett ezt az operátort választják ki. Az előre definiált referenciatípus-egyenlőségi operátorát mindig kiválaszthatja úgy, hogy az egyik vagy mindkét operandust aobject
típusra explicit módon kasztolja.végjegyzet
Példa: Az alábbi példa ellenőrzi, hogy a nem betanított típusú paramétertípus argumentuma
null
-e.class C<T> { void F(T x) { if (x == null) { throw new ArgumentNullException(); } ... } }
A
x == null
szerkezet akkor is engedélyezett, haT
nem null értékű értéktípust jelölhet, és az eredmény egyszerűenfalse
, haT
nem null értékű.példa vége
Az űrlap x == y
vagy x != y
művelete esetén , ha létezik bármilyen vonatkozó operator ==
vagy operator !=
, az operátor túlterhelésének feloldása (§12.4.5) szabályok az előre meghatározott referenciatípus egyenlőségi operátora helyett ezt az operátort választják ki.
Megjegyzés: Az előre definiált referenciatípus egyenlőségi operátorát mindig kiválaszthatja úgy, hogy a két operandust explicite típuskonverzióval a
object
típusra alakítja. végjegyzet
Példa: A példa
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); } }
a kimenetet hozza létre
True False False False
A
s
ést
változók két különböző sztringpéldányra vonatkoznak, amelyek ugyanazokat a karaktereket tartalmazzák. Az első összehasonlító kimenetTrue
, mert az előre definiált sztringegyenlőségi operátor (§12.12.8) akkor van kiválasztva, ha mindkét operandusstring
típusú. A fennmaradó összehasonlítások mindFalse
-t eredményeznek, mert aoperator ==
típusústring
túlterhelése nem alkalmazható, ha bármelyik operandus kötési idő típusaobject
.Vegye figyelembe, hogy a fenti technika nem értelmezhető értéktípusok esetében. A példa
class Test { static void Main() { int i = 123; int j = 123; Console.WriteLine((object)i == (object)j); } }
Kimenet
False
, mert a cast műveletek a boxoltint
értékek két különálló példányára hivatkoznak.példa vége
12.12.8 Karakterlánc-egyenlőségi operátorok
Az előre definiált sztringegyenlőségi operátorok a következők:
bool operator ==(string x, string y);
bool operator !=(string x, string y);
Két string
érték akkor tekinthető egyenlőnek, ha az alábbiak egyike igaz:
- Mindkét érték
null
. - Mindkét érték olyan hivatkozás, amely nem
null
sztringpéldányokra mutat, és ezek a példányok azonos hosszúságúak, illetve minden karakterhelyen azonos karakterek találhatók.
A sztringegyenlőségi operátorok sztringhivatkozások helyett sztringértékeket hasonlítanak össze. Ha két különálló sztringpéldány pontosan ugyanazt a karaktersorozatot tartalmazza, a sztringek értéke egyenlő, de a hivatkozások eltérőek.
Megjegyzés: Amint a 12.12.7szakaszban leírtuk, a referencia típusú egyenlőség operátorok használhatóak string hivatkozások összehasonlítására a string értékek helyett. végjegyzet
12.12.9 Egyenlőségi operátorok delegálása
Az előre definiált meghatalmazotti egyenlőségi operátorok a következők:
bool operator ==(System.Delegate x, System.Delegate y);
bool operator !=(System.Delegate x, System.Delegate y);
Két delegált példány egyenlőnek minősül az alábbiak szerint:
- Ha valamelyik delegált példány
null
, akkor és csak akkor egyenlőek, ha mindkettőnull
. - Ha a delegáltak különböző futásidejű típusokkal rendelkeznek, soha nem egyenlők.
- Ha mindkét delegált példánynak van egy meghívási listája (§20.2), akkor ezek a példányok egyenlők, ha és csak akkor, ha a meghívási listák azonos hosszúságúak, és az egyik híváslistájában szereplő összes bejegyzés egyenlő (az alább meghatározottak szerint) a megfelelő bejegyzéssel, sorrendben a másik híváslistájában.
A meghívási lista bejegyzéseinek egyenlőségére a következő szabályok vonatkoznak:
- Ha két meghívási lista bejegyzése ugyanarra a statikus metódusra hivatkozik, akkor a bejegyzések egyenlők.
- Ha két meghívási listabejegyzés ugyanarra a nem statikus metódusra hivatkozik ugyanazon a célobjektumon (a referenciaegyenlítési operátorok által meghatározottak szerint), akkor a bejegyzések egyenlőek.
- A szemantikailag azonos névtelen függvények (12.19) kiértékeléséből származó meghívási lista bejegyzések azonos (esetleg üres) rögzített külső változópéldányokkal egyenlővé tehetők, de ez nem kötelező.
Ha az operátorok túlterhelésének feloldása a delegált egyenlőségi operátorral történik, és mindkét operandus kötési időtípusa delegált típus, a 20. § szerint, nem pedig System.Delegate
, és nincs azonosító átalakítás a kötés típusú operandusok között, kötési időhibát eredményez.
Megjegyzés: Ez a szabály megakadályozza az olyan összehasonlításokat, amelyek soha nem tekinthetnek nem-
null
értékeket egyenlőnek, mivel azok különböző típusú delegátumok példányaira hivatkoznak. végjegyzet
12.12.10 Egyenlőségi operátorok a null értékű értéktípusok és a null literál között
A ==
és !=
operátorok lehetővé teszik, hogy az egyik operandus null értékű, a másik pedig a null
literál legyen, még akkor is, ha a művelethez nincs előre definiált vagy felhasználó által definiált operátor (emeletlen vagy emelt formában).
Az űrlapok egyikének műveletéhez
x == null null == x x != null null != x
ahol a x
nullálható értéktípus kifejezése, ha az operátor túlterhelés feloldása (12.4.5.) nem talál alkalmazható operátort, akkor az eredményt a HasValue
x
tulajdonságából számítják ki. Pontosabban az első két űrlapot !x.HasValue
-ra, és az utolsó két űrlapot pedig x.HasValue
-re fordítják.
12.12.11 A n-es egyenlőségi operátorai
Az egyenlőségi tuple operátorokat az operandus tuple elemekre párba állítva alkalmazzuk, lexikális sorrendben.
Ha egy x
vagy y
operátor minden operandusa ==
és !=
n-es vagy n-es típusú értékként (§8.3.11) van besorolva, akkor az operátor n-es egyenlőség-operátor.
Ha egy operandus e
rekordként van besorolva, a e1...en
elemek a rekordkifejezés elemkifejezéseinek kiértékelésének eredményei. Ellenkező esetben, ha e
egy rekord típusú érték, akkor az elemek t.Item1...t.Itemn
-ek lesznek, ahol t
a e
kiértékelésének eredménye.
A tupel egyenlőség operátor operandusainak x
és y
azonos aritásúnak kell lenniük, különben fordítási időhiba lép fel. Minden egyes xi
és yi
elempárra ugyanazt az egyenlőségi operátort kell alkalmazni, és az eredmény típusa bool
vagy dynamic
lesz, esetleg egy olyan típus, amely implicit konverzióval rendelkezik a bool
típushoz, vagy egy olyan típus, amely meghatározza a true
és false
operátorokat.
A tömb egyenlőség operátor x == y
a következőképpen értékelhető ki:
- A bal oldali operandus
x
kiértékelése történik. - A jobb oldali operandus
y
-t kiértékelik. - Minden egyes elempár esetében
xi
ésyi
lexikális sorrendben:- A
xi == yi
operátor kiértékelése és abool
típusú eredmény a következő módon történik:- Ha az összehasonlítás egy
bool
eredményezett, akkor ez az eredmény. - Ellenkező esetben, ha az összehasonlítás egy
dynamic
eredményezett, akkor a rendszer dinamikusan meghívja azfalse
operátort, és az eredményül kapottbool
értéket a logikai negation operátorral (!
) tagadja le. - Ellenkező esetben, ha az összehasonlítás típusa implicit módon
bool
átalakítással rendelkezik, a rendszer ezt az átalakítást alkalmazza. - Ellenkező esetben, ha az összehasonlítás típusa
false
operátorral rendelkezik, a rendszer meghívja az operátort, és az eredményként kapottbool
értéket a logikai negation operátorral (!
) tagadja le.
- Ha az összehasonlítás egy
- Ha
bool
eredményként egyenlőfalse
-vel, akkor nincs további értékelés, és a tuple-egyenlőség-operátor eredményefalse
.
- A
- Ha az összes elem-összehasonlítás eredménye
true
lett, akkor az n-többes egyenlőségi operátor eredményetrue
.
A tömb egyenlőség operátor x != y
a következőképpen értékelhető ki:
- A bal oldali operandus
x
kiértékelése történik. - A jobb oldali operandus
y
-t kiértékelik. - Minden egyes elempár esetében
xi
ésyi
lexikális sorrendben:- A
xi != yi
operátor kiértékelése és abool
típusú eredmény a következő módon történik:- Ha az összehasonlítás egy
bool
eredményezett, akkor ez az eredmény. - Ellenkező esetben, ha az összehasonlítás egy
dynamic
eredményezett, akkor atrue
operátor dinamikusan meghívódik rajta, és az eredményül kapottbool
érték az eredmény. - Ellenkező esetben, ha az összehasonlítás típusa implicit módon
bool
átalakítással rendelkezik, a rendszer ezt az átalakítást alkalmazza. - Ellenkező esetben, ha az összehasonlítás típusa
true
operátorral rendelkezik, a függvény meghívja az operátort, és az eredményként kapottbool
érték az eredmény.
- Ha az összehasonlítás egy
- Ha
bool
eredményként egyenlőtrue
-vel, akkor nincs további értékelés, és a tuple-egyenlőség-operátor eredményetrue
.
- A
- Ha az összes elem-összehasonlítás eredménye
false
lett, akkor az n-többes egyenlőségi operátor eredményefalse
.
12.12.12 Az is operátor
A is
operátornak két formája van. Az egyik az típusú operátor, amely a jobb oldalon található típussal rendelkezik. A másik az is-minta operátor, amely jobboldali mintával rendelkezik.
12.12.12.1 Az is-type operátor
Az is-type operátor annak ellenőrzésére szolgál, hogy egy objektum futásidejű típusa kompatibilis-e egy adott típussal. Az ellenőrzés futásidőben történik. A E is T
művelet eredménye, ahol a E
kifejezés, és T
nem dynamic
típus, logikai értéket ad, amely jelzi, hogy E
nem null értékű-e, és sikeresen átalakítható-e T
típusra hivatkozási átalakítással, dobozos átalakítással, kicsomagolási átalakítással, burkoló átalakítással vagy kicsomagolási átalakítással.
A művelet kiértékelése a következőképpen történik:
- Ha
E
egy névtelen függvény vagy metóduscsoport, fordítási időhiba lép fel. - Ha
E
anull
literál, vagy ha aE
értékenull
, akkor az eredményfalse
. - Egyébként:
- Legyen
R
E
futtatási típusa. - Legyen
D
az alábbi módonR
-ből származtatható: - Ha
R
null értékű,D
aR
mögöttes típusa. - Ellenkező esetben
D
vanR
. - Az eredmény a
D
ésT
értékétől függ a következőképpen: - Ha
T
hivatkozástípus, az eredménytrue
, ha:- Azonosság átalakítás létezik
D
ésT
között. -
D
egy referenciatípus, és létezik egy implicit referenciaátalakításD
-rőlT
-re, vagy - Vagy:
D
egy értéktípus, és létezikD
boxtípusú konvertálásaT
-ra.
Vagy:D
egy értéktípus,T
pedig aD
által implementált felülettípus.
- Azonosság átalakítás létezik
- Ha
T
null értékű, az eredménytrue
, haD
aT
mögöttes típusa. - Ha
T
nem null értékű értéktípus, az eredménytrue
, haD
ésT
azonos típusúak. - Ellenkező esetben az eredmény
false
.
A felhasználó által megadott konverziókat a is
operátor nem veszi figyelembe.
Megjegyzés: Mivel a
is
operátor futásidőben van kiértékelve, a rendszer minden típusargumentumot lecserélt, és nincsenek nyitott típusok (§8.4.3). végjegyzet
Megjegyzés: A
is
operátor a fordítási idő típusa és az átalakítások szempontjából az alábbiak szerint értelmezhető, ahol aC
aE
fordítási idő típusa:
- Ha a
e
fordítási idő típusa megegyezik aT
, vagy ha implicit referenciaátalakítás (§10.2.8), boxing conversion (§10.2.9§10.6) burkoló átalakítás vagy explicit feloldó átalakítás (§10.6) létezik aE
fordítási idő típusától aT
:
- Ha
C
nem null értékű, a művelet eredményetrue
.- Ellenkező esetben a művelet eredménye egyenértékű a
E != null
kiértékelésével.- Ellenkező esetben, ha explicit referencia-átalakítás (§10.3.5) vagy kicsomagolási átalakítás (§10.3.7) létezik
C
-rőlT
-ra, vagy haC
vagyT
nyitott típus (§8.4.3), akkor a fenti futtatóidejű ellenőrzéseket el kell végezni.- Ellenkező esetben nem lehetséges a
E
típus átalakításaT
típusra referencia, csomagolás, burkolás vagy kibontás útján, és a művelet eredményefalse
. A fordítók optimalizálást végezhetnek a fordítási idő típusa alapján.végjegyzet
12.12.12.2 Az is-pattern operátor
Az minta operátor annak ellenőrzésére szolgál, hogy a kifejezés által kiszámított érték egyezik-e egy adott mintával (11. §). Az ellenőrzés futásidőben történik. Az is-pattern operátor eredménye igaz, ha az érték megegyezik a mintával; ellenkező esetben hamis.
Egy olyan kifejezés esetén, mint E is P
, ahol a E
egy T
típusú reláció, és a P
egy minta, fordítási idejű hiba lép fel, ha az alábbi feltételek bármelyike igaz:
-
E
nem jelöl meg értéket, vagy nem rendelkezik típussal. - A minta
P
nem alkalmazható (11.2.) típusraT
.
12.12.13 Az "as" operátor
A as
operátor használatával explicit módon konvertálhat egy értéket egy adott referenciatípusra vagy null értékűre. A öntött kifejezéstől (§12.9.7) ellentétben a as
operátor soha nem ad kivételt. Ehelyett, ha a megadott átalakítás nem lehetséges, az eredményként kapott érték null
.
A E as T
alakú műveletben a E
kifejezésnek kifejezésnek kell lennie, és a T
hivatkozástípusnak, ismerten hivatkozástípusnak, vagy null értékkel használható értéktípusnak kell lennie. Továbbá az alábbiak közül legalább az egyiknek igaznak kell lennie, vagy más esetben fordítási időhiba lép fel:
- Létezik identitás (§10.2.2), implicit nullálható (§10.2.6), implicit referencia (§10.2.8), csomagolás (§10.2.9), explicit nullálható (§10.3.4), explicit hivatkozás (§10.3.5), vagy burkolás (§8.3.12) átalakítás
E
-bőlT
-be. - A
E
vagyT
típusa nyitott típus. -
E
aznull
literál.
Ha a E
fordítási idejének típusa nem dynamic
, akkor a E as T
művelet ugyanazt az eredményt eredményezi, mint a
E is T ? (T)(E) : (T)null
kivéve, hogy a E
csak egyszer lesz kiértékelve. A fordító várhatóan optimalizálja a E as T
, hogy legfeljebb egy futtatókörnyezettípus-ellenőrzést hajtson végre, szemben a fenti bővítés által sugallt két futtatókörnyezettípus-ellenőrzéssel.
Ha a E
fordítási idejének típusa dynamic
, az öntött operátorral ellentétben a as
operátor nincs dinamikusan kötve (§12.3.3). Ezért a bővítés ebben az esetben a következő:
E is T ? (T)(object)(E) : (T)null
Vegye figyelembe, hogy egyes átalakítások, például a felhasználó által megadott konverziók nem lehetségesek a as
operátorral, és ehelyett a cast kifejezések használatával kell végrehajtani.
példa: A példában
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 } }
A
T
típusparaméterérőlG
ismert, hogy referenciatípus, mert osztálykötési megszorítással rendelkezik. AU
H
típusparamétere azonban nem megfelelő; ezért aas
operátor használataH
nem engedélyezett.példa vége
12.13 Logikai operátorok
12.13.1 Általános
A &
, ^
és |
operátorokat logikai operátoroknak nevezzük.
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
;
Ha egy logikai operátor operandusa dynamic
fordítási idő típusú, akkor a kifejezés dinamikusan kötött (§12.3.3). Ebben az esetben a kifejezés fordításidőbeli típusa dynamic
, és az alább ismertetett feloldás futásidőben történik azon operandusok futásidejű típusa alapján, amelyek fordításidőbeli típusúak dynamic
.
A x «op» y
űrlap egy olyan művelete esetén, ahol az "op" az egyik logikai operátor, a túlterhelés feloldása (12.4.5. §) egy adott operátor implementációjának kiválasztására lesz alkalmazva. Az operandusok a kiválasztott operátor paramétertípusaiká lesznek konvertálva, az eredmény típusa pedig az operátor visszatérési típusa.
Az előre definiált logikai operátorokat az alábbi alklámok ismertetik.
12.13.2 Egész szám logikai operátorok
Az előre definiált egész szám logikai operátorai a következők:
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);
A &
operátor kiszámítja a két operandus bitenkénti logikai ÉSÉT, a |
operátor kiszámítja a két operandus bitenkénti logikai VAGYÁT, a ^
operátor pedig kiszámítja a két operandus bitenkénti logikai kizáró VAGYÁT. Ezekből a műveletekből nem következhet túlcsordulás.
A fent meghatározott, felemelt (§12.4.8) előre meghatározott egész logikai operátorok felemelt formái is alapértelmezettek.
12.13.3 Enumerálási logikai operátorok
Minden enumerálási típus E
implicit módon a következő előre definiált logikai operátorokat biztosítja:
E operator &(E x, E y);
E operator |(E x, E y);
E operator ^(E x, E y);
A x «op» y
kiértékelésének eredménye, ahol x
és y
a E
enumerációs típus kifejezései a U
alap típussal, és az «op» egyike a logikai operátoroknak, pontosan ugyanaz, mint a (E)((U)x «op» (U)y)
kiértékelése. Más szóval az enumerálási típusú logikai operátorok egyszerűen végrehajtják a logikai műveletet a két operandus mögöttes típusán.
A fent definiált nem emelt (§12.4.8) típusú, nem emelt enumerálású logikai operátorok szintén előre definiálva vannak.
12.13.4 Boole-i logikai operátorok
Az előre definiált logikai operátorok a következők:
bool operator &(bool x, bool y);
bool operator |(bool x, bool y);
bool operator ^(bool x, bool y);
A x & y
eredménye true
, ha x
és y
is true
. Ellenkező esetben az eredmény false
.
A x | y
eredménye true
, ha x
vagy y
a true
. Ellenkező esetben az eredmény false
.
A x ^ y
eredménye true
, ha x
true
és a y
false
, vagy ha x
false
és a y
true
. Ellenkező esetben az eredmény false
. Ha az operandusok bool
típusúak, a ^
operátor ugyanazt az eredményt számítja ki, mint a !=
operátor.
12.13.5 Null értékű bool & és | operátorok
A null értéket felvevő logikai típus bool?
három értéket jelölhet: true
, false
és null
.
A többi bináris operátorhoz hasonlóan a logikai operátorok megemelt formái is, &
és |
(12.13.4.), előre definiáltak.
bool? operator &(bool? x, bool? y);
bool? operator |(bool? x, bool? y);
Az emelt &
és |
operátorok szemantikáját a következő táblázat határozza meg:
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 |
Megjegyzés: A
bool?
típus fogalmilag hasonló az SQL logikai kifejezéseihez használt háromértékű típushoz. A fenti táblázat ugyanazokat a szemantikákat követi, mint az SQL, míg a 12.4.8.8. szabályainak alkalmazása a&
és|
operátorokra nem. A 12.4.8 szabályai már sql-szerű szemantikát biztosítanak az emelt^
operátor számára. végjegyzet
12.14 Feltételes logikai operátorok
12.14.1 Általános
A &&
és ||
operátorokat feltételes logikai operátoroknak nevezzük. Ezeket "rövidzárlatú" logikai operátoroknak is nevezik.
conditional_and_expression
: inclusive_or_expression
| conditional_and_expression '&&' inclusive_or_expression
;
conditional_or_expression
: conditional_and_expression
| conditional_or_expression '||' conditional_and_expression
;
A &&
és ||
operátorok a &
és |
operátorok feltételes verziói:
- A művelet
x && y
megfelel ax & y
műveletnek, azzal a kivételsel, hogy ay
csak akkor lesz kiértékelve, hax
nemfalse
. - A művelet
x || y
megfelel ax | y
műveletnek, azzal a kivételsel, hogy ay
csak akkor lesz kiértékelve, hax
nemtrue
.
Megjegyzés: A rövidzárolás a "nem igaz" és a "nem hamis" feltételt azért használja, mert lehetővé teszi a felhasználó által definiált feltételes operátorok számára a rövidzárolás alkalmazásának meghatározását. A felhasználó által definiált típusok olyan állapotban lehetnek, ahol
operator true
false
ad vissza,operator false
pedigfalse
. Ezekben az esetekben sem&&
, sem||
nem lenne zárlatos. végjegyzet
Ha egy feltételes logikai operátor operandusa dynamic
fordítási idő típusú, akkor a kifejezés dinamikusan kötött (§12.3.3). Ebben az esetben a kifejezés fordításidőbeli típusa dynamic
, és az alább ismertetett feloldás futásidőben történik azon operandusok futásidejű típusa alapján, amelyek fordításidőbeli típusúak dynamic
.
Az űrlap x && y
vagy x || y
művelete túlterhelésfeloldás alkalmazásával (§12.4.5) úgy történik, mintha a művelet x & y
vagy x | y
lett volna megírva. Akkor
- Ha a túlterhelés feloldása nem talál egyetlen legjobb operátort, vagy ha a túlterhelés feloldása kiválaszt egy előre definiált egész logikai operátort vagy null értékű logikai Boole-operátort (§12.13.5), akkor kötési idejű hiba következik be.
- Ellenkező esetben, ha a kiválasztott operátor az előre definiált logikai operátorok egyike (§12.13.4), a művelet a §12.14.2szerint kerül feldolgozásra.
- Ellenkező esetben a kijelölt operátor egy felhasználó által definiált operátor, és a műveletet a §12.14.3leírtak szerint dolgozzák fel.
A feltételes logikai operátorok közvetlen túlterhelése nem lehetséges. Mivel azonban a feltételes logikai operátorok kiértékelése a normál logikai operátorok szempontjából történik, a normál logikai operátorok túlterhelése bizonyos korlátozásokkal a feltételes logikai operátorok túlterhelésének is tekinthető. Ez részletesen ki van fejtve a §12.14.3szakaszban.
12.14.2 Logikai feltételes Boole-operátorok
Ha a &&
vagy ||
operandusai bool
típusúak, vagy ha az operandusok olyan típusúak, amelyek nem határoznak meg alkalmazható operator &
vagy operator |
, de implicit átalakításokat határoznak meg bool
, a művelet a következőképpen lesz feldolgozva:
- A
x && y
műveletetx ? y : false
-ként értékeljük ki. Más szóval, ax
-t először kiértékelik, majdbool
típussá alakítják. Ezután, hax
true
, ay
-t kiértékelik ésbool
típussá alakítják, és ez lesz a művelet eredménye. Ellenkező esetben a művelet eredményefalse
. - A
x || y
műveletetx ? true : y
-ként értékeljük ki. Más szóval, ax
-t először kiértékelik, majdbool
típussá alakítják. Akkor, hax
true
, a művelet eredményetrue
. Ellenkező esetben ay
kiértékelődik ésbool
típussá alakítódik, és ez válik a művelet eredményévé.
12.14.3 Felhasználó által definiált feltételes logikai operátorok
Ha a &&
vagy ||
operandusai olyan típusúak, amelyek a felhasználó által meghatározott operator &
vagy operator |
deklarálnak, az alábbiak közül mindkettő igaz, ahol T
a kiválasztott operátor deklarálásának típusa:
- A kiválasztott operátor visszatérési típusának és minden egyes paraméterének típusának
T
kell lennie. Más szóval az operátornak ki kell számítania azT
típusú két operandus logikai ÉS vagy logikai VAGY értékét , ésT
típusú eredményt kell visszaadni . -
T
operator true
ésoperator false
deklarációit tartalmazzák.
Kötési idejű hiba akkor fordul elő, ha valamelyik követelmény nem teljesül. Ellenkező esetben a &&
vagy ||
művelet kiértékelése a felhasználó által megadott operator true
vagy operator false
a kiválasztott felhasználó által megadott operátorral való kombinálásával történik:
- A
x && y
műveletT.false(x) ? x : T.&(x, y)
a következőként van kiértékelve, ahol aT.false(x)
-ban deklaráltoperator false
aT
meghívása, a kiválasztottT.&(x, y)
pedig aoperator &
meghívása. Más szóval, először kiértékelik ax
-t, majd aoperator false
-t hívják meg az eredmény alapján, hogy meghatározzák, ax
határozottan hamis-e. Ezután, hax
egyértelműen hamis, a művelet eredménye a korábbanx
-hez kiszámított érték. Ellenkező esetben ay
kiértékelésre kerül, és a kiválasztottoperator &
meghívásra kerül a korábban kiszámítottx
értékre és a kiszámítotty
értékre, hogy előállítsa a művelet eredményét. - A
x || y
műveletT.true(x) ? x : T.|(x, y)
a következőként van kiértékelve, ahol aT.true(x)
-ban deklaráltoperator true
aT
meghívása, a kiválasztottT.|(x, y)
pedig aoperator |
meghívása. Más szóval, először ax
van kiértékelve, majd az eredmény alapján meghívják aoperator true
-t annak megállapítására, hogy ax
valóban igaz-e. Ezután, hax
biztosan igaz, a művelet eredménye azx
korábban kiszámított érték. Ellenkező esetben ay
kiértékelésre kerül, és a kiválasztottoperator |
meghívásra kerül a korábban kiszámítottx
értékre és a kiszámítotty
értékre, hogy előállítsa a művelet eredményét.
Ezen műveletek bármelyikében a x
által megadott kifejezés csak egyszer lesz kiértékelve, és az y
által adott kifejezés nem lesz kiértékelve vagy pontosan egyszer kiértékelve.
12.15 A null egyesítő operátor
A ??
operátort null szénerősítési operátornak nevezzük.
null_coalescing_expression
: conditional_or_expression
| conditional_or_expression '??' null_coalescing_expression
| throw_expression
;
Ha a ?? b
nema
, akkor a null
alakú null-egyesítési kifejezés eredménye a
; ellenkező esetben az eredmény b
. A művelet csak akkor értékeli a b
-t, ha a
null
.
A null egyesítő operátor jobbra asszociatív, ami azt jelenti, hogy a műveletek jobbról balra csoportosulnak.
példa: Egy
a ?? b ?? c
formájú kifejezés úgy lesz kiértékelve, minta ?? (b ?? c)
. Általánosságban elmondható, hogy egyE1 ?? E2 ?? ... ?? EN
alakban lévő kifejezés visszaadja az első nemnull
operandust, vagynull
-t, ha az összes operandusnull
. példa vége
A a ?? b
kifejezés típusa attól függ, hogy mely implicit konverziók érhetők el az operandusokon. Előnyben részesítés sorrendjében a a ?? b
típusa lehet A₀
, A
vagy B
, ahol a A
a a
típusa (feltéve, hogy a
rendelkezik típussal), B
a b
típusa (feltéve, hogy b
rendelkezik típussal), és A₀
az alapul szolgáló típusa a A
-nek, ha A
null értékkel rendelkező értéktípus, vagy ellenkező esetben A
. Kifejezetten, a a ?? b
a következőképpen kerül feldolgozásra:
- Ha
A
létezik, és nem felügyelt típus (8.8.§) vagy ismert, hogy nem null értékű, fordítási időhiba lép fel. - Ellenkező esetben, ha
A
létezik, ésb
dinamikus kifejezés, az eredmény típusadynamic
. Futásidőben a rendszer először kiértékelia
. Haa
nemnull
,a
dynamic
lesz, és ez lesz az eredmény. Ellenkező esetben ab
kerül kiértékelésre, és ez lesz az eredmény. - Ellenkező esetben, ha
A
létezik és null értékű értéktípus, amelyről van egy implicit átalakításb
-rőlA₀
-re, az eredménytípusA₀
. Futásidőben a rendszer először kiértékelia
. Haa
nemnull
, aa
kibontvaA₀
típusra, és ez lesz az eredmény. Ellenkező esetben ab
kiértékelésre kerül és átalakulA₀
típussá, és ez lesz az eredmény. - Ellenkező esetben, ha
A
létezik, és létezik egy implicit átalakításb
-rólA
-ra, akkor az eredmény típusaA
. Futásidőben a rendszer először kiértékelia
. Haa
nemnull
,a
lesz az eredmény. Ellenkező esetben ab
kiértékelésre kerül és átalakulA
típussá, és ez lesz az eredmény. - Ellenkező esetben, ha
A
létezik, és null értékkel rendelkező értéktípus,b
B
típussal rendelkezik, és van egy implicit átalakításA₀
-rólB
-re, akkor az eredménytípusB
lesz. Futásidőben a rendszer először kiértékelia
. Haa
nemnull
, akkora
ki van csomagolvaA₀
típusra, majd átalakítvaB
típusúvá, és ez lesz az eredmény. Ellenkező esetben ab
kiértékelődik, és az lesz az eredmény. - Ellenkező esetben, ha
b
B
típussal rendelkezik, és létezik egy implicit konverzióa
-bőlB
-ba, az eredménytípusB
. Futásidőben a rendszer először kiértékelia
. Haa
nincsnull
,a
B
típussá alakul, és ez lesz az eredmény. Ellenkező esetben ab
kiértékelődik, és az lesz az eredmény. - Ellenkező esetben a
a
és ab
nem kompatibilisek, és fordítási időhiba lép fel.
Példa:
T M<T>(T a, T b) => a ?? b; string s = M(null, "text"); int i = M(19, 23);
A metódus
T
típusparamétereM
nincs korlátozva. Ezért a típusargumentum lehet hivatkozástípus vagy null értékű típus, ahogyan az első hívásbanM
látható. A típusargumentum lehet egy nem null értékű értéktípus is, ahogyan az a másodikM
hívásban látható. Ha a típusargumentum nem nullázható értéktípus, a kifejezésa ?? b
értékea
.példa vége
12.16 A dobáskifejezési operátor
throw_expression
: 'throw' null_coalescing_expression
;
A throw_expression a null_coalescing_expressionkiértékelésével előállított értéket adja meg. A kifejezésnek implicit módon átalakíthatónak kell lennie System.Exception
értékre, és a kifejezés kiértékelésének eredménye System.Exception
lesz a dobás előtt. A throw kifejezés kiértékelésének futásidejű viselkedése ugyanaz, mint ahogy a throw utasítás (§13.10.6) esetében van meghatározva.
A throw_expression nem rendelkezik típussal. A throw_expression minden típusra átalakítható egy implicit dobás átalakítással.
A dobókifejezés csak a következő szintaktikai kontextusokban fordulhat elő:
- Ternáris feltételes operátor második vagy harmadik operandusaként (
?:
). - Null egyesítő operátor második operandusaként (
??
). - Kifejezés alapú lambdák vagy tagok esetén.
12.17 Deklarációs kifejezések
A deklarációs kifejezés egy helyi változót deklarál.
declaration_expression
: local_variable_type identifier
;
local_variable_type
: type
| 'var'
;
A simple_name_
akkor is deklarációs kifejezésnek minősül, ha az egyszerű névkeresés nem talált társított deklarációt (§12.8.4). Deklarációs kifejezésként használva a _
egyszerű elvetési kifejezésnek nevezik. Szemantikailag egyenértékű a var _
, de több helyen engedélyezett.
Deklarációs kifejezés csak a következő szintaktikai kontextusokban történhet:
- Mint egy
out
argument_value egy argument_list-ban. - Egyszerű elvetés
_
, amely egy egyszerű hozzárendelés bal oldalát képezi (§12.21.2). - Egy vagy több rekurzív módon beágyazott tuple_expressiontuple_element, amelynek legkülső eleme a dekonstruáló hozzárendelés bal oldalán található. A deconstruction_expression deklarációs kifejezéseket eredményez ebben a helyzetben, még akkor is, ha a deklarációs kifejezések nincsenek szintaktikailag jelen.
Megjegyzés: Ez azt jelenti, hogy a deklarációs kifejezés nem zárójeles. végjegyzet
Hiba, ha egy implicit módon beírt változóra, amely declaration_expression deklarációval van ellátva, hivatkoznak abban az argument_list listában, ahol deklarálták.
Hiba, ha egy declaration_expression segítségével deklarált változóra hivatkoznak abban a dekonstruálási hozzárendelésben, ahol előfordul.
A deklarációs kifejezés, amely egy egyszerű elvetés, vagy ahol a local_variable_type az azonosító, var
implicit módon beírt változóként van besorolva. A kifejezésnek nincs típusa, és a helyi változó típusa a szintaktikai környezet alapján következik, az alábbiak szerint:
- Egy argument_list a változó kikövetkezett típusa a megfelelő paraméter deklarált típusa.
- Egy egyszerű hozzárendelés bal oldalán a változó kikövetkeztetett típusa a hozzárendelés jobb oldalának típusa.
- Egy egyszerű hozzárendelés bal oldalán lévő tuple_expression esetében a változó várható típusa a hozzárendelés jobb oldalán (a felbontás után) lévő megfelelő tömb elem típusa.
Ellenkező esetben a deklarációs kifejezés explicit módon beírt változóként van besorolva, a kifejezés és a deklarált változó típusa pedig a local_variable_typeáltal megadott.
A _
azonosítójú deklarációs kifejezés elvetés (§9.2.9.2), és nem ad nevet a változónak. A nem _
azonosítójú deklarációs kifejezés ezt a nevet a legközelebbi helyi változó deklarációs térbe (7.3. §) vezeti be.
Példa:
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 _);
A
s1
deklarációja explicit és implicit módon beírt deklarációs kifejezéseket is mutat. A(z)b1
típusabool
, mert ez a megfelelő kimeneti paraméter típusa aM1
-ben. A következőWriteLine
hozzáférhet azi1
-hez ésb1
-höz, amelyeket a környező hatókörbe vezettek be.A
s2
deklarációja azt mutatja, hogy megpróbálják használni ai2
-et aM
beágyazott hívásában, ami nem megengedett, mert a hivatkozás abban az argumentumlistában fordul elő, aholi2
deklarálva volt. Másrészt a végső argumentumban engedélyezett ab2
való hivatkozás, mert a beágyazott argumentumlista vége után következik be, aholb2
deklarálva lett.A
s3
deklarációja azt mutatja, hogyan használhatók az implicit és explicit módon típusosan beírt deklarációs kifejezések, amelyek elhanyagolhatók. Mivel az elvetések nem deklarálnak elnevezett változót, az azonosító_
több előfordulása is engedélyezett.(int i1, int _, (var i2, var _), _) = (1, 2, (3, 4), 5);
Ez a példa implicit és explicit módon beírt deklarációs kifejezések használatát mutatja be mind a változók, mind az elvetések esetében egy dekonstruáló hozzárendelésben. A simple_name
_
egyenértékűvar _
, ha nem található_
deklaráció.void M1(out int i) { ... } void M2(string _) { M1(out _); // Error: `_` is a string M1(out var _); }
Ez a példa azt mutatja be, hogy a
var _
implicit módon beírt elvetést biztosít, ha_
nem érhető el, mert egy változót jelöl ki a beágyazási hatókörben.példa vége
12.18 Feltételes operátor
A ?:
operátort feltételes operátornak nevezik. Időnként ternáris operátornak is nevezik.
conditional_expression
: null_coalescing_expression
| null_coalescing_expression '?' expression ':' expression
| null_coalescing_expression '?' 'ref' variable_reference ':'
'ref' variable_reference
;
Ha jelen van, a dobáskifejezés (ref
) nem szerepelhet feltételes operátorban.
Az űrlap feltételes kifejezése b ? x : y
először kiértékeli a feltételt b
. Ezután, ha b
true
, akkor a x
kiértékelésre kerül, és a művelet eredményévé válik. Ellenkező esetben a rendszer kiértékeli a y
-t, és az a művelet eredménye lesz. A feltételes kifejezés soha nem értékeli ki mindkettőt, x
és y
.
A feltételes operátor jobboldali asszociatív, ami azt jelenti, hogy a műveletek jobbról balra csoportosulnak.
példa: Egy
a ? b : c ? d : e
formájú kifejezés úgy lesz kiértékelve, minta ? b : (c ? d : e)
. példa vége
A ?:
operátor első operandusa olyan kifejezés, amely implicit módon átalakítható bool
- vagy operator true
megvalósító típusú kifejezéssé . Ha egyik követelmény sem felel meg, fordítási időhiba lép fel.
Ha ref
van jelen:
- A két variable_referencetípusa között identitáskonverziónak kell lennie, és az eredmény típusa bármelyik lehet. Ha bármelyik típus
dynamic
, a típuskövetkeztetés adynamic
(8.7. §) előnyben részesíti. Ha bármelyik típus rekordtípus (§8.3.11), akkor a típuskövetkezmények tartalmazzák az elemneveket, ha az azonos sorrendben lévő elemnevek mindkét rekordban megegyeznek. - Az eredmény egy változóhivatkozás, amely írható, ha mindkét változó_hivatkozásírható.
Megjegyzés: Ha
ref
van jelen, a conditional_expression egy változóhivatkozást ad vissza, amely hozzárendelhető egy referenciaváltozóhoz a= ref
operátor használatával, vagy referencia-/bemeneti/kimeneti paraméterként továbbítható. végjegyzet
Ha ref
nincs jelen, az x
operátor második és harmadik operandusa, y
és ?:
a feltételes kifejezés típusát szabályozza:
- Ha
x
X
típussal rendelkezik, ésy
Y
,- Ha
X
ésY
között identitáskonvertálás áll fenn, akkor az eredmény a kifejezéskészletek leggyakoribb típusa (§12.6.3.15). Ha bármelyik típusdynamic
, a típuskövetkeztetés adynamic
(8.7. §) előnyben részesíti. Ha bármelyik típus rekordtípus (§8.3.11), akkor a típuskövetkezmények tartalmazzák az elemneveket, ha az azonos sorrendben lévő elemnevek mindkét rekordban megegyeznek. - Ellenkező esetben, ha egy implicit átalakítás (§10.2) létezik
X
-rőlY
-ra, de nemY
-rőlX
-ra, akkorY
a feltételes kifejezés típusa. - Ellenkező esetben, ha implicit számbavételi átalakítás (§10.2.4) létezik
X
-rőlY
-ra, akkorY
a feltételes kifejezés típusa. - Ellenkező esetben, ha implicit számbavételi átalakítás (§10.2.4) létezik
Y
-rőlX
-ra, akkorX
a feltételes kifejezés típusa. - Ellenkező esetben, ha egy implicit átalakítás (§10.2) létezik
Y
-rőlX
-ra, de nemX
-rőlY
-ra, akkorX
a feltételes kifejezés típusa. - Ellenkező esetben nem határozható meg kifejezéstípus, és fordítási időhiba lép fel.
- Ha
- Ha csak az egyik
x
ésy
rendelkezik típussal, ésx
ésy
is implicit módon konvertálható erre a típusra, akkor ez a feltételes kifejezés típusa. - Ellenkező esetben nem határozható meg kifejezéstípus, és fordítási időhiba lép fel.
Az b ? ref x : ref y
formájú ref feltételes kifejezés futásidejű feldolgozása a következő lépésekből áll:
- Először a
b
kiértékelésére kerül sor, majd meghatározásra kerül abool
b
értéke.- Ha a
b
típusábólbool
implicit átalakítás történik, akkor ez az implicit átalakítás egybool
érték előállításához lesz végrehajtva. - Ellenkező esetben a
operator true
típus által definiáltb
kerül meghívásra abool
érték előállításához.
- Ha a
- Ha a fenti lépés által előállított
bool
értéktrue
, akkor a rendszer kiértékelix
, és az eredményül kapott változóhivatkozás a feltételes kifejezés eredménye lesz. - Ellenkező esetben a
y
kiértékelése megtörténik, és a feltételes kifejezés eredménye az így kapott változóhivatkozás lesz.
A b ? x : y
űrlap feltételes kifejezésének futásidejű feldolgozása a következő lépésekből áll:
- Először a
b
kiértékelésére kerül sor, majd meghatározásra kerül abool
b
értéke.- Ha a
b
típusábólbool
implicit átalakítás történik, akkor ez az implicit átalakítás egybool
érték előállításához lesz végrehajtva. - Ellenkező esetben a
operator true
típus által definiáltb
kerül meghívásra abool
érték előállításához.
- Ha a
- Ha a fenti lépés által létrehozott
bool
értéktrue
, akkor ax
kiértékelése és átalakítása a feltételes kifejezés típusára történik, és ez a feltételes kifejezés eredménye lesz. - Ellenkező esetben a
y
kiértékelése és átalakítása a feltételes kifejezés típusára történik, és ez lesz a feltételes kifejezés eredménye.
12.19 Névtelen függvénykifejezések
12.19.1 Általános
A névtelen függvény egy "in-line" metódusdefiníciót jelképező kifejezés. A névtelen függvények önmagukban nem rendelkeznek értékkel vagy típussal, de kompatibilis delegált vagy kifejezésfa típusúvá konvertálhatók. A névtelen függvények konvertálásának kiértékelése az átalakítás céltípusától függ: Ha delegált típusú, akkor az átalakítás delegált értékre kerül, amely a névtelen függvény által definiált metódusra hivatkozik. Ha kifejezésfa típusú, az átalakítás olyan kifejezésfára lesz kiértékelve, amely a metódus szerkezetét objektumszerkezetként jelöli.
Megjegyzés: Történelmi okokból a névtelen függvények két szintaktikai íze van, nevezetesen lambda_expressions és anonymous_method_expression. A lambda_expressionszinte minden célra tömörebb és kifejezőbb, mint a anonymous_method_expression, melyek a visszamenőleges kompatibilitás érdekében maradtak meg a nyelvben. végjegyzet
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
;
Egy anonymous_function_body felismerésekor, ha a null_conditional_invocation_expression és a kifejezés alternatívák is alkalmazhatók, akkor az előbbit kell választani.
Megjegyzés: Az alternatívák átfedése és prioritása itt kizárólag a leíró kényelem érdekében van; a nyelvhelyességi szabályok kidolgozhatók az átfedés megszüntetése érdekében. Az ANTLR és más nyelvhelyességi rendszerek ugyanazt a kényelmes megoldást választják, így anonymous_function_body automatikusan a megadott szemantikával rendelkezik. végjegyzet
megjegyzés: Ha kifejezéskéntkezelnek, akkor a
x?.M()
-hez hasonló szintaktikai forma hiba lenne, ha aM
eredménytípusavoid
(§12.8.13). Ha azonban null_conditional_invocation_expression-ként kezelik, az eredménytípusvoid
megengedett. végjegyzet
példa: A
List<T>.Reverse
eredménytípusavoid
. Az alábbi kódban a névtelen kifejezés törzse egy null_conditional_invocation_expression, így nem jelent hibát.Action<List<int>> a = x => x?.Reverse();
példa vége
A =>
operátor prioritási sorrendje megegyezik a hozzárendelésével (=
), és jobb asszociatív.
A async
módosítóval rendelkező névtelen függvény egy aszinkron függvény, amely a 15.14 bekezdésben leírt szabályokat követi.
Egy névtelen függvény paraméterei lambda_expression formájában explicit vagy implicit módon beírhatók. Egy explicit módon beírt paraméterlistában az egyes paraméterek típusa explicit módon van megadva. Az implicit módon beírt paraméterek listájában a paraméterek típusai abból a környezetből következtetnek, amelyben a névtelen függvény előfordul – különösen akkor, ha a névtelen függvény kompatibilis delegálttípussá vagy kifejezésfatípussá alakul át, az a típus biztosítja a paramétertípusokat (§10.7).
Egy egyetlen, implicit módon beírt paraméterrel rendelkező lambda_expression a zárójelek kihagyhatók a paraméterlistából. Más szóval az űrlap névtelen függvénye
( «param» ) => «expr»
lehet rövidíteni:
«param» => «expr»
Egy névtelen függvény paraméterlistája anonymous_method_expression formájában nem kötelező. Ha meg van adva, a paramétereket kifejezetten be kell gépelni. Ha nem, akkor a névtelen függvény olyan delegálttá alakítható, amelynek paraméterlistája nem tartalmaz kimeneti paramétereket.
Egy névtelen függvény
Példa: A névtelen függvényekre az alábbiakban talál néhány példát:
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
példa vége
Az lambda_expression-ek és anonymous_method_expression-ek viselkedése ugyanaz, kivéve a következő pontokat:
- anonymous_method_expressionlehetővé teszi a paraméterlista teljes elhagyását, így átalakítható bármely értékparaméter-lista típusának delegálására.
- lambda_expressionparamétertípusok elhagyhatók és következtethetők, míg anonymous_method_expressionparamétertípusok explicit megadását igénylik.
- A lambda_expression törzse lehet kifejezés vagy blokk, míg egy anonymous_method_expression törzsének blokknak kell lennie.
- Csak lambda_expressionrendelkezik kompatibilis kifejezésfatípusokra való átalakítással (§8.6).
12.19.2 Névtelen függvény-aláírások
Egy névtelen függvény anonymous_function_signature határozza meg a névtelen függvény nevét és opcionálisan a paraméterek típusait. A névtelen függvény paramétereinek hatóköre a anonymous_function_body (§7.7). A paraméterlistával együtt (ha meg van adva) a névtelen metódus törzse deklarációs területet alkot (§7.3). Tehát fordítási időben hiba lép fel, ha egy névtelen függvény paraméterének neve megegyezik egy helyi változó, helyi állandó vagy másik paraméter nevével, amelynek hatóköre magában foglalja a anonymous_method_expression vagy lambda_expression-t.
Ha egy névtelen függvény rendelkezik explicit_anonymous_function_signature, akkor a kompatibilis delegálttípusok és kifejezésfatípusok halmaza azokra korlátozódik, amelyek ugyanazon paramétertípusokkal és módosítókkal azonos sorrendben rendelkeznek (§10.7). A metóduscsoport-átalakításokkal (§10.8) ellentétben a névtelen függvényparaméter-típusok eltérése nem támogatott. Ha egy névtelen függvény nem rendelkezik anonymous_function_signature, akkor a kompatibilis delegálttípusok és kifejezésfatípusok csak azokra korlátozódnak, amelyek nem rendelkeznek kimeneti paraméterekkel.
Vegye figyelembe, hogy egy anonymous_function_signature nem tartalmazhat attribútumokat vagy paramétertömböket. Mindazonáltal egy anonymous_function_signature kompatibilis lehet egy olyan delegált típussal, amelynek paraméterlistája paramétertömböt tartalmaz.
Vegye figyelembe, hogy a kifejezésfatípusra való konvertálás még akkor is meghiúsulhat, ha kompatibilis, fordításkor (§8.6).
12.19.3 Névtelen függvénytestek
Egy névtelen függvény törzsére (kifejezésre vagy blokk) a következő szabályok vonatkoznak:
- Ha a névtelen függvény tartalmaz aláírást, az aláírásban megadott paraméterek elérhetők a törzsben. Ha a névtelen függvény nem rendelkezik aláírással, delegált típussá vagy kifejezéstípussá alakítható, amelynek paraméterei (§10.7), de a paraméterek nem érhetők el a törzsben.
- A legközelebbi névtelen függvény aláírásában megadott by-reference paramétereket (ha vannak ilyenek) kivéve fordítási idejű hiba, ha a törzs megpróbál hozzáférni egy by-reference paraméterhez.
- A legközelebbi névtelen függvény aláírásában (ha van ilyen) megadott paraméterek kivételével a törzs fordítási idejű hibát követ el, ha hozzáfér egy
ref struct
típusú paraméterhez. - Ha a
this
típusa egy struktúratípus, akkor fordítási időhiba, ha a törzs hozzáférthis
-hez. Ez érvényes arra, amikor a hozzáférés explicit (mint athis.x
), vagy implicit (mint ax
, ahol ax
a struktúra egy példánytagja). Ez a szabály egyszerűen tiltja az ilyen hozzáférést, és nem befolyásolja, hogy a tagkeresés eredménye a struktúra egy tagja legyen. - A törzs hozzáfér a névtelen függvény külső változóihoz (12.19.6). A külső változó elérése a lambda_expression vagy anonymous_method_expression kiértékelésének időpontjában aktív változó példányára hivatkozik (§12.19.7).
- Fordítási idő hibát jelent, ha a törzs egy
goto
utasítást, egybreak
utasítást vagy egycontinue
utasítást tartalmaz, amelynek célja a törzsen kívül vagy egy adott névtelen függvény törzsén belül van. - A
return
utasítás a törzsben a legközelebbi környező névtelen függvény meghívásából ad vissza vezérlést, nem pedig az azt körülvevő függvénytagból.
Nyíltan nincs megadva, hogy létezik-e mód a névtelen függvény blokkjának végrehajtására a lambda_expression vagy a anonymous_method_expressionkiértékelésén és meghívásán kívül. A fordítók dönthetnek úgy, hogy egy névtelen függvényt implementálnak egy vagy több nevesített metódus vagy típus szintetizálásával. Az ilyen szintetizált elemek neve olyan formában kell legyen, amely a fordítóprogram számára van fenntartva (§6.4.3).
12.19.4 Túlterhelés feloldás
Az argumentumlistában szereplő névtelen függvények típuskövetkeztetésben és túlterhelésfeloldásban vesznek részt. A pontos szabályokért tekintse meg §12.6.3 és §12.6.4.
Példa: Az alábbi példa a névtelen függvények túlterhelésfeloldásra gyakorolt hatását mutatja be.
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; } }
A
ItemList<T>
osztály kétSum
metódust használ. Mindegyik egyselector
argumentumot használ, amely kinyeri a listaelemből összegzendő értéket. A kinyert érték lehetint
vagydouble
, és az eredményül kapott összeg hasonlóképpenint
vagydouble
.A
Sum
metódusokkal például összegeket számíthat ki a részletes sorok listájából egy sorrendben.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( ... ) { ... } }
A
orderDetails.Sum
első meghívása során mindkétSum
metódus alkalmazható, mert a névtelen függvényd => d.UnitCount
kompatibilisFunc<Detail,int>
ésFunc<Detail,double>
. A túlterhelés feloldása azonban az elsőSum
metódust választja meg, mivel aFunc<Detail,int>
típusra való átalakítás jobb, mint aFunc<Detail,double>
-re való átalakítás.A
orderDetails.Sum
második meghívásában csak a másodikSum
metódus alkalmazható, mert a névtelen függvényd => d.UnitPrice * d.UnitCount
double
típusú értéket állít elő. Így a túlterhelés feloldása a másodikSum
metódust választja az adott meghíváshoz.példa vége
12.19.5 Névtelen függvények és dinamikus kötés
A névtelen függvények nem lehetnek dinamikusan kötött műveletek fogadói, argumentumai vagy operandusai.
12.19.6 Külső változók
12.19.6.1 Általános
Minden olyan helyi változót, értékparamétert vagy paramétertömböt, amelynek hatóköre tartalmazza a
12.19.6.2 Rögzített külső változók
Ha egy külső változóra névtelen függvény hivatkozik, a külső változóról azt mondják, hogy a külső változót a névtelen függvény rögzítette. A helyi változó élettartama általában annak a blokknak vagy utasításnak a végrehajtására korlátozódik, amelyhez társítva van (§9.2.9.1). A rögzített külső változók élettartama azonban legalább addig meghosszabbodik, amíg a névtelen függvényből létrehozott delegált vagy kifejezésfa jogosulttá nem válik a szemétgyűjtésre.
példa: A példában
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()); } }
a névtelen függvény rögzíti a helyi
x
változót, és ax
élettartama legalább addig meghosszabbodik, amíg aF
visszaadott meghatalmazott jogosulttá nem válik a szemétgyűjtésre. Mivel a névtelen függvény minden egyes meghívása ugyanazon ax
-példányon működik, a példa kimenete a következő:1 2 3
példa vége
Ha egy helyi változót vagy értékparamétert egy névtelen függvény rögzít, a helyi változó vagy paraméter már nem minősül rögzített változónak (23.4. §), hanem áthelyezhető változónak minősül. A rögzített külső változók azonban nem használhatók fixed
utasításban (§23.7), így a rögzített külső változó címe nem vehető fel.
Megjegyzés: A nem foglalt változókkal ellentétben a rögzített helyi változók egyszerre több végrehajtási szálnak is ki lehetnek téve. végjegyzet
12.19.6.3 Helyi változók példányosítása
A helyi változó akkor tekinthető példányosítottnak, amikor a végrehajtás belép a változó hatókörébe.
Példa: Ha például a következő metódust hívja meg, a
x
helyi változó példányosítása és inicializálása háromszor történik – a ciklus minden iterációjához egyszer.static void F() { for (int i = 0; i < 3; i++) { int x = i * 2 + 1; ... } }
Azonban, ha a
x
deklarációját a cikluson kívülre helyezi, azx
csak egyszer kerül példányosításra.static void F() { int x; for (int i = 0; i < 3; i++) { x = i * 2 + 1; ... } }
példa vége
Ha nincs rögzítve, nem lehet pontosan megfigyelni, hogy a rendszer pontosan milyen gyakran példányosít egy helyi változót – mivel a példányok élettartama szétesik, az egyes példányok egyszerűen ugyanazt a tárolási helyet használhatják. Ha azonban egy névtelen függvény rögzít egy helyi változót, a példányosítás hatása nyilvánvalóvá válik.
Példa: A példa
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(); } } }
hozza létre a kimenetet:
1 3 5
Ha azonban a
x
deklarációja a cikluson kívülre kerül: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(); } } }
a kimenet a következő:
5 5 5
Vegye figyelembe, hogy a fordító számára engedélyezett (de nem kötelező) a három példány egyetlen delegált példánnyá optimalizálása (§10.7.2).
példa vége
Ha egy for-loop iterációs változót deklarál, akkor magát a változót a cikluson kívülre deklaráltnak kell tekinteni.
Példa: Így ha a példa megváltozik az iterációs változó rögzítéséhez:
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(); } } }
az iterációs változónak csak egy példánya van rögzítve, amely a kimenetet hozza létre:
3 3 3
példa vége
Előfordulhat, hogy a névtelen függvény delegáltjai megosztanak néhány rögzített változót, de külön példánya van másoknak.
Példa: Ha például a
F
a következőre módosul:static 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; }
a három meghatalmazott ugyanazt a
x
-példányt rögzíti, de ay
különálló példányait, és a kimenet a következő:1 1 2 1 3 1
példa vége
A különálló névtelen függvények rögzíthetik egy külső változó ugyanazon példányát.
Példa: A példában:
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()); } }
a két névtelen függvény rögzíti a helyi változó ugyanazon példányát
x
, és így "kommunikálhatnak" ezen a változón keresztül. A példa kimenete a következő:5 10
példa vége
12.19.7 Névtelen függvénykifejezések kiértékelése
Egy névtelen függvényt F
mindig delegált típusú D
vagy kifejezésfa típusú E
kell átalakítani, akár közvetlenül, akár egy delegáltlétrehozási kifejezés végrehajtásával new D(F)
. Ez az átalakítás határozza meg a névtelen függvény eredményét, ahogy azt a §10.7leírja.
12.19.8 Megvalósítási példa
Ez az alfejezet informatív.
Ez az alklám a névtelen függvénykonvertálások lehetséges megvalósítását ismerteti más C#-szerkezetek tekintetében. Az itt ismertetett implementáció a kereskedelmi C#-fordító által használt alapelveken alapul, de semmiképpen sem kötelező implementáció, és nem is ez az egyetlen lehetséges. Csak röviden említi a kifejezésfákká alakításokat, mivel pontos szemantikáik nem tartoznak e specifikáció hatókörébe.
Az alklám fennmaradó része számos példát ad olyan kódra, amely különböző jellemzőkkel rendelkező névtelen függvényeket tartalmaz. Minden példában egy olyan kód fordítása érhető el, amely csak más C#-szerkezeteket használ. A példákban a D
azonosító a következő delegált típust képviseli:
public delegate void D();
A névtelen függvények legegyszerűbb formája az, amely nem rögzít külső változókat:
delegate void D();
class Test
{
static void F()
{
D d = () => Console.WriteLine("test");
}
}
Ez lefordítható egy delegált példányra, amely egy fordító által létrehozott statikus metódusra hivatkozik, amelyben a névtelen függvény kódja található:
delegate void D();
class Test
{
static void F()
{
D d = new D(__Method1);
}
static void __Method1()
{
Console.WriteLine("test");
}
}
A következő példában a névtelen függvény a this
példányainak tagjaira hivatkozik:
delegate void D();
class Test
{
int x;
void F()
{
D d = () => Console.WriteLine(x);
}
}
Ez lefordítható egy fordító által generált példány metódusra, amely a névtelen függvény kódját tartalmazza:
delegate void D();
class Test
{
int x;
void F()
{
D d = new D(__Method1);
}
void __Method1()
{
Console.WriteLine(x);
}
}
Ebben a példában a névtelen függvény egy helyi változót rögzít:
delegate void D();
class Test
{
void F()
{
int y = 123;
D d = () => Console.WriteLine(y);
}
}
A helyi változó élettartamát ki kell terjeszteni legalább a névtelen függvény delegáltjának élettartamára. Ez úgy érhető el, hogy a helyi változót egy fordító által létrehozott osztály mezőjébe "emeli". A helyi változó példányosítása (§12.19.6.3) ezután megfelel a fordító által létrehozott osztály egy példányának létrehozásához, és a helyi változó elérése megfelel a fordító által létrehozott osztály egy mezőjének elérésének. Ezenkívül a névtelen függvény a fordító által létrehozott osztály példánymetódusává válik:
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);
}
}
}
Végül a következő névtelen függvény rögzíti this
és két különböző élettartamú helyi változót:
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);
}
}
}
Itt egy fordító által létrehozott osztály jön létre minden olyan blokkhoz, amelyben a helyiek rögzítve vannak, így a különböző blokkokban lévő helyiek független élettartamúak lehetnek. A belső blokk fordító által létrehozott osztálya, a __Locals2
egy példánya tartalmazza a helyi változót z
és egy mezőt, amely a __Locals1
egy példányára hivatkozik. A __Locals1
, a külső blokkhoz fordító által létrehozott osztály egy példánya, tartalmazza a helyi változót y
, valamint egy mezőt, amely a környező függvény tagjának this
-jére hivatkozik. Ezekkel az adatstruktúrákkal az összes rögzített külső változót el lehet érni egy __Local2
-példányon keresztül, így a névtelen függvény kódja implementálható az adott osztály példánymetódusaként.
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);
}
}
}
A helyi változók rögzítésére alkalmazott módszer a névtelen függvények kifejezésfává alakításakor is használható: a fordító által létrehozott objektumokra mutató hivatkozások a kifejezésfában tárolhatók, a helyi változókhoz való hozzáférés pedig mezőhozzáférésként jelenhet meg ezeken az objektumokon. Ennek a megközelítésnek az az előnye, hogy lehetővé teszi a helyi változók "felemelt" megosztását a meghatalmazottak és a kifejezésfák között.
Informatív szöveg vége.
12.20 Lekérdezési kifejezések
12.20.1 Általános
lekérdezési kifejezések a relációs és hierarchikus lekérdezési nyelvekhez, például az SQL-hez és az XQueryhez hasonló, nyelvvel integrált szintaxist biztosítanak.
query_expression
: from_clause query_body
;
from_clause
: 'from' type? identifier 'in' expression
;
query_body
: query_body_clause* select_or_group_clause query_continuation?
;
query_body_clause
: from_clause
| let_clause
| where_clause
| join_clause
| join_into_clause
| orderby_clause
;
let_clause
: 'let' identifier '=' expression
;
where_clause
: 'where' boolean_expression
;
join_clause
: 'join' type? identifier 'in' expression 'on' expression
'equals' expression
;
join_into_clause
: 'join' type? identifier 'in' expression 'on' expression
'equals' expression 'into' identifier
;
orderby_clause
: 'orderby' orderings
;
orderings
: ordering (',' ordering)*
;
ordering
: expression ordering_direction?
;
ordering_direction
: 'ascending'
| 'descending'
;
select_or_group_clause
: select_clause
| group_clause
;
select_clause
: 'select' expression
;
group_clause
: 'group' expression 'by' expression
;
query_continuation
: 'into' identifier query_body
;
A lekérdezési kifejezések egy from
záradékkal kezdődnek, és select
vagy group
záradékkal végződnek. A kezdeti from
záradékot nulla vagy több from
, let
, where
, join
vagy orderby
záradék követheti. Minden from
záradék egy generátor, amely bevezet egy tartományváltozót, amely a sorozatelemein fut végig. Minden let
záradék egy olyan tartományváltozót vezet be, amely az előző tartományváltozók alapján kiszámított értéket jelöli. Minden where
záradék egy szűrő, amely kizárja az elemeket az eredményből. Minden join
záradék összehasonlítja a forrásütemezés megadott kulcsait egy másik sorozat kulcsaival, egyező párokat eredményezve. Minden orderby
záradék a megadott feltételek szerint átrendezi az elemeket. Az utolsó select
vagy group
záradék az eredmény alakját adja meg a tartományváltozók szempontjából. Végül egy into
záradék használható a lekérdezések "összezsúfolására", ha egy lekérdezés eredményeit generátorként kezeli egy későbbi lekérdezésben.
12.20.2 Kétértelműségek a lekérdezési kifejezésekben
A lekérdezési kifejezések számos környezetfüggő kulcsszót használnak (§6.4.4): ascending
, by
, descending
, equals
, from
, group
, into
, join
, let
, on
, orderby
, select
és where
.
Az ilyen azonosítók kulcsszavakként és egyszerű nevekként való használata során felmerülő kétértelműségek elkerülése érdekében ezek az azonosítók a lekérdezési kifejezés bármely pontján kulcsszavaknak minősülnek, kivéve, ha "@
" előtaggal rendelkeznek (§6.4.4), amely esetben azonosítónak minősülnek. E célból a lekérdezési kifejezés olyan kifejezés, amely a "from
azonosító" kezdetű, majd a ";
", "=
" vagy ",
" kivételével bármilyen token követi.
12.20.3 Lekérdezési kifejezés fordítása
12.20.3.1 Általános
A C# nyelv nem határozza meg a lekérdezési kifejezések végrehajtási szemantikáját. A lekérdezési kifejezéseket inkább olyan metódusok meghívására fordítjuk le, amelyek megfelelnek a lekérdezés-kifejezés mintájának (§12.20.4). A lekérdezési kifejezések a Where
, Select
, SelectMany
, Join
, GroupJoin
, OrderBy
, OrderByDescending
, ThenBy
, ThenByDescending
, GroupBy
és Cast
nevű metódusok meghívására vannak lefordítva . Ezeket a metódusokat a §12.20.4szerint meghatározott konkrét szignatúrákkal és visszatérési típusokkal kell rendelkezniük. Ezek a metódusok lehetnek a lekérdezett objektum példánymetódusai vagy az objektumon kívüli bővítménymetódusok. Ezek a metódusok implementálják a lekérdezés tényleges végrehajtását.
A lekérdezési kifejezésekről a metódushívásokra való fordítás olyan szintaktikai leképezés, amely a típuskötés vagy túlterhelés feloldása előtt következik be. A lekérdezési kifejezések fordítását követően az eredményül kapott metódushívások normál metódushívásként lesznek feldolgozva, és ez a fordítási idő hibáit is feltárhatja. Ezek a hibafeltételek magukban foglalják a nem létező metódusokat, a helytelen típusú argumentumokat és az általános metódusokat, amelyeknél a típuskövetkezmény meghiúsul.
A lekérdezési kifejezéseket a következő fordítások ismételt alkalmazásával dolgozzuk fel, amíg további csökkentés nem lehetséges. A fordítások alkalmazás szerinti sorrendben vannak felsorolva: az egyes szakaszok feltételezik, hogy az előző szakaszok fordításai teljes mértékben el lettek végezve, és a kimerültség után egy szakaszt később nem fognak újra áttekinteni ugyanazon lekérdezési kifejezés feldolgozása során.
Fordítási idejű hiba lép fel, ha egy lekérdezési kifejezés hozzárendelést tartalmaz egy tartományváltozóhoz, vagy ha egy tartományváltozót használ hivatkozási vagy kimeneti paraméter argumentumaként.
Egyes fordítások tartományváltozókat injektálnak transzparens azonosítókkal, * jelöléssel. Ezek részletesebben a §12.20.3.8szakaszokban vannak leírva.
12.20.3.2 Lekérdezési kifejezések folytatásokkal
Lekérdezési kifejezés a lekérdezés törzsét követő folytatással
from «x1» in «e1» «b1» into «x2» «b2»
lefordítva
from «x2» in ( from «x1» in «e1» «b1» ) «b2»
A következő szakaszok fordításai feltételezik, hogy a lekérdezések nem rendelkeznek folytatással.
példa: A példa:
from c in customers group c by c.Country into g select new { Country = g.Key, CustCount = g.Count() }
fordítása a következőre történik:
from g in (from c in customers group c by c.Country) select new { Country = g.Key, CustCount = g.Count() }
amelynek végső fordítása:
customers. GroupBy(c => c.Country). Select(g => new { Country = g.Key, CustCount = g.Count() })
példa vége
12.20.3.3 Explicit tartomány változótípusai
A tartományváltozó típusát explicit módon meghatározó from
záradék
from «T» «x» in «e»
lefordítva
from «x» in ( «e» ) . Cast < «T» > ( )
A tartományváltozó típusát explicit módon meghatározó join
záradék
join «T» «x» in «e» on «k1» equals «k2»
lefordítva
join «x» in ( «e» ) . Cast < «T» > ( ) on «k1» equals «k2»
A következő szakaszok fordításai feltételezik, hogy a lekérdezések nem rendelkeznek explicit tartományváltozó-típusokkal.
Példa: A példa
from Customer c in customers where c.City == "London" select c
lefordítva
from c in (customers).Cast<Customer>() where c.City == "London" select c
amelynek végleges fordítása
customers. Cast<Customer>(). Where(c => c.City == "London")
példa vége
Megjegyzés: Az explicit tartományváltozó-típusok olyan gyűjtemények lekérdezéséhez hasznosak, amelyek a nem általános
IEnumerable
felületet implementálják, de az általánosIEnumerable<T>
felületet nem. A fenti példában ez a helyzet, ha az ügyfelekArrayList
típusúak lennének. végjegyzet
12.20.3.4 Lekérdezési kifejezések degenerálása
Az űrlap lekérdezési kifejezése
from «x» in «e» select «x»
lefordítva
( «e» ) . Select ( «x» => «x» )
Példa: A példa
from c in customers select c
lefordítva
(customers).Select(c => c)
példa vége
A degenerált lekérdezési kifejezés olyan kifejezés, amely triviálisan kiválasztja a forrás elemeit.
Megjegyzés: A fordítás későbbi fázisai (§12.20.3.6 és §12.20.3.7) eltávolítják az egyéb fordítási lépések által bevezetett degenerált lekérdezéseket a forrásukkal való helyettesítésükkel. Fontos azonban, hogy a lekérdezési kifejezés eredménye soha ne maga a forrásobjektum legyen. Ellenkező esetben az ilyen lekérdezés eredményének visszaadása véletlenül privát adatokat (például elemtömböt) tehet közzé egy hívó számára. Ezért ez a lépés megvédi a közvetlenül forráskódban írt degenerált lekérdezéseket azáltal, hogy explicit módon
Select
-t hív meg a forráson. Ezután aSelect
és más lekérdezési operátorok implementálóinak kell gondoskodniuk arról, hogy ezek a metódusok soha ne magát a forrásobjektumot adják vissza. végjegyzet
12.20.3.5 From, let, where, join és orderby utasítások
Lekérdezési kifejezés egy második from
kikötéssel, majd egy select
kikötéssel
from «x1» in «e1»
from «x2» in «e2»
select «v»
lefordítva
( «e1» ) . SelectMany( «x1» => «e2» , ( «x1» , «x2» ) => «v» )
Példa: A példa
from c in customers from o in c.Orders select new { c.Name, o.OrderID, o.Total }
lefordítva
(customers). SelectMany(c => c.Orders, (c,o) => new { c.Name, o.OrderID, o.Total } )
példa vége
Egy második from
záradékot követően egy olyan lekérdezéstörzs Q
, amely nem üres lekérdezési törzszáradékokat tartalmaz.
from «x1» in «e1»
from «x2» in «e2»
Q
lefordítva
from * in («e1») . SelectMany( «x1» => «e2» ,
( «x1» , «x2» ) => new { «x1» , «x2» } )
Q
Példa: A példa
from c in customers from o in c.Orders orderby o.Total descending select new { c.Name, o.OrderID, o.Total }
lefordítva
from * in (customers). SelectMany(c => c.Orders, (c,o) => new { c, o }) orderby o.Total descending select new { c.Name, o.OrderID, o.Total }
amelynek végleges fordítása
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 })
ahol
x
egy fordító által létrehozott azonosító, amely egyébként láthatatlan és elérhetetlen.példa vége
Egy let
kifejezés az előző from
záradékkal együtt:
from «x» in «e»
let «y» = «f»
...
lefordítva
from * in ( «e» ) . Select ( «x» => new { «x» , «y» = «f» } )
...
Példa: A példa
from o in orders let t = o.Details.Sum(d => d.UnitPrice * d.Quantity) where t >= 1000 select new { o.OrderID, Total = t }
lefordítva
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 }
amelynek végleges fordítása
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 })
ahol
x
egy fordító által létrehozott azonosító, amely egyébként láthatatlan és elérhetetlen.példa vége
Egy where
kifejezés az előző from
záradékkal együtt:
from «x» in «e»
where «f»
...
lefordítva
from «x» in ( «e» ) . Where ( «x» => «f» )
...
Közvetlenül a join
záradékot egy select
záradék követi.
from «x1» in «e1»
join «x2» in «e2» on «k1» equals «k2»
select «v»
lefordítva
( «e1» ) . Join( «e2» , «x1» => «k1» , «x2» => «k2» , ( «x1» , «x2» ) => «v» )
Példa: A példa
from c in customers join o in orders on c.CustomerID equals o.CustomerID select new { c.Name, o.OrderDate, o.Total }
lefordítva
(customers).Join( orders, c => c.CustomerID, o => o.CustomerID, (c, o) => new { c.Name, o.OrderDate, o.Total })
példa vége
Egy join
záradék, amelyet a lekérdezés törzsének záradéka követ:
from «x1» in «e1»
join «x2» in «e2» on «k1» equals «k2»
...
lefordítva
from * in ( «e1» ) . Join(
«e2» , «x1» => «k1» , «x2» => «k2» ,
( «x1» , «x2» ) => new { «x1» , «x2» })
...
Egy join
-into
záradék, amelyet azonnal egy select
záradék követ.
from «x1» in «e1»
join «x2» in «e2» on «k1» equals «k2» into «g»
select «v»
lefordítva
( «e1» ) . GroupJoin( «e2» , «x1» => «k1» , «x2» => «k2» ,
( «x1» , «g» ) => «v» )
Egy join into
záradék, amelyet a lekérdezés törzsének záradéka követ
from «x1» in «e1»
join «x2» in «e2» on «k1» equals «k2» into *g»
...
lefordítva
from * in ( «e1» ) . GroupJoin(
«e2» , «x1» => «k1» , «x2» => «k2» , ( «x1» , «g» ) => new { «x1» , «g» })
...
Példa: A példa
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 }
lefordítva
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 }
amelynek végleges fordítása
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 })
ahol
x
ésy
olyan fordító által generált azonosítók, amelyek egyébként láthatatlanok és elérhetetlenek.példa vége
Egy orderby
záradék és annak előző from
záradéka:
from «x» in «e»
orderby «k1» , «k2» , ... , «kn»
...
lefordítva
from «x» in ( «e» ) .
OrderBy ( «x» => «k1» ) .
ThenBy ( «x» => «k2» ) .
... .
ThenBy ( «x» => «kn» )
...
Ha egy ordering
záradék csökkenő irányjelzőt ad meg, a rendszer ehelyett OrderByDescending
vagy ThenByDescending
hívását hozza létre.
Példa: A példa
from o in orders orderby o.Customer.Name, o.Total descending select o
a végső fordítással rendelkezik
(orders) .OrderBy(o => o.Customer.Name) .ThenByDescending(o => o.Total)
példa vége
Az alábbi fordítások feltételezik, hogy nincsenek let
, where
, join
vagy orderby
záradékok, és nem több, mint az egyes lekérdezési kifejezések egyetlen kezdeti from
záradéka.
12.20.3.6 Kiválasztott záradékok
Az űrlap lekérdezési kifejezése
from «x» in «e» select «v»
lefordítva
( «e» ) . Select ( «x» => «v» )
Kivéve, ha «v»
az «x»
azonosító, a fordítás egyszerűen.
( «e» )
Példa: A példa
from c in customers.Where(c => c.City == "London") select c
szó szerint lefordítva
(customers).Where(c => c.City == "London")
példa vége
12.20.3.7 Csoport záradékai
Egy group
záradék
from «x» in «e» group «v» by «k»
lefordítva
( «e» ) . GroupBy ( «x» => «k» , «x» => «v» )
kivéve, ha «v»
az azonosító «x»
, a fordítás nem szükséges
( «e» ) . GroupBy ( «x» => «k» )
Példa: A példa
from c in customers group c.Name by c.Country
lefordítva
(customers).GroupBy(c => c.Country, c => c.Name)
példa vége
12.20.3.8 Transzparens azonosítók
Egyes fordítások olyan tartományváltozókat injektálnak, amelyek transzparens azonosítók, mint például ,,,, amelyeket a *
jelöl. A transzparens azonosítók csak köztes lépésként léteznek a lekérdezés-kifejezés fordítási folyamatában.
Amikor egy lekérdezésfordítás transzparens azonosítót ad meg, a további fordítási lépések névtelen függvényekbe és névtelen objektuminicializálókba propagálja az transzparens azonosítót. Ezekben a kontextusokban a transzparens azonosítók viselkedése a következő:
- Ha egy transzparens azonosító paraméterként fordul elő egy névtelen függvényben, a társított névtelen típus tagjai automatikusan hatókörbe kerülnek a névtelen függvény törzsében.
- Ha egy transzparens azonosítóval rendelkező tag hatókörben van, az adott tag tagjai is hatókörben vannak.
- Ha egy transzparens azonosító tagdeklarátorként fordul elő egy névtelen objektum inicializálójában, egy transzparens azonosítóval rendelkező tagot vezet be.
A fent leírt fordítási lépésekben a transzparens azonosítók mindig névtelen típusokkal együtt jelennek meg, azzal a szándékkal, hogy több tartományváltozót rögzítsünk egyetlen objektum tagjaiként. A C# implementációja a névtelen típustól eltérő mechanizmussal csoportosíthat több tartományváltozót. Az alábbi fordítási példák feltételezik, hogy névtelen típusokat használnak, és transzparens azonosítók egy lehetséges fordítását jelenítik meg.
Példa: A példa
from c in customers from o in c.Orders orderby o.Total descending select new { c.Name, o.Total }
lefordítva
from * in (customers).SelectMany(c => c.Orders, (c,o) => new { c, o }) orderby o.Total descending select new { c.Name, o.Total }
amelyet tovább fordítunk a
customers .SelectMany(c => c.Orders, (c,o) => new { c, o }) .OrderByDescending(* => o.Total) .Select(\* => new { c.Name, o.Total })
amely transzparens azonosítók törlésekor egyenértékű a
customers .SelectMany(c => c.Orders, (c,o) => new { c, o }) .OrderByDescending(x => x.o.Total) .Select(x => new { x.c.Name, x.o.Total })
ahol
x
egy fordító által létrehozott azonosító, amely egyébként láthatatlan és elérhetetlen.A példa
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 }
lefordítva
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 }
amely tovább csökken a
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 })
amelynek végleges fordítása
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 })
ahol
x
ésy
olyan fordító által generált azonosítók, amelyek egyébként láthatatlanok és elérhetetlenek. példa vége
12.20.4 A lekérdezési-kifejezési minta
A Lekérdezés-kifejezés minta olyan metódusokat hoz létre, amelyeket a típusok implementálhatnak a lekérdezési kifejezések támogatásához.
Egy általános típus C<T>
támogatja a lekérdezési kifejezés mintát, ha a nyilvános tag metódusokat és a nyilvánosan elérhető bővítménymódszereket az alábbi osztálydefinícióval lehet helyettesíteni. Az általános típus C<T>
"alakzatának" nevezzük a tagokat és az elérhető kiterjesztési metódusokat. Általános típust használunk a paraméterek és a visszatérési típusok közötti megfelelő kapcsolatok szemléltetésére, de a nem általános típusok mintáját is implementálhatja.
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; }
}
A fenti metódusok az általános delegálási típusokat Func<T1, R>
és Func<T1, T2, R>
használják, de más delegált- vagy kifejezésfatípusokat is használhattak volna ugyanazokkal a kapcsolatokkal a paraméter- és visszatérési típusok esetében.
Megjegyzés: A
C<T>
és aO<T>
közötti ajánlott kapcsolat, amely biztosítja, hogy aThenBy
ésThenByDescending
metódusok csak egyOrderBy
vagyOrderByDescending
eredménye alapján legyenek elérhetők . végjegyzet
Megjegyzés: A
GroupBy
eredményének ajánlott alakja – egy sorozatsorozat, amelyben minden belső sorozat továbbiKey
tulajdonságú. végjegyzet
Megjegyzés: Mivel a lekérdezési kifejezéseket szintaktikai leképezéssel fordítják le metódushívásokra, a típusok jelentős rugalmasságot biztosítanak a lekérdezési kifejezési minták vagy azok mindegyikének implementálásában. A minta metódusai például példánymetaképpen vagy bővítménymeteként is implementálhatók, mivel a kettő azonos meghívási szintaxist használ, és a metódusok delegáltakat vagy kifejezésfákat kérhetnek, mert a névtelen függvények mindkettőre konvertálhatók. A lekérdezési kifejezésmintát csak néhányat megvalósító típusok csak olyan lekérdezési kifejezésfordításokat támogatnak, amelyek megfeleltethetők a típus által támogatott metódusoknak. végjegyzet
Megjegyzés: A
System.Linq
névtér a lekérdezési kifejezés mintájának implementálását biztosítja minden olyan típushoz, amely megvalósítja aSystem.Collections.Generic.IEnumerable<T>
felületet. végjegyzet
12.21 Hozzárendelési operátorok
12.21.1 Általános
A hozzárendelési operátorok mindegyike új értéket rendel egy változóhoz, tulajdonsághoz, eseményhez vagy indexelő elemhez. A kivétel (= ref
) egy változóhivatkozást (§9.5) rendel egy referenciaváltozóhoz (§9.7).
assignment
: unary_expression assignment_operator expression
;
assignment_operator
: '=' 'ref'? | '+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' | '<<=' | '??='
| right_shift_assignment
;
A hozzárendelés bal operandusa változóként besorolt kifejezés, vagy = ref
kivételével tulajdonsághozzáférés, indexelő hozzáférés, eseményhozzáférés vagy rekord. A deklarációs kifejezés közvetlenül nem engedélyezett bal operandusként, de a dekonstruáló hozzárendelés kiértékelésének lépéseként is előfordulhat.
A =
operátor neve egyszerű hozzárendelési operátor. Hozzárendeli a jobb operandus értékét vagy értékeit a bal operandus által megadott változóhoz, tulajdonsághoz, indexelőelemhez vagy tömbelemekhez. Az egyszerű értékadó operátor bal operandusa nem lehet eseményhozzáférés (kivéve a leírtak szerint a §15.8.2). Az egyszerű hozzárendelési operátor a §12.21.2részben van leírva.
A = ref
operátort referencia hozzárendelési operátornaknevezik. A jobboldali operandus, amelynek egy variable_reference (§9.5) kell lennie, lesz a referenciaváltozó hivatkozási célpontja, amelyet a baloldali operandus jelöl. A ref hozzárendelő operátort a §12.21.3rész foglalja össze.
Az és az =
= ref
operátortól eltérő hozzárendelési operátorokat összetett hozzárendelési operátoroknak nevezzük. Ezeket az operátorokat a következőképpen dolgozzuk fel:
-
??=
operátor esetében csak akkor kerül kiértékelésre a jobb operandus, és csak akkor rendelődik hozzá az eredmény a bal operandus által megadott változóhoz, tulajdonsághoz vagy indexelő elemhez, ha a bal operandus értékenull
. - Ellenkező esetben a jelzett műveletet a két operanduson hajtja végre, majd az eredményként kapott érték a bal operandus által megadott változóhoz, tulajdonsághoz vagy indexelőelemhez lesz rendelve. Az összetett hozzárendelési operátorok leírása a 12.21.4részben található.
A bal operandusként eseményelérési kifejezéssel rendelkező +=
és -=
operátorokat esemény-hozzárendelési operátoroknak nevezzük. Egyetlen más hozzárendelési operátor sem érvényes eseményhozzáféréssel bal operandusként. Az esemény-hozzárendelési operátorok a 12.21.5szakaszban vannak leírva.
A hozzárendelési operátorok jobbról asszociatívak, ami azt jelenti, hogy a műveletek jobbról balra történik a csoportosítás.
példa: Egy
a = b = c
formájú kifejezés úgy lesz kiértékelve, minta = (b = c)
. példa vége
12.21.2 Egyszerű feladat
A =
operátor neve egyszerű hozzárendelési operátor.
Ha egy egyszerű hozzárendelés bal oldali operandusa E.P
vagy E[Ei]
, ahol E
a fordítási idő típusa dynamic
, akkor a hozzárendelés dinamikusan kapcsolódik (§12.3.3). Ebben az esetben a hozzárendelési kifejezés fordítási idejének típusa dynamic
, és az alább ismertetett felbontás futásidőben történik a E
futásidejű típusa alapján. Ha a bal oldali operandus a E[Ei]
formátumú, ahol a Ei
legalább egy elemének fordítási idő alatti típusa dynamic
, és a E
fordítási idő alatti típusa nem tömb, az így kapott indexelő hozzáférés dinamikusan kötött, de korlátozott fordítási idejű ellenőrzéssel (§12.6.5).
Egy egyszerű hozzárendelés, amelyben a bal operandus tupelként van besorolva, dekonstruáló hozzárendelésneknevezik. Ha a bal oldali operandus bármelyik tuple elemének van neve, fordítási idejű hiba lép fel. Ha a bal oldali operandus bármely rekordeleme egy , declaration_expression, és bármely más elem pedig nem , declaration_expression, vagy egyszerű elvetés, fordítási időben hiba lép fel.
Az egyszerű hozzárendelés típusa x = y
a x
y
hozzárendelésének típusa, amely rekurzívan az alábbiak szerint van meghatározva:
- Ha a
x
egy(x1, ..., xn)
, és ay
dekonstruálható egy(y1, ..., yn)
elemekkel rendelkezőn
kifejezésre (§12.7), ésxi
mindenyi
hozzárendeléseTi
típusú, akkor a hozzárendelés(T1, ..., Tn)
típussal rendelkezik. - Ellenkező esetben, ha
x
változóként van besorolva, a változó nemreadonly
,x
T
típussal rendelkezik, ésy
-nek van implicit átalakításaT
-re, akkor a hozzárendelés típusaT
. - Ellenkező esetben, ha
x
implicit módon beírt változóként (azaz implicit módon beírt deklarációs kifejezésként) van besorolva, ésy
T
típussal rendelkezik, akkor a változó kikövetkeztetett típusaT
, és a hozzárendelés típusaT
. - Ellenkező esetben, ha
x
tulajdonságként vagy indexelő-hozzáférésként van besorolva, és a tulajdonságnak vagy az indexelőnek van egy elérhető set hozzáférője,x
T
típusú, ésy
rendelkezik egy implicit átalakítássalT
típusra, akkor a hozzárendelés típusaT
. - Ellenkező esetben a hozzárendelés érvénytelen, és kötési időhiba lép fel.
Az egyszerű hozzárendelésnek a x = y
formában és T
típusban történő futásidejű feldolgozása a x
-hez való y
hozzárendelésként történik T
típussal, amely a következő rekurzív lépésekből áll.
-
x
kiértékelésre kerül, ha még nem történt meg. - Ha
x
változóként van besorolva, a rendszer kiértékeli ay
, és szükség esetén implicit átalakítássalT
konvertálja (§10.2).- Ha az
x
által megadott változó egy reference_typetömbeleme, a rendszer futásidejű ellenőrzést végez annak érdekében, hogy ay
kiszámított értéke kompatibilis legyen azzal a tömbpéldánysal, amelynekx
eleme. Az ellenőrzés akkor sikeres, hay
null
, vagy ha implicit referenciaátalakítás (§10.2.8) létezik ay
által hivatkozott példány típusától ax
tartalmazó tömbpéldány tényleges elemtípusához. Ellenkező esetben egySystem.ArrayTypeMismatchException
lesz dobva. - A
y
kiértékelésével és átalakításával kapott értéket a rendszer ax
kiértékelése által megadott helyre tárolja, és a hozzárendelés eredményeként adja meg.
- Ha az
- Ha
x
tulajdonságként vagy indexelői hozzáférésként van besorolva:-
y
kiértékelése és szükség eseténT
konvertálása implicit átalakítással (§10.2). - A
x
halmaz-tartozéka ay
kiértékelése és átalakítása során kapott értékkel lesz meghívva értékargumentumként. - A
y
kiértékeléséből és átalakításából származó érték a hozzárendelés eredményeként jön létre.
-
- Ha
x
egy(x1, ..., xn)
aritásún
besorolású n-es:-
y
an
elemekkel lebontva egye
kifejezésre. - Az eredménytömb
t
aze
implicit módon történő tuple-átalakítással ésT
-re való átalakításával jön létre. - Minden egyes
xi
esetén balról jobbra haladva történik axi
hozzárendelése at.Itemi
-hez, azzal a kivétellel, hogy axi
nem lesz újra kiértékelve. - A hozzárendelés eredményeként
t
keletkezik.
-
Megjegyzés: ha a
x
fordítási idő típusadynamic
, és implicit átalakítás történik a fordítási idő típusáróly
dynamic
, akkor nincs szükség futásidejű felbontásra. végjegyzet
Megjegyzés: A tömbök szórási szabályai (§17.6) lehetővé teszik, hogy egy tömbtípus értéke
A[]
egy tömbtípusB[]
egy példányára való hivatkozás legyen , feltéve, hogy implicit hivatkozási átalakítás létezikB
-rőlA
. Ezen szabályok miatt a reference_type tömbeleméhez való hozzárendelés futásidejű ellenőrzést igényel annak ellenőrzéséhez, hogy a hozzárendelt érték kompatibilis-e a tömbpéldánysal. A példábanstring[] sa = new string[10]; object[] oa = sa; oa[0] = null; // OK oa[1] = "Hello"; // OK oa[2] = new ArrayList(); // ArrayTypeMismatchException
Az utolsó hozzárendelést következésképpen
System.ArrayTypeMismatchException
dobja fel, amiatt, hogy egyArrayList
-re való hivatkozás nem tárolható egystring[]
elemében.végjegyzet
Ha egy struct_type deklarált tulajdonság vagy indexelő egy hozzárendelés célja, a tulajdonsághoz vagy indexelőhöz való hozzáféréshez társított példánykifejezést változóként kell besorolni. Ha a példánykifejezést értékként sorolják be, akkor egy kötési időbeli hiba következik be.
Megjegyzés: A 12.8.7szakasz miatt ugyanez a szabály érvényes a mezőkre is. végjegyzet
példa: A deklarációk alapján:
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; } } }
a példában
Point p = new Point(); p.X = 100; p.Y = 100; Rectangle r = new Rectangle(); r.A = new Point(10, 10); r.B = p;
a
p.X
,p.Y
,r.A
ésr.B
hozzárendelése engedélyezett, mertp
ésr
változók. A példában azonbanRectangle r = new Rectangle(); r.A.X = 10; r.A.Y = 10; r.B.X = 100; r.B.Y = 100;
a hozzárendelések érvénytelenek, mivel
r.A
ésr.B
nem változók.példa vége
12.21.3 Referencia hozzárendelés
A = ref
operátor az referencia-hozzárendelési operátor.
A bal operandus olyan kifejezés, amely egy referenciaváltozóhoz (§9.7), referenciaparaméterhez (a this
kivételével), kimeneti paraméterhez vagy bemeneti paraméterhez kötődik. A jobb oldali operandus olyan kifejezés legyen, amely egy variable_reference-ként jelöl egy értéket, amely azonos típusú a bal oldali operandussal.
Fordítási idejű hiba, ha a bal operandus ref-safe-context-je (§9.7.2) szélesebb, mint a jobb operandus ref-safe-context-je.
A jobb oldali operandust mindenképpen a ref hozzárendeléséig meg kell határozni.
Ha a bal oldali operandus egy kimeneti paraméterhez kötődik, akkor hiba lép fel, ha a kimeneti paraméter nincs egyértelműen hozzárendelve a ref hozzárendelési operátor elején.
Ha a bal operandus egy írható ref (azaz nem ref readonly
helyi vagy bemeneti paramétert jelöl), akkor a jobb operandusnak írható variable_referencekell lennie. Ha a jobb oldali operandus változó írható, akkor a bal operandus lehet írható vagy írásvédett hivatkozás.
A művelet a bal operandust a jobb operandus változó aliasává teszi. Az alias akkor is írásvédett lehet, ha a megfelelő operandus változó írható.
A ref-hozzárendelési operátor a hozzárendelt típusú variable_reference értéket ad vissza. Írható, ha a bal operandus írható.
A ref-hozzárendelési operátor nem olvassa be a megfelelő operandus által hivatkozott tárolási helyet.
Példa: Íme néhány példa a
= ref
használatára: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 }
példa vége
Megjegyzés: Amikor kódot olvasunk egy
= ref
operátorral, könnyen elcsábulhatunk, hogy aref
részt az operandus részeként olvassuk. Ez különösen zavaró, ha az operandus feltételes?:
kifejezés. Ha példáulref int a = ref b ? ref x : ref y;
olvas, fontos ezt úgy olvasni, hogy= ref
az operátor, ésb ? ref x : ref y
a megfelelő operandus:ref int a = ref (b ? ref x : ref y);
. Fontos, hogy aref b
kifejezés nem része ennek az utasításnak, annak ellenére, hogy első pillantásra ilyennek tűnhet. végjegyzet
12.21.4 Összetett hozzárendelés
Ha egy összetett hozzárendelés bal oldali operandusa E.P
vagy E[Ei]
formában van, ahol E
fordítási időben dynamic
típusú, akkor a hozzárendelés dinamikusan kötött (§12.3.3). Ebben az esetben a hozzárendelési kifejezés fordítási idejének típusa dynamic
, és az alább ismertetett felbontás futásidőben történik a E
futásidejű típusa alapján. Ha a bal oldali operandus a E[Ei]
formátumú, ahol a Ei
legalább egy elemének fordítási idő alatti típusa dynamic
, és a E
fordítási idő alatti típusa nem tömb, az így kapott indexelő hozzáférés dinamikusan kötött, de korlátozott fordítási idejű ellenőrzéssel (§12.6.5).
a ??= b
azzal egyenértékű, hogy (T) (a ?? (a = b))
, kivéve, hogy a
csak egyszer van kiértékelve, ahol T
a a
típusa abban az esetben, ha b
típusa dinamikus, különben T
a a ?? b
típusa.
Ellenkező esetben az űrlap x «op»= y
egy műveletének feldolgozása bináris operátor túlterhelésének feloldásával történik (12.4.5. §), mintha a művelet meg lett volna írva x «op» y
. Akkor
- Ha a kiválasztott operátor visszatérési típusa implicit módon átalakítható a
x
típusára, a műveletx = x «op» y
lesz kiértékelve, azzal a kivételével, hogy ax
csak egyszer lesz kiértékelve. - Ellenkező esetben, ha a kiválasztott operátor előre definiált operátor, ha a kiválasztott operátor visszatérési típusa explicit módon átalakítható a
x
típusára, és hay
implicit módon átalakítható ax
típusára, vagy az operátor egy műszak operátor, akkor a művelet kiértékelésex = (T)(x «op» y)
, aholT
ax
típusa, kivéve, hogy ax
csak egyszer lesz kiértékelve. - Ellenkező esetben az összetett hozzárendelés érvénytelen, és kötési időhiba lép fel.
A "csak egyszer kiértékelt" kifejezés azt jelenti , hogy a x «op» y
kiértékelésekor a x
bármely alkotó kifejezésének eredményét ideiglenesen menti a rendszer, majd újra felhasználja a x
való hozzárendelés során.
példa: A hozzárendelés során
A()[B()] += C()
, ahol aA
egyint[]
visszaadó metódus, ésB
ésC
pedigint
visszaadó metódusok, a metódusok csak egyszer, aA
,B
,C
sorrendben kerülnek hívásra. példa vége
Ha az összetett hozzárendelés bal oldali operandusa tulajdonság-hozzáférés vagy indexelő-hozzáférés, a tulajdonságnak vagy az indexelőnek rendelkeznie kell mind lekérdező metódussal, mind beállító metódussal. Ha nem ez a helyzet, kötési idő hiba lép fel.
A fenti második szabály lehetővé teszi, hogy x «op»= y
bizonyos kontextusokban x = (T)(x «op» y)
ként lehessen értékelni. A szabály úgy létezik, hogy az előre definiált operátorok összetett operátorként használhatók, ha a bal oldali operandus sbyte
, byte
, short
, ushort
vagy char
típusú . Még akkor is, ha mindkét argumentum az egyik ilyen típusú, az előre definiált operátorok int
típusú eredményt adnak, ahogyan azt a §12.4.7.3rész leírja. Így leadás nélkül nem lehet hozzárendelni az eredményt a bal operandushoz.
Az előre definiált operátorokra vonatkozó szabály intuitív hatása egyszerűen az, hogy x «op»= y
engedélyezett, ha mind a x «op» y
, mind a x = y
engedélyezett.
példa: Az alábbi kódban
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
Az egyes hibák intuitív oka az, hogy a megfelelő egyszerű hozzárendelés is hiba lett volna.
példa vége
Megjegyzés: Ez azt is jelenti, hogy az összetett hozzárendelési műveletek támogatják a kiemelt operátorokat. Mivel az összetett hozzárendelési művelet
x «op»= y
kiértékelésex = x «op» y
vagyx = (T)(x «op» y)
, a kiértékelési szabályok implicit módon lefedik a kibővített operátorokat. végjegyzet
12.21.5 Esemény-hozzárendelés
Ha a += or -=
operátor bal operandusa eseményhozzáférésként van besorolva, a kifejezés kiértékelése a következőképpen történik:
- A rendszer kiértékeli az eseményhozzáférés példánykifejezését, ha van ilyen.
- A
+=
vagy-=
operátor jobb operandusa kiértékelésre kerül, és szükség esetén implicit átalakítással (10.2.) konvertálja a bal operandus típusára. - A rendszer meghívja az esemény esemény-tartozékát egy argumentumlistával, amely az előző lépésben kiszámított értékből áll. Ha az operátor
+=
volt, a hozzáadó metódus hívódik meg; ha az operátor-=
volt, az eltávolító metódus hívódik meg.
Az esemény-hozzárendelési kifejezés nem ad értéket. Így az esemény-hozzárendelési kifejezés csak egy statement_expression kontextusában érvényes (§13.7).
12.22 Kifejezés
A kifejezés vagy nem_hozzárendelési_kifejezés vagy hozzárendelés.
expression
: non_assignment_expression
| assignment
;
non_assignment_expression
: declaration_expression
| conditional_expression
| lambda_expression
| query_expression
;
12.23 Állandó kifejezések
Az állandó kifejezés olyan kifejezés, amelyet fordításkor teljesen ki kell értékelni.
constant_expression
: expression
;
Az állandó kifejezés értéke legyen null
vagy az alábbi típusok egyike:
-
sbyte
,byte
,short
,ushort
,int
,uint
,long
,ulong
,char
,float
,double
,decimal
,bool
,string
- enumerálási típus; vagy
- egy referenciatípus alapértelmezett értékkifejezése (§12.8.21).
Állandó kifejezésekben csak a következő szerkezetek engedélyezettek:
- Literálok (beleértve a
null
literális értéket). -
const
osztály- és szerkezettípusokra mutató hivatkozások. - Enumerálási típusok tagjaira mutató hivatkozások.
- Helyi állandókra mutató hivatkozások.
- Zárójeles alkifejezések, amelyek maguk is állandó kifejezések.
- Öntött kifejezések.
-
checked
ésunchecked
kifejezések. -
nameof
kifejezések. - Az előre definiált
+
,-
,!
(logikai tagadás) és~
nem kötelező operátorok. - Az előre definiált
+
,-
,*
,/
,%
,<<
,>>
,&
,|
,^
,&&
,||
,==
,!=
,<
,>
,<=
és>=
bináris operátorok. - A
?:
feltételes operátor. - A
!
null megbocsátó operátor (§12.8.9). -
sizeof
kifejezések, feltéve, hogy a nem felügyelt típus a §23.6.9 szakaszban meghatározott típusok egyike, amelyek esetében asizeof
állandó értéket ad vissza. - Alapértelmezett értékkifejezések, feltéve, hogy a típus a fent felsorolt típusok egyike.
A konstans kifejezésekben a következő átalakítások engedélyezettek:
- Identitáskonvertálások
- Numerikus konverziók
- Enumerációs konverziók
- Állandó kifejezéskonvertálások
- Implicit és explicit referenciakonverziók, feltéve, hogy a konverziók forrása egy állandó kifejezés, amely kiértékeli a
null
értéket.
Megjegyzés: A nem-
null
értékek egyéb konverziói, mint a dobozolás, kicsomagolás és az implicit referenciakonvertálás, nem engedélyezettek állandó kifejezésekben. végjegyzet
példa: Az alábbi kódban
class C { const object i = 5; // error: boxing conversion not permitted const object str = "hello"; // error: implicit reference conversion }
A
i
inicializálása hiba, mert csomagolási átalakításra van szükség. Astr
inicializálása hiba, mert implicit referenciaátalakításra van szükség egy nemnull
értékről.példa vége
Ha egy kifejezés megfelel a fent felsorolt követelményeknek, a kifejezés fordítási időpontban lesz kiértékelve. Ez akkor is igaz, ha a kifejezés egy nagyobb, nem állandó szerkezeteket tartalmazó kifejezés szubexpressziója.
Az állandó kifejezések fordítási idejének kiértékelése ugyanazokat a szabályokat használja, mint a nem állandó kifejezések futásidejű kiértékelése, azzal a kivétellel, hogy ahol a futásidejű kiértékelés kivételt eredményezett volna, a fordítási idő kiértékelése fordítási idő hibát okoz.
Ha egy állandó kifejezés nem kerül explicit módon egy unchecked
kontextusba, a kifejezés fordítási idejének kiértékelése során az integrál típusú aritmetikai műveletekben és átalakításokban előforduló túlcsordulások mindig fordítási időhibákat okoznak (§12.8.20).
Az alábbiakban felsorolt környezetekben állandó kifejezésekre van szükség, és ezt a nyelvhelyesség constant_expressionhasználatával jelzi. Ezekben a kontextusokban fordítási időhiba lép fel, ha egy kifejezés fordításkor nem értékelhető ki teljes mértékben.
- Állandó deklarációk (§15.4)
- Felsorolási tag deklarációk (§19.4)
- Paraméterlisták alapértelmezett argumentumai (§15.6.2)
-
case
switch
utasítás címkéi (§13.8.3). -
goto case
utasítások (§13.10.4) - Egy inicializálót tartalmazó tömblétrehozó kifejezés dimenzióhosszai (§12.8.17.4).
- Attribútumok (§22)
- állandó_minta (§11.2.3)
Az implicit konstanskifejezés-átalakítás (§10.2.11) lehetővé teszi, hogy a int
típusú állandó kifejezés sbyte
, byte
, short
, ushort
, uint
vagy ulong
legyen, feltéve, hogy az állandó kifejezés értéke a céltípus tartományán belül van.
12.24 Logikai kifejezések
A boolean_expression olyan kifejezés, amely bool
típusú eredményt ad; közvetlenül vagy a operator true
alkalmazásával, az alábbiakban meghatározott bizonyos környezetekben:
boolean_expression
: expression
;
Egy if_statement (§13.8.2), while_statement (§13.9.2), do_statement (§13.9.3) vagy for_statement (§13.9.4) feltételét vezérlő kifejezés egy logikai kifejezés. A ?:
operátor vezérlő feltételes kifejezése (12.18. §) ugyanazokat a szabályokat követi, mint a boolean_expression, de az operátorok elsőbbsége miatt null_coalescing_expression.
Egy boolean_expressionE
szükséges ahhoz, hogy bool
típusú értéket állíthasson elő, az alábbiak szerint:
- Ha az E implicit módon konvertálható
bool
, akkor futásidőben az implicit átalakítás lesz alkalmazva. - Ellenkező esetben az unáris operátor túlterhelésének feloldása (§12.4.4) a
operator true
E
egyedi legjobb implementációjának megtalálására szolgál, és azt futásidőben alkalmazzák. - Ha nem található ilyen operátor, kötési idejű hiba lép fel.
ECMA C# draft specification