Megosztás a következőn keresztül:


12 Kifejezések

12.1 Általános

A kifejezés operátorok és operandusok sorozata. Ez a záradék határozza meg az operandusok és operátorok kiértékelési sorrendjét, valamint a kifejezések jelentését.

12.2 Kifejezésbesorolások

12.2.1 Általános

Egy kifejezés eredménye az alábbiak egyikének minősül:

  • Egy érték. Minden értékhez tartozik egy társított típus.
  • Egy változó. Ha másként nincs megadva, a változó explicit módon van begépelve, és társított típussal rendelkezik, nevezetesen a változó deklarált típusával. Az implicit módon beírt változókhoz nincs társított típus.
  • Null literál. Az ezzel a besorolással rendelkező kifejezések implicit módon átalakíthatók hivatkozástípussá vagy null értékűvé.
  • Névtelen függvény. Az ezzel a besorolással rendelkező kifejezések implicit módon konvertálhatók kompatibilis delegálttípussá vagy kifejezésfatípussá.
  • Egy tus. Minden rekordhoz rögzített számú elem tartozik, amelyek mindegyike kifejezéssel és opcionális rekordelemnévvel rendelkezik.
  • Tulajdonsághozzáférés. Minden tulajdonsághozzáférésnek van egy társított típusa, nevezetesen a tulajdonság típusa. A tulajdonsághozzáférés emellett kapcsolódó példánykifejezéssel rendelkezhet. Egy példánytulajdonság-hozzáférés meghívásakor a példánykifejezés kiértékelésének eredménye a this által képviselt példány lesz (§12.8.14).
  • Indexelő funkció elérése. Minden indexelő-hozzáféréshez tartozik egy társított típus, nevezetesen az indexelő elemtípusa. Az indexelői hozzáférés emellett társított példánykifejezéssel és társított argumentumlistával is rendelkezik. Indexelő-hozzáférés meghívásakor a példánykifejezés kiértékelésének eredménye a this által képviselt példány lesz (§12.8.14), és az argumentumlista kiértékelésének eredménye lesz a meghívás paraméterlistája.
  • Semmi. Ez akkor fordul elő, ha a kifejezés egy metódus meghívása, amelynek visszatérési típusa void. A semmiként besorolt kifejezés csak egy statement_expression (§13.7) vagy egy lambda_expression törzsében (§12.19) érvényes.

A nagyobb kifejezések alexpressziójaként előforduló kifejezéseknél a feljegyzett korlátozások mellett az eredmény az alábbiak egyikének is besorolható:

  • Névtér. Az ezzel a besorolással rendelkező kifejezések csak egy member_access bal oldalán jelenhetnek meg (§12.8.7). Bármely más környezetben a névtérnek minősített kifejezés fordítási hibát eredményez.
  • Egy típus. Az ezzel a besorolással rendelkező kifejezések csak egy member_access bal oldalán jelenhetnek meg (§12.8.7). Bármely más környezetben a típusként besorolt kifejezés fordítási idejű hibát okoz.
  • Egy metóduscsoport, amely egy tagkeresésből eredő túlterhelt metódusok halmaza (12.5. §). A metóduscsoportokhoz társított példánykifejezés és társított típusú argumentumlista tartozhat. Egy példánymetódus meghívásakor a példánykifejezés kiértékelésének eredménye a this által képviselt példány lesz (§12.8.14). Egy metóduscsoport a invocation_expression (§12.8.10) vagy a delegate_creation_expression (§12.8.17.5) használatakor engedélyezett, és implicit módon átalakítható egy kompatibilis delegátustípusra (§10.8). Bármely más környezetben a metóduscsoportként besorolt kifejezés fordítási hibát eredményez.
  • Egy eseményhez való hozzáférés. Minden eseményhozzáférésnek van egy társított típusa, nevezetesen az esemény típusa. Az eseményhozzáférés emellett társított példánykifejezéssel is rendelkezhet. Az eseményhozzáférés a += és -= operátor bal operandusaként jelenhet meg (§12.21.5). Bármely más környezetben az eseményhozzáférésként besorolt kifejezés fordítási idejű hibát okoz. Amikor egy példányesemény hozzáférés accessorja kerül meghívásra, a példánykifejezés kiértékelésének eredménye az this által képviselt példány lesz (§12.8.14).
  • Dobókifejezés, amely több kontextusban is használható egy kifejezés kivételének kidobásához. Egy throw kifejezés implicit átalakítással bármilyen típusra konvertálható.

A tulajdonsághozzáférés vagy az indexelőhozzáférés mindig értékként lesz újra osztályozva a getter vagy a setter meghívásával. Az adott tartozékot a tulajdonság- vagy indexelőhozzáférés kontextusa határozza meg: Ha a hozzáférés egy hozzárendelés célja, a rendszer meghívja a készlethez tartozó tartozékot egy új érték hozzárendelésére (12.21.2.. §). Ellenkező esetben a get kiegészítő meghívása az aktuális érték lekéréséhez történik (§12.2.2).

A példány hozzáférése történhet egy példány tulajdonságához, eseményéhez vagy indexelőjéhez való hozzáféréssel.

12.2.2 Kifejezések értékei

A kifejezéseket tartalmazó szerkezetek többsége végső soron megköveteli, hogy a kifejezés értéket jelöljön. Ilyen esetekben, ha a tényleges kifejezés névteret, típust, metóduscsoportot vagy semmit jelöl, fordítási időhiba lép fel. Ha azonban a kifejezés tulajdonsághozzáférést, indexelő-hozzáférést vagy változót jelöl, a tulajdonság, az indexelő vagy a változó értéke implicit módon helyettesíthető:

  • A változó értéke egyszerűen a változó által azonosított tárolási helyen tárolt érték. A változót mindenképpen hozzárendeltnek kell tekinteni (§9.4) az értékének lekértése előtt, vagy ellenkező esetben fordítási időhiba lép fel.
  • A tulajdonságelérési kifejezés értéke a tulajdonság lekérőjének meghívásával nyerhető ki. Ha a tulajdonság nem rendelkezik get hozzáférővel, fordítási idejű hiba lép fel. Ellenkező esetben a függvénytagok meghívása (§12.6.6) történik, és a meghívás eredménye a tulajdonsághozzáférési kifejezés értéke lesz.
  • Az indexelő hozzáférési kifejezés értékét a getter meghívásával szerezheti be. Ha az indexelő nem rendelkezik beolvasási kiegészítővel, fordítási időhiba lép fel. Ellenkező esetben a függvénytagok meghívása (§12.6.6) az indexelő hozzáférési kifejezéséhez társított argumentumlistával történik, a meghívás eredménye pedig az indexelő hozzáférési kifejezés értéke lesz.
  • A tuple kifejezés értékét úgy kapjuk meg, hogy alkalmazzuk az implicit tuple-átalakítást (§10.2.13) a tuple kifejezés típusára. Hiba egy olyan n-es kifejezés értékének lekérdezése, amelynek nincs típusa.

12.3 Statikus és dinamikus kötés

12.3.1 Általános

kötés az a folyamat, amely meghatározza, hogy egy művelet mire vonatkozik a kifejezések típusának vagy értékének (argumentumok, operandusok, fogadók) alapján. A metódushívás kötését például a fogadó típusa és az argumentumok határozzák meg. Az operátor kötését az operandusok típusa alapján határozzuk meg.

A C#-ban a művelet kötése általában fordítási időpontban van meghatározva, az alexpressziók fordítási idejének típusa alapján. Hasonlóképpen, ha egy kifejezés hibát tartalmaz, a rendszer a fordításkor észleli és jelenti a hibát. Ez a megközelítés statikus kötésinéven ismert.

Ha azonban egy kifejezés egy dinamikus kifejezési (azaz 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.WriteLine túlterhelését a rendszer az argumentum fordítási idő típusa alapján választja ki. Így a kötési idő fordítási idő.

A harmadik hívás dinamikusan kötött: a Console.WriteLine túlterhelését a rendszer az argumentum futásidejének típusa alapján választja ki. Ez azért történik, mert az argumentum egy dinamikus kifejezés – a fordítási idő típusa dinamikus. Így a harmadik hívás kötési ideje futásidő.

példa vége

12.3.3 Dinamikus kötés

Ez az alfejezet informatív.

A dinamikus kötés lehetővé teszi a C#-programok számára a dinamikus objektumokkal való interakciót, azaz olyan objektumokat, amelyek nem követik a C# típusú rendszer normál szabályait. A dinamikus objektumok lehetnek más, különböző típusú rendszerekkel rendelkező programozási nyelvek objektumai, vagy lehetnek olyan objektumok, amelyek programozott módon vannak beállítva, hogy saját kötési szemantikáját implementálják a különböző műveletekhez.

A dinamikus objektum saját szemantikáját megvalósító mechanizmus implementálási definícióval rendelkezik. A dinamikus objektumok egy adott interfészt implementálnak, amely jelzi a C# futásidejének, hogy speciális szemantikával rendelkeznek. Így amikor egy dinamikus objektumon végzett műveletek dinamikusan vannak megkötve, a saját kötési szemantikájuk, és nem az ebben a specifikációban megadott C#-értékeket veszi át.

Bár a dinamikus kötés célja, hogy lehetővé tegye a dinamikus objektumokkal való együttműködést, a C# lehetővé teszi a dinamikus kötést minden objektumon, függetlenül attól, hogy dinamikusak-e vagy sem. Ez lehetővé teszi a dinamikus objektumok zökkenőmentesebb integrációját, mivel a rajtuk végzett műveletek eredményei nem mindig dinamikus objektumok, de fordításkor még mindig ismeretlenek a programozó számára. Emellett a dinamikus kötés segít kiküszöbölni a hibákra hajlamos tükröződésalapú kódot, még akkor is, ha az érintett objektumok nem dinamikus objektumok.

12.3.4 Az alkifejezések típusai

Ha egy művelet statikusan van kötve, az alexpresszió típusa (például fogadó és argumentum, index vagy operandus) mindig a kifejezés fordítási idejének típusa.

Ha egy művelet dinamikusan van kötve, az alexpresszió típusa a szubexpresszió fordítási idejének típusától függően különböző módokon lesz meghatározva:

  • A fordítási idejű dinamikus részexpresszió úgy tekinthető, hogy a kifejezés ténylegesen futásidőben értékeli ki a tényleges értékének típusát.
  • Az a részexpresszió, amelynek fordítási idejű típusa típusparaméter, úgy tekinthető, hogy az futásidőben ahhoz a típushoz van kötve, amelyhez a típusparaméter kötődik.
  • Ellenkező esetben az alexpresszió fordítási idő típusúnak minősül.

12.4 Operátorok

12.4.1 Általános

A kifejezések operandusokból és operátorokból állnak. Egy kifejezés operátorai jelzik, hogy mely műveletek vonatkozzanak az operandusokra.

Példa: Az operátorok közé tartoznak például a +, -, *, /és new. 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ául x++) 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)a F metódust a irégi értékével hívjuk meg, majd a G metódust a irégi értékével hívjuk meg, végül pedig a H metódust az új i értékkel. Ez független az operátorok precedenciájától, és nem kapcsolódik hozzá. példa vége

Bizonyos operátorok túlterhelhetők. Az operátorok túlterhelése (§12.4.3) lehetővé teszi a felhasználó által definiált operátor-implementációk megadását olyan műveletekhez, amelyeknél az egyik vagy mindkét operandus felhasználó által definiált osztály vagy szerkezet típusú.

12.4.2 Operátorok elsőbbsége és asszociativitása

Ha egy kifejezés több operátort tartalmaz, az operátorok elsőbbsége az egyes operátorok kiértékelésének sorrendjét vezérli.

Megjegyzés: A x + y * z kifejezés például x + (y * z) ként van kiértékelve, mert a * operátor nagyobb elsőbbséget élvez, mint a bináris + operátor. végjegyzet

Az operátorok elsőbbséget a hozzá tartozó nyelvtani termelés definíciója határozza meg.

Megjegyzés: Egy additive_expression például vagy + operátorok által elválasztott -sorozatból áll, így a + és - operátorok alacsonyabb elsőbbséget élveznek, mint a *, /és % operátorok. végjegyzet

Megjegyzés: Az alábbi táblázat az összes operátort a legmagasabbtól a legalacsonyabbig prioritási sorrendben összegzi:

Alcikkely kategória operátorok
§12.8 Elsődleges x.y x?.y f(x) a[x] a?[x] x++ x-- x! new typeof default checked unchecked delegate stackalloc
§12.9 Unáris + - !x ~ ++x --x (T)x await x
§12.10 Többtényezős * / %
§12.10 Adalékanyag + -
§12.11 Műszak << >>
§12.12 Relációs és típustesztelés < > <= >= is as
§12.12 Egyenlőség == !=
§12.13 Logikai és &
§12.13 Logikai XOR ^
§12.13 Logikai vagy \|
§12.14 Feltételes ÉS &&
§12.14 Feltételes vagy \|\|
§12.15 és §12.16 Null egyesítés és kivételdobó kifejezés ?? throw x
§12.18 Feltételes ?:
12.21. §- és 12.19. §- Hozzárendelés és lambda kifejezés = = ref *= /= %= += -= <<= >>= &= ^= \|= => ??=

végjegyzet

Ha egy operandus két azonos elsőbbséget élvező operátor között fordul elő, az operátorok asszociativitási a műveletek végrehajtásának sorrendjét vezérli:

  • A hozzárendelési operátorok és a null szenesítő operátor kivételével minden bináris operátor bal asszociatív, ami azt jelenti, hogy a műveletek balról jobbra haladnak.

    Példa: x + y + z értékelve van mint (x + y) + z. példa vége

  • A hozzárendelési operátorok, a null egyesítő operátor és a feltételes operátor (?:) jobb-asszociatívak, ami azt jelenti, hogy a műveletek jobbról balra hajtódnak végre.

    Példa: x = y = z értékelve van mint x = (y = z). példa vége

Az elsőbbség és az asszociativitás zárójelekkel szabályozható.

Példa: x + y * z először megszorozza y-at z-gyel, majd hozzáadja az eredményt x-höz, de (x + y) * z először összeadja x-et és y-at, majd megszorozza az eredményt z-cel. példa vége

12.4.3 Operátor túlterhelése

Minden nem szereplő és bináris operátor előre meghatározott implementációkkal rendelkezik. Emellett a felhasználó által definiált implementációk úgy is bevezethetők, hogy operátori deklarációkat (§15.10) belevesznek az osztályokba és a szerkezetekbe. A felhasználó által definiált operátor-implementációk mindig elsőbbséget élveznek az előre definiált operátor-implementációkkal szemben: Az előre definiált operátor-implementációk csak abban az esetben kerülnek figyelembevételre, ha nem találhatók vonatkozó, felhasználó által definiált operátor-implementációk, amint azt a §12.4.4 és a §12.4.5meghatározza.

A túlterhelhető unáris operátorok a következők:

+ - ! (csak logikai tagadás) ~ ++ -- true false

Megjegyzés: Bár a true és a false nincsenek explicit módon használva a kifejezésekben (és ezért nem szerepelnek a 12.4.2 §precedenciatáblázatában), operátoroknak minősülnek, mivel több kifejezéskörnyezetben is hivatkoznak rájuk: logikai kifejezésekben (§12.24), valamint a feltételes (§12.18) és a feltételes logikai operátorokkal (§12.14) kapcsolatos kifejezésekben is. végjegyzet

Megjegyzés: A null-megbocsátó operátor (!, §12.8.9) nem egy túlterhelhető operátor. végjegyzet

A túlterhelhető bináris operátorok a következők:

+  -  *  /  %  &  |  ^  <<  >>  ==  !=  >  <  <=  >=

Csak a fent felsorolt operátorok terhelhetők túl. Különösen nem lehet túlterhelni a tagok hozzáférését, a metódushívást vagy a =, &&, ||, ??, ?:, =>, checked, unchecked, new, typeof, default, asés is operátorokat.

Ha egy bináris operátor túlterhelt, a megfelelő összetett hozzárendelési operátor is implicit módon túlterhelődik.

példa: Az operátor * túlterhelése egyben az operátor *=túlterhelése is. Ez részletesebben kifejtésre kerül a 12.21. §-ban. példa vége

Maga a hozzárendelési operátor (=) nem terhelhető túl. A hozzárendelések mindig egy érték egyszerű tárolását hajtják végre egy változóban (§12.21.2).

Az olyan típuskonverziós műveletek, mint a (T)x, a felhasználó által definiált átalakítások (§10.5) révén vannak túlterhelve.

Megjegyzés: A felhasználó által definiált átalakítások nem befolyásolják a is vagy as operátorok viselkedését. végjegyzet

Az elem hozzáférése (például a[x]) nem tekinthető túlterhelhető operátornak. Ehelyett a felhasználó által definiált indexelést az indexelők támogatják (§15.9).

A kifejezésekben az operátorokra operátorjelöléssel, a deklarációkban pedig függvényjelöléssel hivatkoznak. Az alábbi táblázat az operátorok és a funkcionális jelölések közötti kapcsolatot mutatja be a nemáris és a bináris operátorok esetében. Az első bejegyzésben az "op" bármelyik túlterhelhető unáris előjeles operátort jelöl. A második bejegyzésben az "op" az unáris postfix ++ és -- operátorokat jelöli. A harmadik bejegyzésben az "op" minden túlterhelhető bináris operátort jelöl.

Megjegyzés: A ++ és -- operátorok túlterhelésére példaként lásd 15.10.2. végjegyzet

operátor jelölése funkcionális jelölés
«op» x operator «op»(x)
x «op» operator «op»(x)
x «op» y operator «op»(x, y)

A felhasználó által definiált operátor-deklarációkhoz mindig legalább az egyik paraméternek az operátordeklarációt tartalmazó osztály- vagy szerkezettípusnak kell lennie.

Megjegyzés: Így nem lehetséges, hogy egy felhasználó által definiált operátor ugyanazzal az aláírással rendelkezzen, mint egy előre definiált operátor. végjegyzet

A felhasználó által definiált operátor-deklarációk nem módosíthatják az operátorok szintaxisát, elsőbbségét vagy asszociativitását.

Példa: A operátor mindig bináris operátor, mindig a 12.4.2. §-megadott elsőbbségi szinttel rendelkezik, és mindig bal asszociatív. példa vége

Megjegyzés: Bár a felhasználó által definiált operátorok bármilyen általa kívánt számítást végrehajthatnak, az intuitíven várt eredményektől eltérő eredményeket produkáló implementációkat határozottan nem javasoljuk. Az operátori == implementációjának például össze kell hasonlítania az egyenlőség két operandusát, és megfelelő bool eredményt kell visszaadnia. végjegyzet

A §12.9-től a §12.21-ig az egyes operátorok leírásai meghatározzák az operátorok előre definiált implementációit és az egyes operátorokra vonatkozó további szabályokat. A leírások a unáris operátortúlterhelés feloldását, bináris operátortúlterhelés feloldását, valamint a numerikus előléptetést, és a speciális operátordefiníciókat használják, amelyek az alábbi alfejezetekben találhatók.

12.4.4. Egyváltozós operátor túlterhelés feloldása

A «op» x vagy x «op»alakú művelet, ahol az «op» túlterhelhető egyes művelet, és x egy Xtípusú kifejezés, a következő módon kerül feldolgozásra:

  • A X által biztosított, a operator «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 a Y által a operator «op»(x, y) művelethez megadott, felhasználó által megadott jelölt operátorok készlete lesz meghatározva. A halmaz a X által biztosított jelölt operátorok és a Yá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 és Y identitáskonvertáltak, vagy ha a X és a Y egy közös alaptípusból származik, akkor a megosztott jelölt operátorok csak egyszer fordulnak elő az egyesített csoportban.
    • Ha az X és a Yközött identitásátalakítás történik, az «op»Y által biztosított Y operátor visszatérési típusa megegyezik a «op»X által biztosított X visszatérési típusával, és az «op»Y operandus típusai identitásátalakítással bírnak a megfelelő «op»X operandus típusokhoz, akkor csak «op»X van jelen a halmazban.
  • Ha 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. Ha T null értékű, T₀ a mögöttes típus; ellenkező esetben T₀ 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 argumentumlista Atekintetében, akkor a jelölt operátorok halmaza a T₀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 a T₀közvetlen alaposztálya által biztosított jelölt operátorok készlete, vagy a T₀ tényleges alaposztálya, ha T₀ típusparaméter.

12.4.7 Numerikus promóciók

12.4.7.1 Általános

Ez az alfejezet informatív.

§12.4.7 és annak alpontjai az alábbiak együttes hatásának összefoglalása:

  • az implicit numerikus konverziók szabályai (§10.2.3);
  • Az átalakítás fejlesztésének szabályai (§12.6.4.7);
  • a rendelkezésre álló számtani (§ 12.10), relációs (§ 12.12), és az integrált logikai (§12.13.2) operátorok.

A numerikus előléptetés az előre definiált nemary és bináris numerikus operátorok operandusainak bizonyos implicit konverzióit automatikusan végrehajtja. A numerikus előléptetés nem egy különálló mechanizmus, hanem a túlterhelés feloldásának hatása az előre definiált operátorokra. A numerikus előléptetés kifejezetten nem befolyásolja a felhasználó által definiált operátorok kiértékelését, bár a felhasználó által definiált operátorok hasonló hatást mutatnak.

A numerikus előléptetés példájaként vegye figyelembe a bináris * operátor előre definiált implementációit:

int operator *(int x, int y);
uint operator *(uint x, uint y);
long operator *(long x, long y);
ulong operator *(ulong x, ulong y);
float operator *(float x, float y);
double operator *(double x, double y);
decimal operator *(decimal x, decimal y);

Ha a túlterhelésfeloldási szabályok (§12.6.4) vannak alkalmazva erre az operátorkészletre, a hatás az, hogy kiválasztja az első operátort, amelyhez implicit átalakítások léteznek az operandustípusokból.

Példa: A b * sműveletnél, ahol a b egy byte és a s egy short, a túlterhelés feloldási eljárása a operator *(int, int)-t választja ki a legjobb operátornak. Így a b és a sint-re lesz átalakítva, és az eredmény típusa int. Hasonlóképpen, a i * dművelet esetében , ahol iint, és ddouble, overload felbontás a legjobb operátorként operator *(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 operandus decimaltípussá alakul, vagy kötési idő hibát tapasztal, ha a másik operandus float vagy doubletípusú.
  • Ellenkező esetben, ha bármelyik operandus doubletípusú, a másik operandus doubletípussá alakul.
  • Ellenkező esetben, ha bármelyik operandus floattípusú, a másik operandus floattípussá alakul.
  • Ellenkező esetben, ha az operandus ulongtípusú, a másik operandus ulongtípussá alakul, vagy kötési idő hiba történik, ha a másik operandus type sbyte, short, intvagy long.
  • Ellenkező esetben, ha bármelyik operandus longtípusú, a másik operandus longtípussá alakul.
  • Ellenkező esetben, ha bármelyik operandus uint típusú, a másik operandus pedig sbyte, shortvagy inttípusú, akkor a rendszer mindkét operandus longtípussá alakul.
  • Ellenkező esetben, ha bármelyik operandus uinttípusú, a másik operandus uinttí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 decimal típust a double és float típussal keveri. A szabály abból a tényből következik, hogy a decimal típus, valamint a double és float típus között nincsenek implicit konverziók. végjegyzet

Megjegyzés: Azt is vegye figyelembe, hogy az operandus nem lehet ulong típusú, ha a másik operandus aláírt integrál típusú. Ennek az az oka, hogy nincs olyan integráltípus, amely a ulong teljes tartományát, valamint az aláírt integráltípusokat képviselheti. végjegyzet

A fenti esetekben egy öntött kifejezéssel explicit módon alakítható át az egyik operandus olyan típussá, amely kompatibilis a másik operandussal.

példa: Az alábbi kódban

decimal AddPercent(decimal x, double percent) =>
    x * (1.0 + percent / 100.0);

A kötési idejű hiba azért fordul elő, mert egy decimal nem szorozható egy double-gyel. A hiba úgy oldható meg, hogy a második operandust explicit módon decimal-ra konvertáljuk, az alábbiak szerint:

decimal AddPercent(decimal x, double percent) =>
    x * (decimal)(1.0 + percent / 100.0);

példa vége

Informatív szöveg vége.

12.4.8 Emelt operátorok

emelt operátorok lehetővé teszik, hogy a nem null értékű értéktípusokon működő előre definiált és felhasználó által definiált operátorok is használhatók legyenek ezen típusok null értékű formáival. A felemelt operátorok előre meghatározott és felhasználó által meghatározott operátorokból épülnek fel, amelyek megfelelnek bizonyos követelményeknek, az alábbiak szerint:

  • A +, ++, -, --, ! (logikai negálás) és ~ operátorok esetében az operátor emelt alakja akkor létezik, ha az operandus és az eredménytípus egyaránt nem-null értékűek. A felemelt forma úgy jön létre, hogy egyetlen ? módosítót ad hozzá az operandus és az eredménytípusok számára. Az emelt operátor null értéket állít elő, ha az operandus null. Ellenkező esetben a felemelt operátor kibontja az operandust, alkalmazza az alap operátort, és becsomagolja az eredményt.
  • A bináris operátorok +, -, *, /, %, &, |, ^, <<és >>esetében létezik egy operátor felemelt formája, ha az operandus és az eredménytípus mind nem null értékű. A felemelt forma úgy készül, hogy minden operandushoz és eredmény típushoz hozzáadunk egy ? módosítót. A felemelt operátor null értéket állít elő, ha az egyik vagy mindkét operandus null (kivételt képeznek a & típusú | és bool? operátorok, ahogy azt a §12.13.5leírja). Ellenkező esetben az emelt operátor kibontja az operandusokat, alkalmazza az alapul szolgáló operátort, és becsomagolja az eredményt.
  • Az egyenlőségi operátorok == és !=esetében az operátorok emelt formája akkor létezik, ha az operandusok nem null értékűek, és ha az eredménytípus bool. A megemelt alak úgy jön létre, hogy egyetlen ? módosítót adunk hozzá minden operandustípushoz. A felemelt operátor két null értéket egyenlőnek, és a null értéket pedig nem egyenlőnek tekinti a nem-null értékekkel. Ha mindkét operandus értéke más, mint null, a kibővített operátor lebontja az operandusokat, majd a mögöttes operátort alkalmazza, hogy előállítsa a bool eredményt.
  • A relációs operátorok <, >, <=és >=esetében az operátorok emelt formája akkor létezik, ha az operandusok nem null értékűek, és az eredménytípus bool. A megemelt alak úgy jön létre, hogy egyetlen ? módosítót adunk hozzá minden operandustípushoz. Az false értéket állítja elő a felemelt operátor, ha az egyik vagy mindkét operandus null. Ellenkező esetben a felemelt operátor kibontja az operandusokat, és az alapértelmezett operátort alkalmazza a bool eredmény létrehozásához.

12.5 Tagkeresés

12.5.1 Általános

A tagkeresés az a folyamat, amelynek során a rendszer meghatározza egy név jelentését egy típus kontextusában. A tagkeresés egy kifejezés simple_name (§12.8.4) vagy egy member_access (§12.8.7) kiértékelésének részeként következhet be. Ha a simple_name vagy a member_access egy invocation_expressionprimary_expression (§12.8.10.2), akkor azt mondják, hogy a tag meg van hívva.

Ha egy tag egy metódus vagy esemény, vagy ha egy állandó, mező vagy tulajdonság akár egy delegált típus (§20), akár a dynamic típusú (§8.2.4), akkor a tagot meghívhatónak minősítjük.

A tagkeresés nemcsak a tag nevét, hanem a tag típusparamétereinek számát is figyelembe veszi, valamint azt is, hogy a tag elérhető-e. A tagkeresés céljából az általános metódusok és a beágyazott általános típusok a megfelelő deklarációkban megadott típusparaméterek számával rendelkeznek, és az összes többi tag nulla típusparaméterekkel rendelkezik.

A N típusú tagkeresés K névvel és T típusargumentumokkal a következőképpen van feldolgozva:

  • Először is N nevű akadálymentes tagok halmazát határozzuk meg:
    • Ha a T egy típusparaméter, akkor a halmaz a N-hez§15.2.5-ban elsődleges vagy másodlagos megkötésként meghatározott minden egyes típusban szereplő T nevű elérhető tagok halmazainak uniója, valamint a N-ban lévő object nevű elérhető tagok halmaza.
    • Egyébként a halmaz az §7.5 nevű elérhető tagokból áll a N-ben, beleértve az örökölt tagokat és a T nevű elérhető tagokat a N-ben. Ha a T egy konstrukciós típus, akkor a tagok halmazát a típusargumentumok helyettesítésével kapjuk meg, ahogy azt a § 15.3.3leírja. Azokat a tagokat, amelyek override módosítót tartalmaznak, kizárjuk a csoportból.
  • Ezután, ha K nulla, a program eltávolítja az összes olyan beágyazott típust, amelynek deklarációi típusparamétereket tartalmaznak. Ha K nem nulla, a rendszer eltávolítja a különböző számú típusparaméterrel rendelkező tagokat. Ha K nulla, a típusparaméterekkel rendelkező metódusok nem törlődnek, mivel a típuskövető folyamat (§12.6.3) képes lehet a típusargumentumokra következtetni.
  • Ezután, ha a rendszer meghívja a tagot, a rendszer eltávolítja az összes nem meghívható tagot a halmazból.
  • Ezután a többi tag által elrejtett tagok törlődnek a csoportból. A halmaz minden tagja S.M, ahol S az a típus, amelyben a tag M deklarálva van, a következő szabályok érvényesek:
    • Ha M állandó, mező, tulajdonság, esemény vagy számbavételi tag, akkor a S alaptípusában deklarált összes tag törlődik a készletből.
    • Ha M típusdeklaráció, akkor a S alaptípusában deklarált összes nem típus el lesz távolítva a készletből, és a M alaptípusában deklarált S azonos számú típusparaméterrel rendelkező típusdeklarációk törlődnek a készletből.
    • Ha M egy metódus, akkor a S alaptípusában deklarált nem metódustagok törlődnek a készletből.
  • Ezután az osztálytagok által elrejtett felülettagok törlődnek a készletből. Ennek a lépésnek csak akkor van hatása, ha T típusparaméter, és T-nek van egy object-től eltérő effektív alaposztálya, valamint egy nem üres effektív interfészhalmaza (§15.2.5). A halmaz minden tagja S.M, ahol S a tag M deklarálásának típusa, a következő szabályokat kell alkalmazni, ha S nem objectosztálydeklaráció:
    • Ha M állandó, mező, tulajdonság, esemény, számbavételi tag vagy típusdeklaráció, akkor az interfészdeklarációban deklarált összes tag törlődik a készletből.
    • Ha M egy metódus, akkor az illesztődeklarációban deklarált összes nem metódustag el lesz távolítva a készletből, és az illesztődeklarációban deklarált M aláírással rendelkező összes metódus el lesz távolítva a készletből.
  • Végül, miután eltávolította a rejtett tagokat, a keresés eredménye a következő lesz:
    • Ha a halmaz egyetlen olyan tagból áll, amely nem metódus, akkor ez a tag a keresés eredménye.
    • Ellenkező esetben, ha a készlet csak metódusokat tartalmaz, akkor ez a metóduscsoport a keresés eredménye.
    • Ellenkező esetben a keresés nem egyértelmű, és kötés ideje alatt hiba történik.

A típusparamétereken és interfészeken kívül más típusú tagkeresések, valamint a szigorúan egyöröklésű felületeken lévő tagkeresések esetében (az öröklési lánc minden felülete pontosan nulla vagy egy közvetlen alapfelülettel rendelkezik), a keresési szabályok hatása egyszerűen az, hogy a származtatott tagok azonos nevű vagy aláírású alaptagokat rejtenek el. Az ilyen egyes öröklődéses keresések soha nem kétértelműek. A többszörös örökléssel rendelkező felületekben történő tagkereséseknél esetlegesen felmerülő kétértelműségek a §18.4.6részben vannak leírva.

Megjegyzés: Ez a fázis csak egyfajta kétértelműséget ad. Ha a tagkeresés egy metóduscsoportot eredményez, a metóduscsoport további használata kétértelműség miatt meghiúsulhat, például 12.6.4.1 és §12.6.6.2. végjegyzet

12.5.2 Alaptípusok

Tagkeresés céljából egy T típus a következő alaptípusokkal rendelkezik:

  • Ha Tobject vagy dynamic, akkor T nem rendelkezik alaptípussal.
  • Ha T egy enum_type, a T alaptípusai a System.Enum, System.ValueTypeés objectosztálytípusok .
  • Ha T egy struct_type, a T alaptípusa a System.ValueType és a objectosztálytípus.

    Megjegyzés: Egy nullable_érték_típus egy struktúra_típus (§8.3.1). végjegyzet

  • Ha T egy class_type, akkor a T alaptípusai a Talaposztályai , beleértve a objectosztálytípust is .
  • Ha Tinterface_type, akkor a T alaptípusai az T alaptípusai és az osztálytípus objectalaptípusai.
  • Ha T egy array_type, a T alaptípusai a System.Array és a objectosztálytípusok.
  • Ha T egy delegate_type, akkor a T alaptípusai a System.Delegate és a objectosztá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és value változóként vagy értékként besorolt kifejezéseket, T egy típusként besorolt kifejezést, F egy metódus egyszerű neve, P pedig egy tulajdonság egyszerű neve.

Épít Példa Leírás
Metódushívás F(x, y) A túlterhelés feloldásával kiválasztjuk a legjobb metódust F a tartalmazó osztályban vagy struktúrában. A metódust a (x, y)paraméterlistával hívják meg. Ha a módszer nem static, a példánykifejezés this.
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 nem static. 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észben e legjobb módszer kiválasztására történik. Kötési idejű hiba akkor fordul elő, ha a metódus static. A metódus meghívása a példánykifejezéssel e és az argumentumlistával (x, y).
Tulajdonsághozzáférés P A P tulajdonság lekérési metódusa meghívásra kerül az azt tartalmazó osztályban vagy struktúrában. Fordítási időhiba akkor fordul elő, ha P csak írható. Ha P nem static, a példánykifejezés this.
P = value Az osztály vagy szerkezet P tulajdonságának beállító hozzáférőjét meghívják a(z) (value)argumentumlistával. Fordításkori hiba történik, ha P csak olvasható. Ha P nem static, a példánykifejezés this.
T.P Az osztály vagy a struktúra PT tulajdonságának megkapó metódusa meghívásra kerül. Fordítási időhiba akkor fordul elő, ha P nem static vagy ha P csak írható.
T.P = value Az P osztály vagy struct T tulajdonság beállító metódusa az argumentumlistával van meghívva (value). Fordítási időhiba akkor fordul elő, ha P nem static, vagy ha P írásvédett.
e.P A P típusa által megadott osztályban, struktúrában vagy felületen a E tulajdonság lekérési hozzáférőjét a epéldánykifejezéssel hívják meg. Kötési idejű hiba akkor fordul elő, ha P megegyezik static-gyel, vagy ha P csak írásra alkalmas.
e.P = value A P típus szerinti osztályban, struktúrában vagy interfészben a E tulajdonság 'set' metódusa a példánykifejezéssel e és az argumentumlistával (value)van meghívva. Kötési idejű hiba akkor fordul elő, ha P megegyezik static-gyel, vagy ha P írásvédett.
Eseményhozzáférés E += value Az esemény hozzáadó hozzáférője, E, meghívásra kerül az azt tartalmazó osztályban vagy szerkezetben. Ha E nem static, a példánykifejezés this.
E -= value A(z) E esemény eltávolító hozzáférőjét az azt tartalmazó osztályban vagy struktúrában meghívják. Ha E nem static, a példánykifejezés this.
T.E += value Az esemény E hozzáadó metódusa a(z) T osztályban vagy struktúrában kerül meghívásra. Kötési idejű hiba akkor fordul elő, ha E nem static.
T.E -= value A E esemény eltávolító tartozéka a(z) T osztályban vagy struktúrában van meghívva. Kötési idejű hiba akkor fordul elő, ha E nem static.
e.E += value Az E típus által megadott osztályban, szerkezetben vagy felületen az E esemény add hozzáférője a epéldánykifejezéssel meg van hívva. Kötési idejű hiba akkor fordul elő, ha Estatic.
e.E -= value Az esemény eltávolító metódusa a(z) E osztályban, szerkezetben vagy interfészben, amelyet a(z) E típus ad meg, a példánykifejezés ehasználatával van meghívva. Kötési idejű hiba akkor fordul elő, ha Estatic.
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és e és az argumentumlista (x, y)használatával kerül meghívásra. Kötési idejű hiba akkor fordul elő, ha az indexelő csak írható.
e[x, y] = value A túlterhelés feloldása a 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éssel e és az argumentumlistával (x, y, value). Kötési idejű hiba akkor fordul elő, ha az indexelő írásvédett.
Operátor-meghívás -x A túlterhelés feloldása a 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 + y A túlterhelés feloldása a legjobb bináris operátor kiválasztására vonatkozik a x és ytípusai által megadott osztályokban vagy szerkezetekben. A kijelölt operátor meghívása az argumentumlistával (x, y).
Példánykonstruktor meghívása new T(x, y) A túlterhelés feloldási folyamatát alkalmazzák a legjobb példánykonstruktor kiválasztásához az osztályban vagy a struktúrában T. A példánykonstruktor az (x, y) argumentumlistával kerül meghívásra.

végjegyzet

12.6.2 Argumentumlisták

12.6.2.1 Általános

Minden függvénytag és delegált meghívás tartalmaz egy argumentumlistát, amely tényleges értékeket vagy változóhivatkozásokat biztosít a függvénytag paramétereihez. A függvénytagok meghívásának argumentumlistájának megadására szolgáló szintaxis a függvénytagok kategóriájától függ:

  • Konstruktorok, metódusok, indexelők és meghatalmazottak esetében az argumentumok argument_listként vannak megadva az alábbiak szerint. Indexelők esetén a beállított tartozék meghívásakor az argumentumlista a hozzárendelési operátor megfelelő operandusaként megadott kifejezést is tartalmazza.

    Megjegyzés: Ez a további argumentum nem használt túlterhelésfeloldáshoz, csak a set hozzáférő meghívása során. végjegyzet

  • Tulajdonságok esetén az argumentumlista üres a get kiegészítő meghívásakor, és a hozzárendelési operátor megfelelő operandusaként megadott kifejezésből áll a beállított tartozék meghívásakor.
  • Események esetén az argumentumlista a += vagy -= operátor jobb operandusaként megadott kifejezésből áll.
  • A felhasználó által definiált operátorok esetében az argumentumlista a nem szereplő operátor egyetlen operandusából vagy a bináris operátor két operandusából áll.

A tulajdonságok (§15.7) és az események (§15.8) argumentumai mindig értékparaméterként vannak átadva (§15.6.2.2). A felhasználó által definiált operátorok (§15.10) argumentumai mindig értékparaméterként (§15.6.2.2) vagy bemeneti paraméterekként (§9.2.8) lesznek átadva. Az indexelők (§15.9) argumentumai mindig értékparaméterekként (§15.6.2.2), bemeneti paraméterekként (§9.2.8) vagy paramétertömbökként (§15.6.2.4) lesznek átadva. A kimeneti és referenciaparaméterek nem támogatottak a függvénytagok ezen kategóriáiban.

A példánykonstruktor, metódus, indexelő vagy delegált meghívás argumentumai argument_listként vannak megadva:

argument_list
    : argument (',' argument)*
    ;

argument
    : argument_name? argument_value
    ;

argument_name
    : identifier ':'
    ;

argument_value
    : expression
    | 'in' variable_reference
    | 'ref' variable_reference
    | 'out' variable_reference
    ;

A argument_list egy vagy több argumentumbóls-ből áll, vesszővel elválasztva. Minden argumentum egy választható argument_name, amelyet egy argument_valuekövet. A argument_nameargumentumotelnevezett argumentumnak nevezzük, míg a argument_name nélküli argumentumpozícióargumentum.

A argument_value a következő formák egyikét veheti fel:

  • Egy kifejezés, amely azt jelzi, hogy az argumentumot értékparaméterként adják át, vagy bemeneti paraméterré alakítják át, majd ennek megfelelően továbbítják, amint azt a (§12.6.4.2 és leírva a §12.6.2.3) határozzák meg.
  • A kulcsszó in után egy variable_reference (§9.5), amely azt jelzi, hogy az argumentum bemeneti paraméterként van átadva (§15.6.2.3.2). A változót mindenképpen hozzá kell rendelni (§9.4) ahhoz, hogy bemeneti paraméterként átadható legyen.
  • A kulcsszó ref után egy variable_reference (§9.5), amely azt jelzi, hogy az argumentum hivatkozási paraméterként van átadva (§15.6.2.3.3). A változót mindenképpen hozzárendelni (§9.4) ahhoz, hogy referenciaparaméterként átadható legyen.
  • A kulcsszó out után egy variable_reference (§9.5), amely azt jelzi, hogy az argumentum kimeneti paraméterként van átadva (§15.6.2.3.4). A függvénytagok meghívását követően a változót mindenképpen hozzárendeltnek tekintjük (§9.4), amelyben a változó kimeneti paraméterként van átadva.

Az űrlap az argumentum paraméterátadási módjának határozza meg: érték, bemeneti, hivatkozásivagy kimeneti. Azonban, mint fentebb említettük, egy értékátadási móddal rendelkező argumentum átalakítható bemeneti továbbítási móddal rendelkező argumentummá.

Ha egy illékony mezőt (15.5.4. §) bemenetként, kimenetként vagy referenciaparaméterként ad át, figyelmeztetést okoz, mivel a meghívott metódus nem tudja volatilisként kezelni a mezőt.

12.6.2.2 Megfelelő paraméterek

Az argumentumlista minden argumentumához meg kell adni egy megfelelő paramétert a meghívandó függvénytagban vagy meghatalmazottban.

Az alábbiakban használt paraméterlista a következőképpen van meghatározva:

  • Az osztályokban definiált virtuális metódusok és indexelők esetében a paraméterlista a függvénytag első deklarációjából vagy felülbírálásából lesz kiválasztva, amikor a fogadó statikus típusával kezdődik, és az alaposztályokon keresztül keres.
  • Részleges metódusok esetén a rendszer a definiált részleges metódus deklarációjának paraméterlistáját használja.
  • Az összes többi függvénytag és meghatalmazott esetében csak egyetlen paraméterlista van, amely a használt.

Az argumentumok vagy paraméterek pozícióját az argumentumlistában vagy paraméterlistában az azt megelőző argumentumok vagy paraméterek száma határozza meg.

A függvénytag-argumentumok megfelelő paraméterei a következők:

  • A példánykonstruktorok, metódusok, indexelők és delegáltak argumentumai az argument_list-ben:
    • Az a pozícióargumentum, amelyben egy paraméter a paraméterlistában ugyanazon a helyen történik, megfelel ennek a paraméternek, kivéve, ha a paraméter paramétertömb, és a függvény tagját kibontott formában hívja meg a függvény.
    • A függvénytagok kibővített formában meghívott paramétertömbjének pozícióargumentuma, amely a paraméterlista paramétertömbjének pozíciójánál vagy után fordul elő, a paramétertömb egyik elemének felel meg.
    • A névvel ellátott argumentum a paraméterlistában szereplő azonos nevű paraméternek felel meg.
    • Indexelők esetén a készlet tartozékának meghívásakor a hozzárendelési operátor jobb operandusaként megadott kifejezés megfelel a készlet tartozékdeklarációjának implicit value paraméterének.
  • Tulajdonságok esetén a getter meghívásakor nincsenek argumentumok. A készlet tartozékának meghívásakor a hozzárendelési operátor jobb operandusaként megadott kifejezés megfelel a készlet tartozékdeklarációjának implicit értékparaméterének.
  • A felhasználó által definiált nem kötelező operátorok (beleértve a konverziókat) esetében az egyetlen operandus az operátor-deklaráció egyetlen paraméterének felel meg.
  • Felhasználó által definiált bináris operátorok esetében a bal operandus az első paraméternek, a jobb operandus pedig az operátordeklaráció második paraméterének felel meg.
  • A névtelen argumentumok egyetlen paraméternek sem felelnek meg, ha egy pozíción kívüli nevű argumentumot vagy egy paramétertömbnek megfelelő elnevezett argumentumot követnek.

    Megjegyzés: Ez megakadályozza, hogy a void M(bool a = true, bool b = true, bool c = true);meghívja M(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 a c nevű paraméter harmadik pozícióban van), ezért az alábbi argumentumokat kell elnevezni. Más szavakkal, az elnevezett argumentumok, amelyek nem a mondat végén találhatóak, csak akkor engedélyezettek, ha a nevük és pozíciójuk ugyanazon megfelelő paraméterhez vezetnek. végjegyzet

12.6.2.3 Argumentumlisták futásidejű kiértékelése

A függvénytagok meghívásának futásidejű feldolgozása során (§12.6.6) az argumentumlista kifejezéseit vagy változóhivatkozásait balról jobbra sorrendben értékeli ki a rendszer az alábbiak szerint:

  • Értékargumentum esetén, ha a paraméter átadási módja érték

    • az argumentumkifejezés kiértékelése és implicit átalakítás (§10.2) végrehajtása a megfelelő paramétertípusra. Az eredményül kapott érték lesz az értékparaméter kezdeti értéke a függvénytag-meghívásban.

    • ellenkező esetben a paraméter továbbítási módja inputként van megadva. Ha az argumentum változóhivatkozás, és az argumentum típusa és a paraméter típusa között identitáskonvertálás (§10.2.2) áll fenn, az eredményül kapott tárolóhely lesz a függvénytag meghívásában szereplő paraméter által képviselt tárolási hely. Ellenkező esetben a megfelelő paraméter típusával megegyező típusú tárolóhely jön létre. Az argumentumkifejezés kiértékelése és implicit átalakítás (§10.2) végrehajtása a megfelelő paramétertípusra. Az eredményként kapott érték ezen a tárolóhelyen belül lesz tárolva. Ezt a tárolási helyet a függvénytag-meghívás bemeneti paramétere jelöli.

      Példa: A következő deklarációk és metódushívások alapján:

      static void M1(in int p1) { ... }
      int i = 10;
      M1(i);         // i is passed as an input argument
      M1(i + 5);     // transformed to a temporary input argument
      

      A M1(i) metódushívásban a i bemeneti argumentumként lesz átadva, mivel változóként van besorolva, és ugyanazzal a típussal int, mint a bemeneti paraméter. A M1(i + 5) metódushívásban létrejön egy névtelen int változó, inicializálva az argumentum értékével, majd adja át bemeneti argumentumként. Lásd 12.6.4.2. és 12.6.4.4.

      példa vége

  • Bemenet, kimenet vagy referenciaargumentum esetén a változóhivatkozás kiértékelése után az eredményül kapott tárolóhely lesz a függvénytag meghívásában szereplő paraméter által képviselt tárolási hely. Bemeneti vagy referenciaargumentum esetén a változót mindenképpen a metódushívás helyén kell hozzárendelni. Ha a változóhivatkozás kimeneti argumentumként van megadva, vagy egy reference_typetömbeleme, a rendszer futásidejű ellenőrzést végez annak ellenőrzésére, hogy a tömb elemtípusa megegyezik-e a paraméter típusával. Ha ez az ellenőrzés sikertelen, egy System.ArrayTypeMismatchException kivételt dobunk.

Megjegyzés: ez a futásidejű ellenőrzés a tömb kovariancia miatt szükséges (§17.6). végjegyzet

példa: Az alábbi kódban

class Test
{
    static void F(ref object x) {...}

    static void Main()
    {
        object[] a = new object[10];
        object[] b = new string[10];
        F(ref a[0]); // Ok
        F(ref b[1]); // ArrayTypeMismatchException
    }
}

a F második meghívása System.ArrayTypeMismatchException-t dob, mert a b tényleges elemtípusa string, és nem object.

példa vége

A metódusok, indexelők és példánykonstruktorok a legkedvezőbb paraméterüket paramétertömbnek deklarálhatják (§15.6.2.4). Az ilyen függvénytagok meghívása normál formájukban vagy kiterjesztett formájukban történik attól függően, hogy melyik alkalmazható (§12.6.4.2):

  • Ha egy paramétertömböt tartalmazó függvénytagot a rendszer normál formában hív meg, a paramétertömbhöz megadott argumentumnak egyetlen kifejezésnek kell lennie, amely implicit módon átalakítható (§10.2) a paramétertömb típusára. Ebben az esetben a paramétertömb pontosan úgy működik, mint egy értékparaméter.
  • Ha egy paramétertömböt tartalmazó függvénytagot kibontott formában hív meg, a meghívásnak nulla vagy több pozícióargumentumot kell megadnia a paramétertömbhöz, ahol minden argumentum egy implicit módon átalakítható kifejezés (§10.2) a paramétertömb elemtípusára. Ebben az esetben a meghívás létrehozza a paramétertömbtípus egy példányát, amelynek hossza megfelel az argumentumok számának, inicializálja a tömbpéldány elemeit a megadott argumentumértékekkel, és az újonnan létrehozott tömbpéldányt használja tényleges argumentumként.

Az argumentumlista kifejezéseit a rendszer mindig szöveges sorrendben értékeli ki.

példa: A példa tehát

class Test
{
    static void F(int x, int y = -1, int z = -2) =>
        Console.WriteLine($"x = {x}, y = {y}, z = {z}");

    static void Main()
    {
        int i = 0;
        F(i++, i++, i++);
        F(z: i++, x: i++);
    }
}

a kimenetet hozza létre

x = 0, y = 1, z = 2
x = 4, y = -1, z = 3

példa vége

Ha egy paramétertömböt tartalmazó függvénytagot kibontott formában, legalább egy kibontott argumentummal hívnak meg, a meghívást úgy dolgozza fel a rendszer, mintha egy tömbinicializálóval ellátott tömblétrehozási kifejezést illesztettek volna be a kibontott argumentumok köré (12.8.17.4). A rendszer üres tömböt ad át, ha a paramétertömbhöz nincsenek argumentumok; nem határozza meg, hogy az átadott hivatkozás egy újonnan lefoglalt vagy meglévő üres tömbre utal-e.

Példa: Adott a deklaráció

void F(int x, int y, params object[] args);

a metódus kibontott formájának alábbi meghívásai

F(10, 20, 30, 40);
F(10, 20, 1, "hello", 3.0);

pontosan megfelel

F(10, 20, new object[] { 30, 40 });
F(10, 20, new object[] { 1, "hello", 3.0 });

példa vége

Ha egy függvénytag választható paraméterei el vannak hagyva, a függvénytag deklaráció alapértelmezett argumentumai automatikusan átadásra kerülnek. (Ez magában foglalhatja egy tárolóhely létrehozását a fent leírtak szerint.)

Megjegyzés: Mivel ezek mindig állandóak, a kiértékelésük nem befolyásolja a fennmaradó argumentumok kiértékelését. végjegyzet

12.6.3 Típuskövetkozó

12.6.3.1 Általános

Ha egy általános metódust típusargumentumok megadása nélkül hív meg, egy típusú következtetés folyamat megpróbál típusargumentumokat következtetni a híváshoz. A típuskövetkeztetés jelenléte kényelmesebb szintaxist tesz lehetővé egy általános metódus meghívásához, és lehetővé teszi a programozó számára, hogy elkerülje a redundáns típusinformációk megadását.

Példa:

class Chooser
{
    static Random rand = new Random();

    public static T Choose<T>(T first, T second) =>
        rand.Next(2) == 0 ? first : second;
}

class A
{
    static void M()
    {
        int i = Chooser.Choose(5, 213); // Calls Choose<int>
        string s = Chooser.Choose("apple", "banana"); // Calls Choose<string>
    }
}

A típuskövetkeztetés révén a int és string típusargumentumok az argumentumoktól a metódusig vannak meghatározva.

példa vége

A típusmegkövetkezés egy metódushívás kötésidejű feldolgozásának részeként történik (§12.8.10.2), és a meghívás túlterhelésfeloldási lépése előtt történik. Ha egy metódushívásban egy adott metóduscsoport van megadva, és a metódushívás részeként nem ad meg típusargumentumokat, a rendszer típuskövető következtetést alkalmaz a metóduscsoport minden általános metódusára. Ha a típuskövetkeztetés sikeres, akkor a rendszer a következő típusargumentumokkal határozza meg a későbbi túlterhelés-feloldás argumentumtípusait. Ha a túlterhelés feloldása egy általános metódust választ a meghíváshoz, akkor a rendszer a kikövetkeztetett típusargumentumokat használja a meghívás típusargumentumaként. Ha egy adott metódus típuskövetkeztetése meghiúsul, az a metódus nem vesz részt a túlterhelés feloldásában. A típuskövetkeztetés hibája önmagában nem okoz kötés-idő hibát. Ez azonban gyakran kötési idejű hibához vezet, amikor a túlterhelés feloldása során nem találhatóak megfelelő módszerek.

Ha az egyes megadott argumentumok nem felelnek meg pontosan egy paraméternek a metódusban (§12.6.2.2), vagy van egy nem választható paraméter megfelelő argumentum nélkül, akkor a következtetés azonnal meghiúsul. Ellenkező esetben tegyük fel, hogy az általános metódus a következő aláírást tartalmazza:

Tₑ M<X₁...Xᵥ>(T₁ p₁ ... Tₓ pₓ)

Az űrlap metódushívásával M(E₁ ...Eₓ) a típuskövetkezés feladata az egyes típusparaméterekhez S₁...Sᵥ egyedi típusargumentumok keresése X₁...Xᵥ, hogy a hívás M<S₁...Sᵥ>(E₁...Eₓ) érvényessé váljon.

A típuskövetkezési folyamatot az alábbiakban algoritmusként ismertetjük. A megfelelő fordító alternatív megközelítéssel is implementálható, feltéve, hogy minden esetben ugyanazt az eredményt éri el.

A következtetési folyamat során az egyes típusparaméterek Xᵢ vagy hozzárendelve vannak egy adott típusra Sᵢ, vagy nincs hozzárendelve, és egy kapcsolódó korlátok halmazával rendelkezik . Minden egyes korlát valamilyen típus T. Kezdetben minden Xᵢ típusváltozó nincs rögzítve, és üres határértékekkel rendelkezik.

A típuskövetkeztetés fázisokban történik. Minden fázis megpróbál típusargumentumokat kikövetkezni több típusváltozóra az előző fázis eredményei alapján. Az első fázis a korlátok néhány kezdeti következtetését teszi lehetővé, míg a második fázis a típusváltozókat konkrét típusokra javítja, és további korlátokat is kikövetkeztet. Előfordulhat, hogy a második fázist többször is meg kell ismételni.

Megjegyzés: A típuskövetkeztetést más környezetekben is használják, beleértve a metóduscsoportok átalakítását is (12.6.3.14.) és a kifejezéskészletek legjobb gyakori típusát (§12.6.3.15). végjegyzet

12.6.3.2 Az első fázis

Az egyes metódusargumentumok esetében Eᵢ:

  • Ha Eᵢ névtelen függvény, akkor explicit paramétertípus-következtetés történik a Eᵢ.
  • Ellenkező esetben, ha típus , és a megfelelő paraméter egy értékparaméter (§15.6.2.2), akkor történik egy alsó határú következtetés (§12.6.3.10) -ról -ra.
  • Ellenkező esetben, ha Eᵢ típus U, és a megfelelő paraméter egy referenciaparaméter (§15.6.2.3.3), vagy kimeneti paraméter (§15.6.2.3.4), akkor pontos következtetési (§12.6.3.9) történik innenUodaTᵢ.
  • Ellenkező esetben, ha a Eᵢ típusú U, és a megfelelő paraméter egy bemeneti paraméter (§15.6.2.3.2), valamint Eᵢ egy bemeneti argumentum, akkor pontos következtetést (exact inference) vonnak le §12.6.3.9) a U-ról a Tᵢ-ra.
  • Ellenkező esetben, ha típusú van, és a megfelelő paraméter egy bemeneti paraméter (§15.6.2.3.2), akkor alsó határú következtetés történik a -től a -ba (§12.6.3.10).
  • Ellenkező esetben nincs következtetés az argumentumra vonatkozóan.

12.6.3.3 A második fázis

A második fázis a következőképpen halad:

  • Minden olyan nem rögzített típusváltozó, amely nem Xᵢ (§12.6.3.6) semmilyen másiktól, rögzítve van (Xₑ).
  • Ha ilyen típusváltozók nem léteznek, minden nem rögzített típusváltozó Xᵢrögzített, amelyekre az alábbi feltételek mindegyike igaz:
    • Legalább egy típusváltozó Xₑ van, amely attól függ Xᵢ
    • Xᵢ nem üres határok halmazával rendelkezik
  • Ha nem léteznek ilyen típusváltozók, és továbbra is van meg nem oldott típusváltozó, a típuskövetkeztetés meghiúsul.
  • Ha azonban nincsenek további nem rögzített típusváltozók, a típuskövetkeztetés sikeres lesz.
  • Ellenkező esetben, minden olyan argumentumra , amelynek a megfelelő paramétertípusa , ahol a kimeneti típusok (§12.6.3.5) fixálatlan típusváltozókat tartalmaznak , de a bemeneti típusok (§12.6.3.4) nem, egy kimeneti típusra vonatkozó következtetés (§12.6.3.7) készül ettől erre . Ezután a második fázis ismétlődik.

12.6.3.4 Bemeneti típusok

Ha a metóduscsoport vagy implicit módon megadott névtelen függvény, és a egy delegált típusú vagy egy kifejezésfa típusú, akkor a összes paramétertípusa atípusúbemeneti típus bemeneteként szolgál.

12.6.3.5 Kimeneti típusok

Ha metóduscsoport vagy névtelen függvény, és delegált típusú vagy kifejezésfa típusú, akkor a visszatérési típusa típusúkimeneti típusa.

12.6.3.6 Függőség

A nem rögzített típusváltozó Xᵢ közvetlenül függ egy másik nem rögzített típusváltozótól Xₑ, ha egy bizonyos argumentum Eᵥ, amelynek típusa Tᵥ, megjelenik egy Xₑ, amelynek típusa Eᵥ, és megjelenik egy TᵥXᵢ, amelynek típusa .

Xₑ Xᵢ attól függ, hogy XₑközvetlenülXᵢ függ-e, vagy attól, hogy XᵢközvetlenülXᵥ függ és hogy Xᵥattól függXₑ. Így az “függ”-hoz képest a “közvetlenül függ”-tól való tranzitív, de nem reflexív lezárás.

12.6.3.7 Kimeneti típus következtetései

A kimeneti típusának következtetése történik egy kifejezésből Eegy T típusra a következő módon:

  • Ha a E egy névtelen függvény, amelynek visszatérési típusa U (§12.6.3.13), és T egy delegált típus vagy kifejezésfa típus a visszatérési típussal Tₓesetén, akkor egy alsó határú következtetést (§12.6.3.10) készítenek azU-ból azTₓfelé.
  • Ellenkező esetben, ha a E metóduscsoport, és a T delegált típusú vagy kifejezésfa típusú, T₁...Tᵥ paramétertípusokkal és Tₓvisszatérési típussal rendelkezik, és ha a E tí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.8 Kifejezett paramétertípus-levezetés

A explicit paramétertípus megállapítása egy kifejezésből egy típusra a következő módon történik:

  • Ha E egy explicit módon megadott névtelen függvény U₁...Uᵥ paramétertípusokkal, és T egy delegált vagy kifejezésfa típusú V₁...Vᵥ paramétertípusokkal, akkor minden Uᵢ esetén egy pontos következtetési (§12.6.3.9) történik Uᵢa megfelelő Vᵢ-ra.

12.6.3.9 Pontos következtetések

A pontos következtetésUtípusbólV típusba az alábbiak szerint történik:

  • Ha V az egyik nem rögzítettXᵢ, akkor U hozzáadódik a Xᵢpontos határainak készletéhez.
  • Ellenkező esetben a V₁...Vₑ és a U₁...Uₑ a következő esetek bármelyikének ellenőrzésével határozható meg:
    • V tömbtípusú V₁[...] és U egy azonos rangú tömbtípus U₁[...]
    • V a V₁? típus, U pedig a U₁
    • V egy konstruált típus C<V₁...Vₑ>, és U is egy konstruált típus C<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ᵢ-ra Vᵢ.
  • Ellenkező esetben nem történik következtetés.

12.6.3.10 Alsó határú következtetés

A alsó határa aU-ből aV típusra történő következtetésnél az alábbiak szerint van megállapítva:

  • Ha V az egyik nem rögzítettXᵢ, akkor U hozzáadódik a Xᵢalsó határainak halmazához.
  • Ellenkező esetben, ha V a típus V₁?, és U a típus U₁?, akkor alsó korlát következtetés vezethető le U₁-ből V₁-re.
  • Ellenkező esetben a U₁...Uₑ és a V₁...Vₑ a következő esetek bármelyikének ellenőrzésével határozható meg:
    • V egy tömbtípus V₁[...], és U egy azonos rangú tömbtípus U₁[...]
    • V IEnumerable<V₁>, ICollection<V₁>, IReadOnlyList<V₁>>, IReadOnlyCollection<V₁> vagy IList<V₁> egyike, és U egydimenziós tömbtípus U₁[]
    • V egy class, struct, interface vagy delegate típusú C<V₁...Vₑ>, és létezik egy olyan egyedi típus C<U₁...Uₑ>, amelyre igaz, hogy U (vagy ha Uparametertípusú, akkor a tényleges alaposztálya, vagy a tényleges interfészkészlet bármely tagja) megegyezik vele, inherits belőle származik (közvetlenül vagy közvetve), vagy (közvetlenül vagy közvetve) implementálja C<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 a U és a C<T> közötti következtetéskor, mert U₁ lehet X vagy Y.)
      Ha a fentiek bármelyike érvényes, akkor következtetést vonunk le az egyes Uᵢ és a megfelelő Vᵢ között az alábbiak szerint.
    • Ha Uᵢ nem ismert referenciatípusként, akkor pontos következtetés készül.
    • Ellenkező esetben, ha a U egy tömbtípus, akkor egy alsó határon alapuló következtetés történik.
    • Ellenkező esetben, ha VC<V₁...Vₑ> akkor a következtetés a i-thC típusparaméterétől függ:
      • Ha kovariáns, akkor egy alsó határú következtetés jön létre.
      • Ha ez contravariant, akkor egy felső határú következtetési jön létre.
      • Ha az adott elem invariáns, akkor egy pontos következtetés történik.
  • Ellenkező esetben nem történik következtetés.

12.6.3.11 Felső határú következtetés

Egy típusból a típus UtípusúV felső korlátú következtetése az alábbiak szerint történik:

  • Ha V az egyik nem rögzítettXᵢ, akkor U lesz hozzáadva a Xᵢfelső határainak halmazához.
  • Ellenkező esetben a V₁...Vₑ és a U₁...Uₑ a következő esetek bármelyikének ellenőrzésével határozható meg:
    • U egy tömbtípus U₁[...], és V egy azonos rangú tömbtípus V₁[...]
    • U IEnumerable<Uₑ>, ICollection<Uₑ>, IReadOnlyList<Uₑ>, IReadOnlyCollection<Uₑ> vagy IList<Uₑ> egyike, és V egydimenziós tömbtípus Vₑ[]
    • U a U1? típus, V pedig a V1?
    • U olyan osztály, struktúra, interfész vagy delegált típus, amely C<U₁...Uₑ>, és V olyan class, struct, interface vagy delegate típus, amely közvetlenül vagy közvetetten identical-ra vonatkozik, inherits-ről származik, vagy közvetlenül vagy közvetetten egyedi típust C<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 a C<U₁>-ről a V<Q>-re. Nem történik következtetés a U₁-ról sem a X<Q>-re, sem a Y<Q>-re.)
      Ha a fentiek bármelyike érvényes, akkor következtetést vonunk le az egyes Uᵢ és a megfelelő Vᵢ között az alábbiak szerint.
    • Ha Uᵢ nem ismert referenciatípusként, akkor pontos következtetés készül.
    • Ellenkező esetben, ha V tömbtípus, akkor felső határú következtetési jön létre
    • Ellenkező esetben, ha UC<U₁...Uₑ> akkor a következtetés a i-thC típusparaméterétől függ:
      • Ha kovarianciáns, akkor felső határú következtetési jön létre.
      • Ha ez contravariant, akkor egy alsó határú következtetési jön létre.
      • Ha az adott elem invariáns, akkor egy pontos következtetés történik.
  • Ellenkező esetben nem történik következtetés.

12.6.3.12 Rögzítés

Az nem rögzített típusú változót Xᵢ a következőképpen rögzítették egy halmaz kötéseivel:

  • A jelölttípusokUₑ halmaza a Xᵢ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: A Xᵢ minden pontos korlát U értékére az összes olyan Uₑ típust eltávolítjuk a jelöltkészletből, amely nem azonos a U típussal. A jelöltkészletből eltávolításra kerül a U minden olyan típus Xᵢ, amelyhez nincs Uₑ alól implicit átváltás minden egyes alsó határ miatt. Minden felső határa U Xᵢ esetén minden olyan típus Uₑ 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ípus V, amelyhez implicit konverzió lehetséges az összes többi jelölttípusból, akkor Xᵢ rögzítve van V.
  • Ellenkező esetben a típus következtetés meghiúsul.

12.6.3.13 A következtetett visszatérési típus

A névtelen függvények F kikövetkeztethető visszatérési típusa a típuskövetkeztetés és a túlterhelés feloldása során használatos. A kikövetkeztetett visszatérési típus csak olyan névtelen függvény esetében határozható meg, amelyben az összes paramétertípus ismert, akár azért, mert explicit módon vannak megadva, névtelen függvénykonvertálással, vagy a típuskövetkeztetés során következtetve egy általános metódus meghívására.

A tényleges visszatérési típus a következőképpen határozható meg:

  • Ha a F törzse egy kifejezés, ami típusú, akkor a F tényleges visszatérési típusa az adott kifejezés típusa.
  • Ha a F törzse egy blokk, és a blokk return utasításainak kifejezéskészlete a leggyakrabban használt T (§12.6.3.15), akkor a F kikövetkzett tényleges visszatérési típusa T.
  • Ellenkező esetben a hatékony visszatérési típus nem következtethető ki F.

A -inferált visszatérési típus a következőképpen van meghatározva:

  • Ha F aszinkron, és a törzse F vagy egy semmiként besorolt kifejezés (12.2. §), vagy olyan blokk, amelyben nincsenek return kifejezések, a következtetett visszatérési típus «TaskType» (15.14.1. §).
  • Ha F aszinkron, és tényleges eredménytípusa Tvan, a kikövetkeztetett visszatérési típus «TaskType»<T>»(15.14.1.1. §).
  • Ha F nem aszinkron, és Tkikövetkeztetett tényleges visszatérési típussal rendelkezik, akkor a kikövetkeztetett visszatérési típus T.
  • 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 Select osztályban deklarált System.Linq.Enumerable bővítménymetódust:

namespace System.Linq
{
    public static class Enumerable
    {
        public static IEnumerable<TResult> Select<TSource,TResult>(
            this IEnumerable<TSource> source,
            Func<TSource,TResult> selector)
        {
            foreach (TSource element in source)
            {
                yield return selector(element);
            }
        }
   }
}

Feltéve, hogy a System.Linq névtér egy using namespace irányelvvel lett importálva, és a Customer osztály rendelkezik egy Name típusú stringtulajdonsággal, a Select módszer használható az ügyfelek listájából a nevek kiválasztására.

List<Customer> customers = GetCustomerList();
IEnumerable<string> names = customers.Select(c => c.Name);

A (bővítménymetódus-meghívása (Select)) a meghívás statikus metódusmeghívásra való átírásával történik.

IEnumerable<string> names = Enumerable.Select(customers, c => c.Name);

Mivel a típusargumentumok nincsenek explicit módon megadva, a rendszer a típusargumentumok következtetésére használja a típusargumentumokat. Először is az ügyfél érvelése a forrásparaméterhez kapcsolódik, amely arra következtet, hogy TSourceCustomerlesz. Ezután a fent ismertetett névtelen függvénytípus-következtetési folyamat során a c a Customertípust kapja, és a c.Name kifejezés a választóparaméter visszatérési típusához kapcsolódva TResultstringjelölését kapja meg. Így a meghívás egyenértékű a

Sequence.Select<Customer,string>(customers, (Customer c) => c.Name)

és az eredmény IEnumerable<string>típusú.

Az alábbi példa bemutatja, hogy a névtelen függvénytípus-következtetés hogyan teszi lehetővé a típusadatok "áramlását" egy általános metódushívás argumentumai között. Tekintettel a következő módszerre és meghívásra:

class A
{
    static Z F<X,Y,Z>(X value, Func<X,Y> f1, Func<Y,Z> f2)
    {
        return f2(f1(value));
    }

    static void M()
    {
        double hours = F("1:15:30", s => TimeSpan.Parse(s), t => t.TotalHours);
    }
}

A típuslevezetés a hívás esetében az alábbiak szerint történik: Először is az "1:15:30" argumentum az értékparaméterhez kapcsolódik, amiből az következik, hogy X lesz string. Ezután az első névtelen függvény paramétere (s) megkapja a stringtípusú besorolást, és a TimeSpan.Parse(s) kifejezés a f1visszatérési típusához kapcsolódik, ami alapján a Y értéket System.TimeSpantípusúnak következtetjük. Végül a második névtelen függvény tparaméterének megadott típusa az System.TimeSpan, és a t.TotalHours kifejezés kapcsolódik a f2visszatérési típusához, így a Z típusát double-ként származtatjuk. Így a meghívás eredménye doubletípusú.

példa vége

12.6.3.14 Metóduscsoportok konvertálásának típuskövetkeztetése

Általános metódusok hívásaihoz hasonlóan a típuskövetkeztetést is alkalmazni kell, ha egy általános metódust tartalmazó metóduscsoportot M átalakítanak egy adott delegált típus D-re (§10.8). Adott egy metódus

Tₑ M<X₁...Xᵥ>(T₁ x₁ ... Tₑ xₑ)

és a metóduscsoport M a D delegált típushoz rendelve, a típuskövetkeztetés feladata meg kell találni a típusargumentumokat S₁...Sᵥ annak érdekében, hogy a kifejezés:

M<S₁...Sᵥ>

összeegyeztethetővé válik (§20.2) a D-vel.

Az általános metódushívások típuskövető algoritmusával ellentétben ebben az esetben csak az argumentumok típusai jelennek meg, nincsenek argumentum kifejezések. Különösen nincsenek névtelen függvények, ezért nincs szükség több következtetési fázisra.

Ehelyett minden elemet nem rögzítettnek tekintünk, és alsó határra vonatkozó következtetést vonunk le minden argumentumtípus és a hozzá tartozó paramétertípus . Ha valamelyik Xᵢ-ra nincs meghatározva határ, a típuskövetkeztetés sikertelen. Ellenkező esetben az összes Xᵢrögzített a megfelelő Sᵢ, amely a típuslevezetés eredménye.

12.6.3.15 Kifejezéskészletek leggyakoribb típusának megkeresése

Bizonyos esetekben gyakori típust kell kikövetkeztetni egy kifejezéskészletre. Az implicit típusú tömbök elemtípusai és a blokk testtel rendelkező névtelen függvények visszatérési típusai ilyen módon határozhatók meg.

A kifejezéskészletek E₁...Eᵥ leggyakrabban használt típusa az alábbiak szerint van meghatározva:

  • Új nem rögzített típusváltozó X kerül bevezetésre.
  • Minden egyes kifejezésnél Ei egy kimeneti típus következtetés (§12.6.3.7) hajtódik végre, a X felé.
  • X rögzített (§12.6.3.12), ha lehetséges, és az eredményként kapott típus a legjobb közös típus.
  • Ellenkező esetben a következtetés meghiúsul.

Megjegyzés: Intuitív módon ez a következtetés egyenértékű egy metódus meghívásával, void M<X>(X x₁ ... X xᵥ) a Eᵢ argumentumként és a Xkö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 és jobb függvénytagkifejezések pontos jelentését.

12.6.4.2 Alkalmazható függvénytag

Egy függvénytagot akkor nevezünk alkalmazható függvénytagnak egy argumentumlistával A kapcsolatban, ha az alábbiak mindegyike igaz:

  • A A minden argumentuma megfelel a függvénytag-deklaráció egyik paraméterének, ahogyan a 12.6.2.2szakaszban le van írva. Legfeljebb egy argumentum felelhet meg egy paraméternek, és bármely paraméter, amelyhez nem tartozik argumentum, opcionális paraméter.
  • A 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 in módosítóval, az argumentumkifejezés típusa (ha van ilyen) és a megfelelő paraméter típusa között identitáskonvertálás történik, vagy
    • bemeneti paraméter esetén, ha a megfelelő argumentum kihagyja a in módosító értékét, az argumentumkifejezésből implicit átalakítás (§10.2) létezik az argumentumkifejezésből a megfelelő paraméter típusára.

Paramétertömböt tartalmazó függvénytagok esetén, ha a függvénytagra a fenti szabályok érvényesek, akkor azt mondjuk, hogy a normál formájábanalkalmazható. Ha egy paramétertömböt tartalmazó függvénytag nem alkalmazható a normál formájában, akkor a függvénytag alkalmazható lehet a bővített űrlapon:

  • A kibontott űrlap úgy jön létre, hogy a függvénytag-deklarációban szereplő paramétertömbet a paramétertömb elemtípusának nulla vagy több értékparaméterére cseréli, így az argumentumlistában szereplő argumentumok száma A megegyezik a paraméterek teljes számával. Ha A kevesebb argumentummal rendelkezik, mint a függvénytag-deklaráció rögzített paramétereinek száma, a függvénytag kibontott formája nem hozható létre, ezért nem alkalmazható.
  • Ellenkező esetben a kibontott űrlap akkor alkalmazható, ha a 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.

Ha az argumentumtípusból a bemeneti paraméter paramétertípusára történő implicit átalakítás dinamikus implicit átalakítás (§10.2.10), az eredmények nem lesznek meghatározva.

Példa: A következő deklarációk és metódushívások alapján:

public static void M1(int p1) { ... }
public static void M1(in int p1) { ... }
public static void M2(in int p1) { ... }
public static void Test()
{
    int i = 10; uint ui = 34U;

    M1(in i);   // M1(in int) is applicable
    M1(in ui);  // no exact type match, so M1(in int) is not applicable
    M1(i);      // M1(int) and M1(in int) are applicable
    M1(i + 5);  // M1(int) and M1(in int) are applicable
    M1(100u);   // no implicit conversion exists, so M1(int) is not applicable

    M2(in i);   // M2(in int) is applicable
    M2(i);      // M2(in int) is applicable
    M2(i + 5);  // M2(in int) is applicable
}

példa vége

  • Statikus módszer csak akkor alkalmazható, ha a metóduscsoport egy simple_name vagy member_access egy típuson keresztül származik.
  • A példánymetódus csak akkor alkalmazható, ha a metóduscsoport egy simple_name-ből, egy változón vagy értéken keresztüli member_access-ből vagy base_access-ből ered.
    • Ha a metóduscsoport egy simple_nameeredménye, a példánymetódus csak akkor alkalmazható, ha this hozzáférés engedélyezett §12.8.14.
  • Ha a metóduscsoport egy 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ől Qᵥ-re történő implicit átalakítás nem jobb, mint a Eᵥ-ről Pᵥ-re történő implicit átalakítás, és
  • legalább egy argumentum esetében a Eᵥ-ról Pᵥ-ra való átalakítás jobb, mint a Eᵥ-ról Qᵥ- 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, és Mₑ általános módszer, akkor Mᵢ jobb, mint Mₑ.
  • Ellenkező esetben, ha a Mᵢ a normál formájában alkalmazható, és Mₑ params tömbje van, és csak kibontott formában alkalmazható, akkor Mᵢ jobb, mint Mₑ.
  • 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 a Mₑparams tömbje, akkor Mᵢ jobb, mint Mₑ.
  • Ellenkező esetben, ha Mᵥ több paramétertípust tartalmaz, mint Mₓ, akkor Mᵥ jobb, mint Mₓ. Legyen {R1, R2, ..., Rn} és {S1, S2, ..., Sn} a Mᵥ és Mₓnem példányosított és nem kibontott paramétertípusainak jelölése. Mᵥparamétertípusok Mₓs-nél pontosabbak, ha minden paraméter esetében Rx nem kevésbé specifikus, mint Sx, és legalább egy paraméter esetében Rx pontosabb, mint Sx:
    • 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 a Mₓ, akkor Mᵥ jobb, mint Mₓ.
  • Ha legalább egy paraméter esetében Mᵥ a jobb paraméterátadási módot (§12.6.4.4) használja, mint a Mₓ megfelelő paramétere, és Mₓ paraméterei közül egyik sem használ jobb paraméterátadási módot, mint Mᵥ, akkor Mᵥ jobb, mint Mₓ.
  • Ellenkező esetben egyetlen függvénytag sem jobb.

12.6.4.4 Jobb paraméterátadási mód

Két túlterhelt metódus megfelelő paraméterei csak paraméterátadási módban térhetnek el, feltéve, hogy a két paraméter egyike értékátadási móddal rendelkezik, az alábbiak szerint:

public static void M1(int p1) { ... }
public static void M1(in int p1) { ... }

A int i = 10;alapján, a 12.6.4.2szerint, a M1(i) és M1(i + 5) hívások esetén mindkét túlterhelés alkalmazható. Ilyen esetekben a módszer, amely a paraméterátadás érték szerinti módját használja, a jobb paraméterátadási mód választása.

Megjegyzés: Nincs szükség ilyen választásra a bemeneti, kimeneti vagy referenciaátadási módok argumentumaihoz, mivel ezek az argumentumok csak pontosan ugyanazoknak a paraméterátadási módoknak felelnek meg. végjegyzet

12.6.4.5 Jobb átalakítás kifejezésből

Egy kifejezést C₁ típussá E konvertáló implicit átalakítás, valamint egy kifejezést T₁ típussá C₂ konvertáló implicit átalakítás esetén, E egy T₂, mint C₁, ha az alábbiak valamelyike fennáll.

  • E pontosan megegyezik T₁-gyel, és E nem pontosan egyezik meg T₂-mal (§12.6.4.6)
  • E pontosan egyezik mindkettővel vagy egyikkel sem a következőek közül: T₁ és T₂, illetve T₁ jobb konverziós cél, mint T₂ (§12.6.4.7)
  • E egy metóduscsoport (§12.2), T₁ kompatibilis (§20.4) a metóduscsoport egyetlen legjobb metódusával az átalakításhoz C₁, és T₂ nem kompatibilis a metóduscsoport egyetlen legjobb metódusával az átalakításhoz C₂.

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:

  • E Stípussal rendelkezik, és azonosítási átalakítás létezik S-ról T-ra.
  • E egy névtelen függvény, T vagy delegált típus D, vagy kifejezésfa típus Expression<D>, és az egyik a következő feltételek közül teljesül:
    • A X paraméterlistájában létezik egy E következtetett visszatérési típus D számára (§12.6.3.12), és létezik egy identitásátalakítás X-ről a D visszatérési típusára.
    • E egy async lambda, amelynek nincs visszatérési értéke, és D olyan visszatérési típussal rendelkezik, amely nem általános «TaskType»
    • Vagy E nem aszinkron, és D rendelkezik Y visszatérési típussal, vagy E aszinkron, és D rendelkezik «TaskType»<Y> visszatérési típussal (15.14.1. §), és az alábbiak egyike igaz:
      • A(z) E törzse az a kifejezés, amely pontosan megegyezik Y-jel.
      • A E törzse egy blokk, ahol minden visszatérési utasítás egy pontosan egyező kifejezést ad vissza Y

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ől T₂-ra történő implicit átalakítás létezik, és nem létezik implicit átalakítás T₂-ról T₁-ra
  • T₁ is «TaskType»<S₁>(§15.14.1), T₂ is «TaskType»<S₂>, és S₁ egy jobb konverziós cél, mint S₂
  • T₁ is «TaskType»<S₁>(§15.14.1), T₂ is «TaskType»<S₂>, és T₁ specializáltabb, mint T₂
  • T₁ S₁ vagy S₁?, ahol a S₁ aláírt integráltípus, T₂ pedig S₂ vagy S₂?, ahol a S₂ aláírás nélküli integráltípus. Kifejezetten:
    • S₁ sbyte és S₂byte, ushort, uintvagy ulong
    • S₁ short és S₂ushort, uintvagy ulong
    • S₁ int, és S₂uint, vagy ulong
    • S₁ long és S₂ulong

12.6.4.8 Túlterhelés általános osztályokban

megjegyzés: Bár a deklarált aláírásoknak egyedinek kell lenniük (8.6. §), lehetséges, hogy a típusargumentumok helyettesítése azonos aláírásokat eredményez. Ilyen esetben a túlterhelés feloldása kiválasztja az eredeti aláírások legspecifikusabb (12.6.4.3) pontját (a típusargumentumok helyettesítése előtt), ha létezik, és egyéb esetben hibát jelez. végjegyzet

példa: Az alábbi példák a szabálynak megfelelően érvényes és érvénytelen túlterheléseket mutatnak:

public interface I1<T> { ... }
public interface I2<T> { ... }

public abstract class G1<U>
{
    public abstract int F1(U u);           // Overload resolution for G<int>.F1
    public abstract int F1(int i);         // will pick non-generic

    public abstract void F2(I1<U> a);      // Valid overload
    public abstract void F2(I2<U> a);
}

abstract class G2<U,V>
{
    public abstract void F3(U u, V v);     // Valid, but overload resolution for
    public abstract void F3(V v, U u);     // G2<int,int>.F3 will fail

    public abstract void F4(U u, I1<V> v); // Valid, but overload resolution for
    public abstract void F4(I1<V> v, U u); // G2<I1<int>,int>.F4 will fail

    public abstract void F5(U u1, I1<V> v2);   // Valid overload
    public abstract void F5(V v1, U u2);

    public abstract void F6(ref U u);      // Valid overload
    public abstract void F6(out V v);
}

példa vége

12.6.5 A dinamikus taghívás fordítási idő alatti ellenőrzése

Annak ellenére, hogy egy dinamikusan kötött művelet túlterhelésfeloldása futásidőben történik, néha fordítási időben is lehet tudni azoknak a függvénytagoknak a listáját, amelyek közül túlterhelést választunk:

  • Delegált hívás esetén (§12.8.10.4) a lista egyetlen függvénytagból áll, amely ugyanazzal a paraméterlistával rendelkezik, mint a meghívás delegált_típusa .
  • A metódushívás (§12.8.10.2) esetében egy típuson vagy olyan értéken, amelynek statikus típusa nem dinamikus, a metóduscsoportban elérhető metódusok halmaza fordítási időben ismert.
  • Objektumlétrehozó kifejezés (§12.8.17.2) esetében a típushoz tartozó akadálymentes konstruktorkészlet fordítási időben ismert.
  • Az indexelő hozzáférés (§12.8.12.3) esetében a fogadóban elérhető indexelők készlete fordítási időben ismert.

Ezekben az esetekben az ismert függvénytagok halmazához tartozó minden egyes tagon korlátozott fordítási időben végzett ellenőrzést hajtanak végre annak megállapítására, hogy egyértelműen kizárható-e, hogy soha nem kerül meghívásra futásidőben. Minden egyes függvénytag F esetén összeállításra kerül egy módosított paraméter- és argumentumlista.

  • Először is, ha a F egy általános metódus, és a típusargumentumok meg lettek adva, akkor azokat a paraméterlistában szereplő típusparaméterek helyettesítik. Ha azonban nem adtak meg típusargumentumokat, nem történik ilyen helyettesítés.
  • Ezután minden olyan paraméter, amelynek a típusa nyitott (azaz tartalmaz egy típusparamétert, lásd a 8.4.3. szakaszt) a megfelelő argumentumokkal együtt elhagyásra kerül.

Ahhoz, hogy F megfeleljen az ellenőrzésnek, az alábbiak mindegyikének teljesülnie kell:

  • A F módosított paraméterlistája a §12.6.4.2szerinti módosított argumentumlistára vonatkozik.
  • A módosított paraméterlistában szereplő összes létrehozott típus megfelel a korlátozásainak (§8.4.5).
  • Ha a fenti lépésben a F típusparamétereit helyettesítették, a korlátozások teljesülnek.
  • Ha a F statikus metódus, a metóduscsoport nem származhat olyan member_access-ból, amelynek fogadója fordítási időben ismert változóként vagy értékként.
  • Ha a F egy példánymetódus, akkor a metóduscsoport nem származhat olyan tag_elérésből, amelynek fogadója fordítási időpontban ismert típus.

Ha egy jelölt sem teljesíti ezt a tesztet, fordítási időhiba lép fel.

12.6.6 Függvénytagok meghívása

12.6.6.1 Általános

Ez az alklám egy adott függvénytag meghívásának futásidejű folyamatát ismerteti. Feltételezzük, hogy egy kötési idejű folyamat már meghatározta az adott tag meghívását, esetleg túlterhelésfeloldást alkalmazva a jelölt függvénytagokra.

A meghívási folyamat leírásához a függvénytagok két kategóriába sorolhatók:

  • Statikus függvénytagok. Ezek statikus metódusok, statikus tulajdonságkiegészítők és felhasználó által definiált operátorok. A statikus függvények tagjai mindig nem virtuálisak.
  • Példányfüggvény-tagok. Ezek a példánymetódusok, példánykonstruktorok, példánytulajdonság-elérők és az indexelő-elérők. A példányfüggvény tagjai vagy nem virtuálisak, vagy virtuálisak, és mindig egy adott példányon hívják meg őket. A példányt egy példánykifejezés számítja ki, és a függvénytagon belül this (§12.8.14) válik elérhetővé. Példánykonstruktor esetén a példánykifejezés az újonnan lefoglalt objektumra vonatkozik.

A függvénytagok meghívásának futásidejű feldolgozása a következő lépésekből áll, ahol M a függvénytag, és ha M példánytag, E a példánykifejezés:

  • Ha M statikus függvénytag:

    • Az argumentumlista kiértékelése a 12.6.2 részben leírtak szerint történik.
    • M hívódik meg.
  • Máskülönben, ha a E típusa értéktípusú V, és M deklarálva van vagy felülbírálva van a V:

    • 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 esetben E változóként van besorolva.
    • Ha E nincs változóként besorolva, vagy ha V nem egy könnyen strukturált típus (§16.2.2), és E a következők egyike:
      • bemeneti paraméter (§15.6.2.3.2), vagy
      • egy readonly mező (§15.5.3), vagy
      • egy readonly referenciaváltozó vagy visszatérési érték (§9.7)

    ezután létrejön Etípusának ideiglenes helyi változója, és a E értéke hozzá lesz rendelve ehhez a változóhoz. E ezután át lesz sorolva az ideiglenes helyi változóra mutató hivatkozásként. Az ideiglenes változó this-ként érhető el M-en belül, de más módon nem. Csak akkor képes a hívó megfigyelni a változtatásokat, amelyeket E végez a M-en, ha this írható.

    • Az argumentumlista kiértékelése a 12.6.2 részben leírtak szerint történik.
    • M hívódik meg. Az E által hivatkozott változó lesz az thisáltal hivatkozott változó.
  • Egyébként:

    • E-t értékelik. Ha ez a kiértékelés kivételt okoz, a rendszer nem hajt végre további lépéseket.
    • Az argumentumlista kiértékelése a 12.6.2 részben leírtak szerint történik.
    • Ha a E típusa egy value_type, egy boxing átalakítást (§10.2.9) hajtunk végre Eclass_typekonvertálásához, és E-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önben System.Enum;, a System.ValueType.
    • A E értékét érvényesnek ellenőrzik. Ha a E értéke null, a rendszer System.NullReferenceException dob, és nem hajt végre további lépéseket.
    • A meghívásra kerülő függvénytag implementációját a következőképpen határozzuk meg:
      • Ha a E kötési idejének típusa egy interfész, a meghívandó függvénytag a Máltal hivatkozott példány futásidejű típusa által biztosított E megvalósítása. Ezt a függvénytagot az interfészleképezési szabályok (18.6.5) alkalmazásával határozzuk meg a Máltal hivatkozott példány futásidejű típusa által biztosított E megvalósításának meghatározásához.
      • Ellenkező esetben, ha M egy virtuális függvény tagja, a meghívandó függvénytag a Máltal hivatkozott példány futásidejű típusa által biztosított E megvalósítása. Ezt a függvénytagot úgy határozzuk meg, hogy a leglevezetettebb implementációjának (M) meghatározására vonatkozó szabályokat alkalmazzuk a Eáltal hivatkozott példány futásidejű típusára vonatkozóan.
      • Ellenkező esetben M nem virtuális függvénytag, és a meghívni kívánt függvénytag maga M.
    • A rendszer meghívja a fenti lépésben meghatározott függvénytag-implementációt. A E által hivatkozott objektum az ez által hivatkozott objektummá válik.

A példánykonstruktor meghívásának eredménye (§12.8.17.2) a létrehozott érték. Bármely más függvénytag meghívásának eredménye a törzséből visszaadott (13.10.5. §) érték, ha van ilyen.

12.6.6.2 Meghívások dobozos példányokon

A value_type implementált függvénytagok az adott value_type egy dobozos példányán keresztül hívhatók meg a következő helyzetekben:

  • Ha a függvénytag a class_type típustól örökölt metódus felülbírálása, és azt egy példánykifejezésen keresztül hívják meg az adott class_typeesetén.

    Megjegyzés: A class_type mindig a System.Object, System.ValueType vagy System.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 E egy n elemet tartalmazó tömbkifejezés, akkor a dekonstruálás eredménye maga a E kifejezés.
  • Ellenkező esetben, ha a E egy (T1, ..., Tn) elemű, n típusú tétel, akkor a E egy 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 E nem bontható fel.

Itt __v és __v1, ..., __vn az egyébként láthatatlan és elérhetetlen ideiglenes változókra hivatkoznak.

Megjegyzés: A dynamic típusú kifejezések nem bonthatók fel. végjegyzet

12.8 Elsődleges kifejezések

12.8.1 Általános

Az elsődleges kifejezések közé tartoznak a kifejezések legegyszerűbb formái.

primary_expression
    : literal
    | interpolated_string_expression
    | simple_name
    | parenthesized_expression
    | tuple_expression
    | member_access
    | null_conditional_member_access
    | invocation_expression
    | element_access
    | null_conditional_element_access
    | this_access
    | base_access
    | post_increment_expression
    | post_decrement_expression
    | null_forgiving_expression
    | array_creation_expression
    | object_creation_expression
    | delegate_creation_expression
    | anonymous_object_creation_expression
    | typeof_expression
    | sizeof_expression
    | checked_expression
    | unchecked_expression
    | default_value_expression
    | nameof_expression    
    | anonymous_method_expression
    | pointer_member_access     // unsafe code support
    | pointer_element_access    // unsafe code support
    | stackalloc_expression
    ;

Megjegyzés: Ez a nyelvtani szabály nem ANTLR-kész, mivel olyan kölcsönösen bal rekurzív szabályok (primary_expression, member_access, invocation_expression, element_access, post_increment_expression, post_decrement_expression, null_forgiving_expression, pointer_member_access és pointer_element_access) egy csoportjának része, amelyeket az ANTLR nem kezel. Standard technikákkal átalakíthatja a nyelvtant, hogy eltávolítsa a balrekurziót. Ez a standard nem teszi ezt meg, mivel nem minden elemzési stratégia igényli (például egy LALR-elemző nem), és ez elhomályosítaná a struktúrát és a leírást. végjegyzet

pointer_member_access (§23.6.3) és pointer_element_access (§23.6.4) csak nem biztonságos kódban érhető el (§23).

12.8.2 Literálok

A primary_expression, amely literális (§6.4.5) áll, értékként van besorolva.

12.8.3 Interpolált sztringkifejezések

Egy interpolated_string_expression$-ből, $@-ből, vagy @$-ből áll, amelyet " karaktereken belüli szöveg azonnal követ. Az idézett szövegben nulla vagy több interpoláció{ és } karakterekkel tagolva, amelyek mindegyike kifejezési és választható formázási specifikációkat tartalmaz.

Az interpolált sztringkifejezések két alakot öltenek: reguláris (interpolated_regular_string_expression) és szó szerinti (interpolated_verbatim_string_expression), amelyek lexikálisan hasonlítanak, de szemantikailag eltérnek a sztringkonstansok két formájától (§6.4.5.6).

interpolated_string_expression
    : interpolated_regular_string_expression
    | interpolated_verbatim_string_expression
    ;

// interpolated regular string expressions

interpolated_regular_string_expression
    : Interpolated_Regular_String_Start Interpolated_Regular_String_Mid?
      ('{' regular_interpolation '}' Interpolated_Regular_String_Mid?)*
      Interpolated_Regular_String_End
    ;

regular_interpolation
    : expression (',' interpolation_minimum_width)?
      Regular_Interpolation_Format?
    ;

interpolation_minimum_width
    : constant_expression
    ;

Interpolated_Regular_String_Start
    : '$"'
    ;

// the following three lexical rules are context sensitive, see details below

Interpolated_Regular_String_Mid
    : Interpolated_Regular_String_Element+
    ;

Regular_Interpolation_Format
    : ':' Interpolated_Regular_String_Element+
    ;

Interpolated_Regular_String_End
    : '"'
    ;

fragment Interpolated_Regular_String_Element
    : Interpolated_Regular_String_Character
    | Simple_Escape_Sequence
    | Hexadecimal_Escape_Sequence
    | Unicode_Escape_Sequence
    | Open_Brace_Escape_Sequence
    | Close_Brace_Escape_Sequence
    ;

fragment Interpolated_Regular_String_Character
    // Any character except " (U+0022), \\ (U+005C),
    // { (U+007B), } (U+007D), and New_Line_Character.
    : ~["\\{}\u000D\u000A\u0085\u2028\u2029]
    ;

// interpolated verbatim string expressions

interpolated_verbatim_string_expression
    : Interpolated_Verbatim_String_Start Interpolated_Verbatim_String_Mid?
      ('{' verbatim_interpolation '}' Interpolated_Verbatim_String_Mid?)*
      Interpolated_Verbatim_String_End
    ;

verbatim_interpolation
    : expression (',' interpolation_minimum_width)?
      Verbatim_Interpolation_Format?
    ;

Interpolated_Verbatim_String_Start
    : '$@"'
    | '@$"'
    ;

// the following three lexical rules are context sensitive, see details below

Interpolated_Verbatim_String_Mid
    : Interpolated_Verbatim_String_Element+
    ;

Verbatim_Interpolation_Format
    : ':' Interpolated_Verbatim_String_Element+
    ;

Interpolated_Verbatim_String_End
    : '"'
    ;

fragment Interpolated_Verbatim_String_Element
    : Interpolated_Verbatim_String_Character
    | Quote_Escape_Sequence
    | Open_Brace_Escape_Sequence
    | Close_Brace_Escape_Sequence
    ;

fragment Interpolated_Verbatim_String_Character
    : ~["{}]    // Any character except " (U+0022), { (U+007B) and } (U+007D)
    ;

// lexical fragments used by both regular and verbatim interpolated strings

fragment Open_Brace_Escape_Sequence
    : '{{'
    ;

fragment Close_Brace_Escape_Sequence
    : '}}'
    ;

A fent definiált lexikális szabályok közül hat környezetfüggő az alábbiak szerint:

szabály Kontextuális követelmények
Interpolated_Regular_String_Mid Csak egy Interpolated_Regular_String_Start után, az alábbi interpolációk között, és a megfelelő Interpolated_Regular_String_End előtt ismerhető fel.
Regular_Interpolation_Format Csak a regular_interpolation belül ismerhető fel, és ha a kezdő kettőspont (:) nincs beágyazva semmilyen zárójelbe (kerek, kapcsos vagy szögletes zárójel).
Interpolated_Regular_String_End Csak akkor ismerhető fel, ha egy Interpolated_Regular_String_Start után következik, és csak abban az esetben, ha a közbeékelkedő tokenek vagy Interpolated_Regular_String_Mids vagy olyan tokenek, amelyek a regular_interpolations részét képezhetik, beleértve az ilyen interpolációkban található interpolated_regular_string_expressions tokeneket is.
Interpolált_Szó_szerinti_Sztring_KözépenSzó_szerinti_Beszúrási_FormátumInterpolált_Szó_szerinti_Sztring_Végén E három szabály felismerése a fenti megfelelő szabályokhoz hasonlóan történik, ahol minden említett rendszeres nyelvtani szabályt a megfelelő szó szerinti szabály vált fel.

Megjegyzés: A fenti szabályok környezetérzékenyek, mivel definícióik átfedésben vannak a nyelv más elemeivel. végjegyzet

Megjegyzés: A fenti nyelvtan a környezetfüggő lexikális szabályok miatt nem alkalmas az ANTLR-rel való használatra. A többi lexergenerátorhoz hasonlóan az ANTLR támogatja a környezetfüggő lexikális szabályokat, például a lexikális módjainakhasználatát, de ez egy implementálási részlet, ezért nem része ennek a specifikációnak. végjegyzet

A interpolated_string_expression értékként van besorolva. Ha azonnal átalakul System.IFormattable vagy System.FormattableString implicit interpolált sztring konverzióval (§10.2.5), az interpolált sztringkifejezés ilyen típussá válik. Ellenkező esetben a 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 a System.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) és System.FormattableString (§C.3) dokumentációja ismerteti. Az Regular_Interpolation_Format és a Verbatim_Interpolation_Formatszabványformátumok leírásai megtalálhatók a System.IFormattable dokumentációjában (§C.4), valamint a szabványos könyvtár más típusaiban (§C). végjegyzet

A interpolation_minimum_width esetén a constant_expression implicit módon konvertálódik int. Legyen a mezőszélesség a constant_expression abszolút értéke, a igazítási legyen a constant_expressionértékének jele (pozitív vagy negatív):

  • Ha a mezőszélesség értéke kisebb vagy egyenlő a formázott sztring hosszával, a formázott sztring nem módosul.
  • Ellenkező esetben a formázott sztring üres szóköz karakterekkel van kipárnázva, így a hossza megegyezik a mező szélességével:
    • Ha az igazítás pozitív, a formázott sztring jobbra igazított a kitöltés előtagolásával,
    • Egyéb esetben a kitöltést hozzáadva balra igazítja.

Egy interpolated_string_expressionáltalános jelentését – beleértve a fenti formázást és az interpolációk kitöltését – a kifejezés metódushívásra való konvertálásával határozható meg: ha a kifejezés típusa System.IFormattable vagy System.FormattableString ez a metódus System.Runtime.CompilerServices.FormattableStringFactory.Create (§C.3), amely System.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 ≥ 1 minden számra I0 és N-1között:
    • Helykitöltő specifikáció
      • Bal oldali zárójel ({) karakter
      • A I decimális ábrázolása
      • Ezután, ha a megfelelő regular_interpolation vagy verbatim_interpolation esetén van interpolation_minimum_width, akkor vessző (,), majd a constant_expression értékének tizedes számábrázolása következik.
      • A Regular_Interpolation_Format vagy Verbatim_Interpolation_Formatkarakterei, ha vannak ilyenek, a megfelelő regular_interpolation vagy verbatim_interpolation közül.
      • Jobb oldali kapcsos zárójel (}) karakter
    • 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
  • Végül a Interpolated_Regular_String_End vagy a Interpolated_Verbatim_String_Endkarakterei.

A következő argumentumok az interpolációkból származó kifejezések, ha vannak ilyenek, sorrendben.

Ha egy interpolated_string_expression több interpolációt tartalmaz, az ezekben az interpolációkban szereplő kifejezéseket a rendszer szöveges sorrendben értékeli ki balról jobbra.

Példa:

Ez a példa a következő formátumspecifikációs funkciókat használja:

  • a X formátumspecifikációja, amely az egész számokat nagybetűs hexadecimálisként formázza,
  • egy string érték alapértelmezett formátuma maga az érték,
  • pozitív igazítási értékek, amelyek a megadott minimális mezőszélességen belül jobbra igazítják magukat,
  • negatív igazítási értékek, amelyek a megadott minimális mezőszélességen belül balra igazítanak
  • a interpolation_minimum_widthmeghatározott állandói, és
  • {{ és }}{ és } formátumban vannak formázva.

Adott:

string text = "red";
int number = 14;
const int width = -4;

Akkor:

Interpolált karakterlánc kifejezés Azonos jelentésű, mint string Érték
$"{text}" string.Format("{0}", text) "red"
$"{{text}}" string.Format("{{text}}) "{text}"
$"{ text , 4 }" string.Format("{0,4}", text) " red"
$"{ text , width }" string.Format("{0,-4}", text) "red "
$"{number:X}" string.Format("{0:X}", number) "E"
$"{text + '?'} {number % 3}" string.Format("{0} {1}", text + '?', number % 3) "red? 2"
$"{text + $"[{number}]"}" string.Format("{0}", text + string.Format("[{0}]", number)) "red[14]"
$"{(number==0?"Zero":"Non-zero")}" string.Format("{0}", (number==0?"Zero":"Non-zero")) "Non-zero"

példa vége

12.8.4 Egyszerű nevek

A simple_name egy azonosítóból állnak, amelyet opcionálisan egy típusargumentumlista követ:

simple_name
    : identifier type_argument_list?
    ;

A simple_name az I formájú vagy az I<A₁, ..., Aₑ>formájú, ahol a I egyetlen azonosító, a I<A₁, ..., Aₑ> pedig választható típus_argumentum_lista. Ha nincs megadva type_argument_list, vegye figyelembe, hogy e nulla. A simple_name kiértékelése és besorolása az alábbiak szerint történik:

  • Ha e nulla, és a simple_name egy helyi változó deklarációs területén (§7.3) belül jelenik meg, amely közvetlenül tartalmaz egy helyi változót, paramétert vagy állandót Iné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 a metódus_deklaráció attribútumain kívül, és ha a deklaráció tartalmaz egy nevű típusparamétert, akkor a egyszerű_név erre a típusparaméterre hivatkozik.
  • Ellenkező esetben minden példánytípus esetében T (§15.3.2), kezdve az azonnal belefoglalt típusdeklaráció példánytípusával, és folytatva az egyes belefoglaló osztályok vagy szerkezetdeklarációk példánytípusát (ha van):
    • Ha e nulla, és a T deklarációja tartalmaz egy Inevű típusparamétert, akkor a simple_name erre a típusparaméterre hivatkozik.
    • Ellenkező esetben, ha a taglekérdezés (I) a T-ben e típusú argumentumokkal egyezést eredményez:
      • Ha T az azonnal belefoglalt osztály vagy struktúratípus példánytípusa, és a keresés egy vagy több metódust azonosít, az eredmény egy metóduscsoport, amely a thistá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 űrlap taghozzáférésével (12.8.7. Ez csak akkor fordulhat elő, ha e nulla.
      • Ellenkező esetben az eredmény megegyezik az űrlap vagy T.Itaghozzáférésével (T.I<A₁, ..., Aₑ>).
  • Ellenkező esetben minden névtér N, kezdve azzal a névtérrel, amelyben a simple_name bekövetkezik, az egyes beágyazó névtérekkel folytatva (ha van ilyen), és a globális névtérrel végződik, a rendszer a következő lépéseket értékeli ki, amíg egy entitás nem található:
    • Ha e nulla, és I egy névtér neve N-ben, akkor következik:
      • Ha a simple_name helye egy N névtér deklarációja által van körbezárva, és a névtér-deklaráció tartalmaz egy extern_alias_directive vagy using_alias_directive-t, amely az I nevet egy névtérrel vagy típussal társítja, akkor az simple_name nem egyértelmű, és fordítási időhiba lép fel.
      • Ellenkező esetben a simple_name a IN nevű névtérre hivatkozik.
    • Ellenkező esetben, ha N olyan akadálymentes típust tartalmaz, amelynek neve I és e típusparaméterek, akkor:
      • Ha e nulla, és a simple_name előfordulása egy N névtér deklarációval van körülzárva, és ez a névtér-deklaráció tartalmaz egy extern_alias_directive vagy using_alias_directive, amely a nevet I egy névterrel vagy típussal társítja, akkor a simple_name nem egyértelmű, és fordítási időhiba lép fel.
      • Ellenkező esetben a namespace_or_type_name az adott típusargumentumokkal létrehozott típusra hivatkozik.
    • Ellenkező esetben, ha a egyszerű_név helyét egy Nnévtér deklarációja határozza meg:
      • Ha e nulla, és a névtér-deklaráció tartalmaz egy extern_alias_directive vagy using_alias_directive, amely a nevet I egy importált névtérhez vagy típushoz társítja, akkor a simple_name erre a névtérre vagy típusra hivatkozik.
      • Ellenkező esetben, ha a névtér-deklaráció using_namespace_directives-jei által importált névterek pontosan egy típust tartalmaznak, amely név I és e típusparaméterekkel rendelkezik, akkor a simple_name az adott típusargumentumokkal létrehozott típusra hivatkozik.
      • Ellenkező esetben, ha a névtér-deklaráció using_namespace_directives által importált névterek több típust tartalmaznak, amelyek név I és e típusparamétereket tartalmaznak, akkor a simple_name nem egyértelmű, és fordítási időhiba lép fel.

    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

  • Ellenkező esetben, ha e nulla, és I az azonosító _, a egyszerű_név egy egyszerű eldobás, amely a deklarációs kifejezés egyik formája (§12.17).
  • Ellenkező esetben a simple_name nincs meghatározva, és fordítási időhiba lép fel.

12.8.5 Zárójeles kifejezések

A zárójelben lévő kifejezés egy kifejezés zárójelek közé zárt.

parenthesized_expression
    : '(' expression ')'
    ;

A parenthesized_expression kiértékelésre kerül a zárójelek között kifejezés kiértékelésével. Ha a zárójelben kifejezés névteret vagy típust jelöl, fordítási időhiba lép fel. Ellenkező esetben a parenthesized_expression eredménye a tartalmazott kifejezéskiértékelésének eredménye.

12.8.6 Tuple-kifejezések

A tuple_expression egy tömböt jelöl, és két vagy több vesszővel elválasztott, opcionálisan elnevezett kifejezésekbőláll, zárójelekbe foglalva. A deconstruction_expression egy implicit módon beírt deklarációs kifejezéseket tartalmazó tuple rövidített szintaxisa.

tuple_expression
    : '(' tuple_element (',' tuple_element)+ ')'
    | deconstruction_expression
    ;
    
tuple_element
    : (identifier ':')? expression
    ;
    
deconstruction_expression
    : 'var' deconstruction_tuple
    ;
    
deconstruction_tuple
    : '(' deconstruction_element (',' deconstruction_element)+ ')'
    ;

deconstruction_element
    : deconstruction_tuple
    | identifier
    ;

A tuple_expression tuppelnek minősül.

A deconstruction_expressionvar (e1, ..., en) a tuple_expression(var e1, ..., var en) rövidített formája, és ugyanazt a viselkedést mutatja. Ez rekurzív módon vonatkozik bármely deconstruction_expressionbeágyazott deconstruction_tuple-re. A deconstruction_expression beágyazott azonosítók így egy deklarációs kifejezést vezetnek be (§12.17). Ennek eredményeképpen egy deconstruction_expression csak egy egyszerű feladat bal oldalán fordulhat elő.

A tömbkifejezésnek akkor és csak akkor van típusa, ha minden egyes elemkifejezésének Ei típusa van Ti. A típusnak ugyanolyan aritású tuple típusnak kell lennie, mint a tuple kifejezés, ahol az egyes elemeket a következők adják meg:

  • Ha a megfelelő pozícióban lévő tömbelem neve Ni, akkor a tömb típusú elem Ti Nilegyen.
  • Ellenkező esetben, ha EiNi, E.Ni vagy E?.Ni formájú, akkor a tuple típusú elemnek Ti Ni-nak, -nek kell lennie, kivéve, ha az alábbiak valamelyike fennáll:
    • A tömb egy másik elemének a neve "Ni", vagy
    • Egy név nélküli másik tuppel elem az űrlap Ni, E.Ni vagy E?.Ni, vagy
    • Ni a ItemXformátumú, ahol a X egy nem0által kezdett tizedesjegy-sorozat, amely egy tuple elem pozícióját jelölheti, és X nem az elem pozícióját jelöli.
  • 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 kinyerhető egy tuple kifejezésből úgy, hogy tuple típussá konvertálják (10.2.13 §), értékként átsorolják (12.2.2 §), vagy egy dekonstruáló hozzárendelés céljává teszik (12.21.2 §).

Példa:

(int i, string) t1 = (i: 1, "One");
(long l, string) t2 = (l: 2, null);
var t3 = (i: 3, "Three");          // (int i, string)
var t4 = (i: 4, null);             // Error: no type

Ebben a példában mind a négy tömbkifejezés érvényes. Az első kettő, t1 és t2, nem a tuple kifejezés típusát használja, hanem implicit tuple konverziót alkalmaz. A t2esetében az implicit tuple konverzió az 2-ről long- és null-ről string-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. A t4deklarációja viszont hiba: A tuppelkifejezésnek nincs típusa, mert a második elemnek nincs típusa.

if ((x, y).Equals((1, 2))) { ... };

Ez a példa azt mutatja, hogy a rekordok néha több zárójelréteghez is vezethetnek, különösen akkor, ha a rekordkifejezés az egyetlen argumentum egy metódus meghívásához.

példa vége

12.8.7 Tagok hozzáférése

12.8.7.1 Általános

A member_access egy primary_expression, egy predefined_typevagy egy qualified_alias_member, amelyet egy "." token követ, majd egy azonosító, opcionálisan egy type_argument_listkövethet.

member_access
    : primary_expression '.' identifier type_argument_list?
    | predefined_type '.' identifier type_argument_list?
    | qualified_alias_member '.' identifier type_argument_list?
    ;

predefined_type
    : 'bool' | 'byte' | 'char' | 'decimal' | 'double' | 'float' | 'int'
    | 'long' | 'object' | 'sbyte' | 'short' | 'string' | 'uint' | 'ulong'
    | 'ushort'
    ;

A qualified_alias_member gyártása a 14.8 §alatt van meghatározva.

A member_access vagy az E.I alakja, vagy az E.I<A₁, ..., Aₑ>alakja, ahol E egy primary_expression, predefined_type, vagy qualified_alias_member,I pedig egyedüli azonosító, és <A₁, ..., Aₑ> egy választható type_argument_list. Ha nincs megadva type_argument_list, vegye figyelembe, hogy e nulla.

A member_access egy típusú dynamic esetében dinamikusan kötött (§12.3.3). Ebben az esetben a fordító a taghozzáférést dynamictípusú tulajdonsághozzáférésként sorolja be. Az alábbi szabályokat a member_access jelentésének meghatározására futtatási időben alkalmazzák, a primary_expression fordítási idő típusa helyett a futtatási idő típusa alapján. Ha ez a futásidejű besorolás egy metóduscsoporthoz vezet, akkor a taghozzáférés a hívási_kifejezéselsődleges_kifejezése lesz.

A member_access kiértékelése és besorolása az alábbiak szerint történik:

  • Ha e nulla, és E egy névtér, és E egy beágyazott névteret tartalmaz, amelynek neve I, akkor az eredmény a névtér lesz.
  • Ellenkező esetben, ha E névtér, és E tartalmaz egy elérhető típust, amely név I és K típusparamétereket tartalmaz, akkor az eredmény az adott típusargumentumokkal létrehozott típus.
  • Ha E típusként van besorolva, ha E nem típusparaméter, és ha a IE típusparaméterekkel rendelkező tagkeresése (K) egyezést eredményez, akkor a E.I kiértékelése és besorolása a következőképpen történik:

    Megjegyzés: Ha egy ilyen tagkeresés eredménye metóduscsoport, és K nulla, a metóduscsoport tartalmazhat típusparaméterekkel rendelkező metódusokat. Ez lehetővé teszi, hogy az ilyen metódusok figyelembe legyenek véve a típusargumentumok következtetéséhez. végjegyzet

    • Ha I azonosít egy típust, akkor az eredmény az adott típusargumentumokkal létrehozott típus.
    • Ha I azonosít egy vagy több metódust, akkor az eredmény egy metóduscsoport, amelyhez nincs társítva példánykifejezés.
    • Ha I azonosít egy statikus tulajdonságot, akkor az eredmény egy társított példánykifejezés nélküli tulajdonsághozzáférés.
    • Ha I statikus mezőt azonosít:
      • Ha a mező csak olvasható, és a hivatkozás az osztálynak vagy struktúrának a statikus konstruktorán kívül történik, amelyben a mező deklarálva van, akkor az eredmény egy érték, nevezetesen a statikus mező I értéke a E-ben.
      • Ellenkező esetben az eredmény egy változó, nevezetesen a statikus mező IE.
    • Ha I azonosít egy statikus eseményt:
      • Ha a hivatkozás abban az osztályban vagy szerkezetben történik, amelyben az esemény deklarálva van, és az eseményt event_accessor_declarations nélkül deklarálták (§15.8.1), akkor a E.I feldolgozása pontosan úgy történik, mintha I statikus mező volna.
      • Ellenkező esetben az eredmény egy eseményhozzáférés, amelyhez nincs társított példánykifejezés.
    • Ha I azonosít egy állandót, akkor az eredmény egy érték, nevezetesen az állandó értéke.
    • Ha I enumerálási tagot azonosít, akkor az eredmény egy érték, nevezetesen az enumerálási tag értéke.
    • Ellenkező esetben a E.I érvénytelen taghivatkozás, és fordítási időhiba lép fel.
  • Ha a E tulajdonsághozzáférés, indexelőhozzáférés, változó vagy érték típusa T, és a 12.5. § szerinti tagkeresés során a IT-ben K típusargumentumokkal egyezést talál, akkor a E.I a következőképpen kerül kiértékelésre és besorolásra:
    • Először is, ha a E egy tulajdonság vagy indexelő hozzáférés, akkor a rendszer megszerzi a tulajdonság vagy indexelő hozzáférés értékét (§12.2.2), és az E-t átsorolják értékként.
    • Ha I egy vagy több metódust azonosít, akkor az eredmény egy metóduscsoport, amely a Etársított példánykifejezésével rendelkezik.
    • Ha I azonosít egy példánytulajdonságot, akkor az eredmény egy tulajdonsághozzáférés a E társított példánykifejezésével és egy társított típussal, amely a tulajdonság típusa. Ha a T osztálytípus, akkor a társított típust a tulajdonság első deklarációjából vagy felülbírálásából választják ki úgy, hogy a T elemmel kezdve az alaposztályokon keresztül keresnek.
    • Ha T egy osztálytípus, és I az adott osztálytípuspéldánymezőjét azonosítja:
      • Ha a E értéke null, akkor egy System.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 a Eáltal hivatkozott objektumban.
      • Ellenkező esetben az eredmény egy változó, nevezetesen a Iáltal hivatkozott objektumban E mező.
    • Ha T egy struct_type, és I az adott struct_typepéldánymezőjét azonosítja:
      • Ha E érték, vagy ha a mező olvasható, és a hivatkozás azon szerkezet egy példánykonstruktorán kívül történik, amelyben a mezőt deklarálják, akkor az eredmény egy érték, nevezetesen a Iáltal megadott szerkezetpéldányban E mező értéke.
      • Ellenkező esetben az eredmény egy változó, nevezetesen a Iáltal megadott szerkezetpéldányban E mező.
    • Ha I azonosít egy példányeseményt:
      • Ha a hivatkozás abban az osztályban vagy szerkezetben történik, amelyben az esemény deklarálva van, és az eseményt event_accessor_declarations nélkül deklarálták (§15.8.1), és a hivatkozás nem a a += vagy -= operátor bal oldalán fordul elő, akkor a E.I feldolgozása pontosan úgy történik, mintha I példánymező lett volna.
      • Ellenkező esetben az eredmény egy eseményhozzáférés, amely Etársított példánykifejezéssel rendelkezik.
  • 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, akkor E.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 A osztályon belül azon Color azonosító előfordulásait, amelyek a Color típusra hivatkoznak, «...»határolja el, és azok nem, amelyek a Color mezőre hivatkoznak.

példa vége

12.8.8 Null feltételes taghozzáférés

A null_conditional_member_access a member_access feltételes verziója (§12.8.7), és kötési időhiba, ha az eredménytípus void. Null feltételes kifejezés esetén, ahol az eredménytípus void lehet, lásd (§12.8.11).

A null_conditional_member_access egy primary_expression-ből áll, amelyet a két "?" és "." token követ, majd egy azonosító opcionális type_argument_list-tel, ezt követően pedig nulla vagy több dependent_access, amelyek bármelyikét egy null_forgiving_operator előzheti meg.

null_conditional_member_access
    : primary_expression '?' '.' identifier type_argument_list?
      (null_forgiving_operator? dependent_access)*
    ;
    
dependent_access
    : '.' identifier type_argument_list?    // member access
    | '[' argument_list ']'                 // element access
    | '(' argument_list? ')'                // invocation
    ;

null_conditional_projection_initializer
    : primary_expression '?' '.' identifier type_argument_list?
    ;

A null_conditional_member_access kifejezés E a következő formában van: P?.A. A E jelentése a következőképpen határozható meg:

  • Ha a P típusa null értékű:

    Legyen T a P.Value.Atípusa.

    • Ha T olyan típusparaméter, amely nem ismert referenciatípus vagy nem null értékű értéktípus, fordítási időhiba lép fel.

    • Ha T nem null értékű érték, akkor a E típusa T?, és a E jelentése megegyezik a következő jelentéssel:

      ((object)P == null) ? (T?)null : P.Value.A
      

      Kivéve, hogy a P csak egyszer lesz kiértékelve.

    • Ellenkező esetben a E típusa T, és a E jelentése megegyezik a következők jelentésével:

      ((object)P == null) ? (T)null : P.Value.A
      

      Kivéve, hogy a P csak egyszer lesz kiértékelve.

  • Egyébként:

    Legyen T a P.Akifejezés típusa.

    • Ha T olyan típusparaméter, amely nem ismert referenciatípus vagy nem null értékű értéktípus, fordítási időhiba lép fel.

    • Ha T nem null értékű érték, akkor a E típusa T?, és a E jelentése megegyezik a következő jelentéssel:

      ((object)P == null) ? (T?)null : P.A
      

      Kivéve, hogy a P csak egyszer lesz kiértékelve.

    • Ellenkező esetben a E típusa T, és a E jelentése megegyezik a következők jelentésével:

      ((object)P == null) ? (T)null : P.A
      

      Kivéve, hogy a P csak egyszer lesz kiértékelve.

Megjegyzés: Egy kifejezés alakjában:

P?.A₀?.A₁

Ha Pnull-re van kiértékelve, akkor sem A₀, sem pedig A₁ nincs kiértékelve. Ugyanez igaz, ha egy kifejezés null_conditional_member_access vagy null_conditional_element_access§12.8.13 műveletek sorozata.

végjegyzet

A null_conditional_projection_initializer a null_conditional_member_access korlátozása, és azonos szemantikával rendelkezik. Ez csak egy névtelen objektumlétrehozó kifejezésben (§12.8.17.3) vetületi inicializálóként fordul elő.

12.8.9 Null-megbocsátó kifejezések

12.8.9.1 Általános

A null-megbocsátó kifejezés értéke, típusa, besorolása (12.2. §) és biztonságos kontextusa (16.4.15. §) a primary_expression értéke, típusa, besorolása és biztonságos kontextusa.

null_forgiving_expression
    : primary_expression null_forgiving_operator
    ;

null_forgiving_operator
    : '!'
    ;

Megjegyzés: A null-megbocsátó és a logikai negáció előtagú operátorok (§12.9.4), bár ugyanazon lexikai tokennel (!) vannak jelölve, különbözőek. Csak az utóbbi felülbírálható (§15.10), a null-megbocsátó operátor definíciója rögzített. végjegyzet

Fordítási hiba, ha a null-megbocsátó operátort többször alkalmazzuk ugyanarra a kifejezésre, a zárójelek jelenléte ellenére.

példa: a következők érvénytelenek:

var p = q!!;            // error: applying null_forgiving_operator more than once
var s = ( ( m(t) ! ) )! // error: null_forgiving_operator applied twice to m(t)

példa vége

Az alszakasz fennmaradó része és a következő testvér alszakaszok feltételesen normatív jellegűek.

A statikus nullállapot-elemzést (§8.9.5) végző fordítónak meg kell felelnie az alábbi specifikációnak.

A null-megbocsátó operátor egy fordítási időben végrehajtott látszatművelet, amely a fordító statikus nullállapot-elemzésének segítésére szolgál. Két funkciója van: a fordító megállapításának felülírása, hogy egy kifejezés talán nulllehet; és felülírni egy fordítót, amely figyelmeztetést ad ki a null értékűséggel kapcsolatban.

A null-elbocsátó operátor olyan kifejezésre való alkalmazása, amelyhez a fordító statikus nullállapot-elemzése nem eredményez figyelmeztetést, nem hiba.

12.8.9.2 A "talán null" meghatározás felülírása

Bizonyos körülmények között a fordító statikus nullállapot-elemzése megállapíthatja, hogy egy kifejezés null állapotú, lehet, hogy null, és diagnosztikai figyelmeztetést ad ki, ha más információk azt jelzik, hogy a kifejezés nem lehet null értékű. A null-megbocsátó operátor ilyen kifejezésre való alkalmazása tájékoztatja a fordító statikus nullérték állapot-elemzését arról, hogy a nullérték állapot nem null, amely megakadályozza a diagnosztikai figyelmeztetést, és befolyásolhatja a folyamatban lévő elemzéseket.

példa: Vegye figyelembe a következőket:

#nullable enable
public static void M()
{
    Person? p = Find("John");                  // returns Person?
    if (IsValid(p))
    {
       Console.WriteLine($"Found {p!.Name}");  // p can't be null
    }
}

public static bool IsValid(Person? person) =>
    person != null && person.Name != null;

Ha a IsValidtrue ad vissza, a p biztonságosan meg lehet hivatkozni a Name tulajdonság eléréséhez, és az "esetleg null értékű hivatkozás feloldása" figyelmeztetés a ! használatával elnyomható.

példa vége

Példa: A null-megbocsátó operátort körültekintően kell használni, fontolja meg a következő szempontokat:

#nullable enable
int B(int? x)
{
    int y = (int)x!; // quash warning, throw at runtime if x is null
    return y;
}

Itt a null-elbocsátó operátor egy értéktípusra lesz alkalmazva, és minden figyelmeztetést felold x. Ha azonban a futásidőben xnull, akkor kivételt kapunk, mivel null nem alakítható át int-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?, lv kimeneti paraméter, és egyszerű hozzárendelést hajt végre.

A M metódus a stípusú stringváltozót, mint Assignkimeneti paramétert adja át, a fordító figyelmeztetést ad, mivel s nem nullázható változó. Mivel Assignmásodik argumentuma nem lehet null értékű, a null-elbocsátó operátor a figyelmeztetés feloldására szolgál.

példa vége

feltételesen normatív szöveg vége.

12.8.10 Hívási kifejezések

12.8.10.1 Általános

Metódus meghívására egy invocation_expression szolgál.

invocation_expression
    : primary_expression '(' argument_list? ')'
    ;

A primary_expression lehet null_forgiving_expression, kizárólag akkor, ha delegate_type.

Egy invocation_expression dinamikusan kötött (§12.3.3), ha az alábbiak közül legalább az egyik igaz:

  • A primary_expression fordítási időbeli típusa dynamic.
  • Az opcionális argument_list közül legalább egy argumentum fordítási idejű típusú dynamic.

Ebben az esetben a fordító a invocation_expression-t dynamictípusú értékként osztályozza. Az alábbi szabályokat a invocation_expression jelentésének meghatározására futásidőben alkalmazva, a primary_expression és az argumentumok futási idő típusát használjuk a fordítási idő típusa helyett. Ha a primary_expression nem rendelkezik a fordítási időben meghatározott típusú dynamic, akkor a metódus meghívása korlátozott fordítási idejű ellenőrzésen megy keresztül, ahogy az a §12.6.5részben le van írva.

Az primary_expression egy invocation_expression esetén metóduscsoportnak vagy egy delegate_typeértékének kell lennie. Ha a primary_expression metóduscsoport, akkor a invocation_expression metódushívás (§12.8.10.2). Ha a primary_expression egy delegate_typeértéke, akkor a invocation_expression egy delegált meghívás (§12.8.10.4). Ha a primary_expression sem metóduscsoport, sem a delegate_typeértéke, akkor kötési idejű hiba lép fel.

Az opcionális argument_list (12.6.2. § ) értékeket vagy változóhivatkozásokat biztosít a metódus paramétereihez.

A invocation_expression kiértékelésének eredménye a következőképpen van besorolva:

  • Ha a invocation_expression egy eredmény nélküli metódust hív meg (§15.6.1) vagy egy visszatérési érték nélküli delegáltat, az eredmény semmi. A semmiként besorolt kifejezés csak egy statement_expression (13.7. §) vagy lambda_expression törzse (§12.19) kontextusában engedélyezett. Ellenkező esetben kötési időhiba lép fel.
  • Ellenkező esetben, ha a invocation_expression egy referencia által visszatérő metódust hív meg (§15.6.1) vagy egy referencia által visszatérő delegáltat, az eredmény egy olyan változó, amely a metódus vagy a delegált visszatérési típusával összefüggő típussal rendelkezik. Ha a meghívás egy példány metódusra vonatkozik, és a fogadó 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 az T-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 az T-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ított M metódusok esetében:
    • Ha F nem általános, akkor F a következő esetekben jelölt:
      • M nem tartalmaz típusargumentumlistát, és
      • F a A (§12.6.4.2) vonatkozásában alkalmazható.
    • Ha F általános, és M nem tartalmaz típusargumentumlistát, F a következő esetekben jelölt:
      • A típusinferencia sikeres (§12.6.3), és következtetés eredményeként kapott egy típusargumentumok listáját a híváshoz.
      • Miután a kikövetkeztetett típusargumentumokat helyettesítik a megfelelő metódustípus-paraméterekkel, a F paraméterlistájában szereplő összes típus megfelel a korlátaiknak (§8.4.5), és a F paraméterlistája alkalmazható a A vonatkozásában (§12.6.4.2).
    • Ha a F általános, és M tartalmaz egy típusargumentumlistát, F akkor jelölt, ha:
      • A F ugyanannyi metódustípus-paraméterrel rendelkezik, mint amennyit a típusargumentum-listában megadtak.
      • Miután a típusargumentumokat behelyettesítették a megfelelő metódustípus-paraméterek helyére, a F paraméterlistájában szereplő összes létrehozott típus megfelel a feltételeiknek (§8.4.5), és a F paraméterlistája helyénvaló a A tekintetében (§12.6.4.2).
  • A jelölt metódusok készlete csak a leg származtatottabb típusok metódusait tartalmazza: A készletben C.F metódusok esetében, ahol C az a típus, amelyben a metódus F deklarálva van, a C alaptípusában deklarált összes metódus törlődik a készletből. Továbbá, ha C nem objectosztá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ódusokkal Mₑ, 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 névtér-deklarációban vagy fordítási egységben nem található jelöltkészlet, fordítási időhiba lép fel.
  • Ellenkező esetben a túlterhelés feloldása az 12.6.4szakaszban leírtak szerint történik. Ha nem található egyetlen legjobb módszer sem, fordítási időhiba lép fel.
  • C az a típus, amelyen belül a legjobb módszer bővítménymetódusként van deklarálva.

A C célként való használatával a metódushívás statikus metódushívásként lesz feldolgozva (§12.6.6).

Megjegyzés: A példánymetódus meghívásával ellentétben, ha a kifejezést null értékű hivatkozásra értékelik, nem dob kivételt. Ehelyett ez a null érték a bővítménymetódusnak lesz átadva, mivel az egy normál statikus metódushíváson keresztül történik. A bővítménymetódus implementálásán múlik annak eldöntése, hogyan kell válaszolni egy ilyen hívásra. végjegyzet

Az előző szabályok azt jelentik, hogy a példánymetóciók elsőbbséget élveznek a kiterjesztési módszerekkel szemben, hogy a belső névtér-deklarációkban elérhető bővítménymetelyek elsőbbséget élveznek a külső névtér-deklarációkban elérhető kiterjesztési módszerekkel szemben, és hogy a névtérben közvetlenül deklarált kiterjesztési módszerek elsőbbséget élveznek az ugyanabba a névtérbe egy névtér-irányelvvel importált bővítménymetelyekkel szemben.

Példa:

public static class E
{
    public static void F(this object obj, int i) { }
    public static void F(this object obj, string s) { }
}

class A { }

class B
{
    public void F(int i) { }
}

class C
{
    public void F(object obj) { }
}

class X
{
    static void Test(A a, B b, C c)
    {
        a.F(1);            // E.F(object, int)
        a.F("hello");      // E.F(object, string)
        b.F(1);            // B.F(int)
        b.F("hello");      // E.F(object, string)
        c.F(1);            // C.F(object)
        c.F("hello");      // C.F(object)
    }
}

A példában 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.G elsőbbséget élvez C.G-el szemben, és E.F elsőbbséget élvez mind D.F, mind C.Ffelett.

példa vége

12.8.10.4 Meghatalmazotti meghívások

Delegált meghívás esetén a primary_expression a invocation_expression részeként legyen a delegate_typeegy értéke. Továbbá , figyelembe véve , hogy a delegate_type a delegate_typeparaméterlistával megegyező paraméterlistával rendelkező függvénytagnak minősülnek , a delegate_type az invocation_expressionargument_list tekintetében alkalmazandó (12.6.4.2.

Az D(A) alakú delegált meghívás futásidejű feldolgozása, ahol a D egy primary_expression egy delegate_type-ból, és a A egy választható argument_list, a következő lépésekből áll:

  • D-t értékelik. Ha ez a kiértékelés kivételt okoz, a rendszer nem hajt végre további lépéseket.
  • Az argumentumlistát A kiértékeljük. Ha ez a kiértékelés kivételt okoz, a rendszer nem hajt végre további lépéseket.
  • A D értékét érvényesnek ellenőrzik. Ha a D értéke null, a rendszer System.NullReferenceException dob, és nem hajt végre további lépéseket.
  • Ellenkező esetben a D egy delegált példányra mutató hivatkozás. A függvénytag-meghívások (§12.6.6) a meghatalmazott meghívási listájában szereplő összes meghívható entitáson végrehajthatók. Egy példányból és példány metódusából álló meghívható entitások esetében a hívási példány a meghívható entitásban található példány.

A paraméterek nélküli több meghívási lista részleteiért lásd 20.6.

12.8.11 Nulla feltételes meghívási kifejezés

A null_conditional_invocation_expression szintaktikailag egy null_conditional_member_access (§12.8.8) vagy null_conditional_element_access (§12.8.13), ahol a végső dependent_access egy meghívási kifejezés (§12.8.10).

A null_conditional_invocation_expression egy statement_expression (§13.7), anonymous_function_body (§12.19.1) vagy method_body (§15.6.1) környezetében történik.

A szintaktikailag egyenértékű null_conditional_member_access vagy null_conditional_element_accessellentétben a null_conditional_invocation_expression semmiként besorolható.

null_conditional_invocation_expression
    : null_conditional_member_access null_forgiving_operator? '(' argument_list? ')'
    | null_conditional_element_access null_forgiving_operator? '(' argument_list? ')'
    ;

A választható null_forgiving_operator csakis akkor vehető fel, ha a null_conditional_member_access vagy null_conditional_element_access rendelkezik egy delegate_type.

A null_conditional_invocation_expression kifejezés 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. Legyen PA a P és Aösszefűzése.

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 E semmiként van besorolva, akkor a jelentése megegyezik a blokkjelentésével:

    { if ((object)P != null) PA; }
    

    kivéve, hogy a P csak egyszer lesz kiértékelve.

  • Ellenkező esetben a E jelentése megegyezik a blokkjelentésével:

    { return E; }
    

    és a blokk jelentése attól függ, hogy a E szintaktikailag egyenértékű-e egy null_conditional_member_access (§12.8.8) vagy null_conditional_element_access (§12.8.13).

12.8.12 Elem-hozzáférés

12.8.12.1 Általános

A element_access egy primary_expression-ből, majd egy "[" tokenből, majd egy argument_list-ből, majd egy "]" tokenből áll. A argument_list egy vagy több argumentumbóls-ből áll, vesszővel elválasztva.

element_access
    : primary_expression '[' argument_list ']'
    ;

A primary_expression felismerésekor, ha a element_access és a pointer_element_access (23.6.4. §) is alkalmazható, akkor az utóbbit kell választani, ha a beágyazott primary_expression mutatótípusú (23.3. §).

A element_accessargument_list nem tartalmazhat out vagy ref argumentumokat.

A element_access primary_expression nem lehet array_creation_expression, kivéve, ha array_initializer vagy stackalloc_expression tartalmaz, kivéve, ha stackalloc_initializer tartalmaz.

Megjegyzés: Ez a korlátozás arra vonatkozik, hogy letiltsa a potenciálisan zavaró kódot, például:

    var a = new int[3][1];

amely egyébként a következőképpen értelmezendő:

    var a = (new int[3])[1];

Hasonló korlátozás vonatkozik null_conditional_element_access (12.8.13. §). végjegyzet

Egy element_access dinamikusan lekötött (§12.3.3), ha az alábbiak közül legalább az egyik igaz:

  • A primary_expression fordítási időbeli típusa dynamic.
  • A argument_list legalább egy kifejezése fordítási idő típusú dynamic , és a primary_expression nem rendelkezik tömbtípussal.

Ebben az esetben a fordító a element_accessdynamictípusú értékként sorolja be. Az alábbi szabályok a element_access jelentésének meghatározására futásidőben lesznek alkalmazva, a primary_expression és argument_list kifejezések fordítási időtípusa helyett a futásidő típusának dynamic használatával. Ha a primary_expression nem rendelkezik fordítási idő típussal dynamic, akkor az elemhozzáférés átesik egy korlátozott fordítási idő ellenőrzésen, ahogy azt a §12.6.5 leírja.

Ha az element_accessprimary_expression egy array_type értéke, akkor a element_access tömbelérés (12.8.12.2. §). Ellenkező esetben a primary_expression egy egy vagy több indexelőtaggal rendelkező osztály, szerkezet vagy interfésztípus változója vagy értéke, amely esetben a element_access indexelő hozzáférés (12.8.12.3. §).

12.8.12.2 Tömb hozzáférés

Tömbhozzáférés esetén az primary_expression az element_access egy array_type értékének kell lennie. Ezenkívül a tömbhozzáférés argument_list nem tartalmazhat nevesített argumentumokat. A argument_list kifejezéseinek száma megegyezik a array_typerangjának számával, és minden kifejezésnek int, uint, longvagy ulong, kell lennie, vagy implicit módon átalakíthatónak kell lennie egy vagy több ilyen típusra.

A tömbhozzáférés kiértékelésének eredménye a tömb elemtípusának változója, nevezetesen a argument_list kifejezéseinek értékei által kiválasztott tömbelem.

Az űrlap P[A]tömbhozzáférésének futásidejű feldolgozása , amely P egy array_type primary_expression és Aegy argument_list, a következő lépésekből áll:

  • P-t értékelik. Ha ez a kiértékelés kivételt okoz, a rendszer nem hajt végre további lépéseket.
  • A argument_list indexkifejezéseinek kiértékelése sorrendben történik, balról jobbra. Az egyes indexkifejezések kiértékelését követően implicit átalakítást (§10.2) hajtunk végre a következő típusok egyikére: int, uint, long, ulong. A lista első olyan típusát választja ki, amelyhez implicit konverzió tartozik. Ha például az indexkifejezés short típusú, akkor implicit átalakítást hajt végre int, mivel a short-ről int- és short-ról long implicit átalakításra is lehetőség van. Ha egy indexkifejezés vagy az azt követő implicit átalakítás kiértékelése kivételt okoz, akkor a rendszer nem értékel ki további indexkifejezéseket, és nem hajt végre további lépéseket.
  • A P értékét érvényesnek ellenőrzik. Ha a P értéke null, a rendszer System.NullReferenceException dob, és nem hajt végre további lépéseket.
  • A argument_list minden egyes kifejezésének értéke a Páltal hivatkozott tömbpéldány egyes dimenzióinak tényleges határaival van összevetve. Ha egy vagy több érték kívül esik a tartományon, a rendszer System.IndexOutOfRangeException dob, és nem hajt végre további lépéseket.
  • A rendszer kiszámítja az indexkifejezések által megadott tömbelem helyét, és ez a hely lesz a tömbhozzáférés eredménye.

12.8.12.3 Indexelői hozzáférés

Indexelő hozzáférés esetén a element_access primary_expression egy osztály, szerkezet vagy interfésztípus változója vagy értéke, és ennek a típusnak egy vagy több indexelőt kell implementálnia, amelyek a element_access argument_list tekintetében alkalmazhatók.

Az P[A] formájú indexelői hozzáférés kötésidejű feldolgozása, ahol P egy osztály, struktúra vagy interfész típusú primary_expression, és T egy A, a következő lépésekből áll:

  • A T által biztosított indexelők készlete létre van állítva. A készlet a T-ban deklarált összes indexből vagy a T alaptípusú összes indexből áll, amelyek nem felüldeklarációk, és hozzáférhetőek a jelenlegi környezetben (§7.5).
  • A készlet azokra az indexelőkre lesz csökkentve, amelyek alkalmazhatók, és nem rejtik el más indexelők. A rendszer a következő szabályokat alkalmazza a készletben lévő összes indexelőre S.I, ahol S az indexelő I deklarált típusa:
    • Ha I nem alkalmazható a A-hez (§12.6.4.2), akkor I kikerül a halmazból.
    • Ha I a A (§12.6.4.2) vonatkozásában alkalmazható, akkor minden a S egy alaptípusában deklarált indexelő törlődik a készletből.
    • Ha IA vonatkozásában alkalmazható (§12.6.4.2), és ha S nem objectosztálytípus, akkor az interfészben deklarált összes indexelő el lesz távolítva az indexrendszerből.
  • Ha az eredményként kapott jelölt indexelők készlete üres, akkor nem léteznek megfelelő indexelők, és kötési idő hiba lép fel.
  • A jelölt indexelők halmazának legjobb indexelője a túlterhelési feloldási szabályok alkalmazásával azonosítható a 12.6.4szerint. Ha egyetlen legjobb indexelő nem azonosítható, az indexelő hozzáférése nem egyértelmű, és kötési idejű hiba lép fel.
  • A argument_list indexkifejezéseinek kiértékelése sorrendben történik, balról jobbra. Az indexelői hozzáférés feldolgozásának eredménye egy indexelő hozzáférésként besorolt kifejezés. Az indexelő hozzáférési kifejezés a fenti lépésben meghatározott indexelőre hivatkozik, és rendelkezik a P társított példánykifejezésével, a Atársított argumentumlistával, valamint annak típusával, ami az indexelő típusa. Ha T egy osztály típus, akkor a társított típust az indexelő első deklarációjából vagy felülírásából választják ki, amit úgy találnak meg, hogy a keresést T-től kezdik, és végighaladnak az alaposztályokon.

Attól függően, hogy milyen környezetben használják, az indexelő hozzáférése az indexelő lekéréses tartozékának vagy a beállított tartozékának meghívását okozza. Ha az indexelő hozzáférés egy hozzárendelés célja, a beállító metódus kerül meghívásra új érték hozzárendeléséhez (12.21.2). Minden más esetben a get kiegészítő meghívása történik az aktuális érték lekéréséhez (§12.2.2).

12.8.13 Null conditionális elem elérés

A null_conditional_element_access egy primary_expression-ből áll, amelyet a „?” és „[” tokenek követnek, majd egy argument_list, egy „]” token, végül pedig nullás vagy több dependent_access, amelyek közül bármelyik null_forgiving_operator-rel előzhető meg.

null_conditional_element_access
    : primary_expression '?' '[' argument_list ']'
      (null_forgiving_operator? dependent_access)*
    ;

A null_conditional_element_access argumentumlista nem tartalmazhat és out argumentumokat.

A primary_expression egy null_conditional_element_access esetén nem lehet egy array_creation_expression, hacsak nem tartalmaz egy array_initializer, vagy egy stackalloc_expression, hacsak nem tartalmaz egy stackalloc_initializer.

Megjegyzés: Ez a korlátozás a potenciálisan zavaró kód letiltására vonatkozik. Hasonló korlátozás vonatkozik a element_access (12.8.12. §), ahol a kizárt adatokra lehet példa. végjegyzet

A null_conditional_element_access a element_access feltételes verziója (§12.8.12), és kötési időhiba, ha az eredménytípus void. Null feltételes kifejezés esetén, ahol az eredménytípus void lehet, lásd (§12.8.11).

A null_conditional_element_access kifejezés a következő formában van 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 P típusa null értékű:

    Legyen T a P.Value[A]Bkifejezés típusa.

    • Ha T olyan típusparaméter, amely nem ismert referenciatípus vagy nem null értékű értéktípus, fordítási időhiba lép fel.

    • Ha T nem null értékű érték, akkor a E típusa T?, és a E jelentése megegyezik a következő jelentéssel:

      ((object)P == null) ? (T?)null : P.Value[A]B
      

      Kivéve, hogy a P csak egyszer lesz kiértékelve.

    • Ellenkező esetben a E típusa T, és a E jelentése megegyezik a következők jelentésével:

      ((object)P == null) ? null : P.Value[A]B
      

      Kivéve, hogy a P csak egyszer lesz kiértékelve.

  • Egyébként:

    Legyen T a P[A]Bkifejezés típusa.

    • Ha T olyan típusparaméter, amely nem ismert referenciatípus vagy nem null értékű értéktípus, fordítási időhiba lép fel.

    • Ha T nem null értékű érték, akkor a E típusa T?, és a E jelentése megegyezik a következő jelentéssel:

      ((object)P == null) ? (T?)null : P[A]B
      

      Kivéve, hogy a P csak egyszer lesz kiértékelve.

    • Ellenkező esetben a E típusa T, és a E jelentése megegyezik a következők jelentésével:

      ((object)P == null) ? null : P[A]B
      

      Kivéve, hogy a P csak egyszer lesz kiértékelve.

Megjegyzés: Egy kifejezés alakjában:

P?[A₀]?[A₁]

Ha P kiértékelése null, akkor sem A₀, sem A₁ 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 this egy osztály példánykonstruktorán belül elsődleges_kifejezés formájában van használva, akkor értékként lesz besorolva. Az érték típusa annak az osztálynak a példánytípusa (§15.3.2), amelyben a használat történik, és az érték a létrehozandó objektumra mutató hivatkozás.
  • Ha a this-t egy osztály példány metódusában vagy példány hozzáférőjében lévő primary_expression-ben használják, az értékként lesz besorolva. Az érték típusa annak az osztálynak a példánytípusa (§15.3.2), amelyen belül a használat történik, és az érték annak az objektumnak a hivatkozása, amelyre a metódust vagy tartozékot meghívták.
  • Ha a this egy struktúra példánykonstruktorában lévő elsődleges_kifejezés használva van, akkor változóként kerül besorolásra. A változó típusa annak a szerkezetnek a példánytípusa (§15.3.2), amelyen belül a használat történik, és a változó a felépítendő szerkezetet jelöli.
    • Ha a konstruktor deklarációja nem rendelkezik konstruktor inicializálóval, a this változó pontosan ugyanúgy viselkedik, mint a szerkezettípus kimeneti paramétere. Ez különösen azt jelenti, hogy a változót mindenképpen hozzá kell rendelni a példánykonstruktor minden végrehajtási útvonalához.
    • Ellenkező esetben a this változó pontosan ugyanúgy viselkedik, mint a struktúratípus ref paramétere. Ez különösen azt jelenti, hogy a változót kezdetben hozzárendeltnek tekintik.
  • Ha a this-t egy struktúra példánymódszerén vagy példányhozzáférőjén belüli elsődleges_kifejezés-ként használják, azt változóként sorolják be. A változó típusa annak a szerkezetnek a példánytípusa (§15.3.2), amelyen belül a használat történik.
    • Ha a metódus vagy tartozék nem iterátor (15.15.§) vagy aszinkron függvény (15.14.§), a this változó azt a szerkezetet jelöli, amelyre a metódust vagy tartozékot meghívták.
      • Ha a szerkezet egy readonly struct, a this változó pontosan ugyanúgy viselkedik, mint a struktúratípus bemeneti paramétere.
      • Ellenkező esetben a this változó pontosan ugyanúgy viselkedik, mint a struktúratípus ref paramétere
    • Ha a metódus vagy tartozék egy iterátor vagy aszinkron függvény, a this változó annak a szerkezetnek a másolatát jelöli, amelyhez a metódust vagy tartozékot meghívták, és pontosan ugyanúgy viselkedik, mint egy érték szerkezettípus paramétere.

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 a base önmagában nem kifejezés. Ez a kulcsszó csak egy base_access vagy constructor_initializer kontextusában használatos (§15.11.2). végjegyzet

12.8.16 Postfix növekményes és csökkentő operátorok

post_increment_expression
    : primary_expression '++'
    ;

post_decrement_expression
    : primary_expression '--'
    ;

Egy postfix növekmény vagy csökkentés művelet operandusa olyan kifejezés kell legyen, amely változóként, tulajdonsághozzáférésként vagy indexelőként van besorolva. A művelet eredménye az operandussal azonos típusú érték.

Ha a primary_expression fordítási idejű típusú dynamic, akkor az operátor dinamikusan kötött (§12.3.3), a post_increment_expression vagy post_decrement_expression fordítási idejű típusú dynamic, és a következő szabályokat alkalmazzák futásidőben a primary_expressionfutásidejű típusának használatával.

Ha a postfix inkrementálási vagy dekrementálási művelet operandusa egy tulajdonság vagy indexelő hozzáférés, akkor a tulajdonságnak vagy az indexelőnek rendelkeznie kell mind get, mind set hozzáférőkkel. Ha nem ez a helyzet, kötési idő hiba lép fel.

Az egyoperandusú operátor túlterhelésének feloldását (§12.4.4) alkalmazzák egy adott operátor-megvalósítás kiválasztására. Az előre definiált ++ és -- operátorok a következő típusokhoz léteznek: sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimalés bármely számtípus. Az előre definiált ++ operátorok az operandushoz 1 hozzáadásával előállított értéket, az előre definiált -- operátorok pedig az operandusból 1 kivonásával előállított értéket adja vissza. Ha az összeadás vagy kivonás eredménye az eredménytípus tartományán kívül esik, és az eredménytípus egy integráltípus vagy számtípus, a rendszer System.OverflowException dob.

Implicit átalakítást kell alkalmazni a kiválasztott unáris operátor visszatérési típusáról a primary_expressiontípusára, különben fordítási időben hiba lép fel.

Az x++ vagy x-- alakú postfix növekmény vagy csökkentés művelet futásidejű feldolgozása a következő lépésekből áll:

  • Ha x változóként van besorolva:
    • x kiértékelése révén jön létre a változó.
    • A x értéke mentésre kerül.
    • A x mentett értéke a kiválasztott operátor operandustípusára lesz konvertálva, és az operátor argumentumként ezzel az értékkel lesz meghívva.
    • Az operátor által visszaadott érték a x típusára lesz konvertálva, és a xkorábbi kiértékelése által megadott helyen lesz tárolva.
    • A x mentett értéke lesz a művelet eredménye.
  • Ha x tulajdonságként vagy indexelői hozzáférésként van besorolva:
    • A példánykifejezést (ha x nem static) és a x-hoz társított argumentumlistát (ha x egy indexelői hozzáférés) kiértékeli a rendszer, és az eredmények a következő lekérés és beállítás kiegészítő meghívásaiban kerülnek felhasználásra.
    • A x lekérdező hívása megtörténik, és a visszaadott érték mentésre kerül.
    • A x mentett értéke a kiválasztott operátor operandustípusára lesz konvertálva, és az operátor argumentumként ezzel az értékkel lesz meghívva.
    • Az operátor által visszaadott érték a x típusára lesz konvertálva, és a x halmaz tartozéka ezzel az értékkel lesz meghívva értékargumentumként.
    • A x mentett értéke lesz a művelet eredménye.

A ++ és -- operátorok is támogatják az előtag jelölését (§12.9.6). A vagy eredménye a művelet előtti értéke, míg a vagy eredménye a művelet utáni é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.

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 T egy value_type, és A nincs jelen:
    • A object_creation_expression egy alapértelmezett konstruktorhívás. A object_creation_expression eredménye egy Ttípusú érték, nevezetesen a T alapértelmezett értéke a §8.3.3.
  • Ellenkező esetben, ha T egy type_parameter, és A nincs 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.
  • Ellenkező esetben, ha az Tclass_type vagy struct_type:
    • Ha T absztrakt vagy statikus osztálytípus, fordítási időhiba lép fel.
    • A meghívandó példánykonstruktort a túlterhelés feloldási szabályai határozzák meg a §12.6.4alapján. A jelölt példánykonstruktorok halmaza minden T-ban deklarált elérhető példánykonstruktorból áll, amelyek alkalmazhatók a A tekintetében (§12.6.4.2). Ha a jelölt példánykonstruktorok készlete üres, vagy ha egyetlen legjobb példánykonstruktor nem azonosítható, kötési idejű hiba lép fel.
    • A object_creation_expression eredménye egy 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.

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 T egy class_type:
    • Az osztály T új példánya allokálódik. Ha nincs elegendő memória az új példány lefoglalásához, a rendszer System.OutOfMemoryException dob, és nem hajt végre további lépéseket.
    • Az új példány minden mezője inicializálva lesz az alapértelmezett értékre (§9.3).
    • A példánykonstruktor meghívása a függvénytagok meghívásának szabályai szerint történik (§12.6.6). A rendszer automatikusan átadja az újonnan lefoglalt példányra mutató hivatkozást a példánykonstruktornak, és a példány a konstruktoron belülről is elérhető.
  • Ha T egy struct_type, akkor...
    • Egy ideiglenes helyi változó kiosztásával T típusú példány jön létre. Mivel egy struct_type példánykonstruktorának feltétlenül hozzá kell rendelnie egy értéket a létrehozott példány egyes mezőihez, nem szükséges inicializálni az ideiglenes változót.
    • A példánykonstruktor meghívása a függvénytagok meghívásának szabályai szerint történik (§12.6.6). A rendszer automatikusan átadja az újonnan lefoglalt példányra mutató hivatkozást a példánykonstruktornak, és a példány a konstruktoron belülről is elérhető.
12.8.17.2.2 Objektum-inicializálók

Egy objektum inicializáló egy objektum nulla vagy több mező, tulajdonság vagy indexelt elem értékeit adja meg.

object_initializer
    : '{' member_initializer_list? '}'
    | '{' member_initializer_list ',' '}'
    ;

member_initializer_list
    : member_initializer (',' member_initializer)*
    ;

member_initializer
    : initializer_target '=' initializer_value
    ;

initializer_target
    : identifier
    | '[' argument_list ']'
    ;

initializer_value
    : expression
    | object_or_collection_initializer
    ;

Egy objektum-inicializáló tagok inicializálóinak sorozatából áll, amelyeket a { és } tokenek közé teszünk, és vesszőkkel választunk el. Minden member_initializer meg kell jelölnie az inicializálás célját. Az azonosító az inicializálandó objektum egy akadálymentes mezőjét vagy tulajdonságát, míg a szögletes zárójelek közé zárt argument_list argumentumokat kell megadnia az inicializálandó objektum akadálymentes indexelője számára. Hiba, hogy egy objektum inicializálója több tag inicializálót is tartalmaz ugyanahhoz a mezőhöz vagy tulajdonsághoz.

Megjegyzés: Bár egy objektum inicializálója nem állíthatja be többször ugyanazt a mezőt vagy tulajdonságot, az indexelőkre nincsenek ilyen korlátozások. Az objektum-inicializálók több inicializáló célt is tartalmazhatnak, amelyek indexelőkre hivatkoznak, és akár többször is ugyanazt az indexelő argumentumot használhatják. végjegyzet

Minden initializer_target után egyenlőségjel, valamint egy kifejezés, objektum inicializáló vagy gyűjtemény inicializáló következik. Az objektum inicializálójának kifejezései nem hivatkozhatnak arra az újonnan létrehozott objektumra, amelyet inicializál.

A tag inicializálója, amely az egyenlőségjel utáni kifejezést adja meg, ugyanúgy lesz feldolgozva, mint egy hozzárendelés (§12.21.2) a célhoz.

Egy olyan taginicializáló, amely az egyenlőségjel után objektuminicializálót ad meg, egy beágyazott objektuminicializáló, azaz egy beágyazott objektum inicializálása. Ahelyett, hogy új értéket rendel a mezőhöz vagy tulajdonsághoz, a beágyazott objektum inicializálójában lévő hozzárendeléseket a rendszer a mező vagy tulajdonság tagjaihoz való hozzárendelésként kezeli. A beágyazott objektum-inicializálók nem alkalmazhatók értéktípusú tulajdonságokra vagy értéktípusú, írásvédett mezőkre.

Egy tag inicializáló, amely az egyenlőségjel után egy gyűjtemény inicializálóját határozza meg, egy beágyazott gyűjtemény inicializálása. Ahelyett, hogy új gyűjteményt rendel a célmezőhöz, tulajdonsághoz vagy indexelőhöz, az inicializálóban megadott elemek a cél által hivatkozott gyűjteményhez lesznek hozzáadva. A célnak olyan gyűjteménytípusnak kell lennie, amely megfelel a §12.8.17.2.3által megadott követelményeknek.

Ha egy inicializáló cél indexelőre hivatkozik, az indexelő argumentumait mindig pontosan egyszer kell kiértékelni. Így még akkor is, ha az argumentumok végül soha nem lesznek használatban (például egy üres beágyazott inicializáló miatt), a rendszer kiértékeli a mellékhatásokat.

példa: Az alábbi osztály két koordinátával rendelkező pontot jelöl:

public class Point
{
    public int X { get; set; }
    public int Y { get; set; }
}

A Point egy példánya a következőképpen hozható létre és inicializálható:

Point a = new Point { X = 0, Y = 1 };

Ennek ugyanaz a hatása, mint a

Point __a = new Point();
__a.X = 0;
__a.Y = 1;
Point a = __a;

ahol a __a egy egyébként láthatatlan és elérhetetlen ideiglenes változó.

Az alábbi osztály egy két pontból létrehozott téglalapot, valamint egy Rectangle-példány létrehozását és inicializálását mutatja be:

public class Rectangle
{
    public Point P1 { get; set; }
    public Point P2 { get; set; }
}

A Rectangle egy példánya a következőképpen hozható létre és inicializálható:

Rectangle r = new Rectangle
{
    P1 = new Point { X = 0, Y = 1 },
    P2 = new Point { X = 2, Y = 3 }
};

Ennek ugyanaz a hatása, mint a

Rectangle __r = new Rectangle();
Point __p1 = new Point();
__p1.X = 0;
__p1.Y = 1;
__r.P1 = __p1;
Point __p2 = new Point();
__p2.X = 2;
__p2.Y = 3;
__r.P2 = __p2;
Rectangle r = __r;

ahol __r, __p1 és __p2 ideiglenes változók, amelyek egyébként láthatatlanok és elérhetetlenek.

Ha Rectanglekonstruktora lefoglalja a két beágyazott Point példányt, az új példányok hozzárendelése helyett a beágyazott Point példányok inicializálására használhatók:

public class Rectangle
{
    public Point P1 { get; } = new Point();
    public Point P2 { get; } = new Point();
}

a következő szerkezet használható a beágyazott Point példányok inicializálására új példányok hozzárendelése helyett:

Rectangle r = new Rectangle
{
    P1 = { X = 0, Y = 1 },
    P2 = { X = 2, Y = 3 }
};

Ennek ugyanaz a hatása, mint a

Rectangle __r = new Rectangle();
__r.P1.X = 0;
__r.P1.Y = 1;
__r.P2.X = 2;
__r.P2.Y = 3;
Rectangle r = __r;

példa vége

12.8.17.2.3 Gyűjtemény inicializálók

A gyűjtemény inicializálója a gyűjtemény elemeit határozza meg.

collection_initializer
    : '{' element_initializer_list '}'
    | '{' element_initializer_list ',' '}'
    ;

element_initializer_list
    : element_initializer (',' element_initializer)*
    ;

element_initializer
    : non_assignment_expression
    | '{' expression_list '}'
    ;

expression_list
    : expression (',' expression)*
    ;

A gyűjteményinicializáló egy olyan sorozatból áll, amely elem-inicializálókat tartalmaz, és ezeket a { és } tokenek fogják közre, vesszővel elválasztva. Minden elem inicializálója megad egy elemet, amelyet hozzá kell adni az inicializálandó gyűjteményobjektumhoz, és { és } jogkivonatok által, vesszővel elválasztott kifejezésekből áll. Az egykifejezéses elem-inicializáló kapcsos zárójelek nélkül is írható, de nem lehet hozzárendelési kifejezés, így elkerülhető a tag inicializálókkal való kétértelműség. A non_assignment_expression meghatározása a 12.22. §szakaszban található.

példa: Az alábbi példa egy gyűjtemény inicializálót tartalmazó objektumlétrehozó kifejezésre:

List<int> digits = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

példa vége

A gyűjtemény-inicializálót alkalmazó gyűjteményobjektumnak olyan típusúnak kell lennie, amely megvalósítja a System.Collections.IEnumerable-t, különben fordítási időhiba lép fel. Az egyes megadott elemeknél balról jobbra haladva a rendszer a normál tagkeresést alkalmazza a 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 __c2 ideiglenes változók, amelyek egyébként láthatatlanok és elérhetetlenek.

példa vége

12.8.17.3 Névtelen objektumlétrehozó kifejezések

A anonymous_object_creation_expression egy névtelen típusú objektum létrehozására szolgál.

anonymous_object_creation_expression
    : 'new' anonymous_object_initializer
    ;

anonymous_object_initializer
    : '{' member_declarator_list? '}'
    | '{' member_declarator_list ',' '}'
    ;

member_declarator_list
    : member_declarator (',' member_declarator)*
    ;

member_declarator
    : simple_name
    | member_access
    | null_conditional_projection_initializer
    | base_access
    | identifier '=' expression
    ;

A névtelen objektum inicializálója névtelen típust deklarál, és visszaad egy ilyen típusú példányt. A névtelen típus egy névtelen osztálytípus, amely közvetlenül objectörökli. A névtelen típus tagjai olyan írásvédett tulajdonságok sorozata, amely a névtelen objektum inicializálásának eredményeként jön létre, amikor a típuspéldányt készítik el. Pontosabban egy névtelen objektum-inicializáló az alábbi formában

new { p₁=e₁,p₂=e₂, ... pv=ev}

az űrlap névtelen típusát deklarálja

class __Anonymous1
{
    private readonly «T1» «f1»;
    private readonly «T2» «f2»;
    ...
    private readonly «Tn» «fn»;

    public __Anonymous1(«T1» «a1», «T2» «a2»,..., «Tn» «an»)
    {
        «f1» = «a1»;
        «f2» = «a2»;
        ...
        «fn» = «an»;
    }

    public «T1» «p1» { get { return «f1»; } }
    public «T2» «p2» { get { return «f2»; } }
    ...
    public «Tn» «pn» { get { return «fn»; } }
    public override bool Equals(object __o) { ... }
    public override int GetHashCode() { ... }
}

ahol minden «Tx» a megfelelő «ex» kifejezés típusa. A member_declarator használt kifejezésnek típussal kell rendelkeznie. Jelentkezik fordítási időhiba, ha a member_declarator egy kifejezés null vagy névtelen függvény.

A névtelen típus és annak Equals metódusához tartozó paraméter neveit a fordító automatikusan generálja, és a program szövegében nem lehet rájuk hivatkozni.

Ugyanazon a programon belül két névtelen objektum inicializálója, amelyek azonos nevek és fordítási idő típusú tulajdonságok sorozatát adják meg ugyanabban a sorrendben, azonos névtelen típusú példányokat hoznak létre.

példa: A példában

var p1 = new { Name = "Lawnmower", Price = 495.00 };
var p2 = new { Name = "Shovel", Price = 26.95 };
p1 = p2;

az utolsó sor hozzárendelése engedélyezett, mert a p1 és a p2 azonos névtelen típusúak.

példa vége

A névtelen típusok Equals és GetHashcode metódusai felülbírálják a objectörökölt metódusokat, és a tulajdonságok Equals és GetHashcode szempontjából vannak definiálva, így az azonos névtelen típus két példánya egyenlő, ha és csak akkor, ha minden tulajdonságuk egyenlő.

A tag deklarátor rövidítése lehet egy egyszerű név (§12.8.4), egy taghozzáférés (§12.8.7), egy null feltételes kivetítési inicializáló (§12.8.8) vagy egy alaphozzáférés (§12.8.15). Ezt előrejelzési inicializáló nevezik, és rövidítése egy azonos nevű tulajdonság deklarációjának és hozzárendelésének. Konkrétan az űrlapok tagdeklarátorai

«identifier», «expr» . «identifier» és «expr» ? . «identifier»

pontosan egyenértékűek az alábbiakval:

«identifer» = «identifier», «identifier» = «expr» . «identifier» és «identifier» = «expr» ? . «identifier»

Így egy vetületi inicializálóban az azonosító kiválasztja azt az értéket és mezőt vagy tulajdonságot is, amelyhez az érték hozzá van rendelve. Intuitív módon a kivetítési inicializáló nem csak egy értéket, hanem az érték nevét is kivetíti.

12.8.17.4 Tömblétrehozás kifejezései

A array_creation_expression egy array_typeúj példányának létrehozására szolgál.

array_creation_expression
    : 'new' non_array_type '[' expression_list ']' rank_specifier*
      array_initializer?
    | 'new' array_type array_initializer
    | 'new' rank_specifier array_initializer
    ;

Az első űrlap tömblétrehozási kifejezése egy olyan típusú tömbpéldányt foglal le, amely az egyes kifejezések törlését eredményezi a kifejezéslistából.

Példa: A tömblétrehozó kifejezés new int[10,20]int[,]típusú tömbpéldányt hoz létre, az új int[10][,] pedig int[][,]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 (§23.2) kivételével a tömbök elrendezése meghatározatlan.

Ha az első űrlap tömblétrehozó kifejezése tömbinicializálót tartalmaz, a kifejezéslistában szereplő kifejezéseknek állandónak kell lenniük, és a kifejezéslista által megadott rang- és dimenzióhosszoknak meg kell egyeznie a tömb inicializálóéval.

A második vagy harmadik űrlap tömblétrehozó kifejezésében a megadott tömbtípus vagy rangválasztó rangjának meg kell egyeznie a tömb inicializálójának rangsorával. Az egyes dimenziók hosszát a tömb inicializálójának megfelelő beágyazási szintek elemeinek száma alapján határozzuk meg. Így az inicializáló kifejezés az alábbi deklarációban

var a = new int[,] {{0, 1}, {2, 3}, {4, 5}};

pontosan megfelel a

var a = new int[3, 2] {{0, 1}, {2, 3}, {4, 5}};

A harmadik formájú tömblétrehozó kifejezést implicit módon típusos tömblétrehozó kifejezésnek nevezzük. Hasonló a második űrlaphoz, azzal a kivételével, hogy a tömb elemtípusa nem explicit módon van megadva, hanem a tömb inicializálójának kifejezéskészletének legjobb közös típusaként (§12.6.3.15) határozható meg. Többdimenziós tömb esetén, azaz olyan tömbnél, ahol a rank_specifier legalább egy vesszőt tartalmaz, ez az összeállítás a beágyazott array_initializers-ekben található összes kifejezéstmagában foglalja.

A tömb inicializálóit részletesebben ismertetjük a §17.7-ben.

A tömblétrehozási kifejezés kiértékelésének eredménye értékként van besorolva, nevezetesen az újonnan lefoglalt tömbpéldányra mutató hivatkozásként. A tömblétrehozási kifejezés futásidejű feldolgozása a következő lépésekből áll:

  • A expression_list dimenzióhossz-kifejezéseinek kiértékelése sorrendben történik, balról jobbra. Az egyes kifejezések kiértékelését követően implicit átalakítást (§10.2) hajtunk végre a következő típusok egyikére: int, uint, long, ulong. A lista első olyan típusát választja ki, amelyhez implicit konverzió tartozik. Ha egy kifejezés kiértékelése vagy az azt követő implicit átalakítás kivételt okoz, akkor a rendszer nem értékel ki további kifejezéseket, és nem hajt végre további lépéseket.
  • A dimenzióhosszok számított értékei az alábbiak szerint lesznek érvényesítve: Ha egy vagy több érték nullánál kisebb, a rendszer System.OverflowException dob, és nem hajt végre további lépéseket.
  • A megadott dimenzióhosszúságokkal rendelkező tömbpéldány lesz lefoglalva. Ha nincs elegendő memória az új példány lefoglalásához, a rendszer System.OutOfMemoryException dob, és nem hajt végre további lépéseket.
  • Az új tömbpéldány minden eleme inicializálva lesz az alapértelmezett értékre (§9.3).
  • Ha a tömblétrehozó kifejezés tömb inicializálót tartalmaz, a rendszer kiértékeli és hozzárendeli a tömb inicializálójának minden egyes kifejezését a megfelelő tömbelemhez. A kiértékelések és hozzárendelések abban a sorrendben lesznek végrehajtva, amelyben a kifejezések meg vannak írva a tömb inicializálójában– vagyis az elemek inicializálása növekvő index sorrendben történik, és a jobb szélső dimenzió növekszik először. Ha egy adott kifejezés kiértékelése vagy a megfelelő tömbelemhez való későbbi hozzárendelés kivételt okoz, akkor a rendszer nem inicializál további elemeket (és a többi elem így az alapértelmezett értékekkel fog rendelkezni).

A tömblétrehozási kifejezés lehetővé teszi egy tömb példányosítását egy tömbtípus elemeivel, de az ilyen tömb elemeit manuálisan kell inicializálni.

példa: Az utasítás

int[][] a = new int[100][];

egydimenziós tömböt hoz létre 100 int[]típusú elemből. Az egyes elemek kezdeti értéke null. Nem lehetséges, hogy ugyanaz a tömblétrehozó kifejezés is példányosíthassa az altömböket és az utasítást

int[][] a = new int[100][5]; // Error

fordítási idejű hibát eredményez. Az altömbök példányosítása manuálisan is elvégezhető, ahogyan a

int[][] a = new int[100][];
for (int i = 0; i < 100; i++)
{
    a[i] = new int[5];
}

példa vége

Megjegyzés: Ha tömbök tömbjei "téglalap alakúak", azaz az altömbök azonos hosszúságúak, hatékonyabb a többdimenziós tömbök használata. A fenti példában a tömbök tömbjének példányosítása 101 objektumot hoz létre – egy külső tömböt és 100 altömböt. Ezzel szemben

int[,] a = new int[100, 5];

csak egyetlen objektumot, kétdimenziós tömböt hoz létre, és egyetlen utasításban végzi el a lefoglalást.

végjegyzet

Példa: Az alábbiakban példákat mutatunk be az implicit módon típusozott tömblétrehozó kifejezésekre:

var a = new[] { 1, 10, 100, 1000 };                     // int[]
var b = new[] { 1, 1.5, 2, 2.5 };                       // double[]
var c = new[,] { { "hello", null }, { "world", "!" } }; // string[,]
var d = new[] { 1, "one", 2, "two" };                   // Error

Az utolsó kifejezés fordítási időben hibát okoz, mert sem int, sem string nem konvertálható implicit módon a másikra, ezért nincs legjobb közös típus. Ebben az esetben explicit módon beírt tömblétrehozási kifejezést kell használni, például meg kell adni a object[]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 E metóduscsoport, a delegált létrehozási kifejezés ugyanúgy lesz feldolgozva, mint a metóduscsoport-átalakítás (§10.8) E-ról D.

  • Ha E névtelen függvény, a delegált létrehozási kifejezés ugyanúgy lesz feldolgozva, mint a névtelen függvények konvertálása (10.7. §-) E-ról D.

  • Ha E érték, akkor E-nek kompatibilisnek kell lennie (§20.2) a D-gyel, és az eredmény egy újonnan létrehozott delegáltra mutató hivatkozás, amely E-öt hív meg.

Egy új D(E) futásidejű feldolgozása – melyben a D egy delegate_type és E egy kifejezés– a következő lépésekből áll:

  • Ha E metóduscsoport, a delegált létrehozási kifejezés metóduscsoport-átalakításként (10.8. §-) lesz kiértékelve E-ról D- ra.
  • Ha a E névtelen függvény, akkor a delegált létrehozását olyan névtelen függvénykonverzióként értékelik, amely E-ről D-re történik (§10.7).
  • Ha E egy delegate_typeértéke:
    • E-t értékelik. Ha ez a kiértékelés kivételt okoz, a rendszer nem hajt végre további lépéseket.
    • Ha a E értéke null, a rendszer System.NullReferenceException dob, és nem hajt végre további lépéseket.
    • A delegált típusú D új példánya lesz lefoglalva. Ha nincs elegendő memória az új példány lefoglalásához, a rendszer System.OutOfMemoryException dob, és nem hajt végre további lépéseket.
    • Az új delegált példány inicializálva van egy egyszeri meghívási listával, amely meghívja E.

A delegált meghívási listája akkor kerül meghatározásra, amikor a delegáltat példányosítják, majd az teljes élettartama alatt állandó marad. Más szóval, egy delegátum célul szolgáló hívható entitásait a létrehozása után nem lehet módosítani.

Megjegyzés: Ne feledje, hogy ha két delegátumot egyesítenek, vagy egyiküket eltávolítják a másikból, új delegátum jön létre; egy meglévő delegátum sem változtatja meg a tartalmát. végjegyzet

Nem hozható létre olyan delegált, amely tulajdonságra, indexelőre, felhasználó által definiált operátorra, példánykonstruktorra, véglegesítőre vagy statikus konstruktorra hivatkozik.

Példa: A fentiekben leírtak szerint, amikor egy delegált egy metóduscsoportból jön létre, a delegált paraméterlistája és visszatérési típusa határozza meg, hogy melyik túlterhelt metódust válassza ki. A példában

delegate double DoubleFunc(double x);

class A
{
    DoubleFunc f = new DoubleFunc(Square);

    static float Square(float x) => x * x;
    static double Square(double x) => x * x;
}

a A.f mező inicializálva van egy delegálttal, amely a második Square metódusra hivatkozik, mivel ez a metódus pontosan megfelel a paraméterlistának, és visszaadja a DoubleFunctípusát. Ha a második Square metódus nem jelenik meg, fordítási időhiba történt volna.

példa vége

12.8.18 Az operátor típusa

A typeof operátor a típus System.Type objektumának lekérésére szolgál.

typeof_expression
    : 'typeof' '(' type ')'
    | 'typeof' '(' unbound_type_name ')'
    | 'typeof' '(' 'void' ')'
    ;

unbound_type_name
    : identifier generic_dimension_specifier? ('.' identifier generic_dimension_specifier?)*
    | unbound_qualified_alias_member ('.' identifier generic_dimension_specifier?)*
    ;

unbound_qualified_alias_member
    : identifier '::' identifier generic_dimension_specifier?
    ;

generic_dimension_specifier
    : '<' comma* '>'
    ;

comma
    : ','
    ;

A typeof_expression első formája egy typeof kulcsszóból és egy zárójeles típusból áll. Az űrlap kifejezésének eredménye a megadott típus System.Type objektuma. Egy adott típushoz csak egy System.Type objektum tartozik. Ez azt jelenti, hogy egy 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 object kulcsszót, mint az egyes type_argument.
  • Az eredményként kapott type_name fel van oldva egy létrehozott típusra (7.8.§).
  • A unbound_type_name a feloldott generált típushoz társított kötetlen általános típus (8.4. §).

Megjegyzés: Nincs szükség a tokenek sorozatának átalakítására vagy a közvetítő típus előállítására, csak a kötetlen általános típus meghatározására, mintha ezt a folyamatot követné. végjegyzet

Hiba, ha a típus neve nullázható hivatkozástípus.

A typeof_expression eredménye az eredményül kapott kötetlen általános típus System.Type objektuma.

A typeof_expression harmadik formája egy typeof kulcsszó, majd egy zárójeles void kulcsszó. Az űrlap kifejezésének eredménye az a System.Type objektum, amely egy típus hiányát jelzi. A typeof(void) által visszaadott típusobjektum különbözik a bármely típushoz visszaadott típusobjektumtól.

Megjegyzés: Ez a speciális System.Type objektum olyan osztálykódtárakban hasznos, amelyek lehetővé teszik a nyelv metódusainak tükrözését, ahol ezek a metódusok bármilyen metódus visszatérési típusát szeretnék ábrázolni, beleértve void metó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 és System.Int32 azonos típusúak. A typeof(X<>) eredménye nem függ a típusargumentumtól, de a typeof(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 a sizeof operátor a §23.6.9-ban van meghatározva.

12.8.20 Az ellenőrzött és a nem ellenőrzött operátorok

A checked és unchecked operátorokkal szabályozható az integrált típusú aritmetikai műveletek és átalakítások túlcsordulás-ellenőrzési környezete.

checked_expression
    : 'checked' '(' expression ')'
    ;

unchecked_expression
    : 'unchecked' '(' expression ')'
    ;

A checked operátor egy ellenőrzött környezetben értékeli ki a tartalmazott kifejezést, a unchecked operátor pedig egy nem ellenőrzött környezetben értékeli ki a tartalmazott kifejezést. Egy checked_expression vagy unchecked_expression pontosan egy parenthesized_expression (§12.8.5) felel meg, azzal a kivétellel, hogy a tartalmazott kifejezés kiértékelése a megadott túlcsordulás-ellenőrzési környezetben történik.

A túlcsordulás-ellenőrzési környezet a checked és unchecked utasításokon keresztül is szabályozható (13.12. §-).

Az ellenőrzött és nem ellenőrzött operátorok és utasítások által létrehozott túlcsordulás-ellenőrzési környezet a következő műveleteket érinti:

  • Az előre definiált ++ és -- operátorok (§12.8.16 és §12.9.6), ha az operandus integrál vagy szám típusú.
  • Az előre definiált - egyváltozós operátor (§12.9.3), amikor az operandus egész típusú.
  • Az előre definiált +, -, *és / bináris operátorok (§12.10), ha mindkét operandus integrál- vagy számtípusú.
  • Explicit numerikus átalakítások (§10.3.2) egy egész vagy enumerációs típusból egy másik egész vagy enumerációs típusba, vagy float vagy double egész vagy enumerációs típusra.

Ha a fenti műveletek egyike olyan eredményt hoz létre, amely túl nagy ahhoz, hogy a céltípusban szerepeljen, a művelet végrehajtásának környezete szabályozza az eredményként kapott viselkedést:

  • Ha egy checked környezetben a művelet állandó kifejezés (§12.23), fordítási idejű hiba következik be. Ellenkező esetben, ha a műveletet futásidőben hajtják végre, a rendszer System.OverflowException dob.
  • Egy unchecked környezetben az eredmény úgy csonkítódik, hogy elveti a céltípusba nem illő, magas helyiértékű biteket.

A nem állandó kifejezések (§12.23) (futtatáskor kiértékelt kifejezések) esetében, amelyek nincsenek checked vagy unchecked operátorok vagy utasítások által elzárva, az alapértelmezett túlcsordulás-ellenőrzési környezet nincs bejelölve, kivéve, ha külső tényezők (például fordítókapcsolók és végrehajtási környezet konfigurációja) nem kérnek ellenőrzött értékelést.

Állandó kifejezések (§12.23) (fordításkor teljes mértékben kiértékelhető kifejezések) esetén a rendszer mindig ellenőrzi az alapértelmezett túlcsordulás-ellenőrzési környezetet. Ha egy állandó kifejezés nincs kifejezetten elhelyezve egy unchecked környezetben, a kifejezés fordítási idő alatti kiértékelése során fellépő túlcsordulások mindig fordítási idő hibákat okoznak.

A névtelen függvények törzsét nem érintik checked vagy unchecked környezetek, amelyekben a névtelen függvény előfordul.

példa: Az alábbi kódban

class Test
{
    static readonly int x = 1000000;
    static readonly int y = 1000000;

    static int F() => checked(x * y);    // Throws OverflowException
    static int G() => unchecked(x * y);  // Returns -727379968
    static int H() => x * y;             // Depends on default
}

Nem jelentkeznek fordítási idejű hibák, mivel egyik kifejezés sem értékelhető ki fordítási idő során. Futásidőben a F metódus egy System.OverflowExceptionad vissza, a G metódus pedig 727379968 (a tartományon kívüli eredmény alsó 32 bitje). A H metódus viselkedése a fordítás alapértelmezett túlcsordulás-ellenőrzési környezetétől függ, de vagy ugyanaz, mint F, vagy ugyanaz, mint G.

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 és H á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ése checked környezetben történik. A Gállandó kifejezés értékelésekor is túlcsordulás történik, de mivel az értékelés unchecked környezetben történik, a túlcsordulást nem jelzik.

példa vége

A checked és unchecked operátorok csak a "(" és ")" jogkivonatokban szövegesen tárolt műveletek túlcsordulás-ellenőrzési környezetét befolyásolják. Az operátorok nem befolyásolják a függvény azon tagjait, amelyeket a tartalmazott kifejezés kiértékelése eredményeként hívnak meg.

példa: Az alábbi kódban

class Test
{
    static int Multiply(int x, int y) => x * y;

    static int F() => checked(Multiply(1000000, 1000000));
}

checked használata F-ben nem befolyásolja a x * y kiértékelését Multiply-ben, ezért a x * y alapértelmezett túlcsordulás-ellenőrzési környezetben értékelődik ki.

példa vége

Az unchecked operátor akkor kényelmes, ha hexadecimális jelölésben írja az aláírt integráltípusok állandóit.

Példa:

class Test
{
    public const int AllBits = unchecked((int)0xFFFFFFFF);
    public const int HighBit = unchecked((int)0x80000000);
}

A fenti hexadecimális állandók uinttípusúak. Mivel az állandók a int tartományon kívül vannak, a unchecked operátor nélkül a int áttípusításai fordítási időhibát eredményeznének.

példa vége

Megjegyzés: A checked és unchecked operátorok és utasítások lehetővé teszik a programozók számára, hogy bizonyos numerikus számítások bizonyos aspektusait szabályozják. Egyes numerikus operátorok viselkedése azonban az operandusok adattípusától függ. A két tizedesjegy szorzása például mindig kivételt eredményez a túlcsordulás esetén, még egy explicit módon nem ellenőrzött szerkezeten belül is. Hasonlóképpen, két lebegőpontos szám szorzása soha nem eredményez kivételt túlcsordulás esetén, még egy explicit módon ellenőrzött szerkezeten belül sem. Emellett a többi operátort sem érinti az ellenőrzés módja, akár alapértelmezett, akár explicit. végjegyzet

12.8.21 Alapértelmezett értékkifejezések

A rendszer egy alapértelmezett értékkifejezést használ egy típus alapértelmezett értékének (§9.3) lekéréséhez.

default_value_expression
    : explictly_typed_default
    | default_literal
    ;

explictly_typed_default
    : 'default' '(' type ')'
    ;

default_literal
    : 'default'
    ;

A default_literal alapértelmezett értéket jelölnek (§9.3). Nem rendelkezik típussal, de bármilyen típussá konvertálható egy alapértelmezett konstans átalakítással (§10.2.16).

A alapértelmezett_értékkifejezés eredménye egy kifejezetten_tipizált_alapértelmezésexplicit típusának alapértelmezettje (§9.3), vagy az átalakítás céltípusa egy alapértelmezett_értékkifejezésesetén.

A default_value_expression állandó kifejezés (§12.23), ha a típus az alábbiak egyike:

  • referenciatípus
  • olyan típusparaméter, amely referenciatípusként ismert (§8.2);
  • az alábbi értéktípusok egyike: sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, bool,; vagy
  • bármilyen enumerálási típus.

12.8.22 Veremfoglalás

A veremlefoglalási kifejezés egy memóriablokkot foglal le a végrehajtási veremből. A végrehajtási verem egy memóriaterület, ahol a helyi változók tárolódnak. A végrehajtási verem nem része a felügyelt halomnak. A helyi változó tárterülethez használt memória automatikusan helyreáll, amikor az aktuális függvény visszatér.

A veremfoglalási kifejezés biztonságos környezeti szabályai a 16.4.15.7. szakaszban leírásra kerülnek.

stackalloc_expression
    : 'stackalloc' unmanaged_type '[' expression ']'
    | 'stackalloc' unmanaged_type? '[' constant_expression? ']' stackalloc_initializer
    ;

stackalloc_initializer
     : '{' stackalloc_initializer_element_list '}'
     ;

stackalloc_initializer_element_list
     : stackalloc_element_initializer (',' stackalloc_element_initializer)* ','?
     ;
    
stackalloc_element_initializer
    : expression
    ;

A unmanaged_type (§8.8) az újonnan lefoglalt helyen tárolt elemek típusát jelzi, a kifejezés pedig az elemek számát jelzi. Ezek együttesen határozzák meg a szükséges allokációs méretet. A kifejezés típusa implicit módon átalakítható 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 kihagyják a unmanaged_type megadását, a rendszer a legjobb általános típusra vonatkozó szabályokat (§12.6.3.15) követi a stackalloc_element_initializer-ek halmazához.
  • Ha a constant_expression nincs meghatározva, azt a stackalloc_element_initializer-ek számának tekintjük.
  • Ha constant_expression van jelen, akkor annak az stackalloc_element_initializerszámával egyenlőnek kell lennie.

Minden stackalloc_element_initializer implicit módon átalakítandó unmanaged_type típusúvá (§10.2). A stackalloc_element_initializernövekvő sorrendben inicializálja a lefoglalt memória elemeit, kezdve a nulla indexnél lévő elemet. stackalloc_initializerhiányában az újonnan lefoglalt memória tartalma nincs meghatározva.

Ha egy stackalloc_expression közvetlenül egy local_variable_declaration inicializáló kifejezésként használják (§13.6.2), ahol a local_variable_type mutatótípus (23.3. §) vagy kiderített (var), akkor a stackalloc_expression eredménye egy T* típusú mutató (23.9. §). Ebben az esetben a stackalloc_expression nem biztonságos kódban kell megjelennie. Ellenkező esetben a stackalloc_expression eredménye egy a(z) Span<T> típusú példány, ahol a(z) T a unmanaged_type:

  • A Span<T> (§C.3) egy referencia struct típus (§16.2.3), amely egy memóriablokkot mutat be, itt a stackalloc_expression által lefoglalt blokkot, mint indexelhető gyűjteményt típusosított (T) elemekből.
  • Az eredmény Length tulajdonsága a lefoglalt elemek számát adja vissza.
  • Az eredmény indexelője (§15.9) visszaad egy változó_hivatkozás (§9.5) a lefoglalt blokk egy eleméhez, és tartományellenőrzés történik.

A veremfoglalás inicializálói nem engedélyezettek catch vagy finally blokkokban (§13.11).

Megjegyzés: Nincs mód a 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 a stackalloc egy Span<int>eredményez , amelyet implicit operátor konvertál ReadOnlySpan<int>. Hasonlóképpen az span9esetében az eredményül kapott Span<double> a rendszer a konvertálással Widget<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ő nameof kifejezések eredményeit mutatja be, feltételezve, hogy egy általános típusú List<T> a System.Collections.Generic névtérben van deklarálva.

using TestAlias = System.String;

class Program
{
    static void Main()
    {
        var point = (x: 3, y: 4);

        string n1 = nameof(System);                      // "System"
        string n2 = nameof(System.Collections.Generic);  // "Generic"
        string n3 = nameof(point);                       // "point"
        string n4 = nameof(point.x);                     // "x"
        string n5 = nameof(Program);                     // "Program"
        string n6 = nameof(System.Int32);                // "Int32"
        string n7 = nameof(TestAlias);                   // "TestAlias"
        string n8 = nameof(List<int>);                   // "List"
        string n9 = nameof(Program.InstanceMethod);      // "InstanceMethod"
        string n10 = nameof(Program.GenericMethod);      // "GenericMethod"
        string n11 = nameof(Program.NestedClass);        // "NestedClass"

        // Invalid
        // string x1 = nameof(List<>);            // Empty type argument list
        // string x2 = nameof(List<T>);           // T is not in scope
        // string x3 = nameof(GenericMethod<>);   // Empty type argument list
        // string x4 = nameof(GenericMethod<T>);  // T is not in scope
        // string x5 = nameof(int);               // Keywords not permitted
        // Type arguments not permitted for method group
        // string x6 = nameof(GenericMethod<Program>);
    }

    void InstanceMethod() { }

    void GenericMethod<T>()
    {
        string n1 = nameof(List<T>); // "List"
        string n2 = nameof(T);       // "T"
    }

    class NestedClass { }
}

A példa talán meglepő része, hogy a nameof(System.Collections.Generic) a teljes névtér helyett csak "Generic"-re, a nameof(TestAlias) pedig "Sztring" helyett "TestAlias"-ra van feloldva. példa vége

12.8.24 Névtelen metóduskifejezések

Az anonymous_method_expression a névtelen függvények meghatározásának két módja. Ezek a §12.19részletezve vannak.

12.9 Unary operátorok

12.9.1 Általános

A +, -, ! (logikai tagadás csak a 12.9.4. §-ban), , ~, ++, casting és -- operátorokat unáris operátoroknak nevezzük.

Megjegyzés: A null-elágaztatási operátor (12.8.9. §) !fordítási ideje és nem túlterhelhető jellege miatt nem szerepel a fenti listán. végjegyzet

unary_expression
    : primary_expression
    | '+' unary_expression
    | '-' unary_expression
    | logical_negation_operator unary_expression
    | '~' unary_expression
    | pre_increment_expression
    | pre_decrement_expression
    | cast_expression
    | await_expression
    | pointer_indirection_expression    // unsafe code support
    | addressof_expression              // unsafe code support
    ;

pointer_indirection_expression (§23.6.2) és addressof_expression (§23.6.5) csak nem biztonságos kódban érhető el (§23).

Ha egy unary_expression operandusa fordítási időben meghatározott típusú, akkor dinamikusan kötött (dynamic). Ebben az esetben a unary_expression fordítási idejének típusa dynamic, és az alább ismertetett felbontás futásidőben történik az operandus futásidejű típusával.

12.9.2 Unáris plus operátor

A(z) +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 X nullából való kivonásával történik. Ha a X értéke az operandus típus legkisebb ábrázolható értéke (int esetén −2³¹, longesetén −2⁶³), akkor a X matematikai tagadása nem ábrázolható az operandus típusán belül. Ha ez egy checked környezetben történik, a rendszer System.OverflowException dob; ha egy unchecked környezetben fordul elő, az eredmény az operandus értéke, és a túlcsordulás nem lesz jelentve.

    Ha a negation operátor operandusa uinttípusú, akkor a rendszer longtípussá alakítja, és az eredmény típusa long. Kivételt képez az a szabály, amely lehetővé teszi, hogy a int é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 a long é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. Ha xNaN, akkor az eredmény is NaN.

  • Decimális tagadás:

    decimal operator –(decimal x);
    

    Az eredmény kiszámítása a X nullából való kivonásával történik. A decimális tagadás egyenértékű a System.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 Előtagos növekmény- és csökkenés operátorok

pre_increment_expression
    : '++' unary_expression
    ;

pre_decrement_expression
    : '--' unary_expression
    ;

Az előjel növekményes vagy csökkentő műveletének operandusa olyan kifejezés legyen, amely változóként, tulajdonsághozzáférésként vagy indexelőhozzáférésként van besorolva. A művelet eredménye az operandussal azonos típusú érték.

Ha egy prefix növekményes vagy csökkentő művelet operandusa egy tulajdonsághoz vagy indexelőhöz való hozzáférés, a tulajdonságnak vagy indexelőnek rendelkeznie kell mind get, mind set tartozékkal. Ha nem ez a helyzet, kötési idő hiba lép fel.

Az egyoperandusú operátor túlterhelésének feloldását (§12.4.4) alkalmazzák egy adott operátor-megvalósítás kiválasztására. Az előre definiált ++ és -- operátorok a következő típusokhoz léteznek: sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimalés bármely számtípus. Az előre definiált ++ operátorok az operandushoz 1 hozzáadásával előállított értéket, az előre definiált -- operátorok pedig az operandusból 1 kivonásával előállított értéket adja vissza. Ha egy checked környezetben az összeadás vagy kivonás eredménye kívül esik az eredménytípus tartományán, és az eredménytípus egy integráltípus vagy számtípus, a rendszer System.OverflowException dob.

Implicit átalakítást kell alkalmazni a kiválasztott egyváltozós operátor visszatérési típusáról a unary_expressiontípusára, ellenkező esetben fordítási időben jelentkező hiba lép fel.

Az űrlap ++x vagy --x előtag-növekményének vagy dekrementálási műveletének futásideje a következő lépésekből áll:

  • Ha x változóként van besorolva:
    • x kiértékelése révén jön létre a változó.
    • A x értékét a rendszer a kiválasztott operátor operandustípusára konvertálja, és az operátort ezzel az értékkel hívja meg argumentumként.
    • Az operátor által visszaadott érték xtípusra lesz konvertálva. Az eredményül kapott érték a x kiértékelése által megadott helyen lesz tárolva, és a művelet eredményévé válik.
  • Ha x tulajdonságként vagy indexelői hozzáférésként van besorolva:
    • A példánykifejezést (ha x nem static) és a x-hoz társított argumentumlistát (ha x egy indexelői hozzáférés) kiértékeli a rendszer, és az eredmények a következő lekérés és beállítás kiegészítő meghívásaiban kerülnek felhasználásra.
    • A x lekérési metódusa meghívásra kerül.
    • A beolvasási tartozék által visszaadott érték a kiválasztott operátor operandustípusára lesz konvertálva, és az operátor argumentumként ezzel az értékkel lesz meghívva.
    • Az operátor által visszaadott érték xtípusra lesz konvertálva. A x beállító hozzáférője ezzel az értékkel hívódik meg értékargumentumként.
    • Ez az érték a művelet eredménye is lesz.

A ++ és -- operátorok a postfix jelölést is támogatják (§12.8.16). A x++ vagy x-- eredménye a művelet előtti x értéke, míg a ++x vagy --x eredménye a művelet utáni x értéke. Mindkét esetben x ugyanazzal az értékkel rendelkezik a művelet után.

Az operátor ++ vagy operátor -- implementációja postfix vagy prefix jelöléssel hívható meg. A két jelöléshez nem lehet külön operátor-implementációt létrehozni.

A fenti, emelt formájú (§12.4.8) előtag-növekedés és csökkentés operátorok szintén előre definiáltak.

12.9.7 Öntött kifejezések

A cast_expression egy kifejezés adott típussá alakítására szolgál.

cast_expression
    : '(' type ')' unary_expression
    ;

Az cast_expression alak (T)E formájában, ahol a T egy típus, és E egy unáris_kifejezés, explicit konverziót hajt végre a értékéről a E típusra (T). Ha nincs explicit átalakítás E-ról T-ra, kötési időhiba lép fel. Ellenkező esetben az eredmény az explicit átalakítás által előállított érték. Az eredmény mindig értékként van besorolva, még akkor is, ha E egy változót jelöl.

A cast_expression nyelvtana bizonyos szintaktikai kétértelműségeket eredményez.

példa: A (x)–y kifejezés akár cast_expression értelmezhető, mint a(z) –y típusként való x, vagy akár additive_expression, parenthesized_expression kombinálva (amely a(z) x – yérték kiszámítására szolgál). példa vége

A cast_expression kétértelműségek megoldásához a következő szabály létezik: A zárójelek közé zárt egy vagy több jogkivonat (6.4..) sorozata csak akkor tekinthető cast_expression kezdetének, ha az alábbiak közül legalább az egyik igaz:

  • A tokenek sorrendje egy típushoz helyes nyelvtan, de nem egy kifejezéshez.
  • A tokenek sorrendje helyes nyelvtani szerkezet egy típus számára, és a záró zárójelet közvetlenül követő token a "~" token, a "!" token, a "(" token, egy azonosító (§6.4.3), egy literál (§6.4.5), vagy bármely kulcsszó (§6.4.4) lehet, kivéve as és is.

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 és y azonosítók, akkor x.y helyes nyelvhelyesség egy típushoz, még akkor is, ha x.y valójában nem jelöl típust. példa vége

Megjegyzés: A egyértelműsítési szabályból az következik, hogy ha x és y azonosítók, akkor (x)y, (x)(y)és (x)(-y)cast_expression, de (x)-y nem, még akkor sem, ha x azonosít egy típust. Ha azonban x egy előre definiált típust (például int) azonosító kulcsszó, akkor mind a négy űrlap cast_expressions (mivel az ilyen kulcsszó önmagában nem lehet kifejezés). végjegyzet

12.9.8 Kifejezések várva

12.9.8.1 Általános

A await operátor arra szolgál, hogy felfüggessze a környező aszinkron függvény kiértékelését, amíg az operandus által képviselt aszinkron művelet be nem fejeződik.

await_expression
    : 'await' unary_expression
    ;

A await_expression csak az aszinkron függvény törzsében engedélyezett (15.14. §). A legközelebbi aszinkron függvényen belül egy await_expression nem fordulhat elő az alábbi helyeken:

  • Beágyazott (nem aszinkron) névtelen függvényen belül
  • Egy lock_statement blokkjának belsejében
  • Névtelen függvény kifejezésfatípusra konvertálása (§10.7.3)
  • Nem biztonságos környezetben

Megjegyzés: A await_expression nem fordulhat elő a legtöbb helyen egy query_expressionbelül, mert ezek szintaktikailag nem aszinkron lambda kifejezések használatára vannak átalakítva. végjegyzet

Az aszinkron függvényben a await nem használható available_identifier-ként, bár a betű szerinti azonosító @await használható. Ezért nem áll fenn szintaktikai kétértelműség az await_expressionés az azonosítókat tartalmazó különböző kifejezések között. Az aszinkron függvényen kívül await normál azonosítóként szolgál.

Egy await_expression operandusát feladatnak nevezzük. Olyan aszinkron műveletet jelöl, amely a await_expression kiértékelésének időpontjában esetleg nem fejeződik be. A await operátor célja, hogy felfüggesztse a beágyazó aszinkron függvény végrehajtását, amíg a várt tevékenység be nem fejeződik, majd megkapja annak eredményét.

12.9.8.2 Várakozó kifejezések

Egy await_expression feladatának várhatókell lennie. Egy kifejezés t akkor várakoztatható, ha az alábbiak valamelyike fennáll:

  • t fordítási idejű típus dynamic
  • t rendelkezik egy elérhető, GetAwaiter nevű példány- vagy bővítménymetódussal, amely nem rendelkezik paraméterekkel és típusparaméterekkel, és visszatérési típusa A, amelyre az alábbi feltételek mindegyike érvényes.
    • A implementálja a felületet System.Runtime.CompilerServices.INotifyCompletion (a továbbiakban röviden INotifyCompletion)
    • A-nak van egy elérhető, olvasható tulajdonsága IsCompleted típusú bool formájában.
    • A(z) A rendelkezik egy hozzáférhető példánymetódussal GetResult, 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.8.3 Váró kifejezések besorolása

A await t kifejezés ugyanúgy van besorolva, mint a (t).GetAwaiter().GetResult()kifejezés. Így ha a GetResult visszatérési típusa void, a await_expression semmiként lesz besorolva. Ha a visszatérési típus nem avoidT, akkor a await_expressionTtípusú értékként van besorolva.

12.9.8.4 Várakozási kifejezések futásidejű kiértékelése

Futásidőben a await t kifejezés kiértékelése a következőképpen történik:

  • A váró a a (t).GetAwaiter() kifejezés kiértékelésével szerezhető be.
  • A boolb a (a).IsCompletedkifejezés kiértékelésével kapott eredmény.
  • Ha bfalse, akkor a kiértékelés attól függ, hogy a implementálja-e az interfészt System.Runtime.CompilerServices.ICriticalNotifyCompletion (továbbiakban ICriticalNotifyCompletion). Ez az ellenőrzés kötéskor történik; azaz futásidőben, ha a fordítási idő típusú dynamic, és fordítva van. Jelöljük r meg az újrakezdési meghatalmazottat (15.14.§):
    • Ha a nem implementálja a ICriticalNotifyCompletion, akkor a ((a) as INotifyCompletion).OnCompleted(r) kifejezés lesz kiértékelve.
    • Ha a implementálja a ICriticalNotifyCompletion, 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 btrue, akkor közvetlenül utána, vagy az újrakezdési delegált későbbi meghívása esetén (ha bfalse), a rendszer kiértékeli a (a).GetResult() kifejezést. Ha értéket ad vissza, az az érték a await_expressioneredménye. Ellenkező esetben az eredmény semmi.

Az interfész metódusai INotifyCompletion.OnCompleted és ICriticalNotifyCompletion.UnsafeOnCompleted várakozó általi implementálása esetén a delegált r legfeljebb egyszer hívható meg. Ellenkező esetben a beágyazó aszinkron függvény viselkedése nincs meghatározva.

12.10 Aritmetikai operátorok

12.10.1 Általános

A *, /, %, +és - operátorokat számtani operátoroknak nevezzük.

multiplicative_expression
    : unary_expression
    | multiplicative_expression '*' unary_expression
    | multiplicative_expression '/' unary_expression
    | multiplicative_expression '%' unary_expression
    ;

additive_expression
    : multiplicative_expression
    | additive_expression '+' multiplicative_expression
    | additive_expression '-' multiplicative_expression
    ;

Ha egy aritmetikai operátor operandusa 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.10.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 checked környezetben a termék kívül esik az eredménytípus tartományán, a rendszer System.OverflowException dob. Egy unchecked környezetben a túlcsordulások nem kerülnek jelentésre, és az eredménytípus tartományán kívül eső jelentős magasabb helyiértékű bitek elvetésre kerülnek.

  • Lebegőpontos szorzás:

    float operator *(float x, float y);
    double operator *(double x, double y);
    

    A termék kiszámítása az IEC 60559 aritmetikai szabályok szerint történik. Az alábbi táblázat a nem véges értékek, nullák, végtelenségek és NaN-értékek összes lehetséges kombinációjának eredményeit sorolja fel. A táblázatban a x és a y pozitív véges értékek. z a x * yeredménye, a legközelebbi ábrázolható értékre kerekítve. Ha az eredmény nagysága túl nagy a céltípushoz, z végtelen. A kerekítés miatt a z lehet nulla, még akkor is, ha sem x, sem y nem nulla.

    +y -y +0 -0 +∞ -∞ NaN
    +x +z -z +0 -0 +∞ -∞ NaN
    -x -z +z -0 +0 -∞ +∞ NaN
    +0 +0 -0 +0 -0 NaN NaN NaN
    -0 -0 +0 -0 +0 NaN NaN NaN
    +∞ +∞ -∞ NaN NaN +∞ -∞ NaN
    -∞ -∞ +∞ NaN NaN -∞ +∞ NaN
    NaN NaN NaN NaN NaN NaN NaN NaN

    (Eltérő rendelkezés hiányában a 12.10.2. §12.10.6. §- lebegőpontos tábláiban a "+" használata azt jelenti, hogy az érték pozitív; a "-" használata azt jelenti, hogy az érték negatív; és a jel hiánya azt jelenti, hogy az érték pozitív vagy negatív lehet, vagy nincs jele (NaN).)

  • Decimális szorzás:

    decimal operator *(decimal x, decimal y);
    

    Ha az eredményül kapott érték nagysága túl nagy ahhoz, hogy decimális formátumban legyen ábrázolva, egy System.OverflowException hiba keletkezik. A kerekítés miatt az eredmény nulla lehet, annak ellenére, hogy egyik operandus sem nulla. Az eredmény skálája a kerekítés előtt a két operandus skáláinak összege. A decimális szorzás egyenértékű a System.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.10.3 Osztó 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.DivideByZeroException dob.

    A számtani művelet nullára kerekíti az eredményt. Így az eredmény abszolút értéke a legnagyobb lehetséges egész szám, amely kisebb vagy egyenlő a két operandus hányadosának abszolút értékével. Az eredmény nulla vagy pozitív, ha a két operandus ugyanazzal a jellel rendelkezik, és nulla vagy negatív, ha a két operandus ellentétes előjelekkel rendelkezik.

    Ha a bal operandus a legkisebb ábrázolható int vagy long érték, és a jobb operandus –1, akkor túlcsordulás történik. Egy checked kontextusban ez egy System.ArithmeticException (vagy annak alosztálya) kivétel dobását okozza. Egy unchecked kontextusban az implementáció által meghatározott, hogy egy System.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 a y pozitív véges értékek. z a x / yeredménye, a legközelebbi ábrázolható értékre kerekítve.

    +y -y +0 -0 +∞ -∞ NaN
    +x +z -z +∞ -∞ +0 -0 NaN
    -x -z +z -∞ +∞ -0 +0 NaN
    +0 +0 -0 NaN NaN +0 -0 NaN
    -0 -0 +0 NaN NaN -0 +0 NaN
    +∞ +∞ -∞ +∞ -∞ NaN NaN NaN
    -∞ -∞ +∞ -∞ +∞ NaN NaN NaN
    NaN NaN NaN NaN NaN NaN NaN NaN
  • Decimális osztás:

    decimal operator /(decimal x, decimal y);
    

    Ha a megfelelő operandus értéke nulla, egy System.DivideByZeroException dob. Ha az eredményül kapott érték nagysága túl nagy ahhoz, hogy decimális formátumban legyen ábrázolva, egy System.OverflowException hiba keletkezik. A kerekítés miatt az eredmény nulla lehet, annak ellenére, hogy az első operandus nem nulla. Az eredmény skálája, a kerekítés előtt, az előnyben részesített skálához legközelebb eső skála, amely megegyezik a pontos eredménnyel. Az előnyben részesített skála a x skálája mínusz a yská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.10.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 % y eredménye a x – (x / y) * yáltal megtermelt érték. Ha y nulla, egyet System.DivideByZeroException-et dob.

    Ha a bal oldali operandus a legkisebb int vagy long érték, és a jobb oldali operandus –1, akkor System.OverflowException csak akkor kerül dobásra, ha x / y kivételt dobna.

  • Lebegőpontos maradék:

    float operator %(float x, float y);
    double operator %(double x, double y);
    

    Az alábbi táblázat a nem véges értékek, nullák, végtelenségek és NaN-értékek összes lehetséges kombinációjának eredményeit sorolja fel. A táblázatban a x és a y pozitív véges értékek. z a x % y eredménye, és x – 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 (amelyben n az x / y-hez legközelebbi egész szám).

    +y -y +0 -0 +∞ -∞ NaN
    +x +z +z NaN NaN +x +x NaN
    -x -z -z NaN NaN -x -x NaN
    +0 +0 +0 NaN NaN +0 +0 NaN
    -0 -0 -0 NaN NaN -0 -0 NaN
    +∞ NaN NaN NaN NaN NaN NaN NaN
    -∞ NaN NaN NaN NaN NaN NaN NaN
    NaN NaN NaN NaN NaN NaN NaN NaN
  • Decimális maradék:

    decimal operator %(decimal x, decimal y);
    

    Ha a megfelelő operandus értéke nulla, egy System.DivideByZeroException dob. A függvényimplementáció szabja meg, mikor dobásra kerül egy System.ArithmeticException (vagy annak alosztálya). A megfelelő implementációnak semmilyen esetben sem szabad kivételt dobnia x % y esetén, ha x / y nem dob kivételt. Az eredmény skálája a kerekítés előtt a két operandus skálái közül a nagyobb, és az eredmény jele , ha nem nulla, akkor ugyanaz, mint x.

    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.10.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 checked környezetben az összeg kívül esik az eredménytípus tartományán, a rendszer System.OverflowException dob. Egy unchecked környezetben a túlcsordulások nem kerülnek jelentésre, és az eredménytípus tartományán kívül eső jelentős magasabb helyiértékű bitek elvetésre kerülnek.

  • Lebegőpontos hozzáadás

    float operator +(float x, float y);
    double operator +(double x, double y);
    

    Az összeg kiszámítása az IEC 60559 aritmetikai szabályai szerint történik. Az alábbi táblázat a nem véges értékek, nullák, végtelenségek és NaN-értékek összes lehetséges kombinációjának eredményeit sorolja fel. A táblázatban a x és a y nem véges értékek, z pedig x + yeredménye. Ha x és y azonos nagyságrendű, de ellentétes előjelekkel rendelkeznek, z pozitív nulla. Ha x + y túl nagy ahhoz, hogy a céltípusban szerepeljen, z a x + yjellel megegyező végtelen.

    y +0 -0 +∞ -∞ NaN
    x z x x +∞ -∞ NaN
    +0 y +0 +0 +∞ –∞ NaN
    -0 y +0 -0 +∞ -∞ NaN
    +∞ +∞ +∞ +∞ +∞ NaN NaN
    -∞ -∞ -∞ -∞ NaN -∞ NaN
    NaN NaN NaN NaN NaN NaN NaN
  • Decimális összeadás:

    decimal operator +(decimal x, decimal y);
    

    Ha az eredményül kapott érték nagysága túl nagy ahhoz, hogy decimális formátumban legyen ábrázolva, egy System.OverflowException hiba keletkezik. Az eredmény skálája a kerekítés előtt a két operandus skálái közül a nagyobb.

    A decimális összeadás egyenértékű a System.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 E az enumerálás típusa, U pedig a Emö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 operandusa null, akkor egy üres karakterlánc helyettesíti azt. Ellenkező esetben minden olyan operandus, amely nemstring, a ToStringtípustól örökölt virtuális object metódus meghívásával kerül átalakításra a szöveges ábrázolására. Ha a ToString a null-et adja vissza, akkor egy üres karakterlánc kerül a helyére.

    Példa:

    class Test
    {
        static void Main()
        {
            string s = null;
            Console.WriteLine("s = >" + s + "<");  // Displays s = ><
    
            int i = 1;
            Console.WriteLine("i = " + i);         // Displays i = 1
    
            float f = 1.2300E+15F;
            Console.WriteLine("f = " + f);         // Displays f = 1.23E+15
    
            decimal d = 2.900m;
            Console.WriteLine("d = " + d);         // Displays d = 2.900
       }
    }
    

    A megjegyzésekben látható kimenet egy US-English rendszer tipikus eredménye. A pontos kimenet a végrehajtási környezet területi beállításaitól függhet. Maga a sztringösszefűzési operátor minden esetben ugyanúgy viselkedik, de a végrehajtás során implicit módon meghívott ToString metódusokat érinthetik a területi beállítások.

    példa vége

    A sztringösszefűzési operátor eredménye egy string, amely a bal operandus karaktereiből és a jobb operandus karaktereiből áll. A sztringösszefűzési operátor soha nem ad vissza null értéket. Előfordulhat, hogy System.OutOfMemoryException dobódik, ha nincs elegendő memória a létrehozandó karakterlánc lefoglalásához.

  • Delegálási kombináció. Minden delegálttípus implicit módon a következő előre definiált operátort biztosítja, ahol D a meghatalmazott típusa:

    D operator +(D x, D y);
    

    Ha az első operandus null, a művelet eredménye a második operandus értéke (még akkor is, ha az is null). Ellenkező esetben, ha a második operandus null, akkor a művelet eredménye az első operandus értéke. Ellenkező esetben a művelet eredménye egy új delegált példány, amelynek meghívási listája az első operandus meghívási listájának elemeiből, majd a második operandus meghívási listájának elemeiből áll. Ez azt jelenti, hogy az eredményül kapott delegált meghívási listája a két operandus meghívási listájának összefűzése.

    Megjegyzés: A delegálási kombinációk példáiért lásd 12.10.6. §- és 20.6. Mivel System.Delegate nem delegált típus, az operátor + nincs definiálva. végjegyzet

A fent meghatározott emelt (§12.4.8) formák a nem emelt előre definiált összeadási operátorok esetén is előre definiálva vannak.

12.10.6 Kivonási operátor

Az űrlap x – 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 y
    

    Egy checked kontextusban, ha a különbség kívül esik az eredménytípus tartományán, kivételként System.OverflowException dobódik. Egy unchecked környezetben a túlcsordulások nem kerülnek jelentésre, és az eredménytípus tartományán kívül eső jelentős magasabb helyiértékű bitek elvetésre kerülnek.

  • Lebegőpontos kivonás:

    float operator –(float x, float y);
    double operator –(double x, double y);
    

    A különbség kiszámítása az IEC 60559 aritmetikai szabályok szerint történik. Az alábbi táblázat a nem véges értékek, nullák, végtelenségek és NaN-értékek összes lehetséges kombinációjának eredményeit sorolja fel. A táblázatban a x és a y nem véges értékek, z pedig x – yeredménye. Ha x és y egyenlőek, z pozitív nulla. Ha x – y túl nagy ahhoz, hogy a céltípusban szerepeljen, z a x – yjellel megegyező végtelen.

    y +0 -0 +∞ -∞ NaN
    x z x x -∞ +∞ NaN
    +0 -y +0 +0 -∞ +∞ NaN
    -0 -y -0 +0 -∞ +∞ NaN
    +∞ +∞ +∞ +∞ NaN +∞ NaN
    -∞ -∞ -∞ -∞ -∞ NaN NaN
    NaN NaN NaN NaN NaN NaN NaN

    (A fenti táblázatban a -y bejegyzések a y jelölik, nem pedig azt, hogy az érték negatív.)

  • Decimális kivonás:

    decimal operator –(decimal x, decimal y);
    

    Ha az eredményül kapott érték nagysága túl nagy ahhoz, hogy decimális formátumban legyen ábrázolva, egy System.OverflowException hiba keletkezik. Az eredmény skálája a kerekítés előtt a két operandus skálái közül a nagyobb.

    A decimális kivonás egyenértékű a System.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 E az enumerálás típusa, U pedig a Emö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 a x és a ysorszámértékeinek különbségét, és az eredmény típusa az enumerálás alapjául szolgáló típus.

    E operator –(E x, U y);
    

    Ez az operátor pontosan úgy van kiértékelve, mint (E)((U)x – y). Más szóval az operátor kivon egy értéket az enumerálás alapjául szolgáló típusból, ami az enumerálás értékét eredményezi.

  • Delegált eltávolítása. Minden delegálttípus implicit módon a következő előre definiált operátort biztosítja, ahol D a meghatalmazott típusa:

    D operator –(D x, D y);
    

    A szemantikák a következők:

    • Ha az első operandus null, a művelet eredménye null.
    • Ellenkező esetben, ha a második operandus null, akkor a művelet eredménye az első operandus értéke.
    • Ellenkező esetben mindkét operandus nem üres meghívási listákat jelöl (§20.2).
      • Ha a listák egyenlőek, a delegált egyenlőségi operátor (§12.12.9) meghatározása szerint a művelet eredménye null.
      • Ellenkező esetben a művelet eredménye egy új meghívási lista, amely az első operandus listájából áll, és a második operandus bejegyzései törlődnek belőle, feltéve, hogy a második operandus listája az elsők allistája. (Az allista egyenlőségének meghatározásához a megfelelő bejegyzések összehasonlítása a meghatalmazott egyenlőségi operátorhoz képest.) Ha a második operandus listája megegyezik az első operandus listában szereplő összefüggő bejegyzések több allistájával, az egybefüggő bejegyzések utolsó egyező allistája el lesz távolítva.
      • Ellenkező esetben a művelet eredménye a bal operandus értéke.

    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

Az előbbi bekezdésben meghatározott nem emelt előre definiált kivonási operátorok emelt formái (§12.4.8) szintén előre definiáltak.

12.11 Műszak operátorok

A << és >> operátorok bitváltó műveletek végrehajtására szolgálnak.

shift_expression
    : additive_expression
    | shift_expression '<<' additive_expression
    | shift_expression right_shift additive_expression
    ;

Ha egy shift_expression operandusának kompilációs idejű típusa dynamic, akkor a kifejezés dinamikusan kötött (§12.3.3). Ebben az esetben a kifejezés fordítási idejű típusa dynamic, és az alábbiakban ismertetett feloldás futásidőben történik azoknak az operandusoknak a futásidejű típusával, amelyek fordítási idejű típusa dynamic.

Az űrlap x << count vagy x >> 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átor x-et az alábbiakban meghatározott módon kiszámított bitek számával balra tolja.

    A x eredménytípusának tartományán kívül eső nagy értékű biteket a rendszer elveti, a fennmaradó biteket balra, az alacsony sorrendű üres biteket pedig nullára állítja.

  • Jobbra váltás:

    int operator >>(int x, int count);
    uint operator >>(uint x, int count);
    long operator >>(long x, int count);
    ulong operator >>(ulong x, int count);
    

    A >> operátor jobbra eltolja x egy olyan bitek száma szerint, amit az alábbiakban leírnak.

    Amikor a x típus int vagy long, a x alacsony rendű bitjei elvetésre kerülnek, a fennmaradó bitek jobbra tolódnak, és a magas rendű üres bitpozíciók nullára vannak állítva, ha x nem negatív, illetve egyre vannak állítva, ha x negatív.

    Ha a xuint vagy ulongtípusú, a x alacsonyrendű bitjeit elvetik, majd a megmaradó biteket jobbra tolják, és a magas sorba helyezett üres bithézagokat nulla értékre állítják.

Az előre definiált operátorok esetében az eltolandó bitek száma a következőképpen lesz kiszámítva:

  • Ha a x típusa int vagy uint, a műszakok számát az alacsony sorrendű öt bit countadja meg . Más szóval, a műszakok számát a count & 0x1Falapján számítják ki.
  • Ha a x típusa long vagy ulong, a műszakok számát a countkisrendű hat bitje adja meg. Más szóval, a műszakok számát a count & 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 x egy int típusú változó, a művelet unchecked ((int)((uint)x >> y)) logikai jobbra eltolást hajt végre x. példa vége

A fentebb meghatározott emelt (§12.4.8) típusú, nem emelt, előre definiált váltó operátorok is előre definiálva vannak.

12.12 Relációs és típustesztelő operátorok

12.12.1 Általános

A ==, !=, <, >, <=, >=, isés as operátorokat relációs és típustesztelő operátoroknak nevezzük.

relational_expression
    : shift_expression
    | relational_expression '<' shift_expression
    | relational_expression '>' shift_expression
    | relational_expression '<=' shift_expression
    | relational_expression '>=' shift_expression
    | relational_expression 'is' type
    | relational_expression 'is' pattern
    | relational_expression 'as' type
    ;

equality_expression
    : relational_expression
    | equality_expression '==' relational_expression
    | equality_expression '!=' relational_expression
    ;

Megjegyzés: A is operátor jobb oldali operandusát először típusú-ként kell tesztelni, majd kifejezésként, amely több tokenre is kiterjedhet. Abban az esetben, ha az operandus egy kifejezés, a mintakifejezésnek legalább olyan prioritással kell rendelkeznie, mint a eltolás_kifejezés. végjegyzet

A is operátort a 12.12.12. §, a as operátort pedig a 12.12.13. §írja le.

A ==, !=, <, >, <= és >= operátorok összehasonlító operátorok.

Ha egy default_literal (§12.8.21) egy <, >, <=vagy >= operátor operandusaként használatos, fordítási időhiba lép fel. Ha egy default_literal mindkét operandusként van használva egy == vagy != operátor esetén, akkor fordítási idejű hiba lép fel. Ha egy default_literal van használva a is vagy as operátor bal oldali operandusaként, fordítási időben hiba lép fel.

Ha egy összehasonlító operátor operandusa 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.12.2 Egész szám összehasonlító operátorai

Az előre definiált egész számok összehasonlító operátorai a következők:

bool operator ==(int x, int y);
bool operator ==(uint x, uint y);
bool operator ==(long x, long y);
bool operator ==(ulong x, ulong y);

bool operator !=(int x, int y);
bool operator !=(uint x, uint y);
bool operator !=(long x, long y);
bool operator !=(ulong x, ulong y);

bool operator <(int x, int y);
bool operator <(uint x, uint y);
bool operator <(long x, long y);
bool operator <(ulong x, ulong y);

bool operator >(int x, int y);
bool operator >(uint x, uint y);
bool operator >(long x, long y);
bool operator >(ulong x, ulong y);

bool operator <=(int x, int y);
bool operator <=(uint x, uint y);
bool operator <=(long x, long y);
bool operator <=(ulong x, ulong y);

bool operator >=(int x, int y);
bool operator >=(uint x, uint y);
bool operator >=(long x, long y);
bool operator >=(ulong x, ulong y);

Mindegyik operátor összehasonlítja a két egész szám operandus numerikus értékeit, és egy bool értéket ad vissza, amely jelzi, hogy az adott reláció true vagy false.

Az előre definiált (nem emelt) egész számok összehasonlító operátorainak fent meghatározott emelt formái szintén előre definiáltak.

12.12.3 Lebegőpontos összehasonlító operátorok

Az előre definiált lebegőpontos összehasonlító operátorok a következők:

bool operator ==(float x, float y);
bool operator ==(double x, double y);

bool operator !=(float x, float y);
bool operator !=(double x, double y);

bool operator <(float x, float y);
bool operator <(double x, double y);

bool operator >(float x, float y);
bool operator >(double x, double y);

bool operator <=(float x, float y);
bool operator <=(double x, double y);

bool operator >=(float x, float y);
bool operator >=(double x, double y);

Az operátorok az operandusokat az IEC 60559 szabvány szabályai szerint hasonlítják össze:

Ha bármelyik operandus NaN, az eredmény false az összes operátor esetében, kivéve !=, amelyeknél az eredmény true. Minden két operandus esetében x != y mindig ugyanazt az eredményt adja, mint !(x == y). Ha azonban az egyik vagy mindkét operandus NaN, akkor a <, >, <=, és >= operátorok nem a logikai tagadásával azonos eredményt eredményeznek az ellenkező operátor esetén.

Példa: Ha valamelyik x és y NaN, akkor x < 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.12.4 Decimális összehasonlító operátorok

Az előre definiált decimális összehasonlító operátorok a következők:

bool operator ==(decimal x, decimal y);
bool operator !=(decimal x, decimal y);
bool operator <(decimal x, decimal y);
bool operator >(decimal x, decimal y);
bool operator <=(decimal x, decimal y);
bool operator >=(decimal x, decimal y);

Ezek az operátorok összehasonlítják a két tizedes operandus numerikus értékeit, és egy bool értéket ad vissza, amely jelzi, hogy az adott reláció true vagy false. Minden decimális összehasonlítás egyenértékű a megfelelő System.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.12.5 Logikai egyenlőségi operátorok

Az előre definiált logikai egyenlőség operátorai a következők:

bool operator ==(bool x, bool y);
bool operator !=(bool x, bool y);

A == eredménye true, ha x és y is true, vagy x és y is false. Ellenkező esetben az eredmény false.

A != eredménye false, ha x és y is true, vagy x és y is false. Ellenkező esetben az eredmény true. Ha az operandusok 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.12.6 Enumerálási összehasonlító operátorok

Minden enumerálási típus implicit módon a következő előre definiált összehasonlító operátorokat biztosítja

bool operator ==(E x, E y);
bool operator !=(E x, E y);

bool operator <(E x, E y);
bool operator >(E x, E y);
bool operator <=(E x, E y);
bool operator >=(E x, E y);

Az x «op» 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.12.7 Referenciatípus egyenlőségi operátorai

Minden osztálytípus C implicit módon a következő előre definiált referenciatípus-egyenlőségi operátorokat biztosítja:

bool operator ==(C x, C y);
bool operator !=(C x, C y);

kivéve, ha az C-hoz nem léteznek előre meghatározott egyenlőségi operátorok (például, amikor C is string vagy System.Delegate).

Az operátorok az egyenlőségre vagy nem egyenlőségre vonatkozó két hivatkozás összehasonlításának eredményét adják vissza. operator == akkor és csak akkor ad vissza true, ha x és y ugyanarra a példányra hivatkoznak, vagy mindkettő null, míg operator != akkor és csak akkor ad vissza true, ha az azonos operandusokkal rendelkező operator ==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 egy T típusú érték, ahol a T egy olyan type_parameter, amelyről ismert, hogy nem értéktípus, és nincs megkötése az értéktípusra.
    • Ha futásidőben T nem null értékű értéktípus, a == eredménye false, a != eredménye pedig true.
    • Ha futásidőben a T null értékű értéktípus, az eredményt az operandus HasValue tulajdonságából számítja ki, amint az a (§12.12.10) szakaszban le van írva.
    • Ha futásidőben T egy referenciatípus, az eredmény true, ha az operandus null, ellenkező esetben false.

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 == y vagy x != yművelete esetén , ha létezik a felhasználó által definiált operator == vagy operator !=, 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 a objecttípusra explicit módon kasztolja.

végjegyzet

Példa: Az alábbi példa ellenőrzi, hogy a nem betanított típusú paramétertípus argumentuma null-e.

class C<T>
{
   void F(T x)
   {
      if (x == null)
      {
          throw new ArgumentNullException();
      }
      ...
   }
}

A x == null szerkezet akkor is engedélyezett, ha T nem null értékű értéktípust jelölhet, és az eredmény egyszerűen false, ha T nem 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
False

A s és t változók két különböző sztringpéldányra vonatkoznak, amelyek ugyanazokat a karaktereket tartalmazzák. Az első összehasonlító kimenet True, mert az előre definiált sztringegyenlőségi operátor (§12.12.8) akkor van kiválasztva, ha mindkét operandus stringtípusú. A fennmaradó összehasonlítások mind False-t eredményeznek, mert a operator == típusú string túlterhelése nem alkalmazható, ha bármelyik operandus kötési idő típusa object.

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 boxolt int értékek két különálló példányára hivatkoznak.

példa vége

12.12.8 Karakterlánc-egyenlőségi operátorok

Az előre definiált sztringegyenlőségi operátorok a következők:

bool operator ==(string x, string y);
bool operator !=(string x, string y);

Két string érték akkor tekinthető egyenlőnek, ha az alábbiak egyike igaz:

  • Mindkét érték null.
  • Mindkét érték olyan hivatkozás, amely nemnull sztringpéldányokra mutat, és ezek a példányok azonos hosszúságúak, illetve minden karakterhelyen azonos karakterek találhatók.

A sztringegyenlőségi operátorok sztringhivatkozások helyett sztringértékeket hasonlítanak össze. Ha két különálló sztringpéldány pontosan ugyanazt a karaktersorozatot tartalmazza, a sztringek értéke egyenlő, de a hivatkozások eltérőek.

Megjegyzés: Amint a 12.12.7szakaszban leírtuk, a referencia típusú egyenlőség operátorok használhatóak string hivatkozások összehasonlítására a string értékek helyett. végjegyzet

12.12.9 Egyenlőségi operátorok delegálása

Az előre definiált meghatalmazotti egyenlőségi operátorok a következők:

bool operator ==(System.Delegate x, System.Delegate y);
bool operator !=(System.Delegate x, System.Delegate y);

Két delegált példány egyenlőnek minősül az alábbiak szerint:

  • Ha valamelyik delegált példány null, akkor és csak akkor egyenlőek, ha mindkettő null.
  • Ha a delegáltak különböző futásidejű típusokkal rendelkeznek, soha nem egyenlők.
  • Ha mindkét delegált példánynak van egy meghívási listája (§20.2), akkor ezek a példányok egyenlők, ha és csak akkor, ha a meghívási listák azonos hosszúságúak, és az egyik híváslistájában szereplő összes bejegyzés egyenlő (az alább meghatározottak szerint) a megfelelő bejegyzéssel, sorrendben a másik híváslistájában.

A meghívási lista bejegyzéseinek egyenlőségére a következő szabályok vonatkoznak:

  • Ha két meghívási lista bejegyzése ugyanarra a statikus metódusra hivatkozik, akkor a bejegyzések egyenlők.
  • Ha két meghívási listabejegyzés ugyanarra a nem statikus metódusra hivatkozik ugyanazon a célobjektumon (a referenciaegyenlítési operátorok által meghatározottak szerint), akkor a bejegyzések egyenlőek.
  • A szemantikailag azonos névtelen függvények (12.19) kiértékeléséből származó meghívási lista bejegyzések azonos (esetleg üres) rögzített külső változópéldányokkal egyenlővé tehetők, de ez nem kötelező.

Ha az operátorok túlterhelésének feloldása a delegált egyenlőségi operátorral történik, és mindkét operandus kötési időtípusa delegált típus, a 20. § szerint, nem pedig System.Delegate, és nincs azonosító átalakítás a kötés típusú operandusok között, kötési időhibát eredményez.

Megjegyzés: Ez a szabály megakadályozza az olyan összehasonlításokat, amelyek soha nem tekinthetnek nem-null értékeket egyenlőnek, mivel azok különböző típusú delegátumok példányaira hivatkoznak. végjegyzet

12.12.10 Egyenlőségi operátorok a null értékű értéktípusok és a null literál között

A == és != operátorok lehetővé teszik, hogy az egyik operandus null értékű, a másik pedig a null literál legyen, még akkor is, ha a művelethez nincs előre definiált vagy felhasználó által definiált operátor (emeletlen vagy emelt formában).

Az űrlapok egyikének műveletéhez

x == null    null == x    x != null    null != x

ahol a x nullálható értéktípus kifejezése, ha az operátor túlterhelés feloldása (12.4.5.) nem talál alkalmazható operátort, akkor az eredményt a 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.12.11 A n-es egyenlőségi operátorai

Az egyenlőségi tuple operátorokat az operandus tuple elemekre párba állítva alkalmazzuk, lexikális sorrendben.

Ha egy x vagy y operátor minden operandusa == és != n-es vagy n-es típusú értékként (§8.3.11) van besorolva, akkor az operátor n-es egyenlőség-operátor.

Ha egy operandus e rekordként van besorolva, a e1...en elemek a rekordkifejezés elemkifejezéseinek kiértékelésének eredményei. Ellenkező esetben, ha e egy rekord típusú érték, akkor az elemek t.Item1...t.Itemn-ek lesznek, ahol t a 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 x kiértékelése történik.
  • A jobb oldali operandus y-t kiértékelik.
  • Minden egyes elempár esetében xi és yi lexikális sorrendben:
    • A xi == yi operátor kiértékelése és a bool típusú eredmény a következő módon történik:
      • Ha az összehasonlítás egy bool eredményezett, akkor ez az eredmény.
      • Ellenkező esetben, ha az összehasonlítás egy dynamic eredményezett, akkor a rendszer dinamikusan meghívja az false operátort, és az eredményül kapott bool é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 kapott bool értéket a logikai negation operátorral (!) tagadja le.
    • Ha bool eredményként egyenlő false-vel, akkor nincs további értékelés, és a tuple-egyenlőség-operátor eredménye false.
  • Ha az összes elem-összehasonlítás eredménye truelett, akkor az n-többes egyenlőségi operátor eredménye true.

A tömb egyenlőség operátor x != y a következőképpen értékelhető ki:

  • A bal oldali operandus x kiértékelése történik.
  • A jobb oldali operandus y-t kiértékelik.
  • Minden egyes elempár esetében xi és yi lexikális sorrendben:
    • A xi != yi operátor kiértékelése és a bool típusú eredmény a következő módon történik:
      • Ha az összehasonlítás egy bool eredményezett, akkor ez az eredmény.
      • Ellenkező esetben, ha az összehasonlítás egy dynamic eredményezett, akkor a true operátor dinamikusan meghívódik rajta, és az eredményül kapott bool é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 kapott bool érték az eredmény.
    • Ha bool eredményként egyenlő true-vel, akkor nincs további értékelés, és a tuple-egyenlőség-operátor eredménye true.
  • Ha az összes elem-összehasonlítás eredménye falselett, akkor az n-többes egyenlőségi operátor eredménye false.

12.12.12 Az is operátor

A is operátornak két formája van. Az egyik az típusú operátor, amely a jobb oldalon található típussal rendelkezik. A másik az is-minta operátor, amely jobboldali mintával rendelkezik.

12.12.12.1 Az is-type operátor

Az is-type operátor annak ellenőrzésére szolgál, hogy egy objektum futásidejű típusa kompatibilis-e egy adott típussal. Az ellenőrzés futásidőben történik. A E is 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 kiértékelése a következőképpen történik:

  1. Ha E egy névtelen függvény vagy metóduscsoport, fordítási időhiba lép fel.
  2. Ha E a null literál, vagy ha a E értéke null, akkor az eredmény false.
  3. Egyébként:
  4. Legyen REfuttatási típusa.
  5. Legyen D az alábbi módon R-ből származtatható:
  6. Ha R null értékű, D a Rmögöttes típusa.
  7. Ellenkező esetben D van R.
  8. Az eredmény a D és T értékétől függ a következőképpen:
  9. Ha T hivatkozástípus, az eredmény true, ha:
    • Azonosság átalakítás létezik D és Tközött.
    • D egy referenciatípus, és létezik egy implicit referenciaátalakítás D-ről T-re, vagy
    • Vagy: D egy értéktípus, és létezik D boxtípusú konvertálása T-ra.
      Vagy: D egy értéktípus, T pedig a Dáltal implementált felülettípus.
  10. Ha T null értékű, az eredmény true, ha D a Tmögöttes típusa.
  11. Ha T nem null értékű értéktípus, az eredmény true, ha D és T azonos típusúak.
  12. Ellenkező esetben az eredmény false.

A felhasználó által megadott konverziókat a is operátor nem veszi figyelembe.

Megjegyzés: Mivel a is operátor futásidőben van kiértékelve, a rendszer minden típusargumentumot lecserélt, és nincsenek nyitott típusok (§8.4.3). végjegyzet

Megjegyzés: A is operátor a fordítási idő típusa és az átalakítások szempontjából az alábbiak szerint értelmezhető, ahol a C a Efordítási idő típusa:

  • Ha a e fordítási idő típusa megegyezik a T, 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 a E fordítási idő típusától a T:
    • Ha C nem null értékű, a művelet eredménye true.
    • Ellenkező esetben a művelet eredménye egyenértékű a E != nullkiértékelésével.
  • Ellenkező esetben, ha explicit referencia-átalakítás (§10.3.5) vagy kicsomagolási átalakítás (§10.3.7) létezik C-ről T-ra, vagy ha C vagy T nyitott típus (§8.4.3), akkor a fenti futtatóidejű ellenőrzéseket el kell végezni.
  • Ellenkező esetben nem lehetséges a E típus átalakítása T típusra referencia, csomagolás, burkolás vagy kibontás útján, és a művelet eredménye false. A fordítók optimalizálást végezhetnek a fordítási idő típusa alapján.

végjegyzet

12.12.12.2 Az is-pattern operátor

Az minta operátor annak ellenőrzésére szolgál, hogy a kifejezés által kiszámított érték egyezik-e egy adott mintával (11. §). Az ellenőrzés futásidőben történik. Az is-pattern operátor eredménye igaz, ha az érték megegyezik a mintával; ellenkező esetben hamis.

Egy olyan kifejezés esetén, mint E is P, ahol a E egy T típusú reláció, és a P egy minta, fordítási idejű hiba lép fel, ha az alábbi feltételek bármelyike igaz:

  • E nem jelöl meg értéket, vagy nem rendelkezik típussal.
  • A minta P nem alkalmazható (11.2.) típusra T.

12.12.13 Az "as" operátor

A as operátor használatával explicit módon konvertálhat egy értéket egy adott referenciatípusra vagy null értékűre. A öntött kifejezéstől (§12.9.7) ellentétben a as operátor soha nem ad kivételt. Ehelyett, ha a megadott átalakítás nem lehetséges, az eredményként kapott érték null.

A E as 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ől T-be.
  • A E vagy T típusa nyitott típus.
  • E az null literál.

Ha a E fordítási idejének típusa nem dynamic, akkor a E as T művelet ugyanazt az eredményt eredményezi, mint a

E is T ? (T)(E) : (T)null

kivéve, hogy a E csak egyszer lesz kiértékelve. A fordító várhatóan optimalizálja a E as T, hogy legfeljebb egy futtatókörnyezettípus-ellenőrzést hajtson végre, szemben a fenti bővítés által sugallt két futtatókörnyezettípus-ellenőrzéssel.

Ha a E fordítási idejének típusa dynamic, az öntött operátorral ellentétben a as operátor nincs dinamikusan kötve (§12.3.3). Ezért a bővítés ebben az esetben a következő:

E is T ? (T)(object)(E) : (T)null

Vegye figyelembe, hogy egyes átalakítások, például a felhasználó által megadott konverziók nem lehetségesek a as operátorral, és ehelyett a cast kifejezések használatával kell végrehajtani.

példa: A példában

class X
{
    public string F(object o)
    {
        return o as string;  // OK, string is a reference type
    }

    public T G<T>(object o)
        where T : Attribute
    {
        return o as T;       // Ok, T has a class constraint
    }

    public U H<U>(object o)
    {
        return o as U;       // Error, U is unconstrained
    }
}

A T típusparaméteréről G ismert, hogy referenciatípus, mert osztálykötési megszorítással rendelkezik. A UH típusparamétere azonban nem megfelelő; ezért a as operátor használata H nem engedélyezett.

példa vége

12.13 Logikai operátorok

12.13.1 Általános

A &, ^és | operátorokat logikai operátoroknak nevezzük.

and_expression
    : equality_expression
    | and_expression '&' equality_expression
    ;

exclusive_or_expression
    : and_expression
    | exclusive_or_expression '^' and_expression
    ;

inclusive_or_expression
    : exclusive_or_expression
    | inclusive_or_expression '|' exclusive_or_expression
    ;

Ha egy logikai operátor operandusa 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.13.2 Egész szám logikai operátorok

Az előre definiált egész szám logikai operátorai a következők:

int operator &(int x, int y);
uint operator &(uint x, uint y);
long operator &(long x, long y);
ulong operator &(ulong x, ulong y);

int operator |(int x, int y);
uint operator |(uint x, uint y);
long operator |(long x, long y);
ulong operator |(ulong x, ulong y);

int operator ^(int x, int y);
uint operator ^(uint x, uint y);
long operator ^(long x, long y);
ulong operator ^(ulong x, ulong y);

A & operátor kiszámítja a két operandus bitenkénti logikai ÉSÉT, a | operátor kiszámítja a két operandus bitenkénti logikai VAGYÁT, a ^ operátor pedig kiszámítja a két operandus bitenkénti logikai kizáró VAGYÁT. Ezekből a műveletekből nem következhet túlcsordulás.

A fent meghatározott, felemelt (§12.4.8) előre meghatározott egész logikai operátorok felemelt formái is alapértelmezettek.

12.13.3 Enumerálási logikai operátorok

Minden enumerálási típus E implicit módon a következő előre definiált logikai operátorokat biztosítja:

E operator &(E x, E y);
E operator |(E x, E y);
E operator ^(E x, E y);

A x «op» 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.13.4 Boole-i logikai operátorok

Az előre definiált logikai operátorok a következők:

bool operator &(bool x, bool y);
bool operator |(bool x, bool y);
bool operator ^(bool x, bool y);

A x & y eredménye true, ha x és y is true. Ellenkező esetben az eredmény false.

A x | y eredménye true, ha x vagy y a true. Ellenkező esetben az eredmény false.

A x ^ y eredménye true, ha 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.13.5 Null értékű bool & és | operátorok

A null értéket felvevő logikai típus bool? három értéket jelölhet: true, falseés null.

A többi bináris operátorhoz hasonlóan a logikai operátorok megemelt formái is, & és | (12.13.4.), előre definiáltak.

bool? operator &(bool? x, bool? y);
bool? operator |(bool? x, bool? y);

Az emelt & és | operátorok szemantikáját a következő táblázat határozza meg:

x y x & y x \| y
true true true true
true false false true
true null null true
false true false true
false false false false
false null false null
null true null true
null false false null
null null null null

Megjegyzés: A bool? típus fogalmilag hasonló az SQL logikai kifejezéseihez használt háromértékű típushoz. A fenti táblázat ugyanazokat a szemantikákat követi, mint az SQL, míg a 12.4.8.8. szabályainak alkalmazása a & és | operátorokra nem. A 12.4.8 szabályai már sql-szerű szemantikát biztosítanak az emelt ^ operátor számára. végjegyzet

12.14 Feltételes logikai operátorok

12.14.1 Általános

A && és || operátorokat feltételes logikai operátoroknak nevezzük. Ezeket "rövidzárlatú" logikai operátoroknak is nevezik.

conditional_and_expression
    : inclusive_or_expression
    | conditional_and_expression '&&' inclusive_or_expression
    ;

conditional_or_expression
    : conditional_and_expression
    | conditional_or_expression '||' conditional_and_expression
    ;

A && és || operátorok a & és | operátorok feltételes verziói:

  • A művelet x && y megfelel a x & yműveletnek, azzal a kivételsel, hogy a y csak akkor lesz kiértékelve, ha x nem false.
  • A művelet x || y megfelel a x | yműveletnek, azzal a kivételsel, hogy a y csak akkor lesz kiértékelve, ha x nem true.

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 truefalse ad vissza, operator false pedig false. 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 logikai operátort vagy null értékű logikai Boole-operátort (§12.13.5), akkor kötési idejű hiba következik be.
  • Ellenkező esetben, ha a kiválasztott operátor az előre definiált logikai operátorok egyike (§12.13.4), a művelet a §12.14.2szerint kerül feldolgozásra.
  • Ellenkező esetben a kijelölt operátor egy felhasználó által definiált operátor, és a műveletet a §12.14.3leírtak szerint dolgozzák fel.

A feltételes logikai operátorok közvetlen túlterhelése nem lehetséges. Mivel azonban a feltételes logikai operátorok kiértékelése a normál logikai operátorok szempontjából történik, a normál logikai operátorok túlterhelése bizonyos korlátozásokkal a feltételes logikai operátorok túlterhelésének is tekinthető. Ez részletesen ki van fejtve a §12.14.3szakaszban.

12.14.2 Logikai feltételes Boole-operátorok

Ha a && vagy || operandusai 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 && y műveletet x ? y : false-ként értékeljük ki. Más szóval, a x-t először kiértékelik, majd booltípussá alakítják. Ezután, ha xtrue, a y-t kiértékelik és booltípussá alakítják, és ez lesz a művelet eredménye. Ellenkező esetben a művelet eredménye false.
  • A x || y műveletet x ? true : y-ként értékeljük ki. Más szóval, a x-t először kiértékelik, majd booltípussá alakítják. Akkor, ha xtrue, a művelet eredménye true. Ellenkező esetben a y kiértékelődik és booltípussá alakítódik, és ez válik a művelet eredményévé.

12.14.3 Felhasználó által definiált feltételes logikai operátorok

Ha a && vagy || operandusai olyan típusúak, amelyek a felhasználó által meghatározott operator & vagy operator |deklarálnak, az alábbiak közül mindkettő igaz, ahol T a kiválasztott operátor deklarálásának típusa:

  • A kiválasztott operátor visszatérési típusának és minden egyes paraméterének típusának Tkell lennie. Más szóval az operátornak ki kell számítania az Ttípusú két operandus logikai ÉS vagy logikai VAGY értékét , és Ttípusú eredményt kell visszaadni .
  • T operator true és operator 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 && y művelet T.false(x) ? x : T.&(x, y)a következőként van kiértékelve, ahol a T.false(x)-ban deklarált operator false a T meghívása, a kiválasztott T.&(x, y)pedig a operator & meghívása. Más szóval, először kiértékelik a x-t, majd a operator false-t hívják meg az eredmény alapján, hogy meghatározzák, a x határozottan hamis-e. Ezután, ha x egyértelműen hamis, a művelet eredménye a korábban x-hez kiszámított érték. Ellenkező esetben a y kiértékelésre kerül, és a kiválasztott operator & meghívásra kerül a korábban kiszámított x értékre és a kiszámított y értékre, hogy előállítsa a művelet eredményét.
  • A x || y művelet T.true(x) ? x : T.|(x, y)a következőként van kiértékelve, ahol a T.true(x)-ban deklarált operator true a T meghívása, a kiválasztott T.|(x, y)pedig a operator | meghívása. Más szóval, először a x van kiértékelve, majd az eredmény alapján meghívják a operator true-t annak megállapítására, hogy a x valóban igaz-e. Ezután, ha x biztosan igaz, a művelet eredménye az xkorábban kiszámított érték. Ellenkező esetben a y kiértékelésre kerül, és a kiválasztott operator | meghívásra kerül a korábban kiszámított x értékre és a kiszámított y értékre, hogy előállítsa a művelet eredményét.

Ezen műveletek bármelyikében a x által megadott kifejezés csak egyszer lesz kiértékelve, és az y által adott kifejezés nem lesz kiértékelve vagy pontosan egyszer kiértékelve.

12.15 A null egyesítő operátor

A ?? operátort null szénerősítési operátornak nevezzük.

null_coalescing_expression
    : conditional_or_expression
    | conditional_or_expression '??' null_coalescing_expression
    | throw_expression
    ;

Ha a ?? b nema, akkor a 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 ?? c formájú kifejezés úgy lesz kiértékelve, mint a ?? (b ?? c). Általánosságban elmondható, hogy egy E1 ?? E2 ?? ... ?? EN alakban lévő kifejezés visszaadja az első nemnulloperandust, vagy null-t, ha az összes operandus null. 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 A létezik, és nem felügyelt típus (8.8.§) vagy ismert, hogy nem null értékű, fordítási időhiba lép fel.
  • Ellenkező esetben, ha A létezik, és b dinamikus kifejezés, az eredmény típusa dynamic. Futásidőben a rendszer először kiértékeli a. Ha a nem null, adynamiclesz, és ez lesz az eredmény. Ellenkező esetben a b kerül kiértékelésre, és ez lesz az eredmény.
  • Ellenkező esetben, ha A létezik és null értékű értéktípus, amelyről van egy implicit átalakítás b-ről A₀-re, az eredménytípus A₀. Futásidőben a rendszer először kiértékeli a. Ha a nem null, a a kibontva A₀típusra, és ez lesz az eredmény. Ellenkező esetben a b kiértékelésre kerül és átalakul A₀típussá, és ez lesz az eredmény.
  • Ellenkező esetben, ha A létezik, és létezik egy implicit átalakítás b-ról A-ra, akkor az eredmény típusa A. Futásidőben a rendszer először kiértékeli a. Ha a nem null, a lesz az eredmény. Ellenkező esetben a b kiértékelésre kerül és átalakul Atípussá, és ez lesz az eredmény.
  • Ellenkező esetben, ha A létezik, és null értékkel rendelkező értéktípus, bB típussal rendelkezik, és van egy implicit átalakítás A₀-ról B-re, akkor az eredménytípus Blesz. Futásidőben a rendszer először kiértékeli a. Ha a nem null, akkor a ki van csomagolva A₀ típusra, majd átalakítva Btípusúvá, és ez lesz az eredmény. Ellenkező esetben a b kiértékelődik, és az lesz az eredmény.
  • Ellenkező esetben, ha bB típussal rendelkezik, és létezik egy implicit konverzió a-ből B-ba, az eredménytípus B. Futásidőben a rendszer először kiértékeli a. Ha a nincs null, aBtípussá alakul, és ez lesz az eredmény. Ellenkező esetben a b kiértékelődik, és az lesz az eredmény.
  • Ellenkező esetben a a és a b nem kompatibilisek, és fordítási időhiba lép fel.

Példa:

T M<T>(T a, T b) => a ?? b;

string s = M(null, "text");
int i = M(19, 23);

A metódus T típusparamétere M nincs korlátozva. Ezért a típusargumentum lehet hivatkozástípus vagy null értékű típus, ahogyan az első hívásban Mlátható. A típusargumentum lehet egy nem null értékű értéktípus is, ahogyan az a második M hívásban látható. Ha a típusargumentum nem nullázható értéktípus, a kifejezés a ?? b értéke a.

példa vége

12.16 A dobáskifejezési operátor

throw_expression
    : 'throw' null_coalescing_expression
    ;

A throw_expression a null_coalescing_expressionkiértékelésével előállított értéket adja meg. A kifejezésnek implicit módon átalakíthatónak kell lennie System.Exceptionértékre, és a kifejezés kiértékelésének eredménye System.Exception lesz a dobás előtt. A throw kifejezés kiértékelésének futásidejű viselkedése ugyanaz, mint ahogy a throw utasítás (§13.10.6) esetében van meghatározva.

A throw_expression nem rendelkezik típussal. A throw_expression minden típusra átalakítható egy implicit dobás átalakítással.

A dobókifejezés csak a következő szintaktikai kontextusokban fordulhat elő:

  • Ternáris feltételes operátor második vagy harmadik operandusaként (?:).
  • Null egyesítő operátor második operandusaként (??).
  • Kifejezés alapú lambdák vagy tagok esetén.

12.17 Deklarációs kifejezések

A deklarációs kifejezés egy helyi változót deklarál.

declaration_expression
    : local_variable_type identifier
    ;

local_variable_type
    : type
    | 'var'
    ;

A simple_name_ akkor is deklarációs kifejezésnek minősül, ha az egyszerű névkeresés nem talált társított deklarációt (§12.8.4). Deklarációs kifejezésként használva a _egyszerű elvetési kifejezésnek nevezik. Szemantikailag egyenértékű a var _, de több helyen engedélyezett.

Deklarációs kifejezés csak a következő szintaktikai kontextusokban történhet:

  • Mint egy outargument_value egy argument_list-ban.
  • Egyszerű elvetés _, amely egy egyszerű hozzárendelés bal oldalát képezi (§12.21.2).
  • Egy vagy több rekurzív módon beágyazott tuple_expressiontuple_element, amelynek legkülső eleme a dekonstruáló hozzárendelés bal oldalán található. A deconstruction_expression deklarációs kifejezéseket eredményez ebben a helyzetben, még akkor is, ha a deklarációs kifejezések nincsenek szintaktikailag jelen.

Megjegyzés: Ez azt jelenti, hogy a deklarációs kifejezés nem zárójeles. végjegyzet

Hiba, ha egy implicit módon beírt változóra, amely declaration_expression deklarációval van ellátva, hivatkoznak abban az argument_list listában, ahol deklarálták.

Hiba, ha egy declaration_expression segítségével deklarált változóra hivatkoznak abban a dekonstruálási hozzárendelésben, ahol előfordul.

A deklarációs kifejezés, amely egy egyszerű elvetés, vagy ahol a local_variable_type az azonosító, 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 s1 deklarációja explicit és implicit módon beírt deklarációs kifejezéseket is mutat. A(z) b1 típusa bool, mert ez a megfelelő kimeneti paraméter típusa a M1-ben. A következő WriteLine hozzáférhet az i1-hez és b1-höz, amelyeket a környező hatókörbe vezettek be.

A s2 deklarációja azt mutatja, hogy megpróbálják használni a i2-et a Mbeágyazott hívásában, ami nem megengedett, mert a hivatkozás abban az argumentumlistában fordul elő, ahol i2 deklarálva volt. Másrészt a végső argumentumban engedélyezett a b2 való hivatkozás, mert a beágyazott argumentumlista vége után következik be, ahol b2 deklarálva lett.

A s3 deklarációja azt mutatja, hogyan használhatók az implicit és explicit módon típusosan beírt deklarációs kifejezések, amelyek elhanyagolhatók. Mivel az elvetések nem deklarálnak elnevezett változót, az azonosító _ több előfordulása is engedélyezett.

(int i1, int _, (var i2, var _), _) = (1, 2, (3, 4), 5);

Ez a példa implicit és explicit módon beírt deklarációs kifejezések használatát mutatja be mind a változók, mind az elvetések esetében egy dekonstruáló hozzárendelésben. A simple_name_ egyenértékű var _, ha nem található _ deklaráció.

void M1(out int i) { ... }

void M2(string _)
{
    M1(out _);      // Error: `_` is a string
    M1(out var _);
}

Ez a példa azt mutatja be, hogy a var _ implicit módon beírt elvetést biztosít, ha _ nem érhető el, mert egy változót jelöl ki a beágyazási hatókörben.

példa vége

12.18 Feltételes operátor

A ?: operátort feltételes operátornak nevezik. Időnként ternáris operátornak is nevezik.

conditional_expression
    : null_coalescing_expression
    | null_coalescing_expression '?' expression ':' expression
    | null_coalescing_expression '?' 'ref' variable_reference ':'
      'ref' variable_reference
    ;

Ha jelen van, a dobáskifejezés (ref) nem szerepelhet feltételes operátorban.

Az űrlap feltételes kifejezése b ? x : y először kiértékeli a feltételt b. Ezután, ha 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 : e formájú kifejezés úgy lesz kiértékelve, mint a ? 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 a dynamic (8.7. §) előnyben részesíti. Ha bármelyik típus rekordtípus (§8.3.11), akkor a típuskövetkezmények tartalmazzák az elemneveket, ha az azonos sorrendben lévő elemnevek mindkét rekordban megegyeznek.
  • Az eredmény egy változóhivatkozás, amely írható, ha mindkét változó_hivatkozásírható.

Megjegyzés: Ha ref van jelen, a conditional_expression egy változóhivatkozást ad vissza, amely hozzárendelhető egy referenciaváltozóhoz a = ref operátor használatával, vagy referencia-/bemeneti/kimeneti paraméterként továbbítható. végjegyzet

Ha ref nincs jelen, az x operátor második és harmadik operandusa, y és ?:a feltételes kifejezés típusát szabályozza:

  • Ha xX típussal rendelkezik, és yY,
    • Ha X és Yközött identitáskonvertálás áll fenn, akkor az eredmény a kifejezéskészletek leggyakoribb típusa (§12.6.3.15). Ha bármelyik típus dynamic, a típuskövetkeztetés a dynamic (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ől Y-ra, de nem Y-ről X-ra, akkor Y a feltételes kifejezés típusa.
    • Ellenkező esetben, ha implicit számbavételi átalakítás (§10.2.4) létezik X-ről Y-ra, akkor Y a feltételes kifejezés típusa.
    • Ellenkező esetben, ha implicit számbavételi átalakítás (§10.2.4) létezik Y-ről X-ra, akkor X a feltételes kifejezés típusa.
    • Ellenkező esetben, ha egy implicit átalakítás (§10.2) létezik Y-ről X-ra, de nem X-ről Y-ra, akkor X a feltételes kifejezés típusa.
    • Ellenkező esetben nem határozható meg kifejezéstípus, és fordítási időhiba lép fel.
  • Ha csak az egyik x és y rendelkezik típussal, és x és y is implicit módon konvertálható erre a típusra, akkor ez a feltételes kifejezés típusa.
  • Ellenkező esetben nem határozható meg kifejezéstípus, és fordítási időhiba lép fel.

Az b ? ref x : ref y formájú ref feltételes kifejezés futásidejű feldolgozása a következő lépésekből áll:

  • Először a b kiértékelésére kerül sor, majd meghatározásra kerül a boolb értéke.
    • Ha a b típusából bool implicit átalakítás történik, akkor ez az implicit átalakítás egy bool érték előállításához lesz végrehajtva.
    • Ellenkező esetben a operator true típus által definiált b kerül meghívásra a bool érték előállításához.
  • Ha a fenti lépés által előállított bool érték true, akkor a rendszer kiértékeli x, és az eredményül kapott változóhivatkozás a feltételes kifejezés eredménye lesz.
  • Ellenkező esetben a y kiértékelése megtörténik, és a feltételes kifejezés eredménye az így kapott változóhivatkozás lesz.

A b ? x : y űrlap feltételes kifejezésének futásidejű feldolgozása a következő lépésekből áll:

  • Először a b kiértékelésére kerül sor, majd meghatározásra kerül a boolb értéke.
    • Ha a b típusából bool implicit átalakítás történik, akkor ez az implicit átalakítás egy bool érték előállításához lesz végrehajtva.
    • Ellenkező esetben a operator true típus által definiált b kerül meghívásra a bool érték előállításához.
  • Ha a fenti lépés által létrehozott bool érték true, akkor a x kiértékelése és átalakítása a feltételes kifejezés típusára történik, és ez a feltételes kifejezés eredménye lesz.
  • Ellenkező esetben a y kiértékelése és átalakítása a feltételes kifejezés típusára történik, és ez lesz a feltételes kifejezés eredménye.

12.19 Névtelen függvénykifejezések

12.19.1 Általános

A névtelen függvény egy "in-line" metódusdefiníciót jelképező kifejezés. A névtelen függvények önmagukban nem rendelkeznek értékkel vagy típussal, de kompatibilis delegált vagy kifejezésfa típusúvá konvertálhatók. A névtelen függvények konvertálásának kiértékelése az átalakítás céltípusától függ: Ha delegált típusú, akkor az átalakítás delegált értékre kerül, amely a névtelen függvény által definiált metódusra hivatkozik. Ha kifejezésfa típusú, az átalakítás olyan kifejezésfára lesz kiértékelve, amely a metódus szerkezetét objektumszerkezetként jelöli.

Megjegyzés: Történelmi okokból a névtelen függvények két szintaktikai íze van, nevezetesen lambda_expressions és anonymous_method_expression. A lambda_expressionszinte minden célra tömörebb és kifejezőbb, mint a anonymous_method_expression, melyek a visszamenőleges kompatibilitás érdekében maradtak meg a nyelvben. végjegyzet

lambda_expression
    : 'async'? anonymous_function_signature '=>' anonymous_function_body
    ;

anonymous_method_expression
    : 'async'? 'delegate' explicit_anonymous_function_signature? block
    ;

anonymous_function_signature
    : explicit_anonymous_function_signature
    | implicit_anonymous_function_signature
    ;

explicit_anonymous_function_signature
    : '(' explicit_anonymous_function_parameter_list? ')'
    ;

explicit_anonymous_function_parameter_list
    : explicit_anonymous_function_parameter
      (',' explicit_anonymous_function_parameter)*
    ;

explicit_anonymous_function_parameter
    : anonymous_function_parameter_modifier? type identifier
    ;

anonymous_function_parameter_modifier
    : 'ref'
    | 'out'
    | 'in'
    ;

implicit_anonymous_function_signature
    : '(' implicit_anonymous_function_parameter_list? ')'
    | implicit_anonymous_function_parameter
    ;

implicit_anonymous_function_parameter_list
    : implicit_anonymous_function_parameter
      (',' implicit_anonymous_function_parameter)*
    ;

implicit_anonymous_function_parameter
    : identifier
    ;

anonymous_function_body
    : null_conditional_invocation_expression
    | expression
    | 'ref' variable_reference
    | block
    ;

Egy anonymous_function_body felismerésekor, ha a null_conditional_invocation_expression és a kifejezés alternatívák is alkalmazhatók, akkor az előbbit kell választani.

Megjegyzés: Az alternatívák átfedése és prioritása itt kizárólag a leíró kényelem érdekében van; a nyelvhelyességi szabályok kidolgozhatók az átfedés megszüntetése érdekében. Az ANTLR és más nyelvhelyességi rendszerek ugyanazt a kényelmes megoldást választják, így anonymous_function_body automatikusan a megadott szemantikával rendelkezik. végjegyzet

megjegyzés: Ha kifejezéskéntkezelnek, akkor a x?.M()-hez hasonló szintaktikai forma hiba lenne, ha a M eredménytípusa void (§12.8.13). Ha azonban null_conditional_invocation_expression-ként kezelik, az eredménytípus voidmegengedett. végjegyzet

példa: A List<T>.Reverse eredménytípusa void. 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 blokkja mindig elérhető (§13.2).

Példa: A névtelen függvényekre az alábbiakban talál néhány példát:

x => x + 1                             // Implicitly typed, expression body
x => { return x + 1; }                 // Implicitly typed, block body
(int x) => x + 1                       // Explicitly typed, expression body
(int x) => { return x + 1; }           // Explicitly typed, block body
(x, y) => x * y                        // Multiple parameters
() => Console.WriteLine()              // No parameters
async (t1,t2) => await t1 + await t2   // Async
delegate (int x) { return x + 1; }     // Anonymous method expression
delegate { return 1 + 1; }             // Parameter list omitted

példa vége

Az lambda_expression-ek és anonymous_method_expression-ek viselkedése ugyanaz, kivéve a következő pontokat:

  • anonymous_method_expressionlehetővé teszi a paraméterlista teljes elhagyását, így átalakítható bármely értékparaméter-lista típusának delegálására.
  • lambda_expressionparamétertípusok elhagyhatók és következtethetők, míg anonymous_method_expressionparamétertípusok explicit megadását igénylik.
  • A lambda_expression törzse lehet kifejezés vagy blokk, míg egy anonymous_method_expression törzsének blokknak kell lennie.
  • Csak lambda_expressionrendelkezik kompatibilis kifejezésfatípusokra való átalakítással (§8.6).

12.19.2 Névtelen függvény-aláírások

Egy névtelen függvény anonymous_function_signature határozza meg a névtelen függvény nevét és opcionálisan a paraméterek típusait. A névtelen függvény paramétereinek hatóköre a anonymous_function_body (§7.7). A paraméterlistával együtt (ha meg van adva) a névtelen metódus törzse deklarációs területet alkot (§7.3). Tehát fordítási időben hiba lép fel, ha egy névtelen függvény paraméterének neve megegyezik egy helyi változó, helyi állandó vagy másik paraméter nevével, amelynek hatóköre magában foglalja a anonymous_method_expression vagy lambda_expression-t.

Ha egy névtelen függvény rendelkezik explicit_anonymous_function_signature, akkor a kompatibilis delegálttípusok és kifejezésfatípusok halmaza azokra korlátozódik, amelyek ugyanazon paramétertípusokkal és módosítókkal azonos sorrendben rendelkeznek (§10.7). A metóduscsoport-átalakításokkal (§10.8) ellentétben a névtelen függvényparaméter-típusok eltérése nem támogatott. Ha egy névtelen függvény nem rendelkezik anonymous_function_signature, akkor a kompatibilis delegálttípusok és kifejezésfatípusok csak azokra korlátozódnak, amelyek nem rendelkeznek kimeneti paraméterekkel.

Vegye figyelembe, hogy egy anonymous_function_signature nem tartalmazhat attribútumokat vagy paramétertömböket. Mindazonáltal egy anonymous_function_signature kompatibilis lehet egy olyan delegált típussal, amelynek paraméterlistája paramétertömböt tartalmaz.

Vegye figyelembe, hogy a kifejezésfatípusra való konvertálás még akkor is meghiúsulhat, ha kompatibilis, fordításkor (§8.6).

12.19.3 Névtelen függvénytestek

Egy névtelen függvény törzsére (kifejezésre vagy blokk) a következő szabályok vonatkoznak:

  • Ha a névtelen függvény tartalmaz aláírást, az aláírásban megadott paraméterek elérhetők a törzsben. Ha a névtelen függvény nem rendelkezik aláírással, delegált típussá vagy kifejezéstípussá alakítható, amelynek paraméterei (§10.7), de a paraméterek nem érhetők el a törzsben.
  • A legközelebbi névtelen függvény aláírásában megadott by-reference paramétereket (ha vannak ilyenek) kivéve fordítási idejű hiba, ha a törzs megpróbál hozzáférni egy by-reference paraméterhez.
  • A legközelebbi névtelen függvény aláírásában (ha van ilyen) megadott paraméterek kivételével a törzs fordítási idejű hibát követ el, ha hozzáfér egy ref struct típusú paraméterhez.
  • Ha a this típusa egy struktúratípus, akkor fordítási időhiba, ha a törzs hozzáfér this-hez. Ez érvényes arra, amikor a hozzáférés explicit (mint a this.x), vagy implicit (mint a x, ahol a x a struktúra egy példánytagja). Ez a szabály egyszerűen tiltja az ilyen hozzáférést, és nem befolyásolja, hogy a tagkeresés eredménye a struktúra egy tagja legyen.
  • A törzs hozzáfér a névtelen függvény külső változóihoz (12.19.6). A külső változó elérése a lambda_expression vagy anonymous_method_expression kiértékelésének időpontjában aktív változó példányára hivatkozik (§12.19.7).
  • Fordítási idő hibát jelent, ha a törzs egy goto utasítást, egy break utasítást vagy egy continue utasítást tartalmaz, amelynek célja a törzsen kívül vagy egy adott névtelen függvény törzsén belül van.
  • A return utasítás a törzsben a legközelebbi környező névtelen függvény meghívásából ad vissza vezérlést, nem pedig az azt körülvevő függvénytagból.

Nyíltan nincs megadva, hogy létezik-e mód a névtelen függvény blokkjának végrehajtására a lambda_expression vagy a anonymous_method_expressionkiértékelésén és meghívásán kívül. A fordítók dönthetnek úgy, hogy egy névtelen függvényt implementálnak egy vagy több nevesített metódus vagy típus szintetizálásával. Az ilyen szintetizált elemek neve olyan formában kell legyen, amely a fordítóprogram számára van fenntartva (§6.4.3).

12.19.4 Túlterhelés feloldás

Az argumentumlistában szereplő névtelen függvények típuskövetkeztetésben és túlterhelésfeloldásban vesznek részt. A pontos szabályokért tekintse meg §12.6.3 és §12.6.4.

Példa: Az alábbi példa a névtelen függvények túlterhelésfeloldásra gyakorolt hatását mutatja be.

class ItemList<T> : List<T>
{
    public int Sum(Func<T, int> selector)
    {
        int sum = 0;
        foreach (T item in this)
        {
            sum += selector(item);
        }
        return sum;
    }

    public double Sum(Func<T, double> selector)
    {
        double sum = 0;
        foreach (T item in this)
        {
            sum += selector(item);
        }
        return sum;
    }
}

A ItemList<T> osztály két Sum metódust használ. Mindegyik egy selector argumentumot használ, amely kinyeri a listaelemből összegzendő értéket. A kinyert érték lehet int vagy double, és az eredményül kapott összeg hasonlóképpen int vagy double.

A Sum metódusokkal például összegeket számíthat ki a részletes sorok listájából egy sorrendben.

class Detail
{
    public int UnitCount;
    public double UnitPrice;
    ...
}

class A
{
    void ComputeSums()
    {
        ItemList<Detail> orderDetails = GetOrderDetails( ... );
        int totalUnits = orderDetails.Sum(d => d.UnitCount);
        double orderTotal = orderDetails.Sum(d => d.UnitPrice * d.UnitCount);
        ...
    }

    ItemList<Detail> GetOrderDetails( ... )
    {
        ...
    }
}

A orderDetails.Sumelső meghívása során mindkét Sum metódus alkalmazható, mert a névtelen függvény d => d.UnitCount kompatibilis Func<Detail,int> és Func<Detail,double>. A túlterhelés feloldása azonban az első Sum metódust választja meg, mivel a Func<Detail,int> típusra való átalakítás jobb, mint a Func<Detail,double>-re való átalakítás.

A orderDetails.Summásodik meghívásában csak a második Sum metódus alkalmazható, mert a névtelen függvény d => d.UnitPrice * d.UnitCountdoubletípusú értéket állít elő. Így a túlterhelés feloldása a második Sum metódust választja az adott meghíváshoz.

példa vége

12.19.5 Névtelen függvények és dinamikus kötés

A névtelen függvények nem lehetnek dinamikusan kötött műveletek fogadói, argumentumai vagy operandusai.

12.19.6 Külső változók

12.19.6.1 Általános

Minden olyan helyi változót, értékparamétert vagy paramétertömböt, amelynek hatóköre tartalmazza a lambda_expression vagy anonymous_method_expression, a névtelen függvény külső változójának nevezzük. Egy osztály egy példányfüggvény-tagjában ez az érték értékparaméternek minősül, és a függvény tagjában található névtelen függvény külső változója.

12.19.6.2 Rögzített külső változók

Ha egy külső változóra névtelen függvény hivatkozik, a külső változóról azt mondják, hogy a külső változót a névtelen függvény rögzítette. A helyi változó élettartama általában annak a blokknak vagy utasításnak a végrehajtására korlátozódik, amelyhez társítva van (§9.2.9.1). A rögzített külső változók élettartama azonban legalább addig meghosszabbodik, amíg a névtelen függvényből létrehozott delegált vagy kifejezésfa jogosulttá nem válik a szemétgyűjtésre.

példa: A példában

delegate int D();

class Test
{
    static D F()
    {
        int x = 0;
        D result = () => ++x;
        return result;
    }

    static void Main()
    {
        D d = F();
        Console.WriteLine(d());
        Console.WriteLine(d());
        Console.WriteLine(d());
    }
}

a névtelen függvény rögzíti a helyi x változót, és a x élettartama legalább addig meghosszabbodik, amíg a F visszaadott meghatalmazott jogosulttá nem válik a szemétgyűjtésre. Mivel a névtelen függvény minden egyes meghívása ugyanazon a x-példányon működik, a példa kimenete a következő:

1
2
3

példa vége

Ha egy helyi változót vagy értékparamétert egy névtelen függvény rögzít, a helyi változó vagy paraméter már nem minősül rögzített változónak (23.4. §), hanem áthelyezhető változónak minősül. A rögzített külső változók azonban nem használhatók fixed utasításban (§23.7), így a rögzített külső változó címe nem vehető fel.

Megjegyzés: A nem foglalt változókkal ellentétben a rögzített helyi változók egyszerre több végrehajtási szálnak is ki lehetnek téve. végjegyzet

12.19.6.3 Helyi változók példányosítása

A helyi változó akkor tekinthető példányosítottnak, amikor a végrehajtás belép a változó hatókörébe.

Példa: Ha például a következő metódust hívja meg, a x helyi változó példányosítása és inicializálása háromszor történik – a ciklus minden iterációjához egyszer.

static void F()
{
    for (int i = 0; i < 3; i++)
    {
        int x = i * 2 + 1;
        ...
    }
}

Azonban, ha a x deklarációját a cikluson kívülre helyezi, az xcsak egyszer kerül példányosításra.

static void F()
{
    int x;
    for (int i = 0; i < 3; i++)
    {
        x = i * 2 + 1;
        ...
    }
}

példa vége

Ha nincs rögzítve, nem lehet pontosan megfigyelni, hogy a rendszer pontosan milyen gyakran példányosít egy helyi változót – mivel a példányok élettartama szétesik, az egyes példányok egyszerűen ugyanazt a tárolási helyet használhatják. Ha azonban egy névtelen függvény rögzít egy helyi változót, a példányosítás hatása nyilvánvalóvá válik.

Példa: A példa

delegate void D();
class Test
{
    static D[] F()
    {
        D[] result = new D[3];
        for (int i = 0; i < 3; i++)
        {
            int x = i * 2 + 1;
            result[i] = () => Console.WriteLine(x);
        }
        return result;
    }

    static void Main()
    {
        foreach (D d in F())
        {
            d();
        }
    }
}

hozza létre a kimenetet:

1
3
5

Ha azonban a x deklarációja a cikluson kívülre kerül:

delegate void D();

class Test
{
    static D[] F()
    {
        D[] result = new D[3];
        int x;
        for (int i = 0; i < 3; i++)
        {
            x = i * 2 + 1;
            result[i] = () => Console.WriteLine(x);
        }
        return result;
   }

   static void Main()
   {
       foreach (D d in F())
       {
           d();
       }
   }
}

a kimenet a következő:

5
5
5

Vegye figyelembe, hogy a fordító számára engedélyezett (de nem kötelező) a három példány egyetlen delegált példánnyá optimalizálása (§10.7.2).

példa vége

Ha egy for-loop iterációs változót deklarál, akkor magát a változót a cikluson kívülre deklaráltnak kell tekinteni.

Példa: Így ha a példa megváltozik az iterációs változó rögzítéséhez:

delegate void D();

class Test
{
    static D[] F()
    {
        D[] result = new D[3];
        for (int i = 0; i < 3; i++)
        {
            result[i] = () => Console.WriteLine(i);
        }
        return result;
   }

   static void Main()
   {
       foreach (D d in F())
       {
           d();
       }
   }
}

az iterációs változónak csak egy példánya van rögzítve, amely a kimenetet hozza létre:

3
3
3

példa vége

Előfordulhat, hogy a névtelen függvény delegáltjai megosztanak néhány rögzített változót, de külön példánya van másoknak.

Példa: Ha például a F a következőre módosul:

static D[] F()
{
    D[] result = new D[3];
    int x = 0;
    for (int i = 0; i < 3; i++)
    {
        int y = 0;
        result[i] = () => Console.WriteLine($"{++x} {++y}");
    }
    return result;
}

a három meghatalmazott ugyanazt a x-példányt rögzíti, de a ykülönálló példányait, és a kimenet a következő:

1 1
2 1
3 1

példa vége

A különálló névtelen függvények rögzíthetik egy külső változó ugyanazon példányát.

Példa: A példában:

delegate void Setter(int value);
delegate int Getter();

class Test
{
    static void Main()
    {
        int x = 0;
        Setter s = (int value) => x = value;
        Getter g = () => x;
        s(5);
        Console.WriteLine(g());
        s(10);
        Console.WriteLine(g());
    }
}

a két névtelen függvény rögzíti a helyi változó ugyanazon példányát x, és így "kommunikálhatnak" ezen a változón keresztül. A példa kimenete a következő:

5
10

példa vége

12.19.7 Névtelen függvénykifejezések kiértékelése

Egy névtelen függvényt F mindig delegált típusú D vagy kifejezésfa típusú 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.19.8 Megvalósítási példa

Ez az alfejezet informatív.

Ez az alklám a névtelen függvénykonvertálások lehetséges megvalósítását ismerteti más C#-szerkezetek tekintetében. Az itt ismertetett implementáció a kereskedelmi C#-fordító által használt alapelveken alapul, de semmiképpen sem kötelező implementáció, és nem is ez az egyetlen lehetséges. Csak röviden említi a kifejezésfákká alakításokat, mivel pontos szemantikáik nem tartoznak e specifikáció hatókörébe.

Az alklám fennmaradó része számos példát ad olyan kódra, amely különböző jellemzőkkel rendelkező névtelen függvényeket tartalmaz. Minden példában egy olyan kód fordítása érhető el, amely csak más C#-szerkezeteket használ. A példákban a D azonosító a következő delegált típust képviseli:

public delegate void D();

A névtelen függvények legegyszerűbb formája az, amely nem rögzít külső változókat:

delegate void D();

class Test
{
    static void F()
    {
        D d = () => Console.WriteLine("test");
    }
}

Ez lefordítható egy delegált példányra, amely egy fordító által létrehozott statikus metódusra hivatkozik, amelyben a névtelen függvény kódja található:

delegate void D();

class Test
{
    static void F()
    {
        D d = new D(__Method1);
    }

    static void __Method1()
    {
        Console.WriteLine("test");
    }
}

A következő példában a névtelen függvény a 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.19.6.3) ezután megfelel a fordító által létrehozott osztály egy példányának létrehozásához, és a helyi változó elérése megfelel a fordító által létrehozott osztály egy mezőjének elérésének. Ezenkívül a névtelen függvény a fordító által létrehozott osztály példánymetódusává válik:

delegate void D();

class Test
{
    void F()
    {
        __Locals1 __locals1 = new __Locals1();
        __locals1.y = 123;
        D d = new D(__locals1.__Method1);
    }

    class __Locals1
    {
        public int y;

        public void __Method1()
        {
            Console.WriteLine(y);
        }
    }
}

Végül a következő névtelen függvény rögzíti this és két különböző élettartamú helyi változót:

delegate void D();

class Test
{
   int x;

   void F()
   {
       int y = 123;
       for (int i = 0; i < 10; i++)
       {
           int z = i * 2;
           D d = () => Console.WriteLine(x + y + z);
       }
   }
}

Itt egy fordító által létrehozott osztály jön létre minden olyan blokkhoz, amelyben a helyiek rögzítve vannak, így a különböző blokkokban lévő helyiek független élettartamúak lehetnek. A belső blokk fordító által létrehozott osztálya, a __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.20 Lekérdezési kifejezések

12.20.1 Általános

lekérdezési kifejezések a relációs és hierarchikus lekérdezési nyelvekhez, például az SQL-hez és az XQueryhez hasonló, nyelvvel integrált szintaxist biztosítanak.

query_expression
    : from_clause query_body
    ;

from_clause
    : 'from' type? identifier 'in' expression
    ;

query_body
    : query_body_clause* select_or_group_clause query_continuation?
    ;

query_body_clause
    : from_clause
    | let_clause
    | where_clause
    | join_clause
    | join_into_clause
    | orderby_clause
    ;

let_clause
    : 'let' identifier '=' expression
    ;

where_clause
    : 'where' boolean_expression
    ;

join_clause
    : 'join' type? identifier 'in' expression 'on' expression
      'equals' expression
    ;

join_into_clause
    : 'join' type? identifier 'in' expression 'on' expression
      'equals' expression 'into' identifier
    ;

orderby_clause
    : 'orderby' orderings
    ;

orderings
    : ordering (',' ordering)*
    ;

ordering
    : expression ordering_direction?
    ;

ordering_direction
    : 'ascending'
    | 'descending'
    ;

select_or_group_clause
    : select_clause
    | group_clause
    ;

select_clause
    : 'select' expression
    ;

group_clause
    : 'group' expression 'by' expression
    ;

query_continuation
    : 'into' identifier query_body
    ;

A lekérdezési kifejezések egy from záradékkal kezdődnek, és select vagy group záradékkal végződnek. A kezdeti from záradékot nulla vagy több from, let, where, join vagy orderby záradék követheti. Minden from záradék egy generátor, amely bevezet egy tartományváltozót, amely a sorozatelemein fut végig. Minden let záradék egy olyan tartományváltozót vezet be, amely az előző tartományváltozók alapján kiszámított értéket jelöli. Minden where záradék egy szűrő, amely kizárja az elemeket az eredményből. Minden join záradék összehasonlítja a forrásütemezés megadott kulcsait egy másik sorozat kulcsaival, egyező párokat eredményezve. Minden orderby záradék a megadott feltételek szerint átrendezi az elemeket. Az utolsó select vagy group záradék az eredmény alakját adja meg a tartományváltozók szempontjából. Végül egy into záradék használható a lekérdezések "összezsúfolására", ha egy lekérdezés eredményeit generátorként kezeli egy későbbi lekérdezésben.

12.20.2 Kétértelműségek a lekérdezési kifejezésekben

A lekérdezési kifejezések számos környezetfüggő kulcsszót használnak (§6.4.4): ascending, by, descending, equals, from, group, into, join, let, on, orderby, select és where.

Az ilyen azonosítók kulcsszavakként és egyszerű nevekként való használata során felmerülő kétértelműségek elkerülése érdekében ezek az azonosítók a lekérdezési kifejezés bármely pontján kulcsszavaknak minősülnek, kivéve, ha "@" előtaggal rendelkeznek (§6.4.4), amely esetben azonosítónak minősülnek. E célból a lekérdezési kifejezés olyan kifejezés, amely a "fromazonosító" kezdetű, majd a ";", "=" vagy "," kivételével bármilyen token követi.

12.20.3 Lekérdezési kifejezés fordítása

12.20.3.1 Általános

A C# nyelv nem határozza meg a lekérdezési kifejezések végrehajtási szemantikáját. A lekérdezési kifejezéseket inkább olyan metódusok meghívására fordítjuk le, amelyek megfelelnek a lekérdezés-kifejezés mintájának (§12.20.4). A lekérdezési kifejezések a Where, Select, SelectMany, Join, GroupJoin, OrderBy, OrderByDescending, ThenBy, ThenByDescending, GroupByés Castnevű metódusok meghívására vannak lefordítva . Ezeket a metódusokat a §12.20.4szerint meghatározott konkrét szignatúrákkal és visszatérési típusokkal kell rendelkezniük. Ezek a metódusok lehetnek a lekérdezett objektum példánymetódusai vagy az objektumon kívüli bővítménymetódusok. Ezek a metódusok implementálják a lekérdezés tényleges végrehajtását.

A lekérdezési kifejezésekről a metódushívásokra való fordítás olyan szintaktikai leképezés, amely a típuskötés vagy túlterhelés feloldása előtt következik be. A lekérdezési kifejezések fordítását követően az eredményül kapott metódushívások normál metódushívásként lesznek feldolgozva, és ez a fordítási idő hibáit is feltárhatja. Ezek a hibafeltételek magukban foglalják a nem létező metódusokat, a helytelen típusú argumentumokat és az általános metódusokat, amelyeknél a típuskövetkezmény meghiúsul.

A lekérdezési kifejezéseket a következő fordítások ismételt alkalmazásával dolgozzuk fel, amíg további csökkentés nem lehetséges. A fordítások alkalmazás szerinti sorrendben vannak felsorolva: az egyes szakaszok feltételezik, hogy az előző szakaszok fordításai teljes mértékben el lettek végezve, és a kimerültség után egy szakaszt később nem fognak újra áttekinteni ugyanazon lekérdezési kifejezés feldolgozása során.

Fordítási idejű hiba lép fel, ha egy lekérdezési kifejezés hozzárendelést tartalmaz egy tartományváltozóhoz, vagy ha egy tartományváltozót használ hivatkozási vagy kimeneti paraméter argumentumaként.

Egyes fordítások tartományváltozókat injektálnak transzparens azonosítókkal, * jelöléssel. Ezek részletesebben a §12.20.3.8szakaszokban vannak leírva.

12.20.3.2 Lekérdezési kifejezések folytatásokkal

Lekérdezési kifejezés a lekérdezés törzsét követő folytatással

from «x1» in «e1» «b1» into «x2» «b2»

lefordítva

from «x2» in ( from «x1» in «e1» «b1» ) «b2»

A következő szakaszok fordításai feltételezik, hogy a lekérdezések nem rendelkeznek folytatással.

példa: A példa:

from c in customers
group c by c.Country into g
select new { Country = g.Key, CustCount = g.Count() }

fordítása a következőre történik:

from g in
   (from c in customers
   group c by c.Country)
select new { Country = g.Key, CustCount = g.Count() }

amelynek végső fordítása:

customers.
GroupBy(c => c.Country).
Select(g => new { Country = g.Key, CustCount = g.Count() })

példa vége

12.20.3.3 Explicit tartomány változótípusai

A tartományváltozó típusát explicit módon meghatározó from záradék

from «T» «x» in «e»

lefordítva

from «x» in ( «e» ) . Cast < «T» > ( )

A tartományváltozó típusát explicit módon meghatározó join záradék

join «T» «x» in «e» on «k1» equals «k2»

lefordítva

join «x» in ( «e» ) . Cast < «T» > ( ) on «k1» equals «k2»

A következő szakaszok fordításai feltételezik, hogy a lekérdezések nem rendelkeznek explicit tartományváltozó-típusokkal.

Példa: A példa

from Customer c in customers
where c.City == "London"
select c

lefordítva

from c in (customers).Cast<Customer>()
where c.City == "London"
select c

amelynek végleges fordítása

customers.
Cast<Customer>().
Where(c => c.City == "London")

példa vége

Megjegyzés: Az explicit tartományváltozó-típusok olyan gyűjtemények lekérdezéséhez hasznosak, amelyek a nem általános IEnumerable felületet implementálják, de az általános IEnumerable<T> felületet nem. A fenti példában ez a helyzet, ha az ügyfelek ArrayListtípusúak lennének. végjegyzet

12.20.3.4 Lekérdezési kifejezések degenerálása

Az űrlap lekérdezési kifejezése

from «x» in «e» select «x»

lefordítva

( «e» ) . Select ( «x» => «x» )

Példa: A példa

from c in customers
select c

lefordítva

(customers).Select(c => c)

példa vége

A degenerált lekérdezési kifejezés olyan kifejezés, amely triviálisan kiválasztja a forrás elemeit.

Megjegyzés: A fordítás későbbi fázisai (§12.20.3.6 és §12.20.3.7) eltávolítják az egyéb fordítási lépések által bevezetett degenerált lekérdezéseket a forrásukkal való helyettesítésükkel. Fontos azonban, hogy a lekérdezési kifejezés eredménye soha ne maga a forrásobjektum legyen. Ellenkező esetben az ilyen lekérdezés eredményének visszaadása véletlenül privát adatokat (például elemtömböt) tehet közzé egy hívó számára. Ezért ez a lépés megvédi a közvetlenül forráskódban írt degenerált lekérdezéseket azáltal, hogy explicit módon Select-t hív meg a forráson. Ezután a Select és más lekérdezési operátorok implementálóinak kell gondoskodniuk arról, hogy ezek a metódusok soha ne magát a forrásobjektumot adják vissza. végjegyzet

12.20.3.5 From, let, where, join és orderby utasítások

Lekérdezési kifejezés egy második from kikötéssel, majd egy select kikötéssel

from «x1» in «e1»  
from «x2» in «e2»  
select «v»

lefordítva

( «e1» ) . SelectMany( «x1» => «e2» , ( «x1» , «x2» ) => «v» )

Példa: A példa

from c in customers
from o in c.Orders
select new { c.Name, o.OrderID, o.Total }

lefordítva

(customers).
SelectMany(c => c.Orders,
(c,o) => new { c.Name, o.OrderID, o.Total }
)

példa vége

Egy második from záradékot követően egy olyan lekérdezéstörzs Q, amely nem üres lekérdezési törzszáradékokat tartalmaz.

from «x1» in «e1»
from «x2» in «e2»
Q

lefordítva

from * in («e1») . SelectMany( «x1» => «e2» ,
                              ( «x1» , «x2» ) => new { «x1» , «x2» } )
Q

Példa: A példa

from c in customers
from o in c.Orders
orderby o.Total descending
select new { c.Name, o.OrderID, o.Total }

lefordítva

from * in (customers).
   SelectMany(c => c.Orders, (c,o) => new { c, o })
orderby o.Total descending
select new { c.Name, o.OrderID, o.Total }

amelynek végleges fordítása

customers.
SelectMany(c => c.Orders, (c,o) => new { c, o }).
OrderByDescending(x => x.o.Total).
Select(x => new { x.c.Name, x.o.OrderID, x.o.Total })

ahol x egy fordító által létrehozott azonosító, amely egyébként láthatatlan és elérhetetlen.

példa vége

Egy let kifejezés az előző from záradékkal együtt:

from «x» in «e»  
let «y» = «f»  
...

lefordítva

from * in ( «e» ) . Select ( «x» => new { «x» , «y» = «f» } )  
...

Példa: A példa

from o in orders
let t = o.Details.Sum(d => d.UnitPrice * d.Quantity)
where t >= 1000
select new { o.OrderID, Total = t }

lefordítva

from * in (orders).Select(
    o => new { o, t = o.Details.Sum(d => d.UnitPrice * d.Quantity) })
where t >= 1000
select new { o.OrderID, Total = t }

amelynek végleges fordítása

orders
    .Select(o => new { o, t = o.Details.Sum(d => d.UnitPrice * d.Quantity) })
    .Where(x => x.t >= 1000)
    .Select(x => new { x.o.OrderID, Total = x.t })

ahol x egy fordító által létrehozott azonosító, amely egyébként láthatatlan és elérhetetlen.

példa vége

Egy where kifejezés az előző from záradékkal együtt:

from «x» in «e»  
where «f»  
...

lefordítva

from «x» in ( «e» ) . Where ( «x» => «f» )  
...

Közvetlenül a join záradékot egy select záradék követi.

from «x1» in «e1»  
join «x2» in «e2» on «k1» equals «k2»  
select «v»

lefordítva

( «e1» ) . Join( «e2» , «x1» => «k1» , «x2» => «k2» , ( «x1» , «x2» ) => «v» )

Példa: A példa

from c in customers
join o in orders on c.CustomerID equals o.CustomerID
select new { c.Name, o.OrderDate, o.Total }

lefordítva

(customers).Join(
   orders,
   c => c.CustomerID, o => o.CustomerID,
   (c, o) => new { c.Name, o.OrderDate, o.Total })

példa vége

Egy join záradék, amelyet a lekérdezés törzsének záradéka követ:

from «x1» in «e1»  
join «x2» in «e2» on «k1» equals «k2»  
...

lefordítva

from * in ( «e1» ) . Join(  
«e2» , «x1» => «k1» , «x2» => «k2» ,
( «x1» , «x2» ) => new { «x1» , «x2» })  
...

Egy join-into záradék, amelyet azonnal egy select záradék követ.

from «x1» in «e1»  
join «x2» in «e2» on «k1» equals «k2» into «g»  
select «v»

lefordítva

( «e1» ) . GroupJoin( «e2» , «x1» => «k1» , «x2» => «k2» ,
                     ( «x1» , «g» ) => «v» )

Egy join into záradék, amelyet a lekérdezés törzsének záradéka követ

from «x1» in «e1»  
join «x2» in «e2» on «k1» equals «k2» into *g»  
...

lefordítva

from * in ( «e1» ) . GroupJoin(  
   «e2» , «x1» => «k1» , «x2» => «k2» , ( «x1» , «g» ) => new { «x1» , «g» })
...

Példa: A példa

from c in customers
join o in orders on c.CustomerID equals o.CustomerID into co
let n = co.Count()
where n >= 10
select new { c.Name, OrderCount = n }

lefordítva

from * in (customers).GroupJoin(
    orders,
    c => c.CustomerID,
    o => o.CustomerID,
    (c, co) => new { c, co })
let n = co.Count()
where n >= 10
select new { c.Name, OrderCount = n }

amelynek végleges fordítása

customers
    .GroupJoin(
        orders,
        c => c.CustomerID,
        o => o.CustomerID,
        (c, co) => new { c, co })
    .Select(x => new { x, n = x.co.Count() })
    .Where(y => y.n >= 10)
    .Select(y => new { y.x.c.Name, OrderCount = y.n })

ahol x és y olyan fordító által generált azonosítók, amelyek egyébként láthatatlanok és elérhetetlenek.

példa vége

Egy orderby záradék és annak előző from záradéka:

from «x» in «e»  
orderby «k1» , «k2» , ... , «kn»  
...

lefordítva

from «x» in ( «e» ) .
OrderBy ( «x» => «k1» ) .
ThenBy ( «x» => «k2» ) .
... .
ThenBy ( «x» => «kn» )
...

Ha egy ordering záradék csökkenő irányjelzőt ad meg, a rendszer ehelyett OrderByDescending vagy ThenByDescending hívását hozza létre.

Példa: A példa

from o in orders
orderby o.Customer.Name, o.Total descending
select o

a végső fordítással rendelkezik

(orders)
    .OrderBy(o => o.Customer.Name)
    .ThenByDescending(o => o.Total)

példa vége

Az alábbi fordítások feltételezik, hogy nincsenek let, where, join vagy orderby záradékok, és nem több, mint az egyes lekérdezési kifejezések egyetlen kezdeti from záradéka.

12.20.3.6 Kiválasztott záradékok

Az űrlap lekérdezési kifejezése

from «x» in «e» select «v»

lefordítva

( «e» ) . Select ( «x» => «v» )

Kivéve, ha «v» az «x»azonosító, a fordítás egyszerűen.

( «e» )

Példa: A példa

from c in customers.Where(c => c.City == "London")
select c

szó szerint lefordítva

(customers).Where(c => c.City == "London")

példa vége

12.20.3.7 Csoport záradékai

Egy group záradék

from «x» in «e» group «v» by «k»

lefordítva

( «e» ) . GroupBy ( «x» => «k» , «x» => «v» )

kivéve, ha «v» az azonosító «x», a fordítás nem szükséges

( «e» ) . GroupBy ( «x» => «k» )

Példa: A példa

from c in customers
group c.Name by c.Country

lefordítva

(customers).GroupBy(c => c.Country, c => c.Name)

példa vége

12.20.3.8 Transzparens azonosítók

Egyes fordítások olyan tartományváltozókat injektálnak, amelyek transzparens azonosítók, mint például ,,,, amelyeket a *jelöl. A transzparens azonosítók csak köztes lépésként léteznek a lekérdezés-kifejezés fordítási folyamatában.

Amikor egy lekérdezésfordítás transzparens azonosítót ad meg, a további fordítási lépések névtelen függvényekbe és névtelen objektuminicializálókba propagálja az transzparens azonosítót. Ezekben a kontextusokban a transzparens azonosítók viselkedése a következő:

  • Ha egy transzparens azonosító paraméterként fordul elő egy névtelen függvényben, a társított névtelen típus tagjai automatikusan hatókörbe kerülnek a névtelen függvény törzsében.
  • Ha egy transzparens azonosítóval rendelkező tag hatókörben van, az adott tag tagjai is hatókörben vannak.
  • Ha egy transzparens azonosító tagdeklarátorként fordul elő egy névtelen objektum inicializálójában, egy transzparens azonosítóval rendelkező tagot vezet be.

A fent leírt fordítási lépésekben a transzparens azonosítók mindig névtelen típusokkal együtt jelennek meg, azzal a szándékkal, hogy több tartományváltozót rögzítsünk egyetlen objektum tagjaiként. A C# implementációja a névtelen típustól eltérő mechanizmussal csoportosíthat több tartományváltozót. Az alábbi fordítási példák feltételezik, hogy névtelen típusokat használnak, és transzparens azonosítók egy lehetséges fordítását jelenítik meg.

Példa: A példa

from c in customers
from o in c.Orders
orderby o.Total descending
select new { c.Name, o.Total }

lefordítva

from * in (customers).SelectMany(c => c.Orders, (c,o) => new { c, o })
orderby o.Total descending
select new { c.Name, o.Total }

amelyet tovább fordítunk a

customers
    .SelectMany(c => c.Orders, (c,o) => new { c, o })
    .OrderByDescending(* => o.Total)
    .Select(\* => new { c.Name, o.Total })

amely transzparens azonosítók törlésekor egyenértékű a

customers
    .SelectMany(c => c.Orders, (c,o) => new { c, o })
    .OrderByDescending(x => x.o.Total)
    .Select(x => new { x.c.Name, x.o.Total })

ahol x egy fordító által létrehozott azonosító, amely egyébként láthatatlan és elérhetetlen.

A példa

from c in customers
join o in orders on c.CustomerID equals o.CustomerID
join d in details on o.OrderID equals d.OrderID
join p in products on d.ProductID equals p.ProductID
select new { c.Name, o.OrderDate, p.ProductName }

lefordítva

from * in (customers).Join(
    orders,
    c => c.CustomerID,
    o => o.CustomerID,
    (c, o) => new { c, o })
join d in details on o.OrderID equals d.OrderID
join p in products on d.ProductID equals p.ProductID
select new { c.Name, o.OrderDate, p.ProductName }

amely tovább csökken a

customers
    .Join(orders, c => c.CustomerID,
        o => o.CustomerID, (c, o) => new { c, o })
    .Join(details, * => o.OrderID, d => d.OrderID, (*, d) => new { *, d })
    .Join(products, * => d.ProductID, p => p.ProductID,
        (*, p) => new { c.Name, o.OrderDate, p.ProductName })

amelynek végleges fordítása

customers
    .Join(orders, c => c.CustomerID,
        o => o.CustomerID, (c, o) => new { c, o })
    .Join(details, x => x.o.OrderID, d => d.OrderID, (x, d) => new { x, d })
    .Join(products, y => y.d.ProductID, p => p.ProductID,
        (y, p) => new { y.x.c.Name, y.x.o.OrderDate, p.ProductName })

ahol x és y olyan fordító által generált azonosítók, amelyek egyébként láthatatlanok és elérhetetlenek. példa vége

12.20.4 A lekérdezési-kifejezési minta

A Lekérdezés-kifejezés minta olyan metódusokat hoz létre, amelyeket a típusok implementálhatnak a lekérdezési kifejezések támogatásához.

Egy általános típus C<T> támogatja a lekérdezési kifejezés mintát, ha a nyilvános tag metódusokat és a nyilvánosan elérhető bővítménymódszereket az alábbi osztálydefinícióval lehet helyettesíteni. Az általános típus C<T>"alakzatának" nevezzük a tagokat és az elérhető kiterjesztési metódusokat. Általános típust használunk a paraméterek és a visszatérési típusok közötti megfelelő kapcsolatok szemléltetésére, de a nem általános típusok mintáját is implementálhatja.

delegate R Func<T1,R>(T1 arg1);
delegate R Func<T1,T2,R>(T1 arg1, T2 arg2);

class C
{
    public C<T> Cast<T>() { ... }
}

class C<T> : C
{
    public C<T> Where(Func<T,bool> predicate) { ... }
    public C<U> Select<U>(Func<T,U> selector) { ... }
    public C<V> SelectMany<U,V>(Func<T,C<U>> selector,
        Func<T,U,V> resultSelector) { ... }
    public C<V> Join<U,K,V>(C<U> inner, Func<T,K> outerKeySelector,
        Func<U,K> innerKeySelector, Func<T,U,V> resultSelector) { ... }
    public C<V> GroupJoin<U,K,V>(C<U> inner, Func<T,K> outerKeySelector,
        Func<U,K> innerKeySelector, Func<T,C<U>,V> resultSelector) { ... }
    public O<T> OrderBy<K>(Func<T,K> keySelector) { ... }
    public O<T> OrderByDescending<K>(Func<T,K> keySelector) { ... }
    public C<G<K,T>> GroupBy<K>(Func<T,K> keySelector) { ... }
    public C<G<K,E>> GroupBy<K,E>(Func<T,K> keySelector,
        Func<T,E> elementSelector) { ... }
}

class O<T> : C<T>
{
    public O<T> ThenBy<K>(Func<T,K> keySelector) { ... }
    public O<T> ThenByDescending<K>(Func<T,K> keySelector) { ... }
}

class G<K,T> : C<T>
{
    public K Key { get; }
}

A fenti metódusok az általános delegálási típusokat Func<T1, R> és Func<T1, T2, R>használják, de más delegált- vagy kifejezésfatípusokat is használhattak volna ugyanazokkal a kapcsolatokkal a paraméter- és visszatérési típusok esetében.

Megjegyzés: A C<T> és a O<T> közötti ajánlott kapcsolat, amely biztosítja, hogy a ThenBy és ThenByDescending metódusok csak egy OrderBy vagy OrderByDescendingeredmé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ábbi Key tulajdonságú. végjegyzet

Megjegyzés: Mivel a lekérdezési kifejezéseket szintaktikai leképezéssel fordítják le metódushívásokra, a típusok jelentős rugalmasságot biztosítanak a lekérdezési kifejezési minták vagy azok mindegyikének implementálásában. A minta metódusai például példánymetaképpen vagy bővítménymeteként is implementálhatók, mivel a kettő azonos meghívási szintaxist használ, és a metódusok delegáltakat vagy kifejezésfákat kérhetnek, mert a névtelen függvények mindkettőre konvertálhatók. A lekérdezési kifejezésmintát csak néhányat megvalósító típusok csak olyan lekérdezési kifejezésfordításokat támogatnak, amelyek megfeleltethetők a típus által támogatott metódusoknak. végjegyzet

Megjegyzés: A System.Linq névtér a lekérdezési kifejezés mintájának implementálását biztosítja minden olyan típushoz, amely megvalósítja a System.Collections.Generic.IEnumerable<T> felületet. végjegyzet

12.21 Hozzárendelési operátorok

12.21.1 Általános

A hozzárendelési operátorok mindegyike új értéket rendel egy változóhoz, tulajdonsághoz, eseményhez vagy indexelő elemhez. A kivétel (= ref) egy változóhivatkozást (§9.5) rendel egy referenciaváltozóhoz (§9.7).

assignment
    : unary_expression assignment_operator expression
    ;

assignment_operator
    : '=' 'ref'? | '+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' | '<<=' | '??='
    | right_shift_assignment
    ;

A hozzárendelés bal operandusa változóként besorolt kifejezés, vagy = 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átor a §12.21.2részben van leírva.

A = ref operátort referencia hozzárendelési operátornaknevezik. A jobboldali operandus, amelynek egy variable_reference (§9.5) kell lennie, lesz a referenciaváltozó hivatkozási célpontja, amelyet a baloldali operandus jelöl. A ref hozzárendelő operátort a §12.21.3rész foglalja össze.

Az és az == ref operátortól eltérő hozzárendelési operátorokat összetett hozzárendelési operátoroknak nevezzük. Ezeket az operátorokat a következőképpen dolgozzuk fel:

  • ??= operátor esetében csak akkor kerül kiértékelésre a jobb operandus, és csak akkor rendelődik hozzá az eredmény a bal operandus által megadott változóhoz, tulajdonsághoz vagy indexelő elemhez, ha a bal operandus értéke null.
  • Ellenkező esetben a jelzett műveletet a két operanduson hajtja végre, majd az eredményként kapott érték a bal operandus által megadott változóhoz, tulajdonsághoz vagy indexelőelemhez lesz rendelve. Az összetett hozzárendelési operátorok leírása a 12.21.4részben található.

A bal operandusként eseményelérési kifejezéssel rendelkező += és -= operátorokat esemény-hozzárendelési operátoroknak nevezzük. Egyetlen más hozzárendelési operátor sem érvényes eseményhozzáféréssel bal operandusként. Az esemény-hozzárendelési operátorok a 12.21.5szakaszban vannak leírva.

A hozzárendelési operátorok jobbról asszociatívak, ami azt jelenti, hogy a műveletek jobbról balra történik a csoportosítás.

példa: Egy a = b = c formájú kifejezés úgy lesz kiértékelve, mint a = (b = c). példa vége

12.21.2 Egyszerű feladat

A = operátor neve egyszerű hozzárendelési operátor.

Ha egy egyszerű hozzárendelés bal oldali operandusa E.P vagy E[Ei], ahol E a fordítási idő típusa dynamic, akkor a hozzárendelés dinamikusan kapcsolódik (§12.3.3). Ebben az esetben a hozzárendelési kifejezés fordítási idejének típusa dynamic, és az alább ismertetett felbontás futásidőben történik a E futásidejű típusa alapján. Ha a bal oldali operandus a E[Ei] formátumú, ahol a Ei legalább egy elemének fordítási idő alatti típusa dynamic, és a E fordítási idő alatti típusa nem tömb, az így kapott indexelő hozzáférés dinamikusan kötött, de korlátozott fordítási idejű ellenőrzéssel (§12.6.5).

Egy egyszerű hozzárendelés, amelyben a bal operandus tupelként van besorolva, dekonstruáló hozzárendelésneknevezik. Ha a bal oldali operandus bármelyik tuple elemének van neve, fordítási idejű hiba lép fel. Ha a bal oldali operandus bármely rekordeleme egy , declaration_expression, és bármely más elem pedig nem , declaration_expression, vagy egyszerű elvetés, fordítási időben hiba lép fel.

Az egyszerű hozzárendelés típusa x = y a xy hozzárendelésének típusa, amely rekurzívan az alábbiak szerint van meghatározva:

  • Ha a x egy (x1, ..., xn), és a y dekonstruálható egy (y1, ..., yn) elemekkel rendelkező n kifejezésre (§12.7), és xi minden yi hozzárendelése Titípusú, akkor a hozzárendelés (T1, ..., Tn)típussal rendelkezik.
  • Ellenkező esetben, ha x változóként van besorolva, a változó nem readonly, xTtípussal rendelkezik, és y-nek van implicit átalakítása T-re, akkor a hozzárendelés típusa T.
  • Ellenkező esetben, ha x implicit módon beírt változóként (azaz implicit módon beírt deklarációs kifejezésként) van besorolva, és yTtípussal rendelkezik, akkor a változó kikövetkeztetett típusa T, és a hozzárendelés típusa T.
  • Ellenkező esetben, ha x tulajdonságként vagy indexelő-hozzáférésként van besorolva, és a tulajdonságnak vagy az indexelőnek van egy elérhető set hozzáférője, xTtípusú, és y rendelkezik egy implicit átalakítással Ttípusra, akkor a hozzárendelés típusa T.
  • Ellenkező esetben a hozzárendelés érvénytelen, és kötési időhiba lép fel.

Az egyszerű hozzárendelésnek a x = y formában és T típusban történő futásidejű feldolgozása a x-hez való y hozzárendelésként történik T típussal, amely a következő rekurzív lépésekből áll.

  • x kiértékelésre kerül, ha még nem történt meg.
  • Ha x változóként van besorolva, a rendszer kiértékeli a y, és szükség esetén implicit átalakítással T konvertálja (§10.2).
    • Ha az x által megadott változó egy reference_typetömbeleme, a rendszer futásidejű ellenőrzést végez annak érdekében, hogy a y kiszámított értéke kompatibilis legyen azzal a tömbpéldánysal, amelynek x eleme. Az ellenőrzés akkor sikeres, ha ynull, vagy ha implicit referenciaátalakítás (§10.2.8) létezik a y által hivatkozott példány típusától a xtartalmazó tömbpéldány tényleges elemtípusához. Ellenkező esetben egy System.ArrayTypeMismatchException lesz dobva.
    • A y kiértékelésével és átalakításával kapott értéket a rendszer a xkiértékelése által megadott helyre tárolja, és a hozzárendelés eredményeként adja meg.
  • Ha x tulajdonságként vagy indexelői hozzáférésként van besorolva:
    • y kiértékelése és szükség esetén T konvertálása implicit átalakítással (§10.2).
    • A x halmaz-tartozéka a y kiértékelése és átalakítása során kapott értékkel lesz meghívva értékargumentumként.
    • A y kiértékeléséből és átalakításából származó érték a hozzárendelés eredményeként jön létre.
  • Ha x egy (x1, ..., xn)aritású n besorolású n-es:
    • y a n elemekkel lebontva egy ekifejezésre.
    • Az eredménytömb t az e implicit módon történő tuple-átalakítással és T-re való átalakításával jön létre.
    • Minden egyes xi esetén balról jobbra haladva történik a xi hozzárendelése a t.Itemi-hez, azzal a kivétellel, hogy a xi nem lesz újra kiértékelve.
    • A hozzárendelés eredményeként t keletkezik.

Megjegyzés: ha a x fordítási idő típusa dynamic, és implicit átalakítás történik a fordítási idő típusáról ydynamic, 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ípus B[]egy példányára való hivatkozás legyen , feltéve, hogy implicit hivatkozási átalakítás létezik B-ről A. 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ában

string[] sa = new string[10];
object[] oa = sa;
oa[0] = null;              // OK
oa[1] = "Hello";           // OK
oa[2] = new ArrayList();   // ArrayTypeMismatchException

Az utolsó hozzárendelést következésképpen System.ArrayTypeMismatchException dobja fel, amiatt, hogy egy ArrayList-re való hivatkozás nem tárolható egy string[]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és r.B hozzárendelése engedélyezett, mert p és r változók. A példában azonban

Rectangle 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 és r.B nem változók.

példa vége

12.21.3 Referencia hozzárendelés

A = ref operátor az referencia-hozzárendelési operátor.

A bal operandus olyan kifejezés, amely egy referenciaváltozóhoz (§9.7), referenciaparaméterhez (a 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 = ref operátorral, könnyen elcsábulhatunk, hogy a ref részt az operandus részeként olvassuk. Ez különösen zavaró, ha az operandus feltételes ?: kifejezés. Ha például ref int a = ref b ? ref x : ref y; olvas, fontos ezt úgy olvasni, hogy = ref az operátor, és b ? ref x : ref y a megfelelő operandus: ref int a = ref (b ? ref x : ref y);. Fontos, hogy a ref b kifejezés nem része ennek az utasításnak, annak ellenére, hogy első pillantásra ilyennek tűnhet. végjegyzet

12.21.4 Összetett hozzárendelés

Ha egy összetett hozzárendelés bal oldali operandusa E.P vagy E[Ei] formában van, ahol E fordítási időben 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űvelet x = x «op» ylesz kiértékelve, azzal a kivételével, hogy a x csak egyszer lesz kiértékelve.
  • Ellenkező esetben, ha a kiválasztott operátor előre definiált operátor, ha a kiválasztott operátor visszatérési típusa explicit módon átalakítható a x típusára, és ha y implicit módon átalakítható a x típusára, vagy az operátor egy műszak operátor, akkor a művelet kiértékelése x = (T)(x «op» y), ahol T a xtípusa, kivéve, hogy a x csak egyszer lesz kiértékelve.
  • Ellenkező esetben az összetett hozzárendelés érvénytelen, és kötési időhiba lép fel.

A "csak egyszer kiértékelt" kifejezés azt jelenti , hogy a x «op» 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 a A egy int[]visszaadó metódus, és B és C pedig intvisszaadó metódusok, a metódusok csak egyszer, a A, 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;    // OK

Az egyes hibák intuitív oka az, hogy a megfelelő egyszerű hozzárendelés is hiba lett volna.

példa vége

Megjegyzés: Ez azt is jelenti, hogy az összetett hozzárendelési műveletek támogatják a kiemelt operátorokat. Mivel az összetett hozzárendelési művelet x «op»= y kiértékelése x = x «op» y vagy x = (T)(x «op» y), a kiértékelési szabályok implicit módon lefedik a kibővített operátorokat. végjegyzet

12.21.5 Esemény-hozzárendelés

Ha a += or -= operátor bal operandusa eseményhozzáférésként van besorolva, a kifejezés kiértékelése a következőképpen történik:

  • A rendszer kiértékeli az eseményhozzáférés példánykifejezését, ha van ilyen.
  • A += vagy -= operátor jobb operandusa kiértékelésre kerül, és szükség esetén implicit átalakítással (10.2.) konvertálja a bal operandus típusára.
  • A rendszer meghívja az esemény esemény-tartozékát egy argumentumlistával, amely az előző lépésben kiszámított értékből áll. Ha az operátor +=volt, a hozzáadó metódus hívódik meg; ha az operátor -=volt, az eltávolító metódus hívódik meg.

Az esemény-hozzárendelési kifejezés nem ad értéket. Így az esemény-hozzárendelési kifejezés csak egy statement_expression kontextusában érvényes (§13.7).

12.22 Kifejezés

A kifejezés vagy nem_hozzárendelési_kifejezés vagy hozzárendelés.

expression
    : non_assignment_expression
    | assignment
    ;

non_assignment_expression
    : declaration_expression
    | conditional_expression
    | lambda_expression
    | query_expression
    ;

12.23 Állandó kifejezések

Az állandó kifejezés olyan kifejezés, amelyet fordításkor teljesen ki kell értékelni.

constant_expression
    : expression
    ;

Az állandó kifejezés értéke legyen null vagy az alábbi típusok egyike:

  • sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, bool, string
  • enumerálási típus; vagy
  • egy referenciatípus alapértelmezett értékkifejezése (§12.8.21).

Állandó kifejezésekben csak a következő szerkezetek engedélyezettek:

  • Literálok (beleértve a null literális értéket).
  • const osztály- és szerkezettípusokra mutató hivatkozások.
  • Enumerálási típusok tagjaira mutató hivatkozások.
  • Helyi állandókra mutató hivatkozások.
  • Zárójeles alkifejezések, amelyek maguk is állandó kifejezések.
  • Öntött kifejezések.
  • checked és unchecked kifejezések.
  • nameof kifejezések.
  • Az előre definiált +, -, ! (logikai tagadás) és ~ nem kötelező operátorok.
  • Az előre definiált +, -, *, /, %, <<, >>, &, |, ^, &&, ||, ==, !=, <, >, <=és >= bináris operátorok.
  • A ?: feltételes operátor.
  • A ! null megbocsátó operátor (§12.8.9).
  • sizeof kifejezések, feltéve, hogy a nem felügyelt típus a §23.6.9 szakaszban meghatározott típusok egyike, amelyek esetében a sizeof állandó értéket ad vissza.
  • Alapértelmezett értékkifejezések, feltéve, hogy a típus a fent felsorolt típusok egyike.

A konstans kifejezésekben a következő átalakítások engedélyezettek:

  • Identitáskonvertálások
  • Numerikus konverziók
  • Enumerációs konverziók
  • Állandó kifejezéskonvertálások
  • Implicit és explicit referenciakonverziók, feltéve, hogy a konverziók forrása egy állandó kifejezés, amely kiértékeli a null értéket.

Megjegyzés: A nem-null értékek egyéb konverziói, mint a dobozolás, kicsomagolás és az implicit referenciakonvertálás, nem engedélyezettek állandó kifejezésekben. végjegyzet

példa: Az alábbi kódban

class C
{
    const object i = 5;         // error: boxing conversion not permitted
    const object str = "hello"; // error: implicit reference conversion
}

A i inicializálása hiba, mert csomagolási átalakításra van szükség. A str inicializálása hiba, mert implicit referenciaátalakításra van szükség egy nemnull értékről.

példa vége

Ha egy kifejezés megfelel a fent felsorolt követelményeknek, a kifejezés fordítási időpontban lesz kiértékelve. Ez akkor is igaz, ha a kifejezés egy nagyobb, nem állandó szerkezeteket tartalmazó kifejezés szubexpressziója.

Az állandó kifejezések fordítási idejének kiértékelése ugyanazokat a szabályokat használja, mint a nem állandó kifejezések futásidejű kiértékelése, azzal a kivétellel, hogy ahol a futásidejű kiértékelés kivételt eredményezett volna, a fordítási idő kiértékelése fordítási idő hibát okoz.

Ha egy állandó kifejezés nem kerül explicit módon egy unchecked kontextusba, a kifejezés fordítási idejének kiértékelése során az integrál típusú aritmetikai műveletekben és átalakításokban előforduló túlcsordulások mindig fordítási időhibákat okoznak (§12.8.20).

Az alábbiakban felsorolt környezetekben állandó kifejezésekre van szükség, és ezt a nyelvhelyesség constant_expressionhasználatával jelzi. Ezekben a kontextusokban fordítási időhiba lép fel, ha egy kifejezés fordításkor nem értékelhető ki teljes mértékben.

  • Állandó deklarációk (§15.4)
  • Felsorolási tag deklarációk (§19.4)
  • Paraméterlisták alapértelmezett argumentumai (§15.6.2)
  • case switch utasítás címkéi (§13.8.3).
  • goto case utasítások (§13.10.4)
  • Egy inicializálót tartalmazó tömblétrehozó kifejezés dimenzióhosszai (§12.8.17.4).
  • Attribútumok (§22)
  • állandó_minta (§11.2.3)

Az implicit konstanskifejezés-átalakítás (§10.2.11) lehetővé teszi, hogy a int típusú állandó kifejezés sbyte, byte, short, ushort, uintvagy ulonglegyen, feltéve, hogy az állandó kifejezés értéke a céltípus tartományán belül van.

12.24 Logikai kifejezések

A boolean_expression olyan kifejezés, amely 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. A ?: operátor vezérlő feltételes kifejezése (12.18. §) ugyanazokat a szabályokat követi, mint a boolean_expression, de az operátorok elsőbbsége miatt null_coalescing_expression.

Egy boolean_expressionE szükséges ahhoz, hogy 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 trueE egyedi legjobb implementációjának megtalálására szolgál, és azt futásidőben alkalmazzák.
  • Ha nem található ilyen operátor, kötési idejű hiba lép fel.