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.21. §) é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 az és
+=operátorok bal operandusaként-=jelenhet meg (12.23.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észlet tartozékát egy új érték hozzárendelésére (12.23.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 dynamictí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.WriteLinetú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.WriteLinetú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)aFmetódust airégi értékével hívjuk meg, majd aGmetódust airégi értékével hívjuk meg, végül pedig aHmetó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 * zkifejezé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.yx?.yf(x)a[x]a?[x]x++x--x!newtypeofdefaultcheckeduncheckeddelegatestackalloc§12.9 Unáris +-!x~^++x--x(T)xawait x§12.10 Tartomány ..§12.11 Kapcsoló switch { … }§12.12 Többtényezős */%§12.12 Adalékanyag +-§12.13 Műszak <<>>§12.14 Relációs és típustesztelés <><=>=isas§12.14 Egyenlőség ==!=§12.15 Logikai és &§12.15 Logikai XOR ^§12.15 Logikai vagy \|§12.16 Feltételes ÉS &&§12.16 Feltételes vagy \|\|§12.17 és §12.18 Null egyesítés és kivételdobó kifejezés ??throw x§12.20 Feltételes ?:§12.23 és §12.21 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, a tartomány operátor é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 - A tartományoperátor nem asszociatív, ami azt jelenti, hogy sem a tartományoperátor bal, sem jobb operandusa nem lehet range_expression.
Példa: Mindkettő
x..y..zérvénytelenx..(y..z), mivel..nem asszociatív. példa vége
Az elsőbbség és az asszociativitás zárójelekkel szabályozható.
Példa:
x + y * zelőször megszorozzay-atz-gyel, majd hozzáadja az eredménytx-höz, de(x + y) * zelő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ő nem terhelhető operátoroka következők:
+ - !(csak logikai tagadás)~ ++ -- true false
Csak a fent felsorolt operátorok terhelhetők túl. Különösen nem lehet túlterhelni a null-elbocsátó operátort (postfix !, §12.8.9) vagy a háttérbeli indexet (előtag ^, (12.9.6. §)).
Megjegyzés: Bár
truefalsea kifejezésekben nem kifejezetten használják őket (és ezért nem szerepelnek a 12.4.2. §-ban szereplő precedenciatáblában), operátoroknak minősülnek, mivel több kifejezéskörnyezetben is meghívják őket: logikai kifejezések (12.26. §) és feltételes (12.20. §) és feltételes logikai operátorokat (12.16. §). végjegyzet
A túlterhelhető bináris operátoroka 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 .., , , =, &&, ||, ??, ?:=>checkeduncheckednewtypeofdefaultés as operátorokat. is
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. Ezt a 12.23. 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.23.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
isvagyasoperá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őbooleredményt kell visszaadnia. végjegyzet
A 12.9–12.23. §-ban szereplő egyes operátorok leírása határozza meg az operátorok előre meghatározott 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 Xtí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, xXtípusú kifejezés, y pedig Ytí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ésYidentitáskonvertáltak, vagy ha aXés aYegy 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 aYközött identitásátalakítás történik, az«op»Yáltal biztosítottYoperátor visszatérési típusa megegyezik a«op»Xáltal biztosítottXvisszatérési típusával, és az«op»Yoperandus típusai identitásátalakítással bírnak a megfelelő«op»Xoperandus típusokhoz, akkor csak«op»Xvan 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. HaTnull é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 argumentumlistaAtekinteté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.12.§), relációs (12.14. §) és integrál logikai (12.15.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 * sműveletnél, ahol abegybyteés asegyshort, a túlterhelés feloldási eljárása aoperator *(int, int)-t választja ki a legjobb operátornak. Így abés asint-re lesz átalakítva, és az eredmény típusaint. Hasonlóképpen, ai * dművelet esetében , aholiint, ésddouble,overloadfelbontá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, ushortvagy char típusú operandusok inttí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 longtí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
decimaltípusú, a másik operandusdecimaltípussá alakul, vagy kötési idő hibát tapasztal, ha a másik operandusfloatvagydoubletípusú. - Ellenkező esetben, ha bármelyik operandus
doubletípusú, a másik operandusdoubletípussá alakul. - Ellenkező esetben, ha bármelyik operandus
floattípusú, a másik operandusfloattípussá alakul. - Ellenkező esetben, ha az operandus
ulongtípusú, a másik operandusulongtípussá alakul, vagy kötési idő hiba történik, ha a másik operandustype sbyte,short,intvagylong. - Ellenkező esetben, ha bármelyik operandus
longtípusú, a másik operanduslongtípussá alakul. - Ellenkező esetben, ha bármelyik operandus
uinttípusú, a másik operandus pedigsbyte,shortvagyinttípusú, akkor a rendszer mindkét operanduslongtípussá alakul. - Ellenkező esetben, ha bármelyik operandus
uinttípusú, a másik operandusuinttípussá alakul. - Ellenkező esetben mindkét operandus
inttípussá lesz konvertálva.
Megjegyzés: Az első szabály letilt minden olyan műveletet, amely a
decimaltípust adoubleésfloattípussal keveri. A szabály abból a tényből következik, hogy adecimaltípus, valamint adoubleésfloattípus között nincsenek implicit konverziók. végjegyzet
Megjegyzés: Azt is vegye figyelembe, hogy az operandus nem lehet
ulongtípusú, ha a másik operandus aláírt integrál típusú. Ennek az az oka, hogy nincs olyan integráltípus, amely aulongteljes 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
decimalnem 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
A felemelt operátorok lehetővé teszik, hogy az előre definiált és a felhasználó által definiált operátorok, amelyek nem null értékű típuson működnek, az adott típus null értékű formájával is használhatók legyenek. 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 nem ismétlődő operátorok
+,++,-,--!(logikai tagadás)^és~az operátorok felemelt formája akkor létezik, ha az operandus és az eredménytípus egyaránt nem null értékű értéktípus. 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
+esetében-*/%&|^..<<>>, létezik egy operátor felemelt formája, ha az operandus és az eredménytípus mind nem null értékű értéktípus. 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átor akkor állít előnullértéket, ha az egyik vagy mindkét operandus (nullkivételt képez a típus és&az|bool?operátorok, a 12.15.5. §-ban leírtak szerint). 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 abooleredmé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 abooleredmé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 metódus vagy esemény, vagy ha egy meghatalmazott típusú állandó, mező vagy tulajdonság (21. §) vagy típus dynamic (8.2.4. §), akkor a tagot a rendszer visszavonhatatlannak mondja.
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
Nnevű akadálymentes tagok halmazát határozzuk meg:- Ha a
Tegy 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őTnevű elérhető tagok halmazainak uniója, valamint aN-ban lévőobjectnevű 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 aTnevű elérhető tagokat aN-ben. Ha aTegy 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, amelyekoverridemódosítót tartalmaznak, kizárjuk a csoportból.
- Ha a
- Ezután, ha
Knulla, a program eltávolítja az összes olyan beágyazott típust, amelynek deklarációi típusparamétereket tartalmaznak. HaKnem nulla, a rendszer eltávolítja a különböző számú típusparaméterrel rendelkező tagokat. HaKnulla, 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, aholSaz a típus, amelyben a tagMdeklarálva van, a következő szabályok érvényesek:- Ha
Mállandó, mező, tulajdonság, esemény vagy számbavételi tag, akkor aSalaptípusában deklarált összes tag törlődik a készletből. - Ha
Mtípusdeklaráció, akkor aSalaptípusában deklarált összes nem típus el lesz távolítva a készletből, és aMalaptípusában deklaráltSazonos számú típusparaméterrel rendelkező típusdeklarációk törlődnek a készletből. - Ha
Megy metódus, akkor aSalaptí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
Ttí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, aholSa tagMdeklarálásának típusa, a következő szabályokat kell alkalmazni, haSnemobjectosztá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
Megy 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áltMaláí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öbb öröklési felületen történő tagkeresésekből esetlegesen felmerülő kétértelműségeket a 19.4.11.
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
Tobjectvagydynamic, akkorTnem rendelkezik alaptípussal. - Ha
Tegy enum_type, aTalaptípusai aSystem.Enum,System.ValueTypeésobjectosztálytípusok . - Ha
Tegy struct_type, aTalaptípusa aSystem.ValueTypeés aobjectosztálytípus.Megjegyzés: Egy nullable_érték_típus egy struktúra_típus (§8.3.1). végjegyzet
- Ha
Tegy class_type, akkor aTalaptípusai aTalaposztályai , beleértve aobjectosztálytípust is . - Ha
Tinterface_type, akkor aTalaptípusai azTalaptípusai és az osztálytípusobjectalaptípusai. - Ha
Tegy array_type, aTalaptípusai aSystem.Arrayés aobjectosztálytípusok. - Ha
Tegy delegate_type, akkor aTalaptípusai aSystem.Delegateés aobjectosztá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ésvalueváltozóként vagy értékként besorolt kifejezéseket,Tegy típusként besorolt kifejezést,Fegy metódus egyszerű neve,Ppedig 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 Fa 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 FT. 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 Ftípus által megadott osztályban, szerkezetben vagy interfészbenelegjobb 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 PA Ptulajdonsá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ő, haPcsak írható. HaPnemstatic, a példánykifejezésthis.P = valueAz osztály vagy szerkezet Ptulajdonsá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, haPcsak olvasható. HaPnemstatic, a példánykifejezésthis.T.PAz osztály vagy a struktúra PTtulajdonságának megkapó metódusa meghívásra kerül. Fordítási időhiba akkor fordul elő, haPnemstaticvagy haPcsak írható.T.P = valueAz Posztály vagy structTtulajdonság beállító metódusa az argumentumlistával van meghívva(value). Fordítási időhiba akkor fordul elő, haPnemstatic, vagy haPírásvédett.e.PA Ptípusa által megadott osztályban, struktúrában vagy felületen aEtulajdonság lekérési hozzáférőjét aepéldánykifejezéssel hívják meg. Kötési idejű hiba akkor fordul elő, haPmegegyezikstatic-gyel, vagy haPcsak írásra alkalmas.e.P = valueA Ptípus szerinti osztályban, struktúrában vagy interfészben aEtulajdonsá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ő, haPmegegyezikstatic-gyel, vagy haPírásvédett.Eseményhozzáférés E += valueAz esemény hozzáadó hozzáférője, E, meghívásra kerül az azt tartalmazó osztályban vagy szerkezetben. HaEnemstatic, a példánykifejezésthis.E -= valueA(z) Eesemény eltávolító hozzáférőjét az azt tartalmazó osztályban vagy struktúrában meghívják. HaEnemstatic, a példánykifejezésthis.T.E += valueAz esemény Ehozzáadó metódusa a(z)Tosztályban vagy struktúrában kerül meghívásra. Kötési idejű hiba akkor fordul elő, haEnemstatic.T.E -= valueA Eesemény eltávolító tartozéka a(z)Tosztályban vagy struktúrában van meghívva. Kötési idejű hiba akkor fordul elő, haEnemstatic.e.E += valueAz Etípus által megadott osztályban, szerkezetben vagy felületen azEesemény add hozzáférője aepéldánykifejezéssel meg van hívva. Kötési idejű hiba akkor fordul elő, haEstatic.e.E -= valueAz esemény eltávolító metódusa a(z) Eosztályban, szerkezetben vagy interfészben, amelyet a(z)Etípus ad meg, a példánykifejezésehasználatával van meghívva. Kötési idejű hiba akkor fordul elő, haEstatic.Indexelői hozzáférés e[x, y]A túlterhelés feloldása a etí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] = valueA túlterhelés feloldása a etí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 -xA túlterhelés feloldása a xtí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 + yA túlterhelés feloldása a legjobb bináris operátor kiválasztására vonatkozik a xésytí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ó
inutá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ó
refutá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ó
oututá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
valueparamé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 acnevű 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 argumentA
M1(i)metódushívásban aibemeneti 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évtelenintvá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.ArrayTypeMismatchExceptionkivé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
Fmásodik meghívásaSystem.ArrayTypeMismatchException-t dob, mert abté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 = 3pé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ésstringtí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.15. §), és a kifejezéskészletek legjobb közös típusának megkereséséhez (12.6.3.16. §). végjegyzet
12.6.3.2 Az első fázis
Az egyes metódusargumentumok Eᵢesetében a bemeneti típusú következtetés (12.6.3.7. §) a megfelelő paramétertípushoz EᵢTⱼlesz adva .
12.6.3.3 A második fázis
A második fázis a következőképpen halad:
- Minden nem rögzített típusváltozó
Xᵢ, amely nem függ (12.6.3.6.6. §) rögzítettXₑ(12.6.3.13. §). - 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 a megfelelő paramétertípusú
EᵢargumentumokTⱼesetében, ahol a kimeneti típusok (12.6.3.5. §) nemfixált típusváltozókatXₑtartalmaznak, de a bemeneti típusok (12.6.3.4. §) nem, a kimeneti típus következtetése (12.6.3.8. §) a következőbőlEᵢTⱼszármazik. 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 Bemeneti típusú következtetések
A bemeneti típus következtetése egy kifejezésbőlE egy típusbaT a következő módon történik:
- Ha
Eegy rekordkifejezés (12.8.6. §) aritássalNés elemekkelEᵢ, ésTa megfelelő elemtípusokkalNrendelkező aritásúTₑtuple típusú, vagyTnull értékű,T0?ésT0aritásúNrekordtípus, amely megfelelő elemtípussalTₑrendelkezik, akkor mindegyiknélEᵢbemeneti típusú következtetést kell kikövetkeztetnieEᵢTₑ. - Ha
Enévtelen függvény, explicit paramétertípus-következtetést (12.6.3.9. §) aET - Ellenkező esetben, ha
Erendelkezik típussalU, és a megfelelő paraméter egy értékparaméter (15.6.2.2.2.), akkor a függvény egy alsó határú következtetést (12.6.3.11. §) hoz létreUT. - Ellenkező esetben, ha
Erendelkezik típussalU, és a megfelelő paraméter egy referenciaparaméter (15.6.2.3.3.3.3. §), vagy kimeneti paraméter (15.6.2.3.4. §), akkor pontos következtetést (12.6.3.10. §UT. - Ellenkező esetben, ha
Evan egy típusU, és a megfelelő paraméter egy bemeneti paraméter (15.6.2.3.2.2. §), ésEbemeneti argumentum, akkor a rendszer pontos következtetést (12.6.3.10.)hoz létreUT. - Ellenkező esetben, ha
Erendelkezik típussalU, és a megfelelő paraméter egy bemeneti paraméter (15.6.2.3.2.2.), akkor a rendszer egy alsó határú következtetést (12.6.3.11. §UT. - Ellenkező esetben nincs következtetés az argumentumra vonatkozóan.
12.6.3.8 Kimenettípus-következtetések
A kimeneti típus következtetése egy kifejezésbőlE egy típusbaT a következő módon történik:
- Ha
EaritástNés elemeketEᵢtartalmazó rekord típusú kifejezés, ésTa megfelelő elemtípusokkalNrendelkező aritásúTₑtuple típusú, vagyTnull értékű típusT0?, ésT0aritásúNrekordtípus, amelynek megfelelő elemtípusaTₑvan, akkor minden kimenetiEᵢtípus esetében a kimenet típusa a következőre lesz következtetveEᵢTₑ. - Ha
Eegy névtelen függvény, amely kikövetkeztetett visszatérési típussal rendelkezikU(12.6.3.14.§), ésTdelegált típusú vagy kifejezésfa típusúTₓ, akkor a függvény egy alsó határú következtetést (12.6.3.11. §) adUTₓvissza. - Ellenkező esetben, ha a
Emetóduscsoport, és aTdelegált típusú vagy kifejezésfa típusú,T₁...Tᵥparamétertípusokkal ésTₓvisszatérési típussal rendelkezik, és ha aEtípusúT₁...Tᵥtúlterhelés feloldásával egyetlen,Uvisszaté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.9 Explicit paramétertípus-következtetések
A
- Ha
Eegy explicit módon beírt névtelen függvény paramétertípusokkalU₁...Uᵥ, ésTdelegált típusú vagy kifejezésfa típusú paramétertípusV₁...Vᵥ, akkor minden egyesUᵢpontos következtetés (12.6.3.10. §) a megfelelőbőla megfelelőbeUᵢkerülVᵢ.
12.6.3.10 Pontos következtetések
A pontos következtetésUtípusbólV típusba az alábbiak szerint történik:
- Ha
Vaz egyik nem rögzítettXᵢ, akkorUhozzá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:-
VtömbtípusúV₁[...]ésUegy azonos rangú tömbtípusU₁[...] -
VaV₁?típus,Upedig aU₁ -
Vegy konstruált típusC<V₁...Vₑ>, ésUis 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.11 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
Vaz egyik nem rögzítettXᵢ, akkorUhozzáadódik aXᵢalsó határainak halmazához. - Ellenkező esetben, ha
Va típusV₁?, ésUa 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:-
Vegy tömbtípusV₁[...], ésUegy azonos rangú tömbtípusU₁[...] -
VIEnumerable<V₁>,ICollection<V₁>,IReadOnlyList<V₁>>,IReadOnlyCollection<V₁>vagyIList<V₁>egyike, ésUegydimenziós tömbtípusU₁[] -
Vegyclass,struct,interfacevagydelegatetípusúC<V₁...Vₑ>, és létezik egy olyan egyedi típusC<U₁...Uₑ>, amelyre igaz, hogyU(vagy haUparametertípusú, akkor a tényleges alaposztálya, vagy a tényleges interfészkészlet bármely tagja) megegyezik vele,inheritsbelő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₁lehetXvagyY.)
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
Uegy tömbtípus, akkor egy alsó határon alapuló következtetés történik. - Ellenkező esetben, ha
VC<V₁...Vₑ>akkor a következtetés ai-thCtí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.12 Felső határú következtetés
Egy típusból a típus UtípusúV felső korlátú következtetése az alábbiak szerint történik:
- Ha
Vaz egyik nem rögzítettXᵢ, akkorUlesz 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:-
Uegy tömbtípusU₁[...], ésVegy azonos rangú tömbtípusV₁[...] -
UIEnumerable<Uₑ>,ICollection<Uₑ>,IReadOnlyList<Uₑ>,IReadOnlyCollection<Uₑ>vagyIList<Uₑ>egyike, ésVegydimenziós tömbtípusVₑ[] -
UaU1?típus,Vpedig aV1? -
Uolyan osztály, struktúra, interfész vagy delegált típus, amelyC<U₁...Uₑ>, ésVolyanclass, struct, interfacevagydelegatetí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
Vtömbtípus, akkor felső határú következtetési jön létre - Ellenkező esetben, ha
UC<U₁...Uₑ>akkor a következtetés ai-thCtí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.13 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 aUtípussal. A jelöltkészletből eltávolításra kerül aUminden 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.14 A késleltetett visszatérés típusa
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
Ftörzse egy kifejezés, ami típusú, akkor aFtényleges visszatérési típusa az adott kifejezés típusa. - Ha a törzs
Fegy blokk , és a blokkreturnutasításaiban szereplő kifejezések halmaza a leggyakoribb típusT(12.6.3.16. §), akkor a kikövetkzett tényleges visszatérésiFtípus azT. - 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
Faszinkron, és a törzseFvagy egy semmiként besorolt kifejezés (12.2. §), vagy olyan blokk, amelyben nincsenekreturnkifejezések, a következtetett visszatérési típus«TaskType»(15.14.1. §). - Ha
Faszinkron, és tényleges eredménytípusaTvan, a kikövetkeztetett visszatérési típus«TaskType»<T>»(15.14.1.1. §). - Ha
Fnem aszinkron, ésTkikö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
Fvisszaté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
Selectosztályban deklaráltSystem.Linq.Enumerablebő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.Linqnévtér egyusing namespaceirányelvvel lett importálva, és aCustomerosztály rendelkezik egyNametípusústringtulajdonsággal, aSelectmó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
TSourceCustomerlesz. Ezután a fent ismertetett névtelen függvénytípus-következtetési folyamat során acaCustomertípust kapja, és ac.Namekifejezés a választóparaméter visszatérési típusához kapcsolódvaTResultstringjelö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
Xleszstring. Ezután az első névtelen függvény paramétere (s) megkapja astringtípusú besorolást, és aTimeSpan.Parse(s)kifejezés af1visszatérési típusához kapcsolódik, ami alapján aYértéketSystem.TimeSpantípusúnak következtetjük. Végül a második névtelen függvénytparaméterének megadott típusa azSystem.TimeSpan, és at.TotalHourskifejezés kapcsolódik af2visszatérési típusához, így aZtípusátdouble-ként származtatjuk. Így a meghívás eredményedoubletípusú.példa vége
12.6.3.15 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ᵥ>
kompatibilissé válik (21.2.§) a .D
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.16 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ó
Xkerül bevezetésre. - Minden kifejezéshez
Eikimeneti típusú következtetést (§12.6.3.8) hajtunk végre.X -
Xrögzített (12.6.3.13.§), ha lehetséges, és az eredményként kapott típus a leggyakoribb 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 aXkö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
Aminden 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
Aminden 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
inmó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
inmó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
Amegegyezik a paraméterek teljes számával. HaAkevesebb 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
Aminden 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
thishozzá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ébenRxnem kevésbé specifikus, mintSx, és legalább egy paraméter esetébenRxpontosabb, 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.
-
Epontosan megegyezikT₁-gyel, ésEnem pontosan egyezik megT₂-mal (§12.6.4.6) -
Epontosan 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) -
Eegy metóduscsoport (12.2.§),T₁kompatibilis (21.4. §) az átalakításhozC₁használt metóduscsoport egyetlen legjobb módszerével, ésT₂nem kompatibilis az átalakításhoz használt metóduscsoport egyetlen legjobb módszerévelC₂
12.6.4.6 Pontosan egyező kifejezés
Ha egy kifejezés E és egy Ttípust tartalmaz, Epontosan egyezikT, ha az alábbiak egyikét tartalmazza:
-
EStípussal rendelkezik, és azonosítási átalakítás létezikS-rólT-ra. -
Eegy névtelen függvény,Tvagy 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ának kontextusában egy kikövetkezésre váró visszatérésiEtípusDlétezik, az identitásátalakítás pedig a visszatérési típusraXD -
Eegyasynclambda, amelynek nincs visszatérési értéke, ésDolyan visszatérési típussal rendelkezik, amely nem általános«TaskType» - Vagy
Enem aszinkron, ésDrendelkezikYvisszatérési típussal, vagyEaszinkron, ésDrendelkezik«TaskType»<Y>visszatérési típussal (15.14.1. §), és az alábbiak egyike igaz:- A(z)
Etörzse az a kifejezés, amely pontosan megegyezikY-jel. - A
Etö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,uintvagyulong -
S₁shortésS₂ushort,uintvagyulong -
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.
- Indexelői hozzáférés esetén (12.8.12.4. §) a fogadóban elérhető indexelők készlete fordításkor 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
Fegy á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
Fmó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
Ftípusparamétereit helyettesítették, a korlátozások teljesülnek. - Ha a
Fstatikus 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
Fegy 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
Mstatikus függvénytag:- Az argumentumlista kiértékelése a 12.6.2 részben leírtak szerint történik.
-
Mhívódik meg.
- Ellenkező esetben, ha a típus
Eérték típusúV, ésMdeklarálva van vagy felül van bírálva a következőbenV:-
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 esetbenEváltozóként van besorolva. - Ha
Enem változóként van besorolva, vagy haVnem olvasható strukturált típus (16.2.2. §), ésMnem olvasható függvény tag (16.4.12. §), ésEa következők egyike:- bemeneti paraméter (§15.6.2.3.2), vagy
- egy
readonlymező (§15.5.3), vagy - egy
readonlyreferenciaváltozót vagy visszatérést (9.7.§), majd létrehoz egy ideiglenes helyi változótE,'s típusú, és az érték hozzáEvan rendelve ehhez a változóhoz.Eezutá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, amelyeketEvégez aM-en, hathisírható.
- Az argumentumlista kiértékelése a 12.6.2 részben leírtak szerint történik.
-
Mhí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
Etípusa egy value_type, egy boxing átalakítást (§10.2.9) hajtunk végreEclass_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.NullReferenceExceptiondob, é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
Ekö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ítottEmegvalósítása. Ezt a függvénytagot az illesztőleképezési szabályok (19.6.5.§) alkalmazásával határozzuk meg a hivatkozottMpéldány futásidejű típusa által biztosított implementációEmeghatározásához. - Ellenkező esetben, ha
Megy virtuális függvény tagja, a meghívandó függvénytag aMáltal hivatkozott példány futásidejű típusa által biztosítottEmegvaló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
Mnem 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.
-
Megjegyzés:A 12.2.§ a tulajdonsághozzáférést úgy osztályozza, mint a megfelelő függvénytagot, akár a tartozékot, akár a
gettartozékotset. A fenti folyamat követi a tartozék meghívását. végjegyzet
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.ValueTypevagySystem.Enumegyike 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
Eegynelemet tartalmazó tömbkifejezés, akkor a dekonstruálás eredménye maga aEkifejezés. - Ellenkező esetben, ha a
Eegy(T1, ..., Tn)elemű,ntípusú tétel, akkor aEegy ideiglenes változóként__vlesz 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
Enem bontható fel.
Itt __v és __v1, ..., __vn az egyébként láthatatlan és elérhetetlen ideiglenes változókra hivatkoznak.
Megjegyzés: A
dynamictí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 (24.6.3. §) és pointer_element_access (24.6.4. §) csak nem biztonságos kódban (24. §) érhető el.
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 stringtí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.IFormattabledokumentá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.FormattableStringtípusú értéket ad vissza; ellenkező esetben a típusnak string kell lennie, és a módszer string.Format (§C.2), amely stringtí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 ≥ 1minden számraI0ésN-1között:- Helykitöltő specifikáció
- Bal oldali zárójel (
{) karakter - A
Idecimá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
Xformá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
enulla, é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ótIné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
enulla, és aTdeklarációja tartalmaz egyInevű típusparamétert, akkor a simple_name erre a típusparaméterre hivatkozik. - Ellenkező esetben, ha a taglekérdezés (
I) aT-benetípusú argumentumokkal egyezést eredményez:- Ha
Taz 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 athistá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ő, haenulla. - Ellenkező esetben az eredmény megegyezik az űrlap vagy
T.Itaghozzá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
enulla, ésIegy névtér neveN-ben, akkor következik:- Ha a simple_name helye egy
Nné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 azInevet 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
INnevű névtérre hivatkozik.
- Ha a simple_name helye egy
- Ellenkező esetben, ha
Nolyan akadálymentes típust tartalmaz, amelynek neveIésetípusparaméterek, akkor:- Ha
enulla, és a simple_name előfordulása egyNné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 nevetIegy 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
Nnévtér deklarációja határozza meg:- Ha
enulla, és a névtér-deklaráció tartalmaz egy extern_alias_directive vagy using_alias_directive, amely a nevetIegy 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ésetí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ésetí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
enulla, ésIaz azonosító_, a simple_nameegy egyszerű elvetés, amely a deklarációs kifejezés egyik formája (12.19. §). - 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. Az deconstruction_expression belül beágyazott azonosítók így egy deklarációs kifejezést vezetnek be (12.19. §). Ennek eredményeképpen egy deconstruction_expression csak egy egyszerű feladat bal oldalán fordulhat elő.
Példa: A következő kód három változót deklarál: a, b és c. Ezek mindegyike egy egész szám, és a hozzárendelés jobb oldalán lévő tuple-ból rendeli hozzá az értékét.
var (a, b, c) = (1, 2, 3); // a is 1, b is 2, and c is 3. var sum = a + b + c; // sum is 6.A hozzárendelés bármely egyes eleme lehet egy dekonstruálási kifejezés. Az alábbi destrukciós kifejezés például hat változót rendel hozzá,
a-tólf-ig.var (a, b, (c, d, (e, f))) = (1, 2, (3, 4, (5, 6)));Ebben a példában figyelje meg, hogy a beágyazott csuplok szerkezetének meg kell egyeznie a hozzárendelés mindkét oldalán.
Ha a bal oldalon lévő változó(k) implicit módon vannak begépelve, a megfelelő kifejezésnek a következő típussal kell rendelkeznie:
(int a, string? b) = (42, null); //OK var (c, d) = (42, null); // Invalid as type of d cannot be inferred (int e, var f) = (42, null); // Invalid as type of f cannot be inferredpélda vége
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 Nilegyen. - Ellenkező esetben, ha
EiNi,E.NivagyE?.Niformá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.NivagyE?.Ni, vagy -
NiaItemXformátumú, ahol aXegy nem0által kezdett tizedesjegy-sorozat, amely egy tuple elem pozícióját jelölheti, ésXnem 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
Tikell 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 a tuple kifejezésből úgy szerezhető be, hogy tuple típusúvá alakítja (10.2.13. §), átsorolhatja értékként (12.2.2.2. §),vagy dekonstruálási hozzárendelés céljává teheti (12.23.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 typeEbben 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. At2eseté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. At4deklará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 taghozzáférés típustulajdonságként dynamicvan besorolva. 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
enulla, ésEegy névtér, ésEegy beágyazott névteret tartalmaz, amelynek neveI, akkor az eredmény a névtér lesz. - Ellenkező esetben, ha
Enévtér, ésEtartalmaz egy elérhető típust, amely névIésKtípusparamétereket tartalmaz, akkor az eredmény az adott típusargumentumokkal létrehozott típus. - Ha
Etípusként van besorolva, haEnem típusparaméter, és ha aIEtípusparaméterekkel rendelkező tagkeresése (K) egyezést eredményez, akkor aE.Ikié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
Knulla, 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
Iazonosít egy típust, akkor az eredmény az adott típusargumentumokkal létrehozott típus. - Ha
Iazonosít egy vagy több metódust, akkor az eredmény egy metóduscsoport, amelyhez nincs társítva példánykifejezés. - Ha
Iazonosí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
Istatikus 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ő
IE.
- 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
Iazonosí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.Ifeldolgozása pontosan úgy történik, minthaIstatikus 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
Iazonosít egy állandót, akkor az eredmény egy érték, nevezetesen az állandó értéke. - Ha
Ienumerá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
Etulajdonsá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 aIT-benKtípusargumentumokkal egyezést talál, akkor aE.Ia következőképpen kerül kiértékelésre és besorolásra:- Először is, ha a
Eegy 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
Iegy vagy több metódust azonosít, akkor az eredmény egy metóduscsoport, amely aEtársított példánykifejezésével rendelkezik. - Ha
Iazonosít egy példánytulajdonságot, akkor az eredmény egy tulajdonsághozzáférés aEtársított példánykifejezésével és egy társított típussal, amely a tulajdonság típusa. Ha aTosztá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 aTelemmel kezdve az alaposztályokon keresztül keresnek. - Ha
Tegy osztálytípus, ésIaz 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 objektumbanEmező.
- Ha a
- Ha
Tegy struct_type, ésIaz 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ánybanEmező értéke. - Ellenkező esetben az eredmény egy változó, nevezetesen a
Iáltal megadott szerkezetpéldánybanEmező.
- Ha
- Ha
Iazonosí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.Ifeldolgozása pontosan úgy történik, minthaIpéldánymező lett volna. - Ellenkező esetben az eredmény egy eseményhozzáférés, amely
Etá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.Iformátumú taghozzáférés esetén, ha E egyetlen azonosítóként szerepel, és ha a Esimple_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
Aosztályon belül azonColorazonosító előfordulásait, amelyek aColortípusra hivatkoznak,«...»határolja el, és azok nem, amelyek aColormező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 , amelyet a két "?" és "." jogkivonat követ, majd egy opcionálistype_argument_list azonosító, amelyet nulla vagy több dependent_accesskövet, amelyek közül bármelyiket megelőzheti egy null_forgiving_operator.
null_conditional_member_access
: primary_expression '?' '.' identifier type_argument_list?
(null_forgiving_operator? dependent_access)*
;
dependent_access
: '.' identifier type_argument_list? // member access
| '[' argument_list ']' // element access
| '(' argument_list? ')' // invocation
;
null_conditional_projection_initializer
: primary_expression '?' '.' identifier type_argument_list?
;
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
Ptípusa null értékű:Legyen
TaP.Value.Atípusa.Ha
Tolyan 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
Tnem null értékű érték, akkor aEtípusaT?, és aEjelentése megegyezik a következő jelentéssel:((object)P == null) ? (T?)null : P.Value.AKivéve, hogy a
Pcsak egyszer lesz kiértékelve.Ellenkező esetben a
EtípusaT, és aEjelentése megegyezik a következők jelentésével:((object)P == null) ? (T)null : P.Value.AKivéve, hogy a
Pcsak egyszer lesz kiértékelve.
Egyébként:
Legyen
TaP.Akifejezés típusa.Ha
Tolyan 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
Tnem null értékű érték, akkor aEtípusaT?, és aEjelentése megegyezik a következő jelentéssel:((object)P == null) ? (T?)null : P.AKivéve, hogy a
Pcsak egyszer lesz kiértékelve.Ellenkező esetben a
EtípusaT, és aEjelentése megegyezik a következők jelentésével:((object)P == null) ? (T)null : P.AKivéve, hogy a
Pcsak egyszer lesz kiértékelve.
Megjegyzés: Egy kifejezés alakjában:
P?.A₀?.A₁Ha
Pnull-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 lehet túlterhelve (15.10. §), a null-elbocsá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
IsValidtruead vissza, apbiztonságosan meg lehet hivatkozni aNametulajdonsá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őbenxnull, akkor kivételt kapunk, mivelnullnem 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
Assignmetódus paramétereinek típusai ,lv&rv,string?,lvkimeneti paraméter, és egyszerű hozzárendelést hajt végre.A
Mmetódus astípusústringváltozót, mintAssignkimeneti paramétert adja át, a fordító figyelmeztetést ad, mivelsnem nullázható változó. MivelAssignmá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 invocation_expression típusértékként dynamicvan besorolva. 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 egy lambda_expression törzsében engedélyezett (12.21. §). 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ó
Tosztá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ó
Tosztá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
Fmetóduscsoporthoz társítottMmetódusok esetében:- Ha
Fnem általános, akkorFa következő esetekben jelölt:-
Mnem tartalmaz típusargumentumlistát, és -
FaA(§12.6.4.2) vonatkozásában alkalmazható.
-
- Ha
Fáltalános, ésMnem tartalmaz típusargumentumlistát,Fa 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
Fparaméterlistájában szereplő összes típus megfelel a korlátaiknak (§8.4.5), és aFparaméterlistája alkalmazható aAvonatkozásában (§12.6.4.2).
- Ha a
Fáltalános, ésMtartalmaz egy típusargumentumlistát,Fakkor jelölt, ha:- A
Fugyanannyi 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
Fparaméterlistájában szereplő összes létrehozott típus megfelel a feltételeiknek (§8.4.5), és aFparaméterlistája helyénvaló aAtekinteté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.Fmetódusok esetében, aholCaz a típus, amelyben a metódusFdeklarálva van, aCalaptípusában deklarált összes metódus törlődik a készletből. Továbbá, haCnemobjectosztá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_nameCmegkeresé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.
-
Caz 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
Bmetódusa elsőbbséget élvez az első bővítménymetódusnál,Cmetó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.GelsőbbségetC.Gélvez, ésE.Felsőbbséget élvez mindD.Fa kettő ésC.Fa .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
Akié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.NullReferenceExceptiondob, és nem hajt végre további lépéseket. - Ellenkező esetben a
Degy 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 a 21.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_expressionegy statement_expression (13.7.§), anonymous_function_body (12.21.1. §) vagy method_body (15.6.1. §) összefüggésé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 EP?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. Jelöljük PA meg a következő összefűződését P : és A.
Amikor a Estatement_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 Eanonymous_function_body vagy method_body szerepel mint ilyen, akkor a E jelentése a besorolásától függ.
Ha
Esemmiként van besorolva, akkor a jelentése megegyezik a blokkjelentésével:{ if ((object)P != null) PA; }kivéve, hogy a
Pcsak egyszer lesz kiértékelve.Ellenkező esetben a
Ejelentése megegyezik a blokkjelentésével:{ return E; }és a blokk jelentése attól függ, hogy a
Eszintaktikailag 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 ']'
;
Primary_expression felismerésekor , ha a element_access és a pointer_element_access (24.6.4. §) is alkalmazható, akkor az utóbbit kell választani, ha a beágyazott primary_expression mutatótípusú (24.3. §).
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.
Ebben az esetben a element_access fordítási idejének típusa a primary_expression fordítási idejének típusától függ: ha tömbtípussal rendelkezik, akkor a fordítási idő típusa az adott tömbtípus elemtípusa; ellenkező esetben a fordítási idő típusa és dynamic a element_access típusértékként dynamicvan besorolva. 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.
Példa:
var index = (dynamic)1; // index has compile-time type dynamic int[] a = {0, 1, 2}; var a_elem = a[index]; // dynamically bound, a_elem has compile-time type int string s = "012"; var s_elem = s[index]; // dynamcially bound, s_elem has compile-time type dynamicpélda vége
Ha egy element_access primary_expression a következő:
- egy tömbtípus értéke, a element_access tömbhozzáférés (12.8.12.2. §);
- típusérték
string, a element_access karakterlánc-hozzáférés (12.8.12.3. §); - 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.4. §).
12.8.12.2 Tömb hozzáférés
Tömbhozzáférés esetén a argument_list nem tartalmazhat nevesített argumentumokat vagy hivatkozási argumentumokat (15.6.2.3. §).
A argument_list kifejezéseinek száma megegyezik a array_type rangjának számával, és minden kifejezésnek a következőnek kell lennie:
- típusú
int,uint,longvagy ; vagyulong - csak egysoros tömbhozzáférés esetén, típus
IndexvagyRange; vagy - implicit módon konvertálható legyen a fenti típusok közül egy vagy többre.
Az űrlap P[A]tömbhozzáférésének futásidejű feldolgozása , amely P egy array_type primary_expression, és A indexkifejezések 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 minden indexkifejezése sorrendben, balról jobbra:
- Az indexkifejezés kiértékelése után az eredményül kapott érték típusa legyen T;
- Ez az érték a következő típusok közül az elsőre lesz konvertálva:
int,uint,long,ulong, vagy csak egysoros tömbhozzáférés esetén,IndexvagyRange; amelynek implicit konvertálása (10.2. §) a T-ből létezik. - 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.NullReferenceExceptiondob, és nem hajt végre további lépéseket. - Ha az előző lépések egyetlen típusú
Rangeindexértéket hoztak létre, akkor:-
Legyen l a hivatkozott
Ptömb hossza. -
Aaz L vonatkozásában érvényesnek kell lennie (18.3. §). Ha nem, akkorSystem.ArgumentOutOfRangeExceptionegy dobás történik, és a rendszer nem hajt végre további lépéseket. - A kezdő eltolás, az S és az elemek száma( N) az
Aviszonyítva az alábbiakGetOffsetAndLengthszerint van meghatározva (18.3. §). - A tömbhozzáférés eredménye egy olyan tömb, amely az S indextől kezdődő
Pelemek sekély másolatát tartalmazza. Ha N nulla, a tömb nulla elemekkel rendelkezik.
-
Legyen l a hivatkozott
Jegyzet:S ésN is lehet nulla (24,3 USD). Az üres tömbök indexelése általában érvénytelen, de az üres tartomány nullától kezdődő indexelése érvényes, és üres tömböt ad vissza. A definíció azt is lehetővé teszi, hogy az S legyen L, a múltbeli index (18.1. §), amely esetben az N nulla lesz, és egy üres tömb lesz visszaadva. végjegyzet
Jegyzet: Tömb elemeinek tartománya nem rendelhető hozzá tömbhozzáférés használatával. Ez eltér az indexelő hozzáférésektől (12.8.12.4.§), amelyek azonban támogathatják az érték által meghatározott indextartományhoz való hozzárendelést
Range. végjegyzet
- Egyébként:
- A tömbhozzáférés kiértékelésének eredménye a tömb elemtípusának változóhivatkozása (9.5. §).
- 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.IndexOutOfRangeExceptiondob, és nem hajt végre további lépéseket. - Az indexkifejezések által megadott tömbelem változóhivatkozása ki lesz számítva, és ez lesz a tömbhozzáférés eredménye.
12.8.12.3 Sztringhozzáférés
Sztringhozzáférés esetén a element_access argument_list egyetlen névtelen értékargumentumot (15.6.2.2. §) kell tartalmaznia, amely a következő:
- típus
intIndex, vagyRange; vagy - implicit módon átalakítható a fenti típusok közül egy vagy többre.
Az űrlap P[A]sztringhozzáférésének futásidejű feldolgozása , amely P egy primary_expressionstring típusú és A egyetlen kifejezés, 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. - Az indexkifejezés kiértékelése után az eredményül kapott érték típusa legyen T;
- Ez az érték a következő típusok közül az elsőre lesz konvertálva:
intvagyIndexRange; amelynél a T-ből implicit átalakítás (10.2. §) létezik. - 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.NullReferenceExceptiondob, és nem hajt végre további lépéseket. - Ha az előző lépések egy típusú
Rangeindexértéket hoztak létre, akkor:- A sztringhozzáférés kiértékelésének eredménye típusérték
string. -
Legyen a hivatkozott sztring
Phossza. -
Aaz L vonatkozásában érvényesnek kell lennie (18.3. §), ha nem, akkor a rendszer elvetiSystem.ArgumentOutOfRangeException, és nem hajtja végre a további lépéseket. - A kezdő eltolás, az S és az elemek száma( N) az
Aviszonyítva az alábbiakGetOffsetAndLengthszerint van meghatározva (18.3. §). - A sztringhozzáférés eredménye egy sztring, amely az S-ből kiinduló
Pkarakterek másolásával jön létre, ha N nulla, akkor a sztring üres.
- A sztringhozzáférés kiértékelésének eredménye típusérték
Jegyzet:S és N is lehet nulla (18.3. §). Az üres sztring indexelése általában érvénytelen, de az üres tartomány nullától kezdődő indexelése érvényes, és üres sztringet ad vissza. A defintion azt is lehetővé teszi, hogy az S legyen L, a múltbeli index (18.1. §), amely esetben az N nulla lesz, és egy üres sztring lesz visszaadva. végjegyzet
- Egyébként:
- A sztringhozzáférés kiértékelésének eredménye típusérték
char. - A konvertált indexkifejezés értékét a rendszer a hivatkozott sztringpéldány tényleges határaival összeveti
P. Ha az érték túllépi a tartományt, a rendszer egySystem.IndexOutOfRangeExceptionértéket ad ki, és nem hajt végre további lépéseket. - Az átalakított indexkifejezés és a sztring
Peltolásánál lévő karakter értéke a sztringhozzáférés eredménye lesz.
- A sztringhozzáférés kiértékelésének eredménye típusérték
12.8.12.4 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. A argument_list nem tartalmazhatnak out vagy ref argumentumokat.
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 aTalaptí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, aholSaz indexelőIdeklarált típusa:- Ha
Inem alkalmazható aA-hez (§12.6.4.2), akkorIkikerül a halmazból. - Ha
IaA(§12.6.4.2) vonatkozásában alkalmazható, akkor minden aSegy alaptípusában deklarált indexelő törlődik a készletből. - Ha
IAvonatkozásában alkalmazható (§12.6.4.2), és haSnemobjectosztá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 legjobb indexelő tartozékait a rendszer ellenőrzi:
- Ha az indexelő hozzáférése egy hozzárendelés célja, akkor az indexelőnek rendelkeznie kell egy készlettel vagy egy ref-hez tartozó kiegészítővel, ellenkező esetben kötési idejű hiba történik;
- Ellenkező esetben az indexelőnek rendelkeznie kell egy lekéréses vagy ref lekéréses tartozékával, ellenkező esetben kötési idejű hiba történik.
Az indexelő hozzáférés futtatókörnyezeti feldolgozása a következő lépésekből áll:
- A cél primary_expression
Pkiértékelése történik. - A argument_list
Aindexkifejezéseinek kiértékelése sorrendben történik, balról jobbra. - A kötési időpontban meghatározott legjobb indexelő használata:
- Ha az indexelő hozzáférés egy hozzárendelés célja, a rendszer meghívja a beállított tartozékot vagy a ref get kiegészítőt egy új érték hozzárendeléséhez (12.23.2. §).
- Minden más esetben a get tartozék vagy a ref get tartozék meghívása történik az aktuális érték lekéréséhez (12.2.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 EP?[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
Ptípusa null értékű:Legyen
TaP.Value[A]Bkifejezés típusa.Ha
Tolyan 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
Tnem null értékű érték, akkor aEtípusaT?, és aEjelentése megegyezik a következő jelentéssel:((object)P == null) ? (T?)null : P.Value[A]BKivéve, hogy a
Pcsak egyszer lesz kiértékelve.Ellenkező esetben a
EtípusaT, és aEjelentése megegyezik a következők jelentésével:((object)P == null) ? null : P.Value[A]BKivéve, hogy a
Pcsak egyszer lesz kiértékelve.
Egyébként:
Legyen
TaP[A]Bkifejezés típusa.Ha
Tolyan 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
Tnem null értékű érték, akkor aEtípusaT?, és aEjelentése megegyezik a következő jelentéssel:((object)P == null) ? (T?)null : P[A]BKivéve, hogy a
Pcsak egyszer lesz kiértékelve.Ellenkező esetben a
EtípusaT, és aEjelentése megegyezik a következők jelentésével:((object)P == null) ? null : P[A]BKivéve, hogy a
Pcsak egyszer lesz kiértékelve.
Megjegyzés: Egy kifejezés alakjában:
P?[A₀]?[A₁]Ha
Pkié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 thiskulcsszó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
thisegy 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
thisegy 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
thisvá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
thisváltozó pontosan ugyanúgy viselkedik, mint a struktúratípusrefparamé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
thisváltozó azt a szerkezetet jelöli, amelyre a metódust vagy tartozékot meghívták.- Ha a szerkezet egy
readonly struct, athisváltozó pontosan ugyanúgy viselkedik, mint a struktúratípus bemeneti paramétere. - Ellenkező esetben a
thisváltozó pontosan ugyanúgy viselkedik, mint a struktúratípusrefparamétere
- Ha a szerkezet egy
- Ha a metódus vagy tartozék egy iterátor vagy aszinkron függvény, a
thisvá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
thisellenté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
xváltozóként van besorolva:-
xkiértékelése révén jön létre a változó. - A
xértéke mentésre kerül. - A
xmentett é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
xtípusára lesz konvertálva, és axkorábbi kiértékelése által megadott helyen lesz tárolva. - A
xmentett értéke lesz a művelet eredménye.
-
- Ha
xtulajdonságként vagy indexelői hozzáférésként van besorolva:- A példánykifejezést (ha
xnemstatic) és ax-hoz társított argumentumlistát (haxegy 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
xlekérdező hívása megtörténik, és a visszaadott érték mentésre kerül. - A
xmentett é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
xtípusára lesz konvertálva, és axhalmaz tartozéka ezzel az értékkel lesz meghívva értékargumentumként. - A
xmentett értéke lesz a művelet eredménye.
- A példánykifejezést (ha
Az ++ és -- operátorok az előtag jelölését is támogatják (12.9.7. §). 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 dynamictí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
Tegy value_type, ésAnincs jelen:- A object_creation_expression egy alapértelmezett konstruktorhívás. A object_creation_expression eredménye egy
Ttípusú érték, nevezetesen aTalapé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
Tegy type_parameter, ésAnincs jelen:- Ha nincs megadva értéktípus-korlátozás vagy konstruktorkorlát (§15.2.5) a
Teseté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
Tclass_type vagy struct_type:- Ha
Tabsztrakt 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 aAtekinteté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
Ttí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 Tclass_type vagy struct_type, és A egy opcionális argument_list, a következő lépésekből áll:
- Ha
Tegy 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.OutOfMemoryExceptiondob, é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
Tegy struct_type, akkor...- Egy ideiglenes helyi változó kiosztásával
Ttí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.
Egy initializer_target argument_list nem támogatott implicit módon a típus Index argumentumai (18.4.2. §) vagy Range (18.4.3. §).
Az egyenlőségjel utáni kifejezést meghatározó tag inicializáló a célhoz való hozzárendeléssel (12.23.2. §) megegyező módon dolgoz fel egy kifejezést.
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
Pointegy 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
__aegy 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
Rectangleegy 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__p2ideiglenes változók, amelyek egyébként láthatatlanok és elérhetetlenek.Ha
Rectanglekonstruktora lefoglalja a két beágyazottPointpéldányt, az új példányok hozzárendelése helyett a beágyazottPointpé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
Pointpé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 termelését a 12.24.
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 Addnevű 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__c2ideiglenes 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 ap2azonos 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:
«identifier» = «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, longvagy ulongtí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 (24.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ételrel, hogy a tömb elemtípusa nem explicit módon van megadva, hanem a tömb inicializáló kifejezéskészletének legjobb közös típusaként (§12.6.3.16) 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.OverflowExceptiondob, é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.OutOfMemoryExceptiondob, é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]; // Errorfordí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" }; // ErrorAz utolsó kifejezés fordítási időben hibát okoz, mert sem
int, semstringnem 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ú dynamicrendelkezik, 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
Emetó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
Ené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,Ekompatibilisnek kell lennie (21.2. §),Dés az eredmény egy újonnan létrehozott meghatalmazottra mutató hivatkozás, amely egy egyszeri meghívási listát hív megE.
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
Emetó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
Ené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
Eegy 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.NullReferenceExceptiondob, é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.OutOfMemoryExceptiondob, é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.fmező inicializálva van egy delegálttal, amely a másodikSquaremetódusra hivatkozik, mivel ez a metódus pontosan megfelel a paraméterlistának, és visszaadja aDoubleFunctípusát. Ha a másodikSquaremetó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 Ttí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
objectkulcsszó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.Typeobjektum 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értvevoidmetódusokat is,System.Typeegy 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.Int32azonos 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ú Teseté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 az operátor a sizeof24.6.9.
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.7), 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.12), ha mindkét operandus integrál vagy szám tí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
floatvagydoubleegé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:
-
checkedHa a művelet állandó kifejezés (12.25.§), fordítási időhiba lép fel. Ellenkező esetben, ha a műveletet futásidőben hajtják végre, a rendszerSystem.OverflowExceptiondob. - Egy
uncheckedkörnyezetben az eredmény úgy csonkítódik, hogy elveti a céltípusba nem illő, magas helyiértékű biteket.
Az olyan nem állandó kifejezések (12.25.§) (futtatáskor kiértékelt kifejezések) esetében, amelyekhez nem tartoznak operátorok checked vagy unchecked utasítások, 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álása) nem kérnek ellenőrzött értékelést.
Állandó kifejezések (12.25.§) (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
Fmetódus egySystem.OverflowExceptionad vissza, aGmetódus pedig 727379968 (a tartományon kívüli eredmény alsó 32 bitje). AHmetó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ésecheckedkörnyezetben történik. AGállandó kifejezés értékelésekor is túlcsordulás történik, de mivel az értékelésuncheckedkö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)); }
checkedhasználata F-ben nem befolyásolja ax * ykiértékelésétMultiply-ben, ezért ax * yalapé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
uinttípusúak. Mivel az állandók ainttartományon kívül vannak, auncheckedoperá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ésuncheckedoperá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
: explicitly_typed_default
| default_literal
;
explicitly_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 default_value_expression eredménye egy explicitly_typed_default explicit típusának alapértelmezett (9.3. §-a), vagy egy default_value_expression konvertálásának céltípusa.
A default_value_expression állandó kifejezés (12.25.§), 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ó inttí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 unmanaged_type nincs megadva, a rendszer a stackalloc_element_initializer-ekhalmazára vonatkozó leggyakoribb típusra (12.6.3.16. §) vonatkozó szabályokat követi.
- 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éseként (13.6.2. §), ahol a local_variable_type mutatótípus (24.3. §) vagy következtetés (var), akkor a stackalloc_expression eredménye egy típusú T* mutató (24.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
Lengthtulajdonsá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
stackallochaszná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
span8esetében astackallocegySpan<int>eredményez , amelyet implicit operátor konvertálReadOnlySpan<int>. Hasonlóképpen azspan9eseté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 nameofmeghí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_targetdynamictípussal rendelkezik.
A nameof_expression a stringtí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ő
nameofkifejezések eredményeit mutatja be, feltételezve, hogy egy általános típusúList<T>aSystem.Collections.Genericné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. Ezeket a 12.21.
12.9 Unary operátorok
12.9.1 Általános
A +, -, ! (logikai negation §12.9.4 csak), ~, , ^++, --öntött, és await operátorok nevezzük a nem kötelező operátorok.
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
| '^' unary_expression
| pre_increment_expression
| pre_decrement_expression
| cast_expression
| await_expression
| pointer_indirection_expression // unsafe code support
| addressof_expression // unsafe code support
;
pointer_indirection_expression (24.6.2. §) és addressof_expression (24.6.5. §) csak nem biztonságos kódban (24. §) érhető el.
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 ideje a következő:
-
Indexa^végponti index operátor esetében (12.9.6. §) -
dynamicminden egyéb nem naplózatlan operátor esetében; és
-
- az alább ismertetett felbontás futásidőben, az operandus futásidejű típusával történik.
12.9.2 Unáris plus operátor
A(z) +xformá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) –xformá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
Xnullából való kivonásával történik. Ha aXértéke az operandus típus legkisebb ábrázolható értéke (intesetén −2³¹,longesetén −2⁶³), akkor aXmatematikai tagadása nem ábrázolható az operandus típusán belül. Ha ez egycheckedkörnyezetben történik, a rendszerSystem.OverflowExceptiondob; ha egyuncheckedkörnyezetben fordul elő, az eredmény az operandus értéke, és a túlcsordulás nem lesz jelentve.Ha a negation operátor operandusa
uinttípusú, akkor a rendszerlongtí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
ulongtí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. HaxNaN, akkor az eredmény isNaN.Decimális tagadás:
decimal operator –(decimal x);Az eredmény kiszámítása a
Xnullából való kivonásával történik. A decimális tagadás egyenértékű aSystem.Decimaltí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) !xformá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) ~xformá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 xbitenké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 ~xkiértékelésének eredménye – ahol a X egy olyan enumerációs típusú kifejezés, amelynek alapja a Etí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 Index a végponttól operátortól
A unary ^ operátort a végponttól származó index operátornak nevezzük (amelyet köznyelvben kalap operátornak neveznek). Ez az operátor nem túlterhelhető (12.4.3.§), és egyetlen előre definiált operátor van:
Index operator ^(int x);
Az űrlap ^x egy műveletének eredménye a kifejezés eredményével egyenértékű végpont (Index18.2. §) érték:
new Index(x, true)
A többi unary_expressionis előfordulhat, hogy az operandus fordítási idő típusú dynamic (12.9.1. §) és dinamikusan kötött (12.3.3. §). Az eredmény fordítási idejének típusa mindig Indexaz .
Az index végponttól származó operátorának emelt (12.4.8. §-beli) formája is előre definiálva van.
12.9.7 Előtag-növekmény- és decrement 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
xváltozóként van besorolva:-
xkié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
xtípusra lesz konvertálva. Az eredményül kapott érték axkiértékelése által megadott helyen lesz tárolva, és a művelet eredményévé válik.
-
- Ha
xtulajdonságként vagy indexelői hozzáférésként van besorolva:- A példánykifejezést (ha
xnemstatic) és ax-hoz társított argumentumlistát (haxegy 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
xleké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
xtípusra lesz konvertálva. Axbeá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.8 Ö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)–ykifejezés akár cast_expression értelmezhető, mint a(z)–ytí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ésyazonosítók, akkorx.yhelyes nyelvhelyesség egy típushoz, még akkor is, hax.yvaló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ésyazonosítók, akkor(x)y,(x)(y)és(x)(-y)cast_expression, de(x)-ynem, még akkor sem, haxazonosít egy típust. Ha azonbanxegy 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.9 Kifejezések várnak
12.9.9.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.9.2 Várandós 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:
-
tfordítási idejű típusdynamic -
trendelkezik egy elérhető,GetAwaiternevű 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.-
Aimplementálja a felületetSystem.Runtime.CompilerServices.INotifyCompletion(a továbbiakban rövidenINotifyCompletion) -
A-nak van egy elérhető, olvasható tulajdonságaIsCompletedtípusúboolformájában. - A(z)
Arendelkezik 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.Actiontí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.9.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 avoidT, akkor a await_expressionTtípusú értékként van besorolva.
12.9.9.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ó
aa(t).GetAwaiter()kifejezés kiértékelésével szerezhető be. - A
boolba(a).IsCompletedkifejezés kiértékelésével kapott eredmény. - Ha
bfalse, akkor a kiértékelés attól függ, hogyaimplementá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, haafordítási idő típusúdynamic, és fordítva van. Jelöljükrmeg az újrakezdési meghatalmazottat (15.14.§):- Ha
anem implementálja aICriticalNotifyCompletion, akkor a((a) as INotifyCompletion).OnCompleted(r)kifejezés lesz kiértékelve. - Ha
aimplementá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
btrue, akkor közvetlenül utána, vagy az újrakezdési delegált későbbi meghívása esetén (habfalse), 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 Tartomány operátor
Az .. operátor neve tartomány operátor.
range_expression
: unary_expression
| unary_expression? '..' unary_expression?
;
Az előre definiált tartomány operátora a következő:
Range operator ..(Index x, Index y);
A tartomány operátora nem túlterhelhető (12.4.3. §).
A rendszer minden tartománykifejezést űrlapként x..ykezel, ahol:
-
xa bal operandus, ha van, ellenkező esetben a kifejezés0; és -
ya megfelelő operandus, ha van, ellenkező esetben a kifejezés^0.
A művelet eredménye a Range kifejezés eredményével egyenértékű (18.3.3.) érték:
new Range(x, y)
Ha egy tartománykifejezés egyik vagy mindkét operandusa fordítási idő típusú dynamic, akkor a kifejezés dinamikusan kötött (12.3.3.3. §). Az eredmény fordítási idejének típusa mindig Rangeaz .
A tartomány operátorának emelt (12.4.8. §-a) formája is előre definiálva van.
A tartomány operátora nem asszociatív (12.4.2. §).
12.11 Kifejezésváltás
A switch_expression -szerű szemantikát biztosít switchegy kifejezéskörnyezetben.
switch_expression
: range_expression
| switch_expression 'switch' '{' switch_expression_arms? '}'
;
switch_expression_arms
: switch_expression_arm (',' switch_expression_arm)* ','?
;
switch_expression_arm
: pattern case_guard? '=>' switch_expression_arm_expression
;
switch_expression_arm_expression
: expression
;
Kapcsolókifejezés-átalakítás (§10.2.18) kapcsolókifejezésről típusraT, ha a kapcsolókifejezések switch_expression_arm minden T implicit konverziót eredményez.
Ha a kapcsolókifejezések nem vonatkoznak kapcsolókifejezés-átalakításra, akkor
- Az switch_expression típusa a switch_expression_arm switch_expression_arm_expression s switch_expression_arm_expression12.6.3.16.
- Hiba, ha nincs ilyen típus.
Hiba, ha egyes switch_expression_arm mintája nem befolyásolja az eredményt, mert a korábbi minták és védők mindig egyeznek.
A kapcsolókifejezések teljesnek minősülnek, ha a bemenet minden értékét a kapcsolókifejezés legalább egy karja kezeli. A fordítónak figyelmeztetést kell készítenie, ha egy kapcsolókifejezés nem teljes.
Futásidőben a switch_expression eredménye annak az első switch_expression_armkifejezésének értéke, amelynek a switch_expression bal oldalán lévő kifejezés megegyezik a switch_expression_arm mintájával, és amelynek a switch_expression_arm case_guard jelen esetben kiértékeli.true Ha nincs ilyen switch_expression_arm, a switch_expression a kivétel System.InvalidOperationException egy példányát (vagy abból származtatott osztályt) vet ki.
Példa: A következő egy szám értékeit alakítja át az online térképen látható vizualizációs irányokat a megfelelő számosságirányokká:
static Orientation ToOrientation(Direction direction) => direction switch { Direction.Up => Orientation.North, Direction.Right => Orientation.East, Direction.Down => Orientation.South, Direction.Left => Orientation.West, _ => throw new ArgumentOutOfRangeException(direction.ToString()), }; public enum Direction { Up, Down, Right, Left } public enum Orientation { North, South, East, West }példa vége
12.12 Aritmetikai operátorok
12.12.1 Általános
A *, /, %, +és - operátorokat számtani operátoroknak nevezzük.
multiplicative_expression
: switch_expression
| multiplicative_expression '*' switch_expression
| multiplicative_expression '/' switch_expression
| multiplicative_expression '%' switch_expression
;
additive_expression
: multiplicative_expression
| additive_expression '+' multiplicative_expression
| additive_expression '-' multiplicative_expression
;
Ha egy aritmetikai operátor operandusa dynamicfordí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.12.2 Szorzási operátor
Az űrlap x * ymű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 yszorzatá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
checkedkörnyezetben a termék kívül esik az eredménytípus tartományán, a rendszerSystem.OverflowExceptiondob. Egyuncheckedkö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 aypozitív véges értékek.zax * yeredménye, a legközelebbi ábrázolható értékre kerekítve. Ha az eredmény nagysága túl nagy a céltípushoz,zvégtelen. A kerekítés miatt azlehet nulla, még akkor is, ha semx, semynem nulla.+y-y+0-0+∞-∞NaN+x+z-z+0-0+∞-∞NaN-x-z+z-0+0-∞+∞NaN+0+0-0+0-0NaNNaNNaN-0-0+0-0+0NaNNaNNaN+∞+∞-∞NaNNaN+∞-∞NaN-∞-∞+∞NaNNaN-∞+∞NaNNaNNaNNaNNaNNaNNaNNaNNaN(Eltérő rendelkezés hiányában a 12.12.2–12.12.6. § -ban szereplő lebegőpontos táblázatokban a "
+" használata azt jelenti, hogy az érték pozitív; a "-" használata azt jelenti, hogy az érték negatív; a jel hiánya pedig azt jelenti, hogy az érték pozitív vagy negatív lehet, vagy nincs elő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.OverflowExceptionhiba 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.Decimaltí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.12.3 Részleg operátora
Az űrlap x / ymű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 yhá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.DivideByZeroExceptiondob.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ó
intvagylongérték, és a jobb operandus–1, akkor túlcsordulás történik. Egycheckedkontextusban ez egySystem.ArithmeticException(vagy annak alosztálya) kivétel dobását okozza. Egyuncheckedkontextusban 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 aypozitív véges értékek.zax / yeredménye, a legközelebbi ábrázolható értékre kerekítve.+y-y+0-0+∞-∞NaN+x+z-z+∞-∞+0-0NaN-x-z+z-∞+∞-0+0NaN+0+0-0NaNNaN+0-0NaN-0-0+0NaNNaN-0+0NaN+∞+∞-∞+∞-∞NaNNaNNaN-∞-∞+∞-∞+∞NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNDecimális osztás:
decimal operator /(decimal x, decimal y);Ha a megfelelő operandus értéke nulla, egy
System.DivideByZeroExceptiondob. Ha az eredményül kapott érték nagysága túl nagy ahhoz, hogy decimális formátumban legyen ábrázolva, egySystem.OverflowExceptionhiba 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 axskálája mínusz ayskálája.A decimális osztás egyenértékű a
System.Decimaltí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.12.4 Fennmaradó operátor
Az űrlap x % ymű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 ykö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 % yeredménye ax – (x / y) * yáltal megtermelt érték. Haynulla, egyetSystem.DivideByZeroException-et dob.Ha a bal oldali operandus a legkisebb
intvagylongérték, és a jobb oldali operandus–1, akkorSystem.OverflowExceptioncsak akkor kerül dobásra, hax / ykivé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 aypozitív véges értékek.zax % yeredménye, ésx – n * yké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 (amelybennazx / y-hez legközelebbi egész szám).+y-y+0-0+∞-∞NaN+x+z+zNaNNaN+x+xNaN-x-z-zNaNNaN-x-xNaN+0+0+0NaNNaN+0+0NaN-0-0-0NaNNaN-0-0NaN+∞NaNNaNNaNNaNNaNNaNNaN-∞NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNDecimális maradék:
decimal operator %(decimal x, decimal y);Ha a megfelelő operandus értéke nulla, egy
System.DivideByZeroExceptiondob. 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 % yesetén, hax / ynem 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.Decimaltí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.12.5 Összeadás operátor
Az űrlap x + ymű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 stringtí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
checkedkörnyezetben az összeg kívül esik az eredménytípus tartományán, a rendszerSystem.OverflowExceptiondob. Egyuncheckedkö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 aynem véges értékek,zpedigx + yeredménye. Haxésyazonos nagyságrendű, de ellentétes előjelekkel rendelkeznek,zpozitív nulla. Hax + ytúl nagy ahhoz, hogy a céltípusban szerepeljen,zax + yjellel megegyező végtelen.y+0-0+∞-∞NaNxzxx+∞-∞NaN+0y+0+0+∞–∞NaN-0y+0-0+∞-∞NaN+∞+∞+∞+∞+∞NaNNaN-∞-∞-∞-∞NaN-∞NaNNaNNaNNaNNaNNaNNaNNaNDecimá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.OverflowExceptionhiba 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.Decimaltí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
Eaz enumerálás típusa,Upedig aEmö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)yeseté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, aToStringtípustól örökölt virtuálisobjectmetódus meghívásával kerül átalakításra a szöveges ábrázolására. Ha aToStringanull-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
ToStringmetó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.OutOfMemoryExceptiondobó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
Da 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: Példák a delegálási kombinációra: §12.12.6 és §21.6. Mivel
System.Delegatenem 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.12.6 Kivonási operátor
Az űrlap x – ymű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 yEgy
checkedkontextusban, ha a különbség kívül esik az eredménytípus tartományán, kivételkéntSystem.OverflowExceptiondobódik. Egyuncheckedkö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 aynem véges értékek,zpedigx – yeredménye. Haxésyegyenlőek,zpozitív nulla. Hax – ytúl nagy ahhoz, hogy a céltípusban szerepeljen,zax – yjellel megegyező végtelen.y+0-0+∞-∞NaNxzxx-∞+∞NaN+0-y+0+0-∞+∞NaN-0-y-0+0-∞+∞NaN+∞+∞+∞+∞NaN+∞NaN-∞-∞-∞-∞-∞NaNNaNNaNNaNNaNNaNNaNNaNNaN(A fenti táblázatban a
-ybejegyzések ayjelö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.OverflowExceptionhiba 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.Decimaltí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
Eaz enumerálás típusa,Upedig aEmö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 aysorszá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
Da 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 (21.2. §).
- Ha a listák összehasonlítják az egyenlőséget a meghatalmazott egyenlőségi operátor által meghatározott egyenlőséggel (12.14.9. §), a művelet
nulleredménye . - 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 összehasonlítják az egyenlőséget a meghatalmazott egyenlőségi operátor által meghatározott egyenlőséggel (12.14.9. §), a művelet
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.13 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 >> countmű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
xeredmé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 eltoljaxegy olyan bitek száma szerint, amit az alábbiakban leírnak.Amikor a
xtípusintvagylong, axalacsony 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, haxnem negatív, illetve egyre vannak állítva, haxnegatív.Ha a
xuintvagyulongtípusú, axalacsonyrendű 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
xtípusaintvagyuint, a műszakok számát az alacsony sorrendű öt bitcountadja meg . Más szóval, a műszakok számát acount & 0x1Falapján számítják ki. - Ha a
xtípusalongvagyulong, a műszakok számát acountkisrendű hat bitje adja meg. Más szóval, a műszakok számát acount & 0x3Falapjá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
xegyinttí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.14 Relációs és típustesztelő operátorok
12.14.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
isoperá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
Megjegyzés: A típus és a constant_pattern
relational_expressionközött nyelvtani kétértelműség áll fenn a jobb oldalonis; vagy egy minősített azonosító érvényes elemzése lehet. Ilyen esetben csak akkor, ha nem sikerül típusként kötést végezni (a nyelv korábbi verzióival való kompatibilitás érdekében), akkor az lesz az első megtalált dolog (amelynek állandónak vagy típusnak kell lennie). Ez a kétértelműség csak egy ilyen kifejezés jobb oldalán található.
Az is operátort a 12.14.12. § , az operátort pedig a as12.14.13.
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 dynamicfordí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.14.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.14.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ésyNaN, akkorx < yfalse, 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.14.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.Decimaltí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.14.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 booltí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.14.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» ykié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.14.7 Referencia típusú egyenlőségi operátorok
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 ==falseadna.
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 egyTtípusú érték, ahol aTegy olyan type_parameter, amelyről ismert, hogy nem értéktípus, és nincs megkötése az értéktípusra.- Ha futásidőben
Tnem null értékű értéktípus, a==eredményefalse, a!=eredménye pedigtrue. - Ha futásidőben
Tnull értékű típusról van szó, az eredményt az operandus tulajdonságábólHasValueszámítja ki a rendszer a (12.14.10.) szakaszban leírtak szerint. - Ha futásidőben
Tegy 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
nullhasonlí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 == yvagyx != ymű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 aobjecttí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 == nullszerkezet akkor is engedélyezett, haTnem null értékű értéktípust jelölhet, és az eredmény egyszerűenfalse, haTnem null értékű.példa vége
Az űrlap x == y vagy x != ymű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
objecttí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 FalseA
séstváltozók két különböző sztringpéldányra vonatkoznak, amelyek ugyanazokat a karaktereket tartalmazzák. Az első összehasonlítás azért jön kiTrue, mert az előre definiált sztringegyenlőségi operátor (§12.14.8) akkor van kiválasztva, ha mindkét operandus típusústring. A fennmaradó összehasonlítások mindFalse-t eredményeznek, mert aoperator ==típusústringtú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.14.8 Sztringegyenlő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
nullsztringpé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: A 12.14.7. §-ban leírtak szerint a hivatkozástípus egyenlőségi operátorai sztringértékek helyett sztringhivatkozások összehasonlítására használhatók. végjegyzet
12.14.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 meghívási listája (21.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.21.§) és a rögzített külső változópéldányok azonos (esetleg üres) készletével történő kiértékelése során létrehozott meghívási listabejegyzések megengedettek (de nem kötelezőek).
Ha az operátorok túlterhelésének feloldása a delegált egyenlőségi operátorra oldódik fel, és mindkét operandus kötésideje a 21. § -ban leírt delegálási típusok, és System.Delegatenem történik identitásátalakítás a kötés típusú operandustípusok között, kötési idő hiba lép fel.
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.14.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 HasValuex 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.14.11 A tuple 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 ekié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 yielempárra ugyanazt az egyenlőségi operátort kell alkalmazni, és az eredmény típusa boolvagy dynamiclesz, esetleg egy olyan típus, amely implicit konverzióval rendelkezik a booltí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
xkiértékelése történik. - A jobb oldali operandus
y-t kiértékelik. - Minden egyes elempár esetében
xiésyilexikális sorrendben:- A
xi == yioperátor kiértékelése és abooltípusú eredmény a következő módon történik:- Ha az összehasonlítás egy
booleredményezett, akkor ez az eredmény. - Ellenkező esetben, ha az összehasonlítás egy
dynamiceredményezett, akkor a rendszer dinamikusan meghívja azfalseoperá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
falseoperá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
booleredmé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
truelett, 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
xkiértékelése történik. - A jobb oldali operandus
y-t kiértékelik. - Minden egyes elempár esetében
xiésyilexikális sorrendben:- A
xi != yioperátor kiértékelése és abooltípusú eredmény a következő módon történik:- Ha az összehasonlítás egy
booleredményezett, akkor ez az eredmény. - Ellenkező esetben, ha az összehasonlítás egy
dynamiceredményezett, akkor atrueoperá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
trueoperá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
booleredmé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
falselett, akkor az n-többes egyenlőségi operátor eredményefalse.
12.14.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.14.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 Tművelet eredménye, ahol a E kifejezés, és T nem dynamictí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 E is T kiértékelése a következőképpen történik:
- Ha
Eegy névtelen függvény vagy metóduscsoport, fordítási időhiba lép fel. - Ha
Tnull értékű hivatkozástípus (8.9.3. §), fordítási időhiba lép fel. - Ha
Eanullliterál, vagy ha aEértékenull, akkor az eredményfalse. - Egyébként:
- Legyen
REfuttatási típusa. - Az
DalábbiakbólRszármaztatható:- Ha
Rnull értékű,DaRmögöttes típusa. - Ellenkező esetben
DvanR.
- Ha
- Az eredmény az alábbiaktól
DTfügg:- Ha
Thivatkozástípus, az eredménytrue, ha:- identitáskonvertálás létezik a következők között
D: ésT, vagy -
Degy referenciatípus, és létezik egy implicit referenciaátalakításD-rőlT-re, vagy -
Degy értéktípus, és egy dobozos átalakítás a létezőkreDT.
- identitáskonvertálás létezik a következők között
- Ha
Tnull értékű, az eredménytrue, haDaTmögöttes típusa. - Ha
Tnem null értékű értéktípus, az eredménytrue, haDésTazonos típusúak. - Ellenkező esetben az eredmény
false.
- Ha
- Legyen
A felhasználó által megadott konverziókat a is operátor nem veszi figyelembe.
Megjegyzés: Mivel a
isoperá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
isoperátor a fordítási idő típusa és az átalakítások szempontjából az alábbiak szerint értelmezhető, ahol aCaEfordítási idő típusa:
- Ha a
efordí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 aEfordítási idő típusától aT:
- Ha
Cnem null értékű, a művelet eredményetrue.- Ellenkező esetben a művelet eredménye egyenértékű a
E != nullkiértékelésével.- Ellenkező esetben, ha egy explicit referenciaátalakítás (10.3.5.§) vagy a nem dobozolt átalakítás (10.3.7. §) létezik
CT, vagyCTnyitott típus (8.4.3. §), akkor a futásidejű ellenőrzéseket a fenti módon kell elvégezni.- Ellenkező esetben nem lehetséges a
Etípus átalakításaTtí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.14.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:
-
Enem jelöl meg értéket, vagy nem rendelkezik típussal. - A minta
Pnem alkalmazható (11.2.) típusraT.
A minta minden single_variable_designation egy új helyi változót vezet be, amelyet a megfelelő relational_expression tesztek során mindenképpen hozzárendelnek (true).
12.14.13 Az 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éssel ellentétben (12.9.8. §) az 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 Talakú 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
EvagyTtípusa nyitott típus. -
Eaznullliterá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
TtípusparaméterérőlGismert, hogy referenciatípus, mert osztálykötési megszorítással rendelkezik. AUHtípusparamétere azonban nem megfelelő; ezért aasoperátor használataHnem engedélyezett.példa vége
12.15 Logikai operátorok
12.15.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 dynamicfordí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.15.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.15.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» ykiértékelésének eredménye, ahol x és y a E enumerációs típus kifejezései a Ualap 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.15.4 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 xtrue és a yfalse, vagy ha xfalse és a ytrue. Ellenkező esetben az eredmény false. Ha az operandusok booltípusúak, a ^ operátor ugyanazt az eredményt számítja ki, mint a != operátor.
12.15.5 Null értékű logikai és | Piaci szereplők
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 & emelt formái és | (12.15.4. §) is előre definiálva vannak:
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.16 Feltételes logikai operátorok
12.16.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 && ymegfelel ax & yműveletnek, azzal a kivételsel, hogy aycsak akkor lesz kiértékelve, haxnemfalse. - A művelet
x || ymegfelel ax | yműveletnek, azzal a kivételsel, hogy aycsak akkor lesz kiértékelve, haxnemtrue.
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 truefalsead vissza,operator falsepedigfalse. Ezekben az esetekben sem&&, sem||nem lenne zárlatos. végjegyzet
Ha egy feltételes logikai operátor operandusa dynamicfordí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 | ylett 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 szám logikai operátort vagy null értékű logikai operátort (12.15.5. §), kötési idejű hiba történik.
- Ellenkező esetben, ha a kiválasztott operátor az előre definiált logikai operátorok egyike (12.15.4. §), a műveletet a 12.16.2.
- Ellenkező esetben a kiválasztott operátor egy felhasználó által definiált operátor, és a műveletet a 12.16.3.
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ő. Ezt a 12.16.3.
12.16.2 Logikai feltételes logikai operátorok
Ha a && vagy || operandusai booltí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 && yműveletetx ? y : false-ként értékeljük ki. Más szóval, ax-t először kiértékelik, majdbooltípussá alakítják. Ezután, haxtrue, ay-t kiértékelik ésbooltípussá alakítják, és ez lesz a művelet eredménye. Ellenkező esetben a művelet eredményefalse. - A
x || yműveletetx ? true : y-ként értékeljük ki. Más szóval, ax-t először kiértékelik, majdbooltípussá alakítják. Akkor, haxtrue, a művelet eredményetrue. Ellenkező esetben aykiértékelődik ésbooltípussá alakítódik, és ez válik a művelet eredményévé.
12.16.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
Tkell lennie. Más szóval az operátornak ki kell számítania azTtípusú két operandus logikai ÉS vagy logikai VAGY értékét , ésTtípusú eredményt kell visszaadni . -
Toperator trueésoperator falsedeklará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 && yműveletT.false(x) ? x : T.&(x, y)a következőként van kiértékelve, ahol aT.false(x)-ban deklaráltoperator falseaTmeghí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, axhatározottan hamis-e. Ezután, haxegyértelműen hamis, a művelet eredménye a korábbanx-hez kiszámított érték. Ellenkező esetben aykié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 || yműveletT.true(x) ? x : T.|(x, y)a következőként van kiértékelve, ahol aT.true(x)-ban deklaráltoperator trueaTmeghívása, a kiválasztottT.|(x, y)pedig aoperator |meghívása. Más szóval, először axvan kiértékelve, majd az eredmény alapján meghívják aoperator true-t annak megállapítására, hogy axvalóban igaz-e. Ezután, haxbiztosan igaz, a művelet eredménye azxkorábban kiszámított érték. Ellenkező esetben aykié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.17 A null szénerősítési 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 nullalakú 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 anull.
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 ?? cformájú kifejezés úgy lesz kiértékelve, minta ?? (b ?? c). Általánosságban elmondható, hogy egyE1 ?? E2 ?? ... ?? ENalakban lévő kifejezés visszaadja az első nemnulloperandust, 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₀, Avagy B, ahol a A a a típusa (feltéve, hogy a rendelkezik típussal), B a btí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
Alé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
Alétezik, ésbdinamikus kifejezés, az eredmény típusadynamic. Futásidőben a rendszer először kiértékelia. Haanemnull,adynamiclesz, és ez lesz az eredmény. Ellenkező esetben abkerül kiértékelésre, és ez lesz az eredmény. - Ellenkező esetben, ha
Alé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. Haanemnull, aakibontvaA₀típusra, és ez lesz az eredmény. Ellenkező esetben abkiértékelésre kerül és átalakulA₀típussá, és ez lesz az eredmény. - Ellenkező esetben, ha
Alé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. Haanemnull,alesz az eredmény. Ellenkező esetben abkiértékelésre kerül és átalakulAtípussá, és ez lesz az eredmény. - Ellenkező esetben, ha
Alétezik, és null értékkel rendelkező értéktípus,bBtípussal rendelkezik, és van egy implicit átalakításA₀-rólB-re, akkor az eredménytípusBlesz. Futásidőben a rendszer először kiértékelia. Haanemnull, akkoraki van csomagolvaA₀típusra, majd átalakítvaBtípusúvá, és ez lesz az eredmény. Ellenkező esetben abkiértékelődik, és az lesz az eredmény. - Ellenkező esetben, ha
bBtí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. Haanincsnull,aBtípussá alakul, és ez lesz az eredmény. Ellenkező esetben abkiértékelődik, és az lesz az eredmény. - Ellenkező esetben a
aés abnem 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
TtípusparamétereMnincs korlátozva. Ezért a típusargumentum lehet hivatkozástípus vagy null értékű típus, ahogyan az első hívásbanMlátható. A típusargumentum lehet egy nem null értékű értéktípus is, ahogyan az a másodikMhí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.18 A dobás kifejezésének operátora
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.19 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
outargument_value egy argument_list-ban. - Egyszerű visszadobás
_, amely egy egyszerű feladat bal oldalát tartalmazza (12.23.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ó, varimplicit 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
s1deklarációja explicit és implicit módon beírt deklarációs kifejezéseket is mutat. A(z)b1típusabool, mert ez a megfelelő kimeneti paraméter típusa aM1-ben. A következőWriteLinehozzáférhet azi1-hez ésb1-höz, amelyeket a környező hatókörbe vezettek be.A
s2deklarációja azt mutatja, hogy megpróbálják használni ai2-et aMbeágyazott hívásában, ami nem megengedett, mert a hivatkozás abban az argumentumlistában fordul elő, aholi2deklarálva volt. Másrészt a végső argumentumban engedélyezett ab2való hivatkozás, mert a beágyazott argumentumlista vége után következik be, aholb2deklarálva lett.A
s3deklará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.20 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
;
A dobás kifejezés (12.18.§) nem engedélyezett feltételes operátorban, ha ref van ilyen.
Az űrlap feltételes kifejezése b ? x : y először kiértékeli a feltételt b. Ezután, ha btrue, 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 : eformá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 truemegvaló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
refvan jelen, a conditional_expression egy változóhivatkozást ad vissza, amely hozzárendelhető egy referenciaváltozóhoz a= refoperá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
xXtípussal rendelkezik, ésyY,- Ha az és között identitáskonverzió létezik
X, akkor az eredmény a kifejezéskészletek leggyakoribb típusa (Y). 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, akkorYa 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, akkorYa 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, akkorXa 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, akkorXa 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 az és között identitáskonverzió létezik
- Ha csak az egyik
xésyrendelkezik típussal, ésxésyis 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
bkiértékelésére kerül sor, majd meghatározásra kerül aboolbértéke.- Ha a
btípusábólboolimplicit á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 truetípus által definiáltbkerü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
ykié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
bkiértékelésére kerül sor, majd meghatározásra kerül aboolbértéke.- Ha a
btípusábólboolimplicit á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 truetípus által definiáltbkerü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 axkié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
ykié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.21 Névtelen függvénykifejezések
12.21.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 aMeredménytípusavoid(§12.8.13). Ha azonban null_conditional_invocation_expression-ként kezelik, az eredménytípusvoidmegengedett. végjegyzet
példa: A
List<T>.Reverseeredmé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 omittedpé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.21.2 Névtelen függvényadektitá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.21.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 structtípusú paraméterhez. - Ha a
thistí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 axa 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.21.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.21.7. §).
- Fordítási idő hibát jelent, ha a törzs egy
gotoutasítást, egybreakutasítást vagy egycontinueutasí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
returnutasí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.21.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étSummetódust használ. Mindegyik egyselectorargumentumot használ, amely kinyeri a listaelemből összegzendő értéket. A kinyert érték lehetintvagydouble, és az eredményül kapott összeg hasonlóképpenintvagydouble.A
Summetó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.Sumelső meghívása során mindkétSummetódus alkalmazható, mert a névtelen függvényd => d.UnitCountkompatibilisFunc<Detail,int>ésFunc<Detail,double>. A túlterhelés feloldása azonban az elsőSummetó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.Summásodik meghívásában csak a másodikSummetódus alkalmazható, mert a névtelen függvényd => d.UnitPrice * d.UnitCountdoubletípusú értéket állít elő. Így a túlterhelés feloldása a másodikSummetódust választja az adott meghíváshoz.példa vége
12.21.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.21.6 Külső változók
12.21.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.21.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
xváltozót, és axélettartama legalább addig meghosszabbodik, amíg aFvisszaadott 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 3pé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 rögzített változónak minősül (24.4. §), hanem áthelyezhető változónak minősül. A rögzített külső változók azonban nem használhatók utasításban fixed (24.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.21.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
xhelyi 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
xdeklarációját a cikluson kívülre helyezi, azxcsak 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 5Ha azonban a
xdeklará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 5Vegye 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 3pé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
Fa 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 aykülönálló példányait, és a kimenet a következő:1 1 2 1 3 1pé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 10példa vége
12.21.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ú Ekell á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.21.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 thispé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.21.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 __Locals2egy példánya tartalmazza a helyi változót z és egy mezőt, amely a __Locals1egy 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.22 Lekérdezési kifejezések
12.22.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.22.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 "fromazonosító" kezdetű, majd a ";", "=" vagy "," kivételével bármilyen token követi.
12.22.3 Lekérdezési kifejezés fordítása
12.22.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.22.4. §). A lekérdezési kifejezések a Where, Select, SelectMany, Join, GroupJoin, OrderBy, OrderByDescending, ThenBy, ThenByDescending, GroupByés Castnevű metódusok meghívására vannak lefordítva . Ezek a módszerek a 12.22.4. 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. Ezeket a 12.22.3.8.
12.22.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.22.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 clefordítva
from c in (customers).Cast<Customer>() where c.City == "London" select camelynek 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
IEnumerablefelületet implementálják, de az általánosIEnumerable<T>felületet nem. A fenti példában ez a helyzet, ha az ügyfelekArrayListtípusúak lennének. végjegyzet
12.22.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 clefordí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.22.3.6 és §12.22.3.7) eltávolítják a más fordítási lépések által bevezetett degenerált lekérdezéseket úgy, hogy a forrásukra cserélik őket. 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.22.3.5 A csatlakozási és rendezési záradékok forrás, let, where, orderby záradékai
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
xegy 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
xegy 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ésyolyan 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 oa 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.22.3.6 Záradékok kiválasztása
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 cszó szerint lefordítva
(customers).Where(c => c.City == "London")példa vége
12.22.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.Countrylefordítva
(customers).GroupBy(c => c.Country, c => c.Name)példa vége
12.22.3.8 Transzparens azonosítók
Egyes fordítások tartományváltozókat injektálnak transzparens azonosítókkal, amelyeket a *következő 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
xegy 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ésyolyan fordító által generált azonosítók, amelyek egyébként láthatatlanok és elérhetetlenek. példa vége
12.22.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. A tagokat és az akadálymentes bővítménymetszeteket általános típusú C<T>"alakzatnak" nevezzük. Á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ésThenByDescendingmetódusok csak egyOrderByvagyOrderByDescendingeredménye alapján legyenek elérhetők . végjegyzet
Megjegyzés: A
GroupByeredményének ajánlott alakja – egy sorozatsorozat, amelyben minden belső sorozat továbbiKeytulajdonsá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.Linqné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.23 Hozzárendelési operátorok
12.23.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 = refkivé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átort a 12.23.2.
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 hiv-hozzárendelés operátorát a 12.23.3.
Az és az == ref operátortól eltérő hozzárendelési operátorokat összetett hozzárendelési operátornaknevezzü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átorokat a 12.23.4.
A += bal operandusként esemény-hozzáférési kifejezéssel rendelkező és -= operátorokat esemény-hozzárendelési operátornaknevezzü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átorokat a 12.23.5.
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 = cformájú kifejezés úgy lesz kiértékelve, minta = (b = c). példa vége
12.23.2 Egyszerű hozzárendelés
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 xy hozzárendelésének típusa, amely rekurzívan az alábbiak szerint van meghatározva:
- Ha a
xegy(x1, ..., xn), és aydekonstruálható egy(y1, ..., yn)elemekkel rendelkezőnkifejezésre (§12.7), ésximindenyihozzárendeléseTitípusú, akkor a hozzárendelés(T1, ..., Tn)típussal rendelkezik. - Ellenkező esetben, ha
xváltozóként van besorolva, a változó nemreadonly,xTtípussal rendelkezik, ésy-nek van implicit átalakításaT-re, akkor a hozzárendelés típusaT. - Ellenkező esetben, ha
ximplicit módon beírt változóként (azaz implicit módon beírt deklarációs kifejezésként) van besorolva, ésyTtípussal rendelkezik, akkor a változó kikövetkeztetett típusaT, és a hozzárendelés típusaT. - Ellenkező esetben, ha
xtulajdonsá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,xTtípusú, ésyrendelkezik egy implicit átalakítássalTtí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.
-
xkiértékelésre kerül, ha még nem történt meg. - Ha
xváltozóként van besorolva, a rendszer kiértékeli ay, és szükség esetén implicit átalakítássalTkonvertá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 aykiszámított értéke kompatibilis legyen azzal a tömbpéldánysal, amelynekxeleme. Az ellenőrzés akkor sikeres, haynull, vagy ha implicit referenciaátalakítás (§10.2.8) létezik ayáltal hivatkozott példány típusától axtartalmazó tömbpéldány tényleges elemtípusához. Ellenkező esetben egySystem.ArrayTypeMismatchExceptionlesz dobva. - A
ykiértékelésével és átalakításával kapott értéket a rendszer axkiértékelése által megadott helyre tárolja, és a hozzárendelés eredményeként adja meg.
- Ha az
- Ha
xtulajdonságként vagy indexelői hozzáférésként van besorolva:-
ykiértékelése és szükség eseténTkonvertálása implicit átalakítással (§10.2). - A
xhalmaz-tartozéka aykiértékelése és átalakítása során kapott értékkel lesz meghívva értékargumentumként. - A
ykié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
xegy(x1, ..., xn)aritásúnbesorolású n-es:-
yanelemekkel lebontva egyekifejezésre. - Az eredménytömb
tazeimplicit módon történő tuple-átalakítással ésT-re való átalakításával jön létre. - Minden egyes
xiesetén balról jobbra haladva történik axihozzárendelése at.Itemi-hez, azzal a kivétellel, hogy axinem lesz újra kiértékelve. - A hozzárendelés eredményeként
tkeletkezik.
-
Megjegyzés: ha a
xfordítási idő típusadynamic, és implicit átalakítás történik a fordítási idő típusárólydynamic, 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(); // ArrayTypeMismatchExceptionAz utolsó hozzárendelést következésképpen
System.ArrayTypeMismatchExceptiondobja 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.Bhozzárendelése engedélyezett, mertpésrvá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.Bnem változók.példa vége
12.23.3 Hiv-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 thiskivé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
= refhaszná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
= refoperátorral, könnyen elcsábulhatunk, hogy arefré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= refaz operátor, ésb ? ref x : ref ya megfelelő operandus:ref int a = ref (b ? ref x : ref y);. Fontos, hogy aref bkifejezés nem része ennek az utasításnak, annak ellenére, hogy első pillantásra ilyennek tűnhet. végjegyzet
12.23.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 dynamictí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
xtípusára, a műveletx = x «op» ylesz kiértékelve, azzal a kivételével, hogy axcsak 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
xtípusára, és hayimplicit módon átalakítható axtípusára, vagy az operátor egy műszak operátor, akkor a művelet kiértékelésex = (T)(x «op» y), aholTaxtípusa, kivéve, hogy axcsak 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» ykiértékelésekor a x bármely alkotó kifejezésének eredményét ideiglenesen menti a rendszer, majd újra felhasználja a xvaló hozzárendelés során.
példa: A hozzárendelés során
A()[B()] += C(), ahol aAegyint[]visszaadó metódus, ésBésCpedigintvisszaadó metódusok, a metódusok csak egyszer, aA,B,Csorrendben 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, ushortvagy chartípusú . Még akkor is, ha mindkét argumentum az egyik ilyen típusú, az előre definiált operátorok inttí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; // OKAz 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»= ykiértékelésex = x «op» yvagyx = (T)(x «op» y), a kiértékelési szabályok implicit módon lefedik a kibővített operátorokat. végjegyzet
12.23.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.24 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.25 Á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
nullliterális értéket). -
constAz osztály, a strustruktúra és a felülettípusok tagjaira 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ésuncheckedkifejezések. -
nameofkifejezé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). -
sizeofkifejezéseket, feltéve, hogy a nem felügyelt típus a 24.6.9sizeof. - 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
iinicializálása hiba, mert csomagolási átalakításra van szükség. Astrinicializá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)
- Tagi deklarációk számbavétele (20.4. §)
- Paraméterlisták alapértelmezett argumentumai (§15.6.2)
-
caseswitchutasítás címkéi (§13.8.3). -
goto caseutasítások (§13.10.4) - Egy inicializálót tartalmazó tömblétrehozó kifejezés dimenzióhosszai (§12.8.17.4).
- Attribútumok (23. §)
- á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, uintvagy ulonglegyen, feltéve, hogy az állandó kifejezés értéke a céltípus tartományán belül van.
12.26 Logikai kifejezések
A boolean_expression olyan kifejezés, amely booltí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. Az operátor vezérlő feltételes kifejezése ?: (12.20. §) 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 booltí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 trueEegyedi 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