Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
12.1 Obecné
Výraz je posloupnost operátorů a operandů. Tato klauzule definuje syntaxi, pořadí vyhodnocení operandů a operátorů a význam výrazů.
12.2 Klasifikace výrazů
12.2.1 Obecné
Výsledek výrazu je klasifikován jako jeden z následujících:
- Hodnota. Každá hodnota má přidružený typ.
- Proměnná. Není-li zadáno jinak, je proměnná explicitně zadána a má přidružený typ, konkrétně deklarovaný typ proměnné. Implicitně typovaná proměnná nemá přidružený typ.
- Literál null. Výraz s touto klasifikací lze implicitně převést na referenční typ nebo nulovatelný typ hodnoty.
- Anonymní funkce. Výraz s touto klasifikací lze implicitně převést na kompatibilní typ delegáta nebo typ stromu výrazu.
- Řazená kolekce členů. Každá řazená kolekce členů má pevný počet prvků, každý s výrazem a volitelným názvem elementu řazené kolekce členů.
- Přístup k vlastnosti. Každý přístup k vlastnostem má přidružený typ, konkrétně typ vlastnosti. Navíc může mít přístup k vlastnostem přidružený výraz instance. Při vyvolání přístupového objektu vlastnosti instance se výsledkem vyhodnocení výrazu instance stane instance reprezentovaná
this(§12.8.14). - Přístup k indexeru. Každý přístup indexeru má přidružený typ, konkrétně typ prvku indexeru. Přístup indexeru má navíc přidružený výraz instance a přidružený seznam argumentů. Při vyvolání přístupového objektu indexeru se výsledek vyhodnocení výrazu instance stane instancí reprezentovanou
this(§12.8.14) a výsledkem vyhodnocení seznamu argumentů se stane seznam parametrů vyvolání. - Nic. K tomu dochází, když výraz je vyvolání metody s návratovým typem
void. Výraz klasifikovaný jako nic není platný pouze v kontextu statement_expression (§13.7) nebo jako tělo lambda_expression (§12.21).
U výrazů, které se vyskytují jako dílčí výrazy větších výrazů, s označenými omezeními lze výsledek také klasifikovat jako jeden z následujících výrazů:
- Jmenný prostor. Výraz s touto klasifikací se může objevit pouze na levé straně member_access (§12.8.7). V jakémkoli jiném kontextu výraz klasifikovaný jako obor názvů způsobí chybu v době kompilace.
- Jeden typ. Výraz s touto klasifikací se může objevit pouze na levé straně member_access (§12.8.7). V jakémkoli jiném kontextu výraz klasifikovaný jako typ způsobí chybu v době kompilace.
- Skupina metod, což je sada přetížených metod vyplývajících z vyhledávání členů (§12.5). Skupina metod může mít přidružený výraz instance a seznam argumentů přidruženého typu. Při vyvolání metody instance se výsledkem vyhodnocení výrazu instance stane instance reprezentovaná
this(§12.8.14). Skupina metod je povolena v invocation_expression (§12.8.10) nebo delegate_creation_expression (§12.8.17.5) a lze ji implicitně převést na kompatibilní typ delegáta (§10.8). V jakémkoli jiném kontextu výraz klasifikovaný jako skupina metod způsobí chybu v době kompilace. - Přístup k události. Každý přístup k události má přidružený typ, konkrétně typ události. Přístup k události může mít navíc přidružený výraz instance. Přístup k události se může zobrazit jako levý operand
+=operátorů a-=operátorů (§12.23.5). V jakémkoli jiném kontextu výraz klasifikovaný jako přístup k události způsobí chybu v době kompilace. Při vyvolání přístupového objektu události instance se výsledkem vyhodnocení výrazu instance stane instance reprezentovanáthis(§12.8.14). - Výraz throw, který lze použít v několika kontextech k vyvolání výjimky ve výrazu. Výraz throw může být převeden implicitním převodem na libovolný typ.
Přístup k vlastnosti nebo přístup k indexeru je vždy přetříděn jako hodnota provedením vyvolání přístupového objektu get nebo objektu set. Konkrétní přístupový objekt je určen kontextem vlastnosti nebo přístupu indexeru: Pokud je přístup cílem přiřazení, vyvolá se přístupový objekt sady pro přiřazení nové hodnoty (§12.23.2). V opačném případě je vyvolán get accessor k získání aktuální hodnoty (§12.2.2).
Přístupový objekt instance je přístup k vlastnostem v instanci, přístup k události v instanci nebo přístup k indexeru.
12.2.2 Hodnoty výrazů
Většina konstrukcí, které zahrnují výraz, nakonec vyžaduje, aby výraz označoval hodnotu. Pokud skutečný výraz v takových případech označuje obor názvů, typ, skupinu metod nebo nic, dojde k chybě v době kompilace. Pokud však výraz označuje přístup k vlastnosti, přístup indexeru nebo proměnnou, hodnota vlastnosti, indexeru nebo proměnné je implicitně nahrazena:
- Hodnota proměnné je jednoduše hodnota aktuálně uložená v umístění úložiště identifikované proměnnou. Proměnná se považuje za rozhodně přiřazenou (§9,4) před získáním jeho hodnoty nebo jinak dojde k chybě v době kompilace.
- Hodnota přístupového výrazu vlastnosti se získá vyvoláním přístupového objektu get vlastnosti. Pokud vlastnost nemá přístup k objektu get, dojde k chybě v době kompilace. V opačném případě se provede vyvolání člena funkce (§12.6.6) a výsledkem vyvolání se stane hodnota výrazu přístupu k vlastnosti.
- Hodnota přístupového výrazu indexeru se získá vyvoláním přístupového objektu get indexeru. Pokud indexer nemá žádné přístupové objekty get, dojde k chybě v době kompilace. Jinak se provede volání člena funkce (§12.6.6) s argumentovým seznamem přidruženým k výrazu přístupu indexeru a výsledkem tohoto volání se stane hodnota výrazu přístupu indexeru.
- Hodnota výrazu řazené kolekce členů je získána použitím implicitního převodu řazené kolekce členů (§10.2.13) na typ výrazu řazené kolekce členů. Jedná se o chybu při získání hodnoty výrazu řazené kolekce členů, který nemá typ.
12.3 Statické a dynamické vazby
12.3.1 Obecné
Vazby je proces určení toho, na co operace odkazuje, na základě typu nebo hodnoty výrazů (argumenty, operandy, příjemce). Například vazba volání metody je určena na základě typu objektu a argumentů. Operátorová vazba je určena na základě typu jeho operandů.
V jazyce C# je vazba operace obvykle určena v době kompilace na základě typu kompilace jeho dílčích výrazů. Podobně platí, že pokud výraz obsahuje chybu, je zjištěna a hlášena v době kompilace. Tento přístup se označuje jako statické vazby.
Pokud je však výraz dynamickým výrazem (tj. má typ dynamic), znamená to, že všechny vazby, které se účastní, by měly být založené na jeho typu za běhu, a ne na typu, který má v době kompilace. Vazba takové operace je proto odložena do doby, kdy se má operace spustit během spuštění programu. To se označuje jako dynamické vazby.
Pokud je operace dynamicky svázaná, provádí se v době kompilace malá nebo žádná kontrola. Místo toho v případě selhání vazby za běhu se chyby oznamují jako výjimky.
Na vazby se vztahují následující operace v jazyce C#:
- Přístup ke členům:
e.M - Vyvolání metody:
e.M(e₁,...,eᵥ) - Vyvolání delegáta:
e(e₁,...,eᵥ) - Přístup k elementům:
e[e₁,...,eᵥ] - Vytváření objektů: nové
C(e₁,...,eᵥ) - Přetížené unární operátory:
+,-,!(pouze logická negace),~,++,--,true,false - Přetížené binární operátory:
+,-,*,/,%,&,&&,|,||,??,^,<<,>>,==,!=,>,<,>=,<= - Operátory přiřazení:
=,= ref,+=,-=,*=,/=,%=,&=,|=,^=,<<=,>>=,??= - Implicitní a explicitní převody
Pokud nejsou zahrnuty žádné dynamické výrazy, jazyk C# ve výchozím nastavení použije statickou vazbu, což znamená, že v procesu výběru se použijí typy dílčích výrazů za doby kompilace. Pokud je však jedním z dílčích výrazů v operacích uvedených výše dynamický výraz, operace je místo toho dynamicky vázána.
Jedná se o chybu v době kompilace, pokud je vyvolání metody dynamicky vázané a všechny parametry, včetně příjemce, jsou vstupní parametry.
12.3.2 Doba vazby
Statické vazby probíhají v době kompilace, zatímco dynamické vazby probíhají za běhu. V následujících dílčích návazcích se termín doba vazby odkazuje buď na dobu kompilace, nebo na dobu běhu v závislosti na tom, kdy se vazba provádí.
příklad: Následující příklad znázorňuje pojmy statické a dynamické vazby a doby vazby:
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)První dvě volání jsou staticky svázaná: přetížení
Console.WriteLineje vybráno na základě kompilačního času jejich argumentů. Proto je doba vazby kompilace .Třetí volání je dynamicky vázáno: přetížení
Console.WriteLineje vybráno na základě běhového typu jeho argumentu. K tomu dochází, protože argument je dynamický výraz – jeho typ kompilace je dynamický. Čas vazby třetího volání je tedy za běhu .koncového příkladu
12.3.3 Dynamické vazby
Tato dílčí položka je informativní.
Dynamická vazba umožňuje programům jazyka C# pracovat s dynamickými objekty, tj. objekty, které nedodržují normální pravidla systému typů jazyka C#. Dynamické objekty mohou být objekty z jiných programovacích jazyků s různými typy systémů nebo mohou být objekty, které jsou programově nastaveny tak, aby implementovaly vlastní sémantiku vazby pro různé operace.
Mechanismus, kterým dynamický objekt implementuje vlastní sémantiku, je definován implementací. Dané rozhraní – znovu definované implementací – je implementováno dynamickými objekty, které signalizují prostředí runtime jazyka C#, že mají speciální sémantiku. Kdykoli probíhá operace na dynamickém objektu, převezme jejich vlastní sémantika vazby, nikoli ta jazyka C#, místo těch specifikovaných v této specifikaci pro C#.
Zatímco účelem dynamické vazby je umožnit spolupráci s dynamickými objekty, jazyk C# umožňuje dynamické vazby u všech objektů, ať už jsou dynamické, nebo ne. To umožňuje plynulejší integraci dynamických objektů, protože výsledky operací na nich nejsou vždy dynamické objekty, ale mohou být typu, který je neznámý programátorovi při kompilaci. Dynamické vazby mohou také pomoci eliminovat kód založený na reflexi, i když nejsou zapojeny žádné dynamické objekty.
12.3.4 Typy dílčích výrazů
Pokud je operace staticky svázaná, typ dílčího výrazu (např. příjemce a argument, index nebo operand) se vždy považuje za typ kompilace daného výrazu.
Pokud je operace dynamicky vázána, typ dílčího výrazu je určen různými způsoby v závislosti na typu kompilace dílčího výrazu:
- Dílčí výraz dynamického typu v čase kompilace se považuje za typ hodnoty, ke které se výraz vyhodnotí během běhu programu.
- Dílčí výraz, jehož typ během kompilace je parametr typu, se považuje za typ, ke kterému je parametr typu vázán při běhu programu.
- V opačném případě se podvýraz považuje za typ určený během kompilace.
12.4 Operátory
12.4.1 Obecné
Výrazy se vytvářejí z operandů a operátorů. Operátory výrazu označují, které operace se mají použít na operandy.
Příklad: Příklady operátorů zahrnují
+,-,*,/anew. Mezi příklady operandů patří literály, pole, místní proměnné a výrazy. koncového příkladu
Existují tři druhy operátorů:
- Unární operátory. Unární operátory přebírají jeden operand a používají buď prefixovou notaci (například
–x), nebo postfixovou notaci (napříkladx++). - Binární operátory. Binární operátory mají dva operandy a všechny používají infixační notaci (například
x + y). - Ternární operátor. Existuje pouze jeden ternární operátor,
?:; přebírá tři operandy a používá infixační notaci (c ? x : y).
Pořadí vyhodnocování operátorů ve výrazu je určeno prioritou a asociativitou operátorů (§12.4.2).
Operandy ve výrazu se vyhodnocují zleva doprava.
Příklad: V
F(i) + G(i++) * H(i)se volá metodaFpomocí staré hodnotyi, metodaGje volána se starou hodnotouia nakonec metodaHje volána s novou hodnotou i. Toto je oddělené od a nesouvisí s pořadím operátoru. koncového příkladu
Některé operátory mohou být přetížené. Přetížení operátoru (§12.4.3) umožňuje určit implementace operátoru definované uživatelem pro operace, kde jeden nebo oba operandy jsou uživatelsky definované třídy nebo typu struktury.
12.4.2 Přednost operátorů a asociativita
Pokud výraz obsahuje více operátorů, prioritu operátorů určuje pořadí, ve kterém jsou jednotlivé operátory vyhodnoceny.
Poznámka: Například výraz
x + y * zse vyhodnotí jakox + (y * z), protože operátor*má vyšší prioritu než binární operátor+. koncová poznámka
Priorita operátoru je stanovena definicí přidružené gramatické produkce.
Poznámka: Například výraz aditivní se skládá z posloupnosti výrazů multiplikativníoddělených operátory
+nebo-, takže operátory+a-mají nižší prioritu než operátory*,/a%. koncová poznámka
Poznámka: Následující tabulka shrnuje všechny operátory v pořadí od nejvyššího po nejnižší:
podčlánek kategorie operátory §12.8 Primární x.yx?.yf(x)a[x]a?[x]x++x--x!newtypeofdefaultcheckeduncheckeddelegatestackalloc§12.9 Unární +-!x~^++x--x(T)xawait x§12.10 Rozmezí ..§12.11 Přepínač switch { … }§12.12 Multiplikativní */%§12.12 Aditivní +-§12.13 Směna <<>>§12.14 Relační a typové testování <><=>=isas§12.14 Rovnost ==!=§12.15 Logická operace AND &§12.15 Logický XOR ^§12.15 Logické NEBO \|§12.16 Podmíněný operátor AND &&§12.16 Podmíněné OR \|\|§12.17 a §12.18 Spojení pro null a výraz throw ??throw x§12.20 Podmíněný ?:§12.23 a §12.21 Výraz přiřazení a lambda == ref*=/=%=+=-=<<=>>=&=^=\|==>??=koncová poznámka
Pokud dojde k operandu mezi dvěma operátory se stejnou prioritou, asociativita operátorů řídí pořadí provádění operací:
- S výjimkou operátorů přiřazení, operátoru rozsahu a operátoru nulového sjednocení jsou všechny binární operátory asociativní, což znamená, že operace se provádějí zleva doprava.
Příklad:
x + y + zse vyhodnotí jako(x + y) + z. koncového příkladu - Operátory přiřazení, operátor nulového sjednocení a podmíněný operátor (
?:) jsou asociativní, což znamená, že operace se provádějí zprava doleva.Příklad:
x = y = zse vyhodnotí jakox = (y = z). koncového příkladu - Operátor rozsahu není asociativní, což znamená, že operátor oblasti nemůže být range_expression.
Příklad: Obojí
x..y..zax..(y..z)jsou neplatné, protože..nejsou asociativní. koncového příkladu
Prioritu a asociativitu lze řídit pomocí závorek.
Příklad:
x + y * znejprve vynásobíyza potom přidá výsledek kx, ale(x + y) * znejprve přidáxaya výsledek pak vynásobíz. koncového příkladu
12.4.3 Přetížení operátoru
Všechny unární a binární operátory mají předdefinované implementace. Kromě toho lze uživatelem definované implementace zavést zahrnutím deklarací operátorů (§15.10) ve třídách a strukturách. Implementace operátoru definované uživatelem mají vždy přednost před předdefinovanými implementacemi operátorů: Pouze pokud neexistují implementace předdefinovaných operátorů definované uživatelem, budou se považovat za předdefinované implementace operátoru, jak je popsáno v §12.4.4 a §12.4.5.
Přetížitelné unární operátoryjsou:
+ - !(pouze logická negace)~ ++ -- true false
Přetížit můžou být pouze výše uvedené operátory. Zejména není možné přetížit operátor null-progiving (příponu !, §12.8.9) nebo unární index od koncového operátoru (předpona ^, §12.9.6).).
Poznámka: Přestože
trueafalsenejsou explicitně použity ve výrazech (a proto nejsou zahrnuty do tabulky priority v §12.4.2), jsou považovány za operátory, protože jsou vyvolány v několika kontextech výrazů: logické výrazy (§12.26) a výrazy zahrnující podmíněné (§12.20) a podmíněné logické operátory (§12.16). koncová poznámka
Přetížitelné binární operátoryjsou:
+ - * / % & | ^ << >> == != > < <= >=
Přetížit můžou být pouze výše uvedené operátory. Konkrétně není možné přetížit přístup člena, metodu vyvolání nebo .., , =&&, ||, , ??, ?:=>, checkedunchecked, newtypeofdefaultasa is operátory.
Pokud je binární operátor přetížen, odpovídající složený operátor přiřazení je také implicitně přetížen.
Příklad: Přetížení operátoru
*je také přetížení operátoru*=. Toto je popsáno dále v §12.23. koncového příkladu
Samotný operátor přiřazení (=) nelze přetížit. Přiřazení vždy provádí jednoduché uložení hodnoty do proměnné (§12.23.2).
Přetypování, například (T)x, jsou přetíženy poskytováním uživatelsky definovaných převodů (§10,5).
Poznámka: Uživatelem definované převody nemají vliv na chování operátorů
isneboas. koncová poznámka
Přístup k prvkům, jako je například a[x], není považován za přetížitelný operátor. Místo toho je uživatelsky definované indexování podporováno prostřednictvím indexerů (§15,9).
Ve výrazech se na operátory odkazuje pomocí zápisu operátoru a v deklaracích se na operátory odkazuje pomocí funkčního zápisu. Následující tabulka ukazuje vztah mezi operátorem a funkčním zápisem pro unární a binární operátory. V prvním záznamu "op" označuje jakýkoli přetížitelný unární prefixový operátor. Ve druhé položce označuje „op“ unární postfixové operátory ++ a --. Ve třetí položce "op» označuje jakýkoli přetížitelný binární operátor.
Poznámka: Příklad přetížení operátorů
++a--viz §15.10.2. koncová poznámka
| notace operátoru | funkční notace |
|---|---|
«op» x |
operator «op»(x) |
x «op» |
operator «op»(x) |
x «op» y |
operator «op»(x, y) |
Deklarace operátoru definované uživatelem vždy vyžadují, aby nejméně jeden z parametrů byl typu třídy nebo struktury, který obsahuje deklaraci operátoru.
Poznámka: Proto není možné, aby operátor definovaný uživatelem měl stejný podpis jako předdefinovaný operátor. koncová poznámka
Deklarace operátoru definované uživatelem nemohou upravit syntaxi, prioritu ani asociativitu operátoru.
Příklad: Operátor
/je vždy binárním operátorem, vždy má úroveň priority zadanou v §12.4.2a vždy je asociativní. koncového příkladu
Poznámka: Ačkoli je možné, aby operátor definovaný uživatelem provedl libovolné výpočty, implementace, které vytvářejí jiné výsledky než ty, které by bylo možné intuitivně očekávat, se důrazně nedoporučují. Například implementace operátoru
==by měla porovnat dva operandy pro rovnost a vrátit odpovídajícíboolvýsledek. koncová poznámka
Popisy jednotlivých operátorů v §12.9 až §12.23 určují předdefinované implementace operátorů a všechna další pravidla, která platí pro jednotlivé operátory. Popisy používají termíny přetížení unárního operátoru, přetížení binárního operátoru, číselné povýšenía definice zvednutých operátorů, které jsou nalezeny v následujících pododstavcích.
12.4.4 Rozlišení přetížení unárního operátoru
Operace formuláře «op» x nebo x «op», kde «op» je přetížitelný unární operátor a x je výraz typu X, je zpracován takto:
- Sada kandidátských uživatelem definovaných operátorů poskytovaná
Xpro operacioperator «op»(x)je určena pomocí pravidel §12.4.6. - Pokud sada kandidátských uživatelem definovaných operátorů není prázdná, stane se tím sada kandidátských operátorů pro operaci. V opačném případě se předdefinované binární implementace
operator «op», včetně jejich zdvižených forem, stanou sadou kandidátských operátorů pro danou operaci. Předdefinované implementace daného operátoru jsou uvedeny v popisu operátoru. Předdefinované operátory poskytované výčtovými nebo delegátovými typy jsou zahrnuty pouze v této sadě, pokud typ vazby – nebo základní typ, pokud se jedná o typ s možnou hodnotou null – jednoho z operandů je výčtový nebo delegátový typ. - Pravidla rozlišení přetížení §12.6.4 se použijí na sadu kandidátských operátorů, aby vybrali nejlepší operátor s ohledem na seznam argumentů
(x)a tento operátor se stane výsledkem procesu řešení přetížení. Pokud se řešení přetížení nepodaří vybrat jeden nejlepší operátor, dojde k chybě doby vazby.
12.4.5 Rozlišení přetížení binárního operátoru
Operace formuláře x «op» y, kde «op» je přetížitelný binární operátor, x je výraz typu Xa y je výraz typu Y, je zpracován takto:
- Sada kandidátních operátorů definovaných uživatelem, poskytovaných
XaYpro operacioperator «op»(x, y), je určena. Sada je tvořena sjednocením kandidátských operátorů poskytovanýchXa kandidátskými operátory poskytovanýmiY, z nichž každý je určen pravidly §12.4.6. Pro kombinovanou sadu jsou kandidáti sloučeni takto:- Pokud jsou
XaYzaměnitelné identity nebo pokud jsouXaYodvozeny od společného základního typu, pak se sdílené kandidátské operátory vyskytují v kombinované sadě pouze jednou. - Pokud existuje převod identity mezi
XaY, operátor«op»YposkytovanýYmá stejný návratový typ jako«op»XposkytovanéXa typy operandů«op»Ymají převod identity na odpovídající typy operandů«op»X, pak se v sadě vyskytuje pouze«op»X.
- Pokud jsou
- Pokud sada kandidátských uživatelem definovaných operátorů není prázdná, stane se tím sada kandidátských operátorů pro operaci. V opačném případě se předdefinované binární implementace
operator «op», včetně jejich zdvižených forem, stanou sadou kandidátských operátorů pro danou operaci. Předdefinované implementace daného operátoru jsou uvedeny v popisu operátoru. Pro předdefinované operátory výčtu a delegáta jsou operátory považovány pouze ty, které jsou definovány výčtem nebo typem delegáta, jenž je typem v době vazby pro jeden z operandů. - Pravidla rozlišení přetížení §12.6.4 se použijí na sadu kandidátských operátorů, aby vybrali nejlepší operátor s ohledem na seznam argumentů
(x, y)a tento operátor se stane výsledkem procesu řešení přetížení. Pokud se řešení přetížení nepodaří vybrat jeden nejlepší operátor, dojde k chybě doby vazby.
12.4.6 Kandidátské uživatelem definované operátory
Při zadání typu T a operace operator «op»(A), kde «op» je přetížitelný operátor a A je seznam argumentů, sada kandidátských uživatelem definovaných operátorů poskytovaná T pro operátor «op»(A) je určena takto:
- Určete typ
T₀. PokudTje typ hodnoty null,T₀je jeho základní typ; jinak seT₀rovnáT. - Pro všechna prohlášení
operator «op»veT₀a pro všechny zdvižené formy těchto operátorů, pokud je s ohledem na seznam argumentů použitelný alespoň jeden operátor (A), pak sada kandidátských operátorů se skládá ze všech těchto použitelných operátorů vT₀. - V opačném případě, pokud je
T₀object, je množina kandidátských operátorů prázdná. - Jinak je sada kandidátských operátorů poskytovaná
T₀tou sadou kandidátských operátorů, kterou poskytuje přímá základní třídaT₀, nebo efektivní základní třídaT₀, je-liT₀parametr typu.
12.4.7 Číselné propagační akce
12.4.7.1 Obecné
Tato dílčí položka je informativní.
§12.4.7 a jeho dílčích ustanovení jsou souhrnem kombinovaného účinku:
- pravidla pro implicitní číselné převody (§10.2.3);
- pravidla pro lepší převod (§ 12.6.4.7); a
- dostupné aritmetické operátory (§12.12), relační (§12.14) a integrální logické operátory (§12.15.2).
Číselné povýšení se skládá z automatického provádění určitých implicitních převodů operandů předdefinovaných unárních a binárních číselných operátorů. Číselné povýšení není odlišný mechanismus, ale spíše účinek použití rozlišení přetížení na předdefinované operátory. Číselné povýšení konkrétně nemá vliv na vyhodnocení uživatelem definovaných operátorů, i když uživatelem definované operátory lze implementovat, aby se projevily podobné účinky.
Jako příklad číselného povýšení zvažte předdefinované implementace binárního operátoru *:
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);
Při použití pravidel rozlišení přetížení (§12.6.4) u této sady operátorů je účinkem vybrat první z operátorů, pro které existují implicitní převody z typů operandu.
Příklad: Pro operaci
b * s, kdebjebyteasjeshort, rozlišení přetížení vybereoperator *(int, int)jako nejlepší operátor. Účinek je tedy, žebasjsou převedeny nainta typ výsledku jeint. Stejně tak pro operacii * d, kdeijeintadjedouble,overloadrozlišení vybereoperator *(double, double)jako nejlepší operátor. koncového příkladu
Konec informativního textu.
12.4.7.2 Unární číselné propagační akce
Tato dílčí položka je informativní.
U operandů předdefinovaných unárních operátorů +, -a ~ dochází k unárnímu číselnému povýšení. Unární číselné povýšení se jednoduše skládá z převodu operandů typu sbyte, byte, short, ushortnebo char na typ int. Kromě toho pro unární – operátor, unární číselné povýšení převede operandy typu uint na typ long.
Konec informativního textu.
12.4.7.3 Binární číselné povýšení
Tato dílčí položka je informativní.
Binární číselná propagace probíhá pro operandy předdefinovaných binárních operátorů +, -, *, /, %, &, |, ^, ==, !=, >, <, >=a <=. Binární číselné povýšení implicitně převede oba operandy na běžný typ, který v případě nerelačních operátorů se také stane typem výsledku operace. Binární číselné povýšení se skládá z použití následujících pravidel v pořadí, v jakém se zde zobrazují:
- Pokud je jeden z operandů typu
decimal, druhý operand se převede na typdecimal, nebo nastane chyba doby vazby, pokud je druhý operand typufloatnebodouble. - V opačném případě je-li jeden operand typu
double, druhý operand je převeden na typdouble. - V opačném případě je-li jeden operand typu
float, druhý operand je převeden na typfloat. - V opačném případě, pokud je jeden operand typu
ulong, druhý operand je převeden na typulong, nebo dojde k chybě vazby, pokud druhý operand jetype sbyte,short,intnebolong. - V opačném případě je-li jeden operand typu
long, druhý operand je převeden na typlong. - V opačném případě, je-li jeden operand typu
uinta druhý operand je typusbyte,shortneboint, oba operandy jsou převedeny na typlong. - V opačném případě je-li jeden operand typu
uint, druhý operand je převeden na typuint. - V opačném případě jsou oba operandy převedeny na typ
int.
Poznámka: První pravidlo zakáže všechny operace, které kombinují typ
decimals typydoubleafloat. Pravidlo vyplývá ze skutečnosti, že mezi typemdecimala typydoubleafloatneexistují implicitní převody. koncová poznámka
Poznámka: Všimněte si také, že operand nemůže být typu
ulong, pokud je druhý operand celočíselného typu se znaménkem. Důvodem je, že neexistuje žádný celočíselný typ, který by dokázal představit úplný rozsah hodnotulongstejně dobře jako podepsané celočíselné typy. koncová poznámka
V obou výše uvedených případech lze výraz přetypování použít k explicitní převodu jednoho operandu na typ, který je kompatibilní s druhým operandem.
příklad: V následujícím kódu
decimal AddPercent(decimal x, double percent) => x * (1.0 + percent / 100.0);k chybě doby vazby dochází, protože
decimalnelze vynásobitdouble. Tato chyba je vyřešena explicitním převodem druhého operandu nadecimalnásledujícím způsobem:decimal AddPercent(decimal x, double percent) => x * (decimal)(1.0 + percent / 100.0);koncového příkladu
Konec informativního textu.
12.4.8 Zvedané operátory
Zvednutý operátor povoluje předdefinované a uživatelem definované operátory, které pracují s nenulovým typem hodnoty, aby se také používaly s tvarem s možnou hodnotou null daného typu. Zvedané operátory se vytvářejí z předdefinovaných a uživatelem definovaných operátorů, které splňují určité požadavky, jak je popsáno v následujících případech:
- Pro unární operátory
+, , ,++,---!(logická negace)^a~, zdvižená forma operátoru existuje, pokud operand a výsledné typy jsou oba typy hodnot bez hodnoty null. Zvedená forma je konstruována přidáním jediného modifikátoru?k typům operandu a výsledku. Zvýšený operátor vytvoří hodnotunull, pokud je operandnull. Jinak zvednutý operátor rozbalí operand, použije základní operátor a zabalí výsledek. - Pro binární operátory
+, ,-*/%&|^..<<a>>, vyvýžená forma operátoru existuje, pokud operand a výsledné typy jsou všechny typy hodnot, které nemají hodnotu null. Zdvižená forma je vytvořena přidáním jednoho modifikátoru?do každého operandu a typu výsledku. Operátor lifted vytvořínullhodnotu, pokud jeden nebo oba operandy jsounull(výjimka je&a|operátorbool?typu, jak je popsáno v §12.15.5). Jinak zvednutý operátor rozbalí operandy, použije základní operátor a zabalí výsledek. - Pro operátory rovnosti
==a!=existuje zdvižená forma operátoru, pokud jsou oba typy operandů nenulovými typy hodnot a typ výsledku jebool. Zdvižená forma je vytvořena přidáním jednoho modifikátoru?ke každému typu operandu. Operátor lifted považuje dvěnullhodnoty za stejné anullhodnotu za nerovnou s libovolnou ne-nullhodnotou. Pokud oba operandy nejsounull, operátor zvednutím rozbalí operandy a použije podkladový operátor k vytvořeníboolvýsledku. - U relačních operátorů
<,>,<=a>=existuje zdvižená forma operátoru, pokud jsou typy operandů nenulové hodnotové typy a pokud je typ výsledkubool. Zdvižená forma je vytvořena přidáním jednoho modifikátoru?ke každému typu operandu. Operátor lifted vytvoří hodnotufalse, pokud jsou jeden nebo oba operandynull. Jinak povýšený operátor rozvine operandy a použije podkladový operátor, aby vytvořil výsledekbool.
12.5 Vyhledávání členů
12.5.1 Obecné
Vyhledávání člena je proces, kdy je určen význam názvu v kontextu typu. Vyhledávání členů může být součástí vyhodnocení simple_name (§12.8.4) nebo member_access (§12.8.7) ve výrazu. Pokud simple_name nebo member_access nastane jako primary_expressioninvocation_expression (§12.8.10.2), je člen vyvolán.
Je-li člen metodou nebo událostí nebo je-li konstantním, polem nebo majetkem delegáta (§21) nebo typu dynamic (§8.2.4), je člen vyzván k vyvolání.
Vyhledávání členů bere v úvahu nejen název člena, ale také počet parametrů typu, které člen má a zda je člen přístupný. Pro účely vyhledávání členů mají obecné metody a vnořené obecné typy počet parametrů typu uvedených v příslušných deklaracích a všichni ostatní členové mají parametry nulového typu.
Vyhledání členu názvu N s argumenty typu K v typu T se zpracovává následujícím způsobem:
- Nejprve se určí sada přístupných členů s názvem
N:- Je-li
Tparametr typu, pak je sada sjednocení sad přístupných členů pojmenovanýchNv každém z typů zadaných jako primární omezení nebo sekundární omezení (§15.2.5) proT, spolu se sadou přístupných členů pojmenovanýchNvobject. - V opačném případě se sada skládá ze všech přístupných (§7.5) členů pojmenovaných
NvT, včetně zděděných členů a přístupných členů pojmenovanýchNvobject. Je-liTkonstruovaný typ, sada členů je získána nahrazením argumentů typu, jak je popsáno v §15.3.3. Členy, které obsahují modifikátoroverride, jsou ze sady vyloučeny.
- Je-li
- Pokud je
Knula, odeberou se všechny vnořené typy, jejichž deklarace zahrnují parametry typu. PokudKnení nula, odeberou se všechny členy s jiným počtem parametrů typu. Pokud jeKnula, metody s parametry typu se neodeberou, protože proces odvozování typu (§12.6.3) může být schopen odvodit argumenty typu. - Dále, pokud je člen vyvolán, odeberou se ze sady všechny nevyvolatelné členy.
- V dalším kroku se ze sady odeberou členové, kteří jsou skryti ostatními členy. Pro každý člen
S.Mv sadě, kdeSje typ, ve kterém je členMdeklarován, jsou použita následující pravidla:- Pokud je
Mkonstanta, pole, vlastnost, událost nebo člen výčtu, odeberou se ze sady všechny členy deklarované v základním typuS. - Je-li
Mdeklarace typu, všechny typy, které nejsou deklarovány v základním typuSjsou ze sady odebrány a všechny deklarace typů se stejným počtem parametrů typu jakoMdeklarované v základním typuSjsou ze sady odebrány. - Pokud
Mje metoda, odeberou se ze sady všechny členy bez metody deklarované v základním typuS.
- Pokud je
- Dále se ze sady odeberou členové rozhraní, které jsou skryti členy třídy. Tento krok má účinek pouze v případě, že
Tje parametr typu aTmá efektivní základní třídu jinou nežobjecta neprázdnou efektivní sadu rozhraní (§15.2.5). Pro každý členS.Mv sadě, kdeSje typ, ve kterém jeMčlen deklarován, použijí se následující pravidla, pokudSje deklarace třídy jiná nežobject:- Pokud
Mje konstanta, pole, vlastnost, událost, člen výčtu nebo deklarace typu, všechny členy deklarované v deklaraci rozhraní jsou ze sady odebrány. - Pokud
Mje metoda, odeberou se ze sady všechny členy bez metody deklarované v deklaraci rozhraní a všechny metody se stejným podpisem jakoMdeklarované v deklaraci rozhraní se ze sady odeberou.
- Pokud
- Nakonec se po odebrání skrytých členů určí výsledek vyhledávání:
- Pokud se sada skládá z jednoho členu, který není metodou, je tento člen výsledkem vyhledávání.
- V opačném případě, pokud sada obsahuje pouze metody, pak tato skupina metod je výsledkem vyhledávání.
- V opačném případě je vyhledávání nejednoznačné a vyskytne se chyba při vyhodnocování vazby.
Pro vyhledávání členů v typech jiných než jsou typové parametry a rozhraní a vyhledávání členů v rozhraních, která jsou přísně jednoduchá dědičnost (každé rozhraní v řetězci dědičnosti má přesně nula nebo jedno přímé základní rozhraní), je účinek pravidel vyhledávání jednoduše ten, že odvození členové skryjí základní členy se stejným názvem nebo podpisem. Takové vyhledávání s jednou dědičností nejsou nikdy nejednoznačné. Nejednoznačnosti, které mohou vzniknout z vyhledávání členů v rozhraních vícenásobné dědičnosti, jsou popsány v §19.4.11.
Poznámka: Tato fáze představuje pouze jeden druh nejednoznačnosti. Pokud hledání členů vede ke skupině metod, může další použití skupiny metod selhat z důvodu nejednoznačnosti, například popsané v §12.6.4.1 a §12.6.6.2. koncová poznámka
12.5.2 Základní typy
Pro účely vyhledávání členů je typ T považován za následující základní typy:
- Pokud je
Tobjectnebodynamic,Tnemá žádný základní typ. - Pokud je
Tenum_type, základní typyTjsou typy třídSystem.Enum,System.ValueTypeaobject. - Pokud je
Tstruktura typu , základní typyTjsou typy třídSystem.ValueTypeaobject.Poznámka: nullable_value_type je struct_type (§8.3.1). koncová poznámka
- Je-li
Ttyp třídy, základní typyTjsou základními třídamiT, včetně typu třídyobject. - Je-li
Tinterface_type, základní typyTjsou základní rozhraníTa typ třídyobject. - Pokud
Tje array_type, základní typyTjsou typy třídSystem.Arrayaobject. - Je-li
Tdelegate_type, základní typyTjsou typy třídSystem.Delegateaobject.
12.6 Členy funkce
12.6.1 Obecné
Členy funkce jsou členy, které obsahují spustitelné příkazy. Funkční členové jsou vždy členy typů a nemohou být členy jmenných prostorů. Jazyk C# definuje následující kategorie členů funkce:
- Metody
- Vlastnosti
- Dění
- Indexování
- Uživatelem definované operátory
- Konstruktory instancí
- Statické konstruktory
- Finalizátory
S výjimkou finalizátorů a statických konstruktorů (které nelze explicitně vyvolat), příkazy obsažené ve členech funkce se provádějí prostřednictvím vyvolání členů funkce. Skutečná syntaxe pro zápis vyvolání člena funkce závisí na konkrétní kategorii člena funkce.
Seznam argumentů (§12.6.2) vyvolání člena funkce poskytuje skutečné hodnoty nebo proměnné odkazy na parametry člena funkce.
Vyvolání obecných metod může použít odvození typu k určení sady argumentů typu, které se mají předat metodě. Tento proces je popsán v §12.6.3.
Vyvolání metod, indexerů, operátorů a konstruktorů instancí využívá rozlišení přetížení k určení, která z kandidátských skupin členů funkce se má vyvolat. Tento proces je popsán v §12.6.4.
Jakmile je při vazbě identifikován určitý člen funkce, pravděpodobně pomocí rozlišení přetížení, skutečný proces vyvolání členu funkce za běhu programu je popsán v §12.6.6.
Poznámka: Následující tabulka shrnuje zpracování, které probíhá v konstruktech zahrnujících šest kategorií členů funkce, které lze explicitně vyvolat. V tabulce
e,x,yavalueoznačují výrazy klasifikované jako proměnné nebo hodnoty,Toznačuje výraz klasifikovaný jako typ,Fje jednoduchý název metody aPje jednoduchý název vlastnosti.
Konstruovat Příklad Popis Vyvolání metody F(x, y)Rozlišení přetížení se použije k výběru nejlepší metody Fv obsahující třídě nebo struktuře. Metoda je vyvolána se seznamem argumentů(x, y). Pokud metoda nenístatic, výraz instance jethis.T.F(x, y)K výběru nejlepší metody Fve třídě nebo struktuřeTse používá rozlišení přetížení. K chybě doby vazby dochází, pokud metoda nenístatic. Metoda je vyvolána se seznamem argumentů(x, y).e.F(x, y)Rozlišení přetížení se použije k výběru nejlepší metody Fve třídě, struktuře nebo rozhraní zadaném typeme. K chybě doby vazby dojde, pokud je metodastatic. Metoda je vyvolána pomocí výrazu instanceea seznamu argumentů(x, y).Přístup k vlastnostem PPřístupový prvek typu get vlastnosti Pve vnitřní třídě nebo struktuře je vyvolán. Pokud jePjen pro zápis, dojde k chybě v době kompilace. PokudPnenístatic, výraz instance jethis.P = valueNastavovací metoda vlastnosti Pv obsahující třídě nebo struktuře je vyvolána se seznamem argumentů(value). Pokud jePjen pro čtení, dojde k chybě v době kompilace. PokudPnenístatic, výraz instance jethis.T.PPřístupový objekt get vlastnosti Pve třídě nebo struktuřeTje vyvolán. K chybě v době kompilace dochází, pokudPnenístaticnebo jePjen pro zápis.T.P = valueSet akcesor vlastnosti Pve třídě nebo struktuřeTje vyvolán se seznamem argumentů(value). K chybě v době kompilace dochází, pokudPnenístaticnebo jePjen pro čtení.e.PGet přístup k vlastnosti Pv třídě, struktuře nebo rozhraní zadaném typemEje vyvolán výrazem instancee. K chybě při vazbě dochází, pokud jePstaticnebo jePurčeno pouze pro zápis.e.P = valueNastavovací metoda vlastnosti Pve třídě, struktuře nebo rozhraní určeném typemEse vyvolává s výrazem instanceea seznamem argumentů(value). Dochází k chybě doby vazby, pokud jePstatic, nebo pokud jePurčeno pouze pro čtení.Přístup k událostem E += valueVyvolá se doplněk události Ev obsahující třídě nebo struktuře. PokudEnenístatic, výraz instance jethis.E -= valuePřístup k odebrání události Eve včleněné třídě nebo struktuře je vyvolán. PokudEnenístatic, výraz instance jethis.T.E += valuePřidání přístupového členu události Eve třídě nebo struktuřeTje vyvoláno. K chybě při vazbě dochází, pokudEnenístatic.T.E -= valueOdebrání přístupového objektu události Eve třídě nebo struktuřeTje vyvoláno. K chybě při vazbě dochází, pokudEnenístatic.e.E += valuePřidávací přístupový prvek události Eve třídě, struktuře nebo rozhraní daném typemEje vyvolán s výrazem instancee. Dochází k chybě doby vazby, pokud jeEstatic.e.E -= valueOdebrání přístupového objektu události Eve třídě, struktuře nebo rozhraní zadaném typemEje vyvoláno výrazem instancee. Dochází k chybě doby vazby, pokud jeEstatic.Přístup k indexeru e[x, y]Výběr nejlepšího indexeru ve třídě, struktuře nebo rozhraní určených typem ese provádí pomocí rozlišení přetížení. Přístupový objekt get indexeru je vyvolán výrazem instanceea seznamem argumentů(x, y). Chyba při vazebním čase nastane, pokud je indexer pouze pro zápis.e[x, y] = valueVýběr nejlepšího indexeru ve třídě, struktuře nebo rozhraní určených typem ese provádí pomocí rozlišení přetížení. Objekt set indexeru se vyvolá pomocí výrazu instanceea seznamu argumentů(x, y, value). Pokud je indexer pouze pro čtení, nastane chyba při určování doby vazby.Vyvolání operátoru -xRozlišení přetížení se použije k výběru nejlepšího unárního operátoru ve třídě nebo struktuře určené typem x. Vybraný operátor je vyvolán se seznamem argumentů(x).x + yRozlišení přetížení se použije k výběru nejlepšího binárního operátoru ve třídách nebo strukturách zadaných typy xay. Vybraný operátor je vyvolán se seznamem argumentů(x, y).Vyvolání konstruktoru instance new T(x, y)Rozlišení přetížení se použije k výběru nejlepšího konstruktoru instance ve třídě nebo ve struktuře T. Konstruktor instance je vyvolán se seznamem argumentů(x, y).koncová poznámka
12.6.2 Seznamy argumentů
12.6.2.1 Obecné
Každý člen funkce a vyvolání delegáta obsahuje seznam argumentů, který poskytuje skutečné hodnoty nebo odkazy na proměnné pro parametry člena funkce. Syntaxe pro zadání seznamu argumentů vyvolání člena funkce závisí na kategorii člena funkce:
- Například pro konstruktory, metody, indexery a delegáty jsou argumenty uvedeny jako argument_list, jak je popsáno níže. Při volání přístupového modifikátoru indexeru seznam argumentů pro indexery navíc obsahuje výraz určený jako pravý operand přiřazovacího operátoru.
Poznámka: Tento další argument se nepoužívá pro rozlišení přetížení, právě při vyvolání přístupového objektu sady. koncová poznámka
- Pro vlastnosti je seznam argumentů prázdný při vyvolání přístupového objektu get a skládá se z výrazu zadaného jako správný operand operátoru přiřazení při vyvolání přístupového objektu set.
- V případě událostí se seznam argumentů skládá z výrazu zadaného jako pravý operand operátoru
+=nebo-=. - Pro uživatelem definované operátory se seznam argumentů skládá z jednoho operandu unárního operátoru nebo dvou operandů binárního operátoru.
Argumenty vlastností (§15,7) a události (§15,8) jsou vždy předány jako parametry hodnoty (§15.6.2.2). Argumenty uživatelem definovaných operátorů (§15.10) se vždy předávají jako parametry hodnot (§15.6.2.2) nebo vstupní parametry (§9.2.8). Argumenty indexerů (§15,9) jsou vždy předány jako parametry hodnot (§15.6.2.2), vstupní parametry (§9.2.8) nebo pole parametrů (§15.6.2.4). Výstupní a referenční parametry nejsou podporovány pro tyto kategorie členů funkce.
Argumenty konstruktoru instance, metody, indexeru nebo vyvolání delegáta jsou zadány jako argument_list:
argument_list
: argument (',' argument)*
;
argument
: argument_name? argument_value
;
argument_name
: identifier ':'
;
argument_value
: expression
| 'in' variable_reference
| 'ref' variable_reference
| 'out' variable_reference
;
argument_list se skládá z jednoho nebo více argumentů, oddělených čárkami. Každý argument se skládá z volitelného argument_name následovaného argument_value. Argument s argument_name se označuje jako pojmenovaný argument, zatímco argument bez argument_name je poziční argument.
argument_value může mít jednu z následujících forem:
- Výraz označující, že argument je předán jako parametr hodnoty nebo je transformován do vstupního parametru a následně předán tak, jak je určeno (§12.6.4.2 a popsán v §12.6.2.3.
- Klíčové slovo
innásledované variable_reference (§9,5), označující, že argument je předán jako vstupní parametr (§15.6.2.3.2). Proměnná musí být rozhodně přiřazena (§9,4) před předáním jako vstupního parametru. - Klíčové slovo
refnásledované variable_reference (§9,5), označující, že argument je předán jako referenční parametr (§15.6.2.3.3). Proměnná musí být rozhodně přiřazena (§9.4) před předáním jako referenčního parametru. - Klíčové slovo
outnásledované variable_reference (§9,5), označující, že argument je předán jako výstupní parametr (§15.6.2.3.4). Proměnná je považována za rozhodně přiřazenou (§9,4) po vyvolání člena funkce, ve kterém je proměnná předána jako výstupní parametr.
Formulář určuje režimu předávání parametrů
Předání nestálého pole (§15.5.4) jako vstupní, výstupní nebo referenční parametr způsobí upozornění, protože pole nelze považovat za nestálé metodou vyvolání.
12.6.2.2 Odpovídající parametry
Pro každý argument v seznamu argumentů musí být odpovídající parametr ve funkčním členu nebo delegátovi, který se volá.
Seznam parametrů použitý dále je určen následujícím způsobem:
- U virtuálních metod a indexerů definovaných ve třídách je seznam parametrů vybrán z první deklarace nebo přepsání člena funkce nalezen při spuštění se statickým typem příjemce a vyhledáváním v jeho základních třídách.
- Pro částečné metody se použije seznam parametrů definující deklarace částečné metody.
- Pro všechny ostatní členy funkce a delegáty existuje pouze jeden seznam parametrů, který se používá.
Pozice argumentu nebo parametru je definována jako počet argumentů nebo parametrů před ním v seznamu argumentů nebo seznamu parametrů.
Odpovídající parametry pro argumenty členů funkce jsou vytvořeny takto:
- Argumenty v argument_list u konstruktorů instancí, metod, indexerů a delegátů:
- Poziční argument, kde se parametr vyskytuje na stejné pozici v seznamu parametrů, odpovídá tomuto parametru, pokud parametr není polem parametrů a člen funkce je vyvolán v rozšířené podobě.
- Poziční argument člena funkce s polem parametrů vyvolaným v rozšířené podobě, který se vyskytuje na pozici pole parametrů v seznamu parametrů nebo za tím, odpovídá prvku v matici parametrů.
- Pojmenovaný argument odpovídá parametru se stejným názvem v seznamu parametrů.
- U indexerů při vyvolání přístupového objektu set výraz zadaný jako pravý operand operátoru přiřazení odpovídá implicitnímu
valueparametru deklarace objektu set accessor.
- Při vyvolání přístupového objektu get nejsou pro vlastnosti žádné argumenty. Při vyvolání přístupového objektu set odpovídá výraz zadaný jako správný operand operátoru přiřazení implicitnímu parametru hodnoty deklarace objektu set accessor.
- Pro uživatelem definované unární operátory (včetně převodů) jeden operand odpovídá jedinému parametru deklarace operátoru.
- U uživatelem definovaných binárních operátorů levý operand odpovídá prvnímu parametru a pravý operand odpovídá druhému parametru deklarace operátoru.
- Nepojmenovaný argument neodpovídá žádnému parametru, pokud následuje pojmenovaný argument na nesprávné pozici nebo pojmenovaný argument, který odpovídá poli parametrů.
Poznámka: Tím se zabrání vyvolání
void M(bool a = true, bool b = true, bool c = true);pomocíM(c: false, valueB);. První argument se používá mimo pozici (argument se používá na první pozici, ale parametr s názvemcje ve třetí pozici), takže by se měly pojmenovat následující argumenty. Jinými slovy, neukončující pojmenované argumenty jsou povoleny pouze v případě, že název a pozice způsobí nalezení stejného odpovídajícího parametru. koncová poznámka
12.6.2.3 Vyhodnocení seznamů argumentů za běhu
Během zpracování běhu vyvolání člena funkce (§12.6.6), výrazy nebo proměnné odkazy na seznam argumentů se vyhodnocují v pořadí zleva doprava takto:
Pokud je režim předávání parametru hodnota pro argument hodnoty.
výraz argumentu se vyhodnotí a provede se implicitní převod (§10.2) na odpovídající typ parametru. Výsledná hodnota se stane počáteční hodnotou parametru hodnoty ve volání člena funkce.
jinak je režim předávání parametru vstup. Pokud je argument odkazem na proměnnou a existuje převod identity (§10.2.2) mezi typem argumentu a typem parametru, výsledné umístění úložiště se stane umístěním úložiště reprezentovaným parametrem v vyvolání člena funkce. V opačném případě se vytvoří umístění úložiště se stejným typem jako odpovídající parametr. Výraz argumentu se vyhodnotí a provede se implicitní převod (§10,2) na odpovídající typ parametru. Výsledná hodnota je uložena v daném umístění úložiště. Toto umístění úložiště je reprezentováno vstupním parametrem při vyvolání člena funkce.
Příklad: S ohledem na následující deklarace a volání metody:
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 argumentVe volání metody
M1(i)seipředává jako vstupní argument, protože je klasifikován jako proměnná a má stejný typintjako vstupní parametr. Ve volání metodyM1(i + 5)se vytvoří nepojmenovanáintproměnná, inicializuje se s hodnotou argumentu a pak se předá jako vstupní argument. Viz §12.6.4.2 a §12.6.4.4.koncového příkladu
U vstupního, výstupního nebo referenčního argumentu se vyhodnotí odkaz na proměnnou a výsledné umístění úložiště se stane umístěním úložiště reprezentovaným parametrem při vyvolání člena funkce. U vstupního nebo referenčního argumentu musí být proměnná rozhodně přiřazena v okamžiku volání metody. Pokud je odkaz na proměnnou uveden jako výstupní argument nebo jako prvek pole reference_type, provede se kontrola za běhu programu, která zajistí, že typ prvku pole je identický s typem parametru. Pokud tato kontrola selže, vyvolá se
System.ArrayTypeMismatchException.
Poznámka: tato kontrola za běhu je vyžadována z důvodu kovariance pole (§17.6). koncová poznámka
příklad: V následujícím kódu
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 } }druhé vyvolání
Fzpůsobí vyvoláníSystem.ArrayTypeMismatchException, protože skutečný typ prvkubjestringa nikoliobject.koncového příkladu
Metody, indexery a konstruktory instancí mohou deklarovat, že nejpravější parametr je parametrické pole (§15.6.2.4). Tyto členy funkce jsou vyvolány buď v jejich normální podobě, nebo v rozbalené podobě v závislosti na tom, která je použitelná (§12.6.4.2):
- Pokud je člen funkce s polem parametrů vyvolán v normální podobě, argument zadaný pro pole parametrů musí být jediný výraz, který je implicitně konvertibilní (§10.2) na typ pole parametru. V tomto případě pole parametrů funguje přesně jako parametr hodnoty.
- Pokud je člen funkce s polem parametrů vyvolán ve své rozšířené podobě, vyvolání musí pro pole parametrů zadat nula nebo více pozičních argumentů, kde každý argument je implicitně konvertibilní (§10.2) na typ prvku pole parametrů. V tomto případě vyvolání vytvoří instanci typu pole parametru s délkou odpovídající počtu argumentů, inicializuje prvky instance pole s danými hodnotami argumentu a použije nově vytvořenou instanci matice jako skutečný argument.
Výrazy seznamu argumentů se vždy vyhodnocují v textovém pořadí.
příklad: Tedy příklad
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++); } }vytvoří výstup.
x = 0, y = 1, z = 2 x = 4, y = -1, z = 3koncového příkladu
Když je člen funkce s polem parametrů vyvolán v rozšířené podobě s alespoň jedním rozbaleným argumentem, vyvolání se zpracuje, jako by byl výraz pro vytvoření pole s inicializátorem pole (§12.8.17.4) vložen kolem rozbalených argumentů. Prázdné pole je předáno, pokud neexistují žádné argumenty pro pole parametrů; není zadáno, zda je odkaz předán do nově přiděleného nebo existujícího prázdného pole.
Příklad: Vzhledem k deklaraci
void F(int x, int y, params object[] args);následující vyvolání rozšířené formy metody
F(10, 20, 30, 40); F(10, 20, 1, "hello", 3.0);přesně odpovídá
F(10, 20, new object[] { 30, 40 }); F(10, 20, new object[] { 1, "hello", 3.0 });koncového příkladu
Pokud argumenty vynecháte ze člena funkce s odpovídajícími volitelnými parametry, výchozí argumenty deklarace člena funkce se implicitně předají. (To může zahrnovat vytvoření umístění úložiště, jak je popsáno výše.)
Poznámka: Protože jsou vždy konstantní, jejich vyhodnocení nebude mít vliv na vyhodnocení zbývajících argumentů. koncová poznámka
12.6.3 Odvození typu
12.6.3.1 Obecné
Pokud je volána obecná metoda bez zadání argumentů typu, proces odvození typu se pokusí odvodit argumenty typu pro volání. Přítomnost odvození typu umožňuje použití pohodlnější syntaxe pro volání obecné metody a umožňuje programátorům vyhnout se zadávání redundantních informací o typu.
Příklad :
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> } }Prostřednictvím odvození typu jsou argumenty typu
intastringurčeny z argumentů metody.koncového příkladu
K odvození typu dochází jako součást zpracování vazební doby při vyvolání metody (§12.8.10.2) a probíhá před krokem řešení přetížení volání. Pokud je v vyvolání metody zadána konkrétní skupina metod a v rámci vyvolání metody nejsou zadány žádné argumenty typu, použije se pro každou obecnou metodu ve skupině metod odvození typu. Pokud je odvození typu úspěšné, použijí se argumenty odvozeného typu k určení typů argumentů pro následné řešení přetížení. Pokud rozlišení přetížení zvolí obecnou metodu, která se má vyvolat, pak odvozené argumenty typu se použijí jako argumenty typu pro vyvolání. Pokud odvození typu pro konkrétní metodu selže, tato metoda se nezaúčastní rozlišení přetížení. Selhání odvození typu samo o sobě chybu v době vazby nezpůsobí. Často však vede k chybě v době vazby při řešení přetížení a pak se nepodaří najít žádné použitelné metody.
Pokud každý zadaný argument neodpovídá přesně jednomu parametru v metodě (§12.6.2.2), nebo existuje nepovinný parametr bez odpovídajícího argumentu, pak odvození okamžitě selže. V opačném případě předpokládejme, že obecná metoda má následující podpis:
Tₑ M<X₁...Xᵥ>(T₁ p₁ ... Tₓ pₓ)
Při volání metody formuláře M(E₁ ...Eₓ) úkolem odvození typu je najít jedinečné argumenty typu S₁...Sᵥ pro každý z parametrů typu X₁...Xᵥ tak, aby volání M<S₁...Sᵥ>(E₁...Eₓ) bylo platné.
Proces odvození typu je popsán níže jako algoritmus. Odpovídající kompilátor může být implementován pomocí alternativního přístupu, pokud dosáhne stejného výsledku ve všech případech.
Během procesu odvozování je každý typový parametr Xᵢ buď upevněn na konkrétní typ Sᵢ, nebo nezafixován s přidruženou sadou omezení. Každé z omezení je určitý typ T. Na začátku není každá proměnná typu Xᵢ vázána s prázdnou množinou omezení.
Odvození typu probíhá ve fázích. Každá fáze se pokusí odvodit argumenty typu pro více proměnných typů na základě zjištění předchozí fáze. První fáze provede několik počátečních odvozování hranic, zatímco druhá fáze nastavuje proměnné typu na konkrétní typy a vyvozuje další hranice. Druhá fáze se může několikrát opakovat.
Poznámka: Odvození typu se používá také v jiných kontextech, včetně převodu skupin metod (§12.6.3.15) a nalezení nejlepšího společného typu sady výrazů (§12.6.3.16). koncová poznámka
12.6.3.2 První fáze
Pro každý argument Eᵢmetody je vstupní typ odvození (§12.6.3.7) vyroben z Eᵢ odpovídajícího typu Tⱼparametru .
12.6.3.3 Druhá fáze
Druhá fáze pokračuje takto:
- Všechny opravené proměnné
Xᵢtypu, které nezávisí na (§12.6.3.6), jsouXₑpevné (§12.6.3.13). - Pokud neexistují žádné takové proměnné typu, všechny neurčené proměnné typu
Xᵢjsou určeny, pro které platí vše z následujícího:- Existuje alespoň jedna proměnná typu
Xₑ, která závisí naXᵢ -
Xᵢmá neprázdnou sadu hranic.
- Existuje alespoň jedna proměnná typu
- Pokud žádné takové proměnné typu neexistují a stále existují neurčené proměnné typu, odvození typu selže.
- Jinak pokud neexistují žádné další nepřipravené proměnné typu, odvození typu proběhne úspěšně.
- V opačném případě pro všechny argumenty
EᵢTⱼs odpovídajícím typem parametru, kde výstupní typy (§12.6.3.5) obsahujíXₑproměnné typu, ale vstupní typy (§12.6.3.4) nejsou, odvozování výstupního typu (§12.6.3.8) se provádí zEᵢboduTⱼ12.6.3.8. Pak se opakuje druhá fáze.
12.6.3.4 Vstupní typy
Pokud je E skupina metod nebo implicitně zadaná anonymní funkce a T je typ delegáta nebo výrazový strom, pak jsou všechny typy parametrů Tvstupní typyEs typemT.
12.6.3.5 Výstupní typy
Pokud E je skupina metod nebo anonymní funkce a T je typ delegáta nebo stromu výrazů, návratový typ T je typ výstupuEo typuT.
12.6.3.6 Závislost
nefixovaná proměnná typuXᵢzávisí přímo na nefixované proměnné typuXₑ, pokud nějaký argument Eᵥ s typem TᵥXₑ nastává ve vstupním typu Eᵥ s typem Tᵥ a Xᵢ se vyskytuje ve výstupním typu Eᵥ s typem Tᵥ.
Xₑ
závisí naXᵢ, pokud Xₑzávisí přímo naXᵢ nebo jestli Xᵢzávisí přímo naXᵥ a Xᵥzávisí naXₑ. Proto "závisí na" je tranzitivním, ale nikoli reflexivním uzávěrem "závisí přímo na".
12.6.3.7 Odvození vstupního typu
Odvození vstupního typu je provedeno z výrazu E na typ T následujícím způsobem:
- Je-li
Evýraz řazené kolekce členů (§12.8.6) s arityNa prvkyEᵢaTje typ řazené kolekce členů s odpovídajícímiNtypyTₑprvků neboTje typ hodnoty null aT0?je typT0řazené kolekce členů s arityN, který má odpovídající typTₑelementu , pak pro každý z nichEᵢse odvozuje vstupní typ odvozování zEᵢTₑ. - Je-li
Eanonymní funkce, je odvození explicitního typu parametru (§12.6.3.9) provedeno odET - V opačném případě,
EpokudUmá typ a odpovídající parametr je parametr hodnoty (§15.6.2.2) pak se odvozujezUdolní hranice (T). - V opačném případě, pokud
Emá typUa odpovídající parametr je referenčním parametrem (§15.6.2.3.3) nebo výstupní parametr (§15.6.2.3.4), provede se přesný odvození (§12.6.3.10)UTbodu 12.6.10). - V opačném případě, pokud
EUmá typEa odpovídající parametr je vstupní parametr (§15.6.2.3.2) a je vstupním argumentem, je odvozována přesná odvození (U).T - V opačném případě, pokud
Emá typUa odpovídající parametr je vstupní parametr (§15.6.2.3.2) pak dolní mez odvození (§12.6.3.11) se provede zUbodu.T - Jinak se pro tento argument nevyvozuje žádná odvození.
12.6.3.8 Odvození výstupního typu
Odvození výstupního
- Pokud
Eje výraz řazené kolekce členů s arity a elementyNEᵢ, aTje typ řazené kolekce členů s odpovídajícímiNtypy elementůTₑneboTje hodnota nullable typ aT0?je typ řazenéT0kolekce členů s odpovídajícím typemNelementu , pak pro každýTₑvýstupní typEᵢodvození typu je vyroben zEᵢdoTₑ. - Je-li
Eanonymní funkce s odvozeným návratovým typemU(§12.6.3.14) aTjedná se o typ delegáta nebo typ stromu výrazu s návratovým typemTₓ, je odvození dolní hranice (§12.6.3.11) provedeno zU.Tₓ - V opačném případě, pokud
je skupina metod a je typ delegáta nebo stromový typ výrazu s typy parametrů a návratového typu , a přetížení rozlišení s typy vrací jednu metodu s návratovým typem , pak se provede dolní meze odvozování z do . - V opačném případě pokud je
výraz s typem , odvození se vytvoří z do . - Jinak se nedělají žádné závěry.
12.6.3.9 Explicitní odvození typu parametru
explicitní odvození typu parametru sez výrazu E typ T následujícím způsobem:
- Je-li
Eexplicitně zadaná anonymní funkce sU₁...UᵥtypyTparametrů aV₁...Vᵥjedná se o typ delegovaného typu nebo stromu výrazů s typyUᵢparametrů, je pro každý z nich proveden přesný odvození (Uᵢ) z odpovídajícíhoVᵢ.
12.6.3.10 Přesné odvozy
Následujícím způsobem je provedeno přesné odvozeníz typu Una typ V:
- Pokud je
Vjedním z neopravenýchXᵢ,Use přidá do sady přesných hranic proXᵢ. - V opačném případě se sady
V₁...VₑaU₁...Uₑurčují ověřením, zda se vztahuje některý z následujících případů:-
Vje typ poleV₁[...]aUje typ poleU₁[...]stejného pořadí. -
Vje typV₁?aUje typU₁ -
Vje konstruovaný typC<V₁...Vₑ>aUje vytvořený typC<U₁...Uₑ>
Pokud se některý z těchto případů použije, provede se přesné odvození z každéhoUᵢpro odpovídajícíVᵢ.
-
- Jinak se nedělají žádné závěry.
12.6.3.11 Odvození dolní hranice
Odvození dolní mez z typu Una typ V je provedeno takto:
- Pokud je
Vjedním z neopravenýchXᵢ,Use přidá do sady dolních mezí proXᵢ. - V opačném případě pokud je
VtypV₁?aUje typU₁?pak se odvozuje dolní mez zU₁doV₁. - V opačném případě se sady
U₁...UₑaV₁...Vₑurčují ověřením, zda se vztahuje některý z následujících případů:-
Vje typ poleV₁[...]aUje typ poleU₁[...]stejného pořadí. -
Vje jedním zIEnumerable<V₁>,ICollection<V₁>,IReadOnlyList<V₁>>,IReadOnlyCollection<V₁>neboIList<V₁>aUje jednorozměrný typ poleU₁[] -
Vje vytvořenýclass,struct,interfacenebodelegatetypC<V₁...Vₑ>a existuje jedinečný typC<U₁...Uₑ>tak, abyU(nebo pokudUje typparameter, jeho efektivní základní třída nebo jakýkoli člen jeho efektivní sady rozhraní) je identický,inheritsz (přímo nebo nepřímo) nebo implementuje (přímo nebo nepřímo)C<U₁...Uₑ>. - (Omezení "jedinečnosti" znamená, že v rozhraní
C<T>{} class U: C<X>, C<Y>{}, při odvozování zUdoC<T>, neprobíhá žádné odvozování, protožeU₁může býtXneboY.)
Pokud se některý z těchto případů použije, provede se odvozování z každéhoUᵢna odpovídajícíVᵢnásledujícím způsobem: - Pokud
Uᵢnení známo, že se jedná o referenční typ, provede se přesný závěr. - Jinak, pokud je
Utyp pole, provede se odvození dolní meze . - Jinak pokud je
VC<V₁...Vₑ>, odvozování závisí na parametru typui-thC:- Pokud je kovariantní, provede se odvození spodní meze .
- Pokud je kontravariantní, je provedeno odvození horní hranice .
- Pokud je invariantní, provede se přesný závěr.
-
- Jinak se nedělají žádné závěry.
12.6.3.12 Odvození horní hranice
Horní mez vyplývající z typu Una typ V je stanovena následujícím způsobem:
- Pokud je
Vjedním z neopravenýchXᵢ, přidá seUdo sady horních hranic proXᵢ. - V opačném případě se sady
V₁...VₑaU₁...Uₑurčují ověřením, zda se vztahuje některý z následujících případů:-
Uje typ poleU₁[...]aVje typ poleV₁[...]stejného pořadí. -
Uje jedním zIEnumerable<Uₑ>,ICollection<Uₑ>,IReadOnlyList<Uₑ>,IReadOnlyCollection<Uₑ>neboIList<Uₑ>aVje jednorozměrný typ poleVₑ[] -
Uje typU1?aVje typV1? -
Uje vytvořený typ třídy, struktury, rozhraní nebo delegátaC<U₁...Uₑ>aVje typclass, struct, interfacenebodelegate, který jeidenticalna,inheritsz (přímo nebo nepřímo), nebo implementuje (přímo nebo nepřímo) jedinečný typC<V₁...Vₑ> - (Omezení "jedinečnost" znamená, že vzhledem k
C<T>{} class V<Z>: C<X<Z>>, C<Y<Z>>{}rozhraní se při odvozování zC<U₁>doV<Q>nevyvozuje žádná odvození . Odvozování se neprovozuje zU₁doX<Q>neboY<Q>.)
Pokud se některý z těchto případů použije, provede se odvozování z každéhoUᵢna odpovídajícíVᵢnásledujícím způsobem: - Pokud
Uᵢnení známo, že se jedná o referenční typ, provede se přesný závěr. - Jinak, pokud je
Vtyp pole, vytvoří se odvozování horní hranice pro . - Jinak pokud je
UC<U₁...Uₑ>, odvozování závisí na parametru typui-thC:- Pokud je kovariantní, provede se odvození horní hranice .
- Pokud je kontravariantní, provádí se odvození dolní hranice .
- Pokud je invariantní, provede se přesný závěr.
-
- Jinak se nedělají žádné závěry.
12.6.3.13 Oprava
neuzamčená typová proměnnáXᵢ se sadou hranic je uzamčená následujícím způsobem:
- Sada kandidátských typů
Uₑpočíná jako soubor všech typů v rámci hranic proXᵢ. - Každá mez
Xᵢje postupně zkoumána: Pro každou přesnou mez UXᵢjsou z kandidátské sady odebrány všechny typyUₑ, které nejsou identické sU. Pro každou dolní mezUzXᵢjsou z kandidátské sady odebrány všechny typyUₑ, ke kterým není implicitní převod zU. Pro každou horní mez UXᵢjsou z kandidátské sady odebrány všechny typyUₑ, ze kterých neexistuje implicitní převod naU. - Pokud mezi zbývajícími kandidátskými typy
Uₑexistuje jedinečný typV, na který existuje implicitní převod ze všech ostatních kandidátských typů,Xᵢje pevně nastaven naV. - V opačném případě se odvození typu nezdaří.
12.6.3.14 Odvozený návratový typ
Odvozený návratový typ anonymní funkce F se používá při odvozování typu a rozlišení přetížení. Odvozený návratový typ lze určit pouze pro anonymní funkci, kde jsou známy všechny typy parametrů, a to buď proto, že jsou explicitně zadány, poskytnuty prostřednictvím převodu anonymní funkce, nebo odvozeny při odvozování typu během vyhodnocování volání obklopující obecné metody.
účinný návratový typ je odvozen a určen následujícím způsobem:
- Pokud je tělo
Fvýrazem , který má typ, odvozený efektivní návratový typFje typ tohoto výrazu. - Pokud je tělo
Fbloku a sada výrazů v příkazech blokureturnmá nejlepší společný typT(§12.6.3.16), je odvozený účinný návratovýFTtyp . - V opačném případě nelze efektivní návratový typ odvodit pro
F.
odvozený návratový typ je určen takto:
- Pokud
Fje asynchronní a těloFje buď výraz klasifikovaný jako nic (§12.2), nebo blok, kde žádnéreturnpříkazy nemají výrazy, odvozený návratový typ je«TaskType»(§15.14.1). - Pokud
Fje asynchronní a má odvozený účinný návratový typT, odvozený návratový typ je«TaskType»<T>»(§15.14.1). - Pokud
Fnení asynchronní a má odvozený účinný návratový typT, odvozený návratový typ jeT. - V opačném případě nelze návratový typ odvodit pro
F.
Příklad: Jako příklad odvození typu zahrnujícího anonymní funkce zvažte metodu rozšíření
Selectdeklarovanou ve tříděSystem.Linq.Enumerable:namespace System.Linq { public static class Enumerable { public static IEnumerable<TResult> Select<TSource,TResult>( this IEnumerable<TSource> source, Func<TSource,TResult> selector) { foreach (TSource element in source) { yield return selector(element); } } } }Za předpokladu, že obor názvů
System.Linqbyl importován pomocí direktivyusing namespacea je dána třídaCustomers vlastnostíNametypustring, lze použít metoduSelectk výběru názvů seznamu zákazníků.List<Customer> customers = GetCustomerList(); IEnumerable<string> names = customers.Select(c => c.Name);Vyvolání metody rozšíření (§12.8.10.3)
Selectse zpracuje přepsáním na vyvolání statické metody.IEnumerable<string> names = Enumerable.Select(customers, c => c.Name);Vzhledem k tomu, že argumenty typu nebyly explicitně zadány, je odvození typu použito k odvození argumentů typu. Za prvé, argument customers souvisí se zdrojovým parametrem, odvozuje
TSourcebýtCustomer. Poté, pomocí výše popsaného procesu odvozování typu anonymní funkce, jecpřiřazen typCustomer, a výrazc.Namesouvisí s návratovým typem parametru selektoru, přičemž se odvozuje, žeTResultjestring. Vyvolání je tedy rovnocennéSequence.Select<Customer,string>(customers, (Customer c) => c.Name)a výsledek je typu
IEnumerable<string>.Následující příklad ukazuje, jak odvození typu anonymních funkcí umožňuje "proudění" informací o typech mezi argumenty při volání obecné metody. Při použití následující metody a vyvolání:
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); } }odvození typu pro vyvolání pokračuje následujícím způsobem: Nejprve se argument "1:15:30" vztahuje k hodnotovému parametru, odvozuje se, že
Xjestring. Parametr první anonymní funkce,s, má odvozený typstringa výrazTimeSpan.Parse(s)souvisí s návratovým typemf1, odvozujeYjakoSystem.TimeSpan. Nakonec, parametr druhé anonymní funkce,t, má odvozený typSystem.TimeSpan, a výrazt.TotalHourssouvisí s návratovým typemf2, a odvozuje se, žeZjedouble. Výsledkem vyvolání je tedy typdouble.koncového příkladu
12.6.3.15 Odvození typu pro převod skupin metod
Podobně jako volání obecných metod se použije také odvození typu, pokud je skupina metod M obsahující obecnou metodu převedena na daný typ delegáta D (§10.8). Daná metoda
Tₑ M<X₁...Xᵥ>(T₁ x₁ ... Tₑ xₑ)
a skupina metod M přiřazená k typu delegáta D má úkol odvození typu najít argumenty typu S₁...Sᵥ tak, aby výraz:
M<S₁...Sᵥ>
se stává kompatibilním (§21.2) s D.
Na rozdíl od algoritmu odvození typu pro volání obecných metod v tomto případě existují pouze argumenty jako typy, ale žádné argumenty jako výrazy. Konkrétně neexistují žádné anonymní funkce, a proto není nutné provádět více fází odvozování.
Místo toho jsou všechny Xᵢ považovány za nepřipravenéa odvozování z každý typ argumentu UₑD odpovídající typ parametru TₑM. Pokud pro některý z Xᵢ nebyly nalezeny žádné hranice, odvození typu selže. V opačném případě jsou všechny Xᵢpřiřazeny k odpovídajícím Sᵢ, které jsou výsledkem odvozování typu.
12.6.3.16 Nalezení nejlepšího společného typu sady výrazů
V některých případech je potřeba odvodit běžný typ pro sadu výrazů. Zejména typy elementů implicitně zadaných polí a návratové typy anonymních funkcí s blokem těla jsou nalezeny tímto způsobem.
Nejlepší společný typ sady výrazů E₁...Eᵥ je určen následujícím způsobem:
- Zavádí se nová nefixovaná proměnná typu
X. - Pro každý výraz
Eise z něj provádí odvození výstupního typu (§12.6.3.8) doX. -
X(§12.6.3.13), je-li to možné, a výsledný typ je nejlepším běžným typem. - Jinak se odvození nezdaří.
Poznámka: Intuitivně toto odvozování odpovídá volání metody
void M<X>(X x₁ ... X xᵥ)sEᵢjako argumenty a odvozeníX. koncová poznámka
12.6.4 Rozlišení přetížení
12.6.4.1 Obecné
Řešení přetížení je mechanismus vazby pro výběr nejlepší funkce k vyvolání na základě zadaného seznamu argumentů a sady kandidátských členů funkcí. Rozlišení přetížení vybere člena funkce, který se má vyvolat v následujících jedinečných kontextech v jazyce C#:
- Vyvolání metody uvedené v rámci invocation_expression (§12.8.10).
- Vyvolání konstruktoru instance pojmenovaného v object_creation_expression (§12.8.17.2).
- Vyvolání přístupového objektu indexeru prostřednictvím element_access (§12.8.12).
- Vyvolání předdefinovaného nebo uživatelem definovaného operátoru odkazovaného ve výrazu (§12.4.4 a §12.4.5).
Každý z těchto kontextů definuje sadu členů kandidátské funkce a seznam argumentů vlastním jedinečným způsobem. Například sada kandidátů pro vyvolání metody nezahrnuje metody označené jako překryté (§12.5), a metody v základní třídě nejsou kandidáty, pokud je v odvozené třídě použitelná jakákoli metoda (§12.8.10.2).
Po zjištění členů kandidátské funkce a seznamu argumentů je výběr nejlepšího člena funkce ve všech případech stejný:
- Zaprvé je sada členů kandidátské funkce omezena na členy funkce, které se vztahují k danému seznamu argumentů (§12.6.4.2). Pokud je tato omezená sada prázdná, dojde k chybě v době kompilace.
- Pak se nachází nejlepší člen funkce ze sady použitelných členů kandidátské funkce. Pokud sada obsahuje pouze jeden člen funkce, je tento člen funkce nejlepším členem funkce. V opačném případě je nejlepším členem funkce jeden člen funkce, který je lepší než všechny ostatní členy funkce s ohledem na daný seznam argumentů za předpokladu, že každý člen funkce je porovnán se všemi ostatními členy funkce pomocí pravidel v §12.6.4.3. Pokud neexistuje pouze jeden člen funkce, který je lepší než všechny ostatní členy funkce, je volání člena funkce nejednoznačné a dojde k chybě vazby.
Následující pododstavce definují přesné významy těchto termínů použitelný člen funkce a lepší člen funkce.
12.6.4.2 Použitelný člen funkce
Člen funkce se označuje jako použitelný člen funkce s ohledem na seznam argumentů A, pokud jsou splněny všechny následující podmínky:
- Každý argument v
Aodpovídá parametru v deklaraci členu funkce, jak je popsáno v §12.6.2.2, maximálně jeden argument odpovídá každému parametru a jakýkoli parametr, ke kterému žádný argument neodpovídá, je volitelný parametr. - Pro každý argument v
Aje režim předávání parametrů argumentu identický s režimem předávání parametrů odpovídajícího parametru a- pro parametr hodnoty nebo pole parametrů existuje implicitní převod (§10.2) z výrazu argumentu na typ odpovídajícího parametru, nebo
- pro odkaz nebo výstupní parametr existuje převod identity mezi typem výrazu argumentu (pokud existuje) a typem odpovídajícího parametru nebo
- pro vstupní parametr, pokud má odpovídající argument modifikátor
in, existuje převod identity mezi typem výrazu argumentu (pokud existuje) a typem odpovídajícího parametru, nebo - pro vstupní parametr, pokud odpovídající argument vynechá
inmodifikátor, implicitní převod (§10.2) existuje z výrazu argumentu na typ odpovídajícího parametru.
Pro člen funkce, který obsahuje pole parametrů, pokud je člen funkce použitelný podle výše uvedených pravidel, je uvedeno, že je použitelné v jeho normálním formátu. Pokud člen funkce, který obsahuje pole parametrů, není použitelný v jeho normální podobě, může být místo toho člen funkce použitelný ve svém rozšířeném formuláři:
- Rozšířená forma je vytvořena nahrazením pole parametrů v deklaraci člena funkce na nula nebo více hodnotových parametrů typu prvku pole parametrů tak, aby počet argumentů v seznamu argumentů
Aodpovídal celkovému počtu parametrů. PokudAobsahuje méně argumentů než počet pevných parametrů v deklaraci členu funkce, nelze zvětšovanou formu členu funkce vytvořit, a proto ji nelze použít. - V opačném případě je rozbalený formulář použitelný, pokud pro každý argument v
A, platí jedna z následujících hodnot:- režim předávání parametrů argumentu je identický s režimem předávání parametrů odpovídajícího parametru a:
- pro parametr pevné hodnoty nebo parametr hodnoty vytvořený rozšířením existuje implicitní převod (§10.2) z výrazu argumentu na typ odpovídajícího parametru; nebo
- pro parametr předávaný odkazem je typ výrazu argumentu shodný s typem odpovídajícího parametru.
- Režim předávání argumentu je hodnota, režim předávání odpovídajícího parametru je vstup a existuje implicitní převod (§10.2) z výrazu argumentu na typ odpovídajícího parametru.
- režim předávání parametrů argumentu je identický s režimem předávání parametrů odpovídajícího parametru a:
Pokud implicitní převod typu argumentu na typ parametru vstupního parametru je dynamický implicitní převod (§10.2.10), výsledky nejsou definovány.
Příklad: S ohledem na následující deklarace a volání metody:
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 }koncového příkladu
- Statická metoda je použitelná pouze v případě, že skupina metod je výsledkem simple_name nebo member_access prostřednictvím typu.
- Metoda instance je použitelná pouze v případě, že skupina metod je výsledkem simple_name, member_access prostřednictvím proměnné nebo hodnoty nebo base_access.
- Pokud je skupina metod výsledkem simple_name, lze metodu instance použít pouze v případě, že
thispřístup je povolen §12.8.14.
- Pokud je skupina metod výsledkem simple_name, lze metodu instance použít pouze v případě, že
- Pokud je skupina metod výsledkem member_access, který může být prostřednictvím instance nebo typu, jak je popsáno v §12.8.7.2, lze použít jak metody instance, tak statické metody.
- Obecná metoda, jejíž argumenty typu (explicitně zadané nebo odvozené) nesplňují všechna jejich omezení, není použitelná.
- V kontextu převodu skupiny metod existuje převod identity (§10.2.2) nebo implicitní odkazový převod (§10.2.8) z metody návratového typu do návratového typu delegáta. V opačném případě není metoda kandidáta použitelná.
12.6.4.3 Lepší člen funkce
Pro účely určení lepšího člena funkce je vytvořen zjednodušený seznam argumentů A obsahující pouze samotné výrazy argumentů v pořadí, v jakém se zobrazují v původním seznamu argumentů, a vynechává jakékoli argumenty out nebo ref.
Seznamy parametrů pro každý z členů kandidátské funkce jsou sestaveny následujícím způsobem:
- Rozbalený formulář se používá, pokud byl člen funkce použitelný pouze v rozšířeném formuláři.
- Volitelné parametry bez odpovídajících argumentů se odeberou ze seznamu parametrů.
- Odkazy a výstupní parametry se odeberou ze seznamu parametrů.
- Parametry se přeuspořádají tak, aby se vyskytovaly na stejné pozici jako odpovídající argument v seznamu argumentů.
Při A seznamu argumentů se sadou výrazů argumentů {E₁, E₂, ..., Eᵥ} a dvěma platnými členy funkce Mᵥ a Mₓ s typy parametrů {P₁, P₂, ..., Pᵥ} a {Q₁, Q₂, ..., Qᵥ}, Mᵥ je definován jako lepší člen funkce než Mₓ pokud
- pro každý argument není implicitní převod z
EᵥnaQᵥlepší než implicitní převod zEᵥnaPᵥa - pro alespoň jeden argument je převod z
EᵥnaPᵥlepší než převod zEᵥnaQᵥ.
Pokud jsou sekvence typu parametru {P₁, P₂, ..., Pᵥ} a {Q₁, Q₂, ..., Qᵥ} ekvivalentní (tj. každá Pᵢ má převod identity na odpovídající Qᵢ), použijí se následující pravidla pro řešení konfliktů, aby bylo možné určit lepšího člena funkce.
- Pokud
Mᵢje ne generická metoda aMₑje obecná metoda,Mᵢje lepší nežMₑ. - V opačném případě, pokud
Mᵢje použitelná v normální podobě aMₑmá pole parametrů a je použitelné pouze v rozšířené podobě, pakMᵢje lepší nežMₑ. - V opačném případě, pokud obě metody mají pole parametrů a jsou použitelné pouze ve svých rozbalených formách, a pokud pole parametrů
Mᵢmá méně prvků než pole parametrůMₑ, pakMᵢje lepší nežMₑ. - V opačném případě, pokud
Mᵥmá konkrétnější typy parametrů nežMₓ,Mᵥje lepší nežMₓ. Nechť{R1, R2, ..., Rn}a{S1, S2, ..., Sn}představují nepotřebné a nevyexpandované typy parametrůMᵥaMₓ.Mᵥjsou typy parametrů specifičtější nežMₓ, pokud u každého parametruRxnení méně specifický nežSx, a u alespoň jednoho parametru jeRxspecifičtější nežSx:- Parametr typu je méně specifický než parametr bez typu.
- Rekurzivně je konstruovaný typ konkrétnější než jiný konstruovaný typ (se stejným počtem argumentů typu), pokud je alespoň jeden argument typu konkrétnější a argument typu není menší než odpovídající argument typu v druhém.
- Typ pole je konkrétnější než jiný typ pole (se stejným počtem dimenzí), pokud je typ prvku prvního konkrétnější než typ prvku druhého.
- Jinak, pokud je jeden člen nezvednutým operátorem a druhý je povýšeným operátorem, nezvednutý operátor je lepší.
- Pokud nebyl nalezen žádný člen funkce, aby byl lepší a všechny parametry
Mᵥmají odpovídající argument, zatímco výchozí argumenty musí být nahrazeny alespoň jedním volitelným parametrem vMₓ, je lepšíMᵥnežMₓ. - Pokud pro alespoň jeden parametr
Mᵥpoužívá lepší volbu předávání parametrů (§12.6.4.4) než odpovídající parametr vMₓa žádný z parametrů vMₓnepoužívá lepší volbu předávání parametrů nežMᵥ, pak jeMᵥlepší nežMₓ. - Jinak není žádný člen funkce lepší.
12.6.4.4 Lepší režim předávání parametrů
Je povoleno mít odpovídající parametry ve dvou přetížených metodách se liší pouze režimem předávání parametrů za předpokladu, že jeden z těchto dvou parametrů má režim předávání hodnot, jak je znázorněno níže:
public static void M1(int p1) { ... }
public static void M1(in int p1) { ... }
Vzhledem k int i = 10;podle §12.6.4.2mohou být volání M1(i) a M1(i + 5) výsledkem obou přetížení. V takových případech je režim předávání parametrů hodnotou lepší volbou způsobu předávání parametrů.
Poznámka: Pro argumenty vstupu, výstupu nebo předávání odkazů neexistuje žádná taková volba, protože tyto argumenty odpovídají pouze stejným režimům předávání parametrů. koncová poznámka
12.6.4.5 Lepší převod z výrazu
Vzhledem k implicitnímu převodu C₁, který převádí z výrazu E na typ T₁, a implicitnímu převodu C₂, který převádí z výrazu E na typ T₂, C₁ je lepším převodem než C₂, pokud platí jedna z následujících možností:
-
Epřesně odpovídáT₁aEpřesně neodpovídáT₂(§12.6.4.6) -
Epřesně odpovídá buď oběma, nebo žádnému zT₁aT₂, aT₁je lepším cílem konverze nežT₂(§12.6.4.7) -
Eje skupina metod (§12.2),T₁je kompatibilní (§21.4) s jedinou nejlepší metodou ze skupiny metod pro převodC₁aT₂není kompatibilní s jedinou nejlepší metodou ze skupiny metod pro převod.C₂
12.6.4.6 Přesně odpovídající výraz
Při zadání výrazu E a typu TEpřesně odpovídáT, pokud platí některá z následujících možností:
-
Emá typSa převod identity existuje zSnaT -
Eje anonymní funkce,Tje buď typ delegátaDnebo typ stromu výrazůExpression<D>a platí jedna z následujících podmínek:- Odvozený návratový typ
XexistujeEv kontextu seznamuDparametrů (§12.6.3.13) a převod identity existuje zXnávratového typuD -
Ejeasynclambda bez návratové hodnoty aDmá návratový typ, kterým je negenerický«TaskType» - Buď
Eje ne-asynchronní aDmá návratový typY, neboEje asynchronní aDmá návratový typ«TaskType»<Y>(§15.14.1) a platí jedna z následujících možností:- Tělo
Eje výraz, který přesně odpovídajícíY - Tělo
Eje blok, ve kterém každý návratový příkaz vrátí výraz, který přesně odpovídáY
- Tělo
- Odvozený návratový typ
Lepší cílová hodnota převodu
Vzhledem k dvěma typům T₁ a T₂je T₁lepším cílovým převodu než T₂, pokud platí některá z následujících možností:
- Implicitní převod z
T₁naT₂existuje a neexistuje žádný implicitní převod zT₂naT₁. -
T₁je«TaskType»<S₁>(§15.14.1),T₂je«TaskType»<S₂>aS₁je lepším cílem převodu nežS₂ -
T₁je«TaskType»<S₁>(§15.14.1),T₂je«TaskType»<S₂>aT₁je specializovanější nežT₂ -
T₁jeS₁neboS₁?, kdeS₁je celočíselný typ aT₂jeS₂neboS₂?, kdeS₂je celočíselný typ bez znaménka. Konkrétně:-
S₁jesbyteaS₂jebyte,ushort,uintneboulong -
S₁jeshortaS₂jeushort,uintneboulong -
S₁jeintaS₂jeuintneboulong -
S₁jelongaS₂jeulong
-
12.6.4.8 Přetížení v obecných třídách
Poznámka: Zatímco podpisy deklarované musí být jedinečné (§8.6), je možné, že nahrazení argumentů typu vede k identickým podpisům. V takové situaci řešení přetížení vybere tu nejvíce specifickou možnost (§12.6.4.3) z původních podpisů (před nahrazením argumentů typu), pokud existuje; v opačném případě nahlásí chybu. koncová poznámka
Příklad: Následující příklady ukazují přetížení, která jsou platná a neplatná podle tohoto pravidla:
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); }koncového příkladu
12.6.5 Kontrola času kompilace dynamického vyvolání členů
I když se rozlišení přetížení dynamicky vázané operace provádí za běhu, je někdy možné v době kompilace zjistit seznam členů funkce, ze kterých bude vybráno přetížení:
- Pro vyvolání delegáta (§12.8.10.4) je seznam jediným členem funkce se stejným seznamem parametrů jako u typu delegáta při vyvolání.
- Pro vyvolání metody (§12.8.10.2) u typu nebo hodnoty, jejíž statický typ není dynamický, je sada přístupných metod ve skupině metod známa v době kompilace.
- Výraz vytvoření objektu (§12.8.17.2) sada přístupných konstruktorů v typu je známa v době kompilace.
- Pro přístup indexeru (§12.8.12.4) je sada přístupných indexerů v přijímači známa v době kompilace.
V těchto případech se provádí omezená kontrola během kompilace u každého člena v známé sadě členů funkce, aby bylo zjištěno, zda může být s jistotou známo, že nikdy nebude vyvolán během běhu programu. Pro každý člen funkce F upravený parametr a seznam argumentů se vytvoří:
- Za prvé, pokud
Fje obecná metoda a byly zadány argumenty typu, pak jsou nahrazeny parametry typu v seznamu parametrů. Pokud však nebyly zadány argumenty typu, nedojde k žádné takové náhradě. - Pak se vynechá jakýkoli parametr, jehož typ je otevřený (tj. obsahuje parametr typu; viz §8.4.3) společně s přidruženými argumenty.
Aby F prošel kontrolou, musí platit všechny následující podmínky:
- Upravený seznam parametrů pro
Fse vztahuje na seznam upravených argumentů z hlediska §12.6.4.2. - Všechny vytvořené typy v seznamu upravených parametrů splňují svá omezení (§8.4.5).
- Pokud byly parametry typu
Fv předchozím kroku nahrazeny, jsou jejich omezení splněna. - Je-li
Fstatickou metodou, skupina metod nesmí pocházet z přístupu člena , jehož příjemce je při kompilaci znám jako proměnná nebo hodnota. - Je-li
Fmetodou instance, skupina metod nesmí vzniknout z member_access, jejíchž příjemce je v době kompilace známý jako typ.
Pokud žádný kandidát tento test neprojde, dojde k chybě v době kompilace.
12.6.6 Vyvolání člena funkce
12.6.6.1 Obecné
Tato dílčí část popisuje proces, který probíhá při běhu programu k vyvolání konkrétního člena funkce. Předpokládá se, že proces vazby času již určil konkrétního člena, který má být vyvolán, pravděpodobně rozlišením přetížení na množinu funkčních členů.
Pro účely popisu procesu vyvolání jsou členy funkce rozdělené do dvou kategorií:
- Statické funkční členy. Jedná se o statické metody, přístupové objekty statických vlastností a uživatelem definované operátory. Členové statické funkce jsou vždy ne virtualní.
- Členy funkce instance Jedná se o metody instance, konstruktory instancí, přístupové objekty vlastností instance a přístupové objekty indexeru. Členové instančních funkcí jsou buď nevirtuální, nebo virtuální a jsou vždy voláni na konkrétní instanci. Instance je vypočítána výrazem instance a stává se přístupnou v rámci člena funkce jako
this(§12.8.14). U konstruktoru instance se výraz instance použije jako nově přidělený objekt.
Zpracování běhu volání člena funkce se skládá z následujících kroků, kde M je členem funkce a pokud M je členem instance, E je výraz instance:
- Pokud
Mje členem statické funkce:- Seznam argumentů je vyhodnocen tak, jak je popsáno v §12.6.2.
-
Mje vyvolána.
- V opačném případě, pokud je typ
Etypu hodnotyVaMje deklarován nebo přepsán vV:-
Ese vyhodnotí. Pokud toto vyhodnocení způsobí výjimku, neprojdou žádné další kroky. Pro konstruktor instance se toto vyhodnocení skládá z přidělení úložiště (obvykle ze zásobníku spouštění) pro nový objekt. V tomto případě seEklasifikuje jako proměnná. - Pokud
Enení klasifikovaný jako proměnná neboVnení typ struktury jen pro čtení (§16.2.2) aMnení členem funkce readonly (§16.4.12) aEje jedním z:- vstupní parametr (§ 15.6.2.3.2), nebo
-
readonlypole (§ 15.5.3), nebo - odkazová
readonlyproměnná nebo návrat (§9.7), vytvoří se dočasná místní proměnnáEtypu a hodnotaEje k této proměnné přiřazena.Eje pak překlasifikováno jako odkaz na tuto dočasnou místní proměnnou. Dočasná proměnná je přístupná jakothisv rámciM, ale ne jiným způsobem. Proto pouze v případě, žeElze zapsat, je pro volajícího možné sledovat změny, kteréMprovedethis.
- Seznam argumentů je vyhodnocen tak, jak je popsáno v §12.6.2.
-
Mje vyvolána. Proměnná, na kterou se odkazujeE, se stane proměnnou, na kterou se odkazujethis.
-
- Jinak:
-
Ese vyhodnotí. Pokud toto vyhodnocení způsobí výjimku, neprojdou žádné další kroky. - Seznam argumentů je vyhodnocen tak, jak je popsáno v §12.6.2.
- Pokud je typ
Evalue_type, provede se převod boxingu (§10.2.9) pro převodEna class_typeaEse považuje za class_type v následujících krocích. Pokud je value_typeenum_type, pak je class_typeSystem.Enum;, jinak jeSystem.ValueType. - Hodnota
Eje kontrolována tak, aby byla platná. Pokud je hodnotaEnull, vyvolá seSystem.NullReferenceExceptiona neprojdou žádné další kroky. - Je určena implementace člena funkce, která se má vyvolat:
- Pokud je typ vazby
Erozhraní, člen funkce, který se má vyvolat, je implementaceMposkytované typem běhu instance odkazovanéE. Tento člen funkce je určen použitím pravidel mapování rozhraní (§19.6.5) k určení prováděníMposkytnutého typem za běhu instance, na kterouEodkazuje . - V opačném případě, pokud
Mje členem virtuální funkce, člen funkce, který se má vyvolat, je implementaceMposkytované typem běhu instance odkazovanéE. Tento člen funkce je určen použitím pravidel pro určení nejvíce odvozené implementace (§15.6.4)Ms ohledem na typ běhu instance odkazovanéE. - V opačném případě je
Mne virtuálním členem funkce a člen funkce, který se má vyvolat, jeMsám.
- Pokud je typ vazby
- Vyvolá se implementace člena funkce určená v kroku výše. Objekt, na který odkazuje
E, se stane objektem, na který odkazuje toto.
-
Poznámka:§12.2 klasifikuje přístup k vlastnosti jako vyvolání odpovídajícího člena funkce, buď přístupového objektu
get, nebo přístupového objektuset. Výše uvedený postup následuje pro vyvolání daného přístupového objektu. koncová poznámka
Výsledkem vyvolání konstruktoru instance (§12.8.17.2) je vytvořená hodnota. Výsledkem vyvolání jakéhokoli jiného členu funkce je hodnota, pokud existuje, vrácená (§13.10.5) z jeho těla.
12.6.6.2 Vyvolání v krabicových instancích
Člen funkce implementovaný v value_type lze vyvolat prostřednictvím boxované instance tohoto value_type v následujících situacích:
- Je-li člen funkce přepsanou metodou zděděnou z typu class_type a je vyvolán prostřednictvím výrazu instance tohoto class_type.
Poznámka: class_type bude vždy jedním z
System.Object,System.ValueTypeneboSystem.Enum. koncová poznámka - Pokud je člen funkce implementací člena funkce rozhraní a je vyvolán prostřednictvím výrazu instance interface_type.
- Když je člen funkce vyvolán prostřednictvím delegáta.
V těchto situacích se instance typu box považuje za obsahující proměnnou typu value_type, a tato proměnná se stane proměnnou, na kterou se odkazuje v rámci tohoto vyvolání členské funkce.
Poznámka: Konkrétně to znamená, že když je v boxované instanci vyvolán člen funkce, je možné, aby člen funkce upravil hodnotu obsaženou v boxované instanci. koncová poznámka
12.7 Dekonstrukce
Dekonstrukce je proces, při kterém se výraz změní na řazenou kolekci jednotlivých výrazů. Dekonstrukce se používá, když je cílem jednoduchého přiřazení n-tice, k získání hodnot pro přiřazení ke každému prvku této n-tice.
Výraz E je dekonstruovaný výrazu řazené kolekce členů s n prvky následujícím způsobem:
- Pokud je
Evýraz n-tice snprvky, výsledkem dekonstrukce je výrazEsamotný. - Jinak, pokud má
Etyp n-tice(T1, ..., Tn)snprvky, pak seEvyhodnotí do dočasné proměnné__va výsledkem dekonstrukce je výraz(__v.Item1, ..., __v.Itemn). - Jinak pokud se výraz
E.Deconstruct(out var __v1, ..., out var __vn)přeloží v době kompilace na jedinečnou instanci nebo rozšiřující metodu, vyhodnotí se tento výraz a výsledkem dekonstrukce je výraz(__v1, ..., __vn). Taková metoda se označuje jako dekonstruktor. - V opačném případě nelze
Edekonstruovat.
Tady __v a __v1, ..., __vn odkazují na jinak neviditelné a nepřístupné dočasné proměnné.
Poznámka: Výraz typu
dynamicnelze dekonstruovat. koncová poznámka
12.8 Primární výrazy
12.8.1 Obecné
Primární výrazy zahrnují nejjednodušší formy výrazů.
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
;
Poznámka: Toto gramatické pravidlo není připraveno pro ANTLR, protože je součástí sady vzájemně se levostranných rekurzivních pravidel (
primary_expression,member_access,invocation_expression,element_access,post_increment_expression,post_decrement_expression,null_forgiving_expression,pointer_member_access, apointer_element_access), které ANTLR nezpracuje. Standardní techniky lze použít k transformaci gramatiky, aby se odebrala vzájemná rekurze vlevo. Tento standard to neudělá, protože ne všechny strategie analýzy ji vyžadují (např. analyzátor LALR by to neudělal) a tím by obfukovala strukturu a popis. koncová poznámka
pointer_member_access (§24.6.3) a pointer_element_access (§24.6.4) jsou k dispozici pouze v nebezpečném kódu (§24).
12.8.2 Literály
primární_výraz, který se skládá z literálu (§6.4.5), je klasifikován jako hodnota.
12.8.3 Interpolované řetězcové výrazy
interpolated_string_expression se skládá z $, $@nebo @$, následovaný textem uvnitř " znaků. V citovaném textu je nula nebo více interpolací vymezených znaky { a }, z nichž každý zahrnuje výraz a volitelné specifikace formátování.
Interpolované řetězcové výrazy mají dvě formy; regular (interpolated_regular_string_expression) a doslovné (interpolated_verbatim_string_expression); které jsou lexicky podobné, ale liší se od dvou forem řetězcových literá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
: '}}'
;
Šest výše definovaných lexikálních pravidel je kontextově citlivých následujícím způsobem:
| pravidlo | kontextové požadavky |
|---|---|
| Interpolated_Regular_String_Mid | Rozpoznáno pouze po Interpolated_Regular_String_Start, mezi jakýmikoli následujícími interpolacemi a před odpovídajícími Interpolated_Regular_String_End. |
| Pravidelný_Interpolace_Formát | Rozpoznává se pouze v rámci regular_interpolation a pokud počáteční dvojtečka (:) není vnořená do žádného typu závorky (kulaté, složené/hranaté). |
| Interpolated_Regular_String_End | Rozpoznává se pouze po Interpolated_Regular_String_Start a pouze v případě, že jakékoli mezisahující tokeny jsou buď Interpolated_Regular_String_Mids, nebo tokeny, které mohou být součástí regular_interpolations, včetně tokenů pro všechny interpolated_regular_string_expressionobsažené v těchto interpolacích. |
| Interpolated_Verbatim_String_MidVerbatim_Interpolation_Format Interpolated_Verbatim_String_End | Uznání těchto tří pravidel je stejné jako u odpovídajících pravidel výše s tím, že každé zmíněné pravidelné gramatické pravidlo je nahrazeno příslušným doslovným pravidlem. |
Poznámka: Výše uvedená pravidla jsou kontextově citlivá, protože se jejich definice překrývají s jinými tokeny v jazyce. koncová poznámka
Poznámka: Výše uvedená gramatika není připravená pro ANTLR kvůli kontextově citlivým lexikálním pravidlům. Stejně jako u jiných generátorů lexerů ANTLR podporuje kontextová lexikální pravidla, například použití lexikálních režimů, ale jedná se o podrobnosti implementace, a proto není součástí této specifikace. koncová poznámka
interpolated_string_expression se klasifikuje jako hodnota. Pokud je okamžitě převeden na System.IFormattable nebo System.FormattableString s implicitní interpolovanou konverzí řetězce (§10.2.5), má interpolovaný řetězcový výraz tento typ. V opačném případě má typ string.
Poznámka: Rozdíly mezi možnými typy interpolated_string_expression mohou být určeny z dokumentace pro
System.String(§C.2) aSystem.FormattableString(§C.3). koncová poznámka
Význam interpolace, regular_interpolation i verbatim_interpolation, je formátovat hodnotu výrazu jako string buď podle formátu určeného Regular_Interpolation_Format nebo Verbatim_Interpolation_Format, nebo podle výchozího formátu pro typ výrazu . Formátovaný řetězec je následně upraven pomocí interpolation_minimum_width, pokud existuje, aby vznikl konečný string, který bude interpolován do interpolated_string_expression.
Poznámka: Jak je určen výchozí formát pro typ, je podrobně popsán v dokumentaci pro
System.String(§C.2) aSystem.FormattableString(§C.3). Popis standardních formátů, které jsou identické pro Regular_Interpolation_Format a Verbatim_Interpolation_Format, lze nalézt v dokumentaci proSystem.IFormattable(§C.4) a v jiných typech standardní knihovny (§C). koncová poznámka
V interpolation_minimum_width má constant_expression implicitní převod na int. Šířka pole by měla být absolutní hodnotou tohoto výrazu constant_expression a zarovnání by mělo být určeno znaménkem (kladným nebo záporným) hodnoty tohoto výrazu constant_expression:
- Pokud je hodnota šířky pole menší nebo rovna délce formátovaného řetězce, formátovaný řetězec se nezmění.
- V opačném případě je formátovaný řetězec vycpaný prázdnými znaky, aby jeho délka byla rovna šířce pole:
- Pokud je zarovnání kladné, je formátovaný řetězec zarovnaný doprava tím, že předsadí odsazení,
- V opačném případě je zarovnána doleva přidáním odsazení.
Celkový význam výrazu interpolated_string_expression, včetně výše uvedeného formátování a odsazení interpolací, je definován převodem výrazu na vyvolání metody: pokud je typ výrazu System.IFormattable nebo System.FormattableString, je tato metoda System.Runtime.CompilerServices.FormattableStringFactory.Create (§C.3), která vrátí hodnotu typu System.FormattableString; v opačném případě má typ string a metoda je string.Format (§C.2), která vrátí hodnotu typu string.
V obou případech se seznam argumentů volání skládá z řetězcového literálu formátu se specifikacemi formátu pro každou interpolaci a argumentem pro každý výraz odpovídající specifikacím formátu.
Řetězcový literál formátu je vytvořen následujícím způsobem, kde N je počet interpolací v interpolated_string_expression. Řetězcový literál formátu se skládá z pořadí:
- Znaky Interpolated_Regular_String_Start nebo Interpolated_Verbatim_String_Start
- Znaky Interpolated_Regular_String_Mid nebo Interpolated_Verbatim_String_Mid, pokud existují
- Poté pokud
N ≥ 1pro každé čísloIod0doN-1:- Specifikace zástupného symbolu:
- Levá složená závorka (
{) - Desítkové znázornění
I - Pokud má odpovídající regular_interpolation nebo verbatim_interpolationinterpolation_minimum_width, následuje čárka (
,) a desítková reprezentace hodnoty constant_expression. - Znaky Regular_Interpolation_Format nebo Verbatim_Interpolation_Format, pokud existují, odpovídajících regular_interpolation nebo verbatim_interpolation
- Pravá složená závorka (
})
- Levá složená závorka (
- Znaky Interpolated_Regular_String_Mid nebo Interpolated_Verbatim_String_Mid, které bezprostředně následují po odpovídající interpolaci, pokud nějaká existuje
- Specifikace zástupného symbolu:
- Nakonec se jedná o znaky Interpolated_Regular_String_End nebo Interpolated_Verbatim_String_End.
Následující argumenty jsou výrazy ze zadaných interpolací, pokud nějaké existují, v daném pořadí.
Pokud interpolated_string_expression obsahuje více interpolací, výrazy v těchto interpolacích se vyhodnocují v textovém pořadí zleva doprava.
Příklad :
Tento příklad používá následující funkce specifikace formátu:
- specifikace formátu
X, která formátuje celá čísla jako šestnáctková velká písmena, - výchozí formát pro
stringhodnotu je samotná hodnota. - kladné hodnoty zarovnání, které jsou zarovnané doprava v zadané minimální šířce pole,
- záporné hodnoty zarovnání, které jsou zarovnané doleva v zadané minimální šířce pole,
- definované konstanty pro interpolation_minimum_widtha
-
{{a}}jsou formátovány jako{a}.
Daný:
string text = "red";
int number = 14;
const int width = -4;
Potom:
| Interpolovaný řetězcový výraz |
ekvivalentní význam jako string |
hodnota |
|---|---|---|
$"{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" |
koncového příkladu
12.8.4 Jednoduché názvy
simple_name se skládá z identifikátoru, volitelně následovaného seznamem argumentů typu:
simple_name
: identifier type_argument_list?
;
simple_name je buď I formuláře, nebo I<A₁, ..., Aₑ>formuláře, kde I je jeden identifikátor a I<A₁, ..., Aₑ> je volitelná type_argument_list. Pokud není zadán žádný type_argument_list, považujte e za nulu.
simple_name se vyhodnotí a klasifikuje takto:
- Je-li
enula a simple_name se zobrazí v prostoru deklarace místní proměnné (§7.3), který přímo obsahuje místní proměnnou, parametr nebo konstantu s názvemI, pak simple_name odkazuje na tuto místní proměnnou, parametr nebo konstantu a je klasifikována jako proměnná nebo hodnota. - Pokud je
enula a simple_name se zobrazí v deklaraci obecné metody, ale mimo atributy jeho method_declarationa pokud tato deklarace obsahuje parametr typu s názvemI, pak simple_name odkazuje na tento parametr typu. - Jinak platí, že pro každý typ instance
T(§15.3.2), počínaje typem instance bezprostředně uzavřené deklarace typu a pokračováním s typem instance každé nadřazené třídy nebo deklarace struktury (pokud existuje):- Pokud je
enula a deklaraceTobsahuje parametr typu s názvemI, simple_name odkazuje na tento parametr typu. - V opačném případě, pokud vyhledávání členů (§12.5)
IvTs argumenty typuevytvoří shodu:- Pokud
Tje typ instance bezprostředně ohraničující typ třídy nebo struktury a vyhledávání identifikuje jednu nebo více metod, je výsledkem skupina metod s přidruženým výrazem instancethis. Pokud byl zadán seznam argumentů typu, používá se při volání obecné metody (§12.8.10.2). - Jinak pokud je
Ttypem instance bezprostředně ohraničujícího typu třídy nebo struktury, pokud vyhledávání identifikuje člena instance a pokud se odkaz vyskytuje v rámci bloku konstruktoru instance, metody instance nebo objektu instance (§12.2.1), výsledek je stejný jako přístup člena (§12.8.7) formulářethis.I. K tomu může dojít pouze v případě, žeeje nula. - V opačném případě je výsledek stejný jako přístup člena (§12.8.7) formuláře
T.IneboT.I<A₁, ..., Aₑ>.
- Pokud
- Pokud je
- V opačném případě, pro každý obor názvů
N, počínaje oborem názvů, ve kterém se objeví simple_name, pokračujeme s každým nadřazeným oborem názvů (pokud existuje), a končíme globálním oborem názvů, následující kroky jsou vyhodnocovány, dokud není nalezena entita:- Pokud je
enula aIje název oboru názvů vN, pak:- Pokud je umístění, kde dojde k simple_name, uzavřeno deklarací oboru názvů pro
Na deklarace oboru názvů obsahuje extern_alias_directive nebo using_alias_directive, které přidruží názevIk oboru názvů nebo typu, pak je simple_name nejednoznačný a dojde k chybě v době kompilace. - V opačném případě simple_name odkazuje na obor názvů pojmenovaný
IvN.
- Pokud je umístění, kde dojde k simple_name, uzavřeno deklarací oboru názvů pro
- Pokud
Nobsahuje přístupný typ s názvemIa parametry typue, pak:- Pokud je
enula a místo, kde se vyskytuje simple_name, je v rámci deklarace oboru názvů proN, která obsahuje extern_alias_directive nebo using_alias_directive, jenž přidružuje názevIk oboru názvů nebo typu, pak je simple_name nejednoznačný a dojde k chybě při kompilaci. - V opačném případě namespace_or_type_name odkazuje na typ vytvořený s danými argumenty typu.
- Pokud je
- V opačném případě, pokud je místo, kde se objeví simple_name, uzavřeno deklarací oboru názvů pro
N:- Pokud je
enula a deklarace oboru názvů obsahuje extern_alias_directive nebo using_alias_directive, které přidruží názevIk importovanému oboru názvů nebo typu, odkazuje simple_name na tento obor názvů nebo typ. - Jinak, pokud obory názvů importované prostřednictvím using_namespace_directivedeklarací oboru názvů obsahují přesně jeden typ s názvem
Ia s parametry typue, pak simple_name odkazuje na tento typ vytvořený s uvedenými argumenty typu. - Jinak, pokud obory názvů importované using_namespace_directive, deklarace oboru názvů, obsahují více než jeden typ s názvem
Ia parametry typue, pak je simple_name nejednoznačný a dojde k chybě při kompilaci.
- Pokud je
Poznámka: Tento krok přesně odpovídá odpovídajícímu kroku ve zpracování namespace_or_type_name (§7.8). koncová poznámka
- Pokud je
- Jinak je-li
enula aIje identifikátorem_, simple_name je jednoduchá zahození, což je forma deklarace výrazu (§12.19). - V opačném případě je simple_name nedefinovaný a dojde k chybě v době kompilace.
12.8.5 Výrazy v závorkách
výraz v závorkách se skládá z výrazu uzavřeného v závorkách.
parenthesized_expression
: '(' expression ')'
;
Výraz "uzavřený_v závorkách" se vyhodnotí tím, že se nejprve vyhodnotí výraz uvnitř závorek. Pokud výraz v závorkách označuje obor názvů nebo typ, dojde k chybě v době kompilace. Jinak je výsledek parenthesized_expression výsledkem vyhodnocení obsaženého výrazu .
12.8.6 Výrazy n-tic
tuple_expression představuje řazenou kolekci členů a skládá se ze dvou nebo více čárkami oddělených a volitelně pojmenovaných výrazů uzavřených v závorkách. deconstruction_expression je zkratka pro řazenou kolekci členů obsahující implicitně napsané výrazy deklarace.
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
;
tuple_expression se řadí jako n-tice.
deconstruction_expressionvar (e1, ..., en) je zkratka pro tuple_expression(var e1, ..., var en) a řídí se stejným chováním. To platí rekurzivně u všech vnořených deconstruction_tuplev deconstruction_expression. Každý identifikátor vnořený do deconstruction_expression zavádí výraz deklarace (§12.19). V důsledku toho může k deconstruction_expression dojít pouze na levé straně jednoduchého přiřazení.
Příklad: Následující kód deklaruje tři proměnné: a, b a c. Každé z nich je celé číslo a má přiřazenu hodnotu z n-tice na pravé straně přiřazení.
var (a, b, c) = (1, 2, 3); // a is 1, b is 2, and c is 3. var sum = a + b + c; // sum is 6.Každý jednotlivý prvek přiřazení může být sám o sobě dekonstrukčním výrazem. Například následující dekonstrukční výraz přiřadí šest proměnných
aprostřednictvímf.var (a, b, (c, d, (e, f))) = (1, 2, (3, 4, (5, 6)));V tomto příkladu si všimněte, že struktura vnořených řazených kolekcí členů se musí shodovat na obou stranách přiřazení.
Pokud jsou proměnné na levé straně implicitně zadány, musí mít odpovídající výraz typ:
(int a, string? b) = (42, null); //OK var (c, d) = (42, null); // Invalid as type of d cannot be inferred (int e, var f) = (42, null); // Invalid as type of f cannot be inferredkoncového příkladu
Výraz řazené kolekce členů má typ, pokud a pouze pokud každý z jeho výrazů prvků Ei má typ Ti. Typ musí být typ n-tice se stejnou početností jako n-tice ve výrazu, kde každý prvek je uveden následujícím způsobem:
- Pokud má prvek tuplu v odpovídající pozici název
Ni, pak prvek typu tuplu budeTi Ni. - Jinak platí, že pokud je
EiformulářeNineboE.NineboE?.Ni, musí být prvek typu řazené kolekce členůTi Ni, , pokud některé z následujících blokování:- Další prvek výrazu n-tice má název
Ni, nebo - Další prvek n-tice beze jména má výraz ve tvaru
Ni,E.NineboE?.Ni, nebo -
Nimá tvarItemX, kdeXje posloupnost desítkových číslic, které nejsou zahájeny0a které by mohly představovat pozici prvku n-tice, aXnepředstavuje pozici prvku.
- Další prvek výrazu n-tice má název
- V opačném případě musí být prvek typu n-tice
Ti.
Výraz ntice se vyhodnocuje vyhodnocením výrazů jednotlivých prvků v pořadí zleva doprava.
Hodnotu řazené kolekce členů lze získat z výrazu řazené kolekce členů tak, že ji převedete na typ řazené kolekce členů (§10.2.13), a to tak, že ji přetřídíte jako hodnotu (§12.2.2)) nebo jejím cílem dekonstrukčního přiřazení (§12.23.2).
Příklad :
(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 typeV tomto příkladu jsou všechny čtyři výrazy n-tic platné. První dva,
t1at2, nepoužívají typ návratové hodnoty n-tice, místo toho použijí implicitní převod n-tice. V případět2se implicitní převod n-tice spoléhá na implicitní převody z2nalonga znullnastring. Třetí výraz n-tice má typ(int i, string), a lze proto překlasifikovat na hodnotu tohoto typu. Deklaracet4je naopak chyba: výraz n-tice nemá typ, protože jeho druhý prvek není typovaný.if ((x, y).Equals((1, 2))) { ... };Tento příklad ukazuje, že řazené kolekce členů můžou někdy vést k několika vrstvám závorek, zejména pokud je výraz řazené kolekce členů jediným argumentem vyvolání metody.
koncového příkladu
12.8.7 Přístup členů
12.8.7.1 Obecné
member_access se skládá z primary_expression, predefined_typenebo qualified_alias_member, následované tokenem ".", následované identifikátorem, volitelně následované type_argument_list.
member_access
: primary_expression '.' identifier type_argument_list?
| predefined_type '.' identifier type_argument_list?
| qualified_alias_member '.' identifier type_argument_list?
;
predefined_type
: 'bool' | 'byte' | 'char' | 'decimal' | 'double' | 'float' | 'int'
| 'long' | 'object' | 'sbyte' | 'short' | 'string' | 'uint' | 'ulong'
| 'ushort'
;
Produkce qualified_alias_member je definována v §14.8.
member_access může mít podobu E.I nebo E.I<A₁, ..., Aₑ>, kde E je buď primary_expression, predefined_type, nebo qualified_alias_member,I je jediný identifikátor a <A₁, ..., Aₑ> je volitelný type_argument_list. Pokud není zadán žádný type_argument_list, považujte e za nulu.
přístup_člena s výrazem_primární typu dynamic je dynamicky vázán (§12.3.3). V tomto případě je přístup člena klasifikován jako vlastnost přístup typu dynamic. Níže uvedená pravidla k určení významu přístupu member_access se použijí za běhu, přičemž se místo typu kompilace použije typ za běhu z primary_expression. Pokud tato klasifikace za běhu vede ke skupině metod, pak přístup k členu musí být primární výraz ve výrazu vyvolání .
member_access se vyhodnotí a klasifikuje takto:
- Pokud
eje nula aEje obor názvů aEobsahuje vnořený obor názvů s názvemI, výsledek je tento obor názvů. - Pokud je
Eobor názvů aEobsahuje přístupný typ s názvemIa parametry typuK, je výsledkem tento typ vytvořený s danými argumenty typu. - Pokud je
Eklasifikovaný jako typ, pokudEnení parametr typu, a pokud vyhledávání členů (§12,5)IvEs parametry typuKvytvoří shodu,E.Ise vyhodnotí a klasifikuje takto:Poznámka: Pokud je výsledkem takového vyhledávání člena skupina metod a
Kje nula, může skupina metod obsahovat metody s parametry typu. To umožňuje, aby takové metody byly považovány za odvozování argumentů typu. koncová poznámka- Pokud
Iidentifikuje typ, je výsledkem tento typ vytvořený s libovolnými zadanými argumenty typu. - Pokud
Iidentifikuje jednu nebo více metod, je výsledkem skupina metod bez přidruženého výrazu instance. - Pokud
Iidentifikuje statickou vlastnost, je výsledkem přístup k vlastnosti bez přidruženého výrazu instance. - Pokud
Iidentifikuje statické pole:- Pokud je pole jen pro čtení a odkaz se vyskytuje mimo statický konstruktor třídy nebo struktury, ve které je pole deklarováno, je výsledkem hodnota, a to hodnota statického pole
IvE. - V opačném případě je výsledkem proměnná, konkrétně statické pole
IvE.
- Pokud je pole jen pro čtení a odkaz se vyskytuje mimo statický konstruktor třídy nebo struktury, ve které je pole deklarováno, je výsledkem hodnota, a to hodnota statického pole
- Pokud
Iidentifikuje statickou událost:- Pokud se odkaz vyskytuje v rámci třídy nebo struktury, ve které je událost deklarována, a událost byla deklarována bez event_accessor_declarations (§15.8.1),
E.Ije zpracována přesně tak, jako byIbyla statická pole. - V opačném případě je výsledkem přístup k události bez přidruženého výrazu instance.
- Pokud se odkaz vyskytuje v rámci třídy nebo struktury, ve které je událost deklarována, a událost byla deklarována bez event_accessor_declarations (§15.8.1),
- Pokud
Iidentifikuje konstantu, je výsledkem hodnota, konkrétně hodnota této konstanty. - Pokud
Iidentifikuje člen výčtu, je výsledkem hodnota, konkrétně hodnota tohoto člena výčtu. - V opačném případě je
E.Ineplatným odkazem na člena a dojde k chybě v době kompilace.
- Pokud
- Pokud je
Epřístupem k vlastnosti, indexeru, proměnnou nebo hodnotou, jehož typ jeT, a vyhledávání člena (§12.5)IvTs argumenty typuKvytvoří shodu, pak seE.Ivyhodnotí a klasifikuje takto:- Nejprve, pokud je
Evlastnost nebo přístup indexeru, získá se hodnota této vlastnosti nebo přístupu k indexeru (§12.2.2) a E se přetřídí jako hodnota. - Pokud
Iidentifikuje jednu nebo více metod, je výsledkem skupina metod s přidruženým výrazem instanceE. - Pokud
Iidentifikuje vlastnost instance, je výsledkem přístup k vlastnosti s přidruženým výrazem instanceEa přidruženým typem, který je typem vlastnosti. Je-liTtyp třídy, je přidružený typ vybrán z první deklarace nebo přepsání vlastnosti, které se najde při zahájení odTa vyhledávání v jejích základních třídách. - Pokud je
Tclass_type aIidentifikuje instanční pole daného class_type:- Pokud je hodnota
Enull, vyvolá seSystem.NullReferenceException. - V opačném případě, pokud je pole jen pro čtení a odkaz nastane mimo konstruktor instance třídy, ve které je pole deklarováno, je výsledkem hodnota, a to hodnota pole
Iv objektu odkazovanémE. - Jinak je výsledkem proměnná, konkrétně pole
Iv objektu odkazovanémE.
- Pokud je hodnota
- Pokud je
Tstruktura typu aIidentifikuje pole instance této struktury typu :- Pokud
Eje hodnota, nebo pokud je pole jen pro čtení a odkaz nastane mimo konstruktor instance struktury, ve které je pole deklarováno, je výsledkem hodnota, a to hodnota poleIv instanci struktury danéE. - V opačném případě je výsledkem proměnná, konkrétně pole
Iv instanci struktury poskytnutéE.
- Pokud
- Pokud
Iidentifikuje událost instance:- Pokud se odkaz vyskytuje v rámci třídy nebo struktury, ve které je událost deklarována, a událost byla deklarována bez event_accessor_declarations (§15.8.1), a odkaz nenastane jako levá strana
a +=nebo-=operátor, pak seE.Izpracuje přesně tak, jako byIbylo pole instance. - V opačném případě je výsledkem přístup k události s přidruženým výrazem instance
E.
- Pokud se odkaz vyskytuje v rámci třídy nebo struktury, ve které je událost deklarována, a událost byla deklarována bez event_accessor_declarations (§15.8.1), a odkaz nenastane jako levá strana
- Nejprve, pokud je
- V opačném případě je proveden pokus o zpracování
E.Ijako vyvolání rozšiřující metody (§12.8.10.3). Pokud se to nezdaří,E.Ije neplatný odkaz na člena a dojde k chybě při vazbě času.
12.8.7.2 Stejné jednoduché názvy a názvy typů
V případě přístupu k členu ve formě E.I, pokud je E jediným identifikátorem a pokud význam E jako simple_name (§12.8.4) představuje konstantu, pole, vlastnost, místní proměnnou nebo parametr se stejným typem jako význam E jako type_name (§7.8.1), pak jsou povoleny oba možné významy E. Vyhledávání členů E.I není nikdy nejednoznačné, protože I musí být nutně členem typu E v obou případech. Jinými slovy pravidlo jednoduše povoluje přístup ke statickým členům a vnořeným typům E, kdy by jinak došlo k chybě kompilace.
Příklad :
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 } }Pouze pro vysvětlující účely jsou v rámci třídy
Aty výskyty identifikátoruColor, které odkazují na typColor, odděleny pomocí«...», zatímco ty, které odkazují na poleColor, nejsou.koncového příkladu
12.8.8 Null Podmíněný přístup člena
null_conditional_member_access je podmíněná verze member_access (§12.8.7) a jedná se o chybu doby vazby, pokud je typ výsledku void. Pro podmíněný výraz s hodnotou null, kde může být typ výsledku void viz (§12.8.11).
Null_conditional_member_access se skládá z primary_expression následovaných dvěma tokeny "?" a "." a identifikátorem s volitelným type_argument_list, za kterým následuje nula nebo více dependent_accesses, které může předcházet null_forgiving_operator.
null_conditional_member_access
: primary_expression '?' '.' identifier type_argument_list?
(null_forgiving_operator? dependent_access)*
;
dependent_access
: '.' identifier type_argument_list? // member access
| '[' argument_list ']' // element access
| '(' argument_list? ')' // invocation
;
null_conditional_projection_initializer
: primary_expression '?' '.' identifier type_argument_list?
;
Výraz null_conditional_member_accessE má tvar P?.A. Význam E se určuje takto:
Pokud typ
Pje typ hodnoty nullable:Nechť je
TtypemP.Value.A.Pokud
Tje parametr typu, o který není známo, že se jedná o odkazový typ nebo nenulový typ hodnoty, dojde k chybě v době kompilace.Pokud je
Tnenulový typ hodnoty, typEjeT?a významEje stejný jako význam:((object)P == null) ? (T?)null : P.Value.AKromě toho, že
Pse vyhodnotí pouze jednou.V opačném případě je typ
ETa významEje stejný jako význam:((object)P == null) ? (T)null : P.Value.AKromě toho, že
Pse vyhodnotí pouze jednou.
Jinak:
Nechte
Tbýt typem výrazuP.A.Pokud
Tje parametr typu, o který není známo, že se jedná o odkazový typ nebo nenulový typ hodnoty, dojde k chybě v době kompilace.Pokud je
Tnenulový typ hodnoty, typEjeT?a významEje stejný jako význam:((object)P == null) ? (T?)null : P.AKromě toho, že
Pse vyhodnotí pouze jednou.V opačném případě je typ
ETa významEje stejný jako význam:((object)P == null) ? (T)null : P.AKromě toho, že
Pse vyhodnotí pouze jednou.
Poznámka: Ve výrazu formuláře:
P?.A₀?.A₁pokud se
Pvyhodnotí jakonull, nevyhodnotí se aniA₀aniA₁. Totéž platí, pokud je výraz posloupností operací null_conditional_member_access nebo null_conditional_element_access§12.8.13.koncová poznámka
null_conditional_projection_initializer je omezením null_conditional_member_access a má stejnou sémantiku. Vyskytuje se pouze jako inicializátor projekce ve výrazu pro vytvoření anonymního objektu (§12.8.17.3).
12.8.9 Výrazy ignorující hodnotu Null
12.8.9.1 Obecné
Hodnota výrazu odpouštějícího nulovou hodnotu, typ, klasifikace (§12.2) a bezpečný kontext (§16.4.15) je hodnota, typ, klasifikace a bezpečný kontext jeho primárního výrazu.
null_forgiving_expression
: primary_expression null_forgiving_operator
;
null_forgiving_operator
: '!'
;
Poznámka: Postfixové operátory pro toleranci hodnoty null a prefixové operátory logické negace (§12.9.4), ačkoliv jsou reprezentovány stejným lexikálním tokenem (!), jsou odlišné. Pouze ten druhý může být přetížen (§15.10), je pevná definice operátoru pro odpouštění null.
koncová poznámka
Jedná se o chybu v době kompilace při použití operátoru potlačení null více než jednou u stejného výrazu, bez ohledu na přítomnost závorek.
příklad: Toto jsou všechny neplatné:
var p = q!!; // error: applying null_forgiving_operator more than once var s = ( ( m(t) ! ) )! // error: null_forgiving_operator applied twice to m(t)koncového příkladu
Zbytek tohoto dílčího ustanovení a dalších souvisejících dílčích ustanovení jsou podmíněně normativní.
Kompilátor, který provádí statickou analýzu stavu null (§8.9.5) musí splňovat následující specifikaci.
Operátor null-forgiving je předkompilační pseudo-operace, která slouží jako vstup pro kompilátorovou statickou analýzu stavu null. Má dvě použití: k přepsání určení kompilátoru, že výraz může být nulla k přepsání varování kompilátoru souvisejícího s nulovou hodnotou.
Použití operátoru null-forgiving na výraz, pro který analýza statického stavu null kompilátoru nevyvolá žádná upozornění není chyba.
12.8.9.2 Přepsání určení "může být null"
Za určitých okolností může analýza statického stavu null kompilátoru určit, že výraz má nulový stav možná null a vydá diagnostické upozornění, pokud jiné informace indikují, že výraz nemůže mít hodnotu null. Použití operátoru null-forgiving na takový výraz informuje statickou analýzu stavu null kompilátoru, že stav null je v a není null, což brání diagnostickému upozornění a také může informovat jakoukoli probíhající analýzu.
příklad: Zvažte následující:
#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;Pokud
IsValidvrátítrue,pmůže být bezpečně dereferencováno pro přístup ke svéNamevlastnosti a upozornění na "dereferencování potenciálně nulové hodnoty" lze potlačit pomocí!.koncového příkladu
Příklad: Operátor null-forgiving by měl být používán s opatrností, zvažte:
#nullable enable int B(int? x) { int y = (int)x!; // quash warning, throw at runtime if x is null return y; }Zde je operátor odpouštějící nuly použit na typ hodnoty a potlačuje všechna upozornění na
x. Pokud je všakxnullběhem běhu programu, dojde k výjimce, protoženullnelze přetypovat naint.koncového příkladu
12.8.9.3 Přepsání dalších upozornění z analýzy nulových hodnot
Kromě přepsání určení, že může být null, jak je uvedeno výše, mohou existovat další okolnosti, kdy je žádoucí přepsat kompilátorovou analýzu stavu null, která stanoví, že výraz vyžaduje vydání jednoho nebo více upozornění. Použití operátoru null-forgiving na takový výraz vyžaduje, aby kompilátor nevydá žádné upozornění pro výraz. Kompilátor se může rozhodnout nevydávat upozornění a může také upravit svou další analýzu.
příklad: Zvažte následující:
#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; }Typy parametrů metody
Assign,lv&rv, jsoustring?, přičemžlvje výstupní parametr, a provádí jednoduché přiřazení.Metoda
Mpředává proměnnous, typustring, jako výstupní parametrAssign, a kompilátor vydává upozornění, protožesnení nullovatelná proměnná. Vzhledem k tomu, že druhý argumentAssignnemůže být null, operátor null-forgiving se používá k potlačení upozornění.koncového příkladu
Konec podmíněného normativního textu.
12.8.10 Vyvolání výrazů
12.8.10.1 Obecné
K vyvolání metody se používá invocation_expression.
invocation_expression
: primary_expression '(' argument_list? ')'
;
primary_expression může být null_forgiving_expression, právě tehdy, když má delegate_type.
invocation_expression je dynamicky vázán (§12.3.3), pokud platí alespoň jedna z následujících podmínek:
-
primary_expression má typ kompilace
dynamic. - Nejméně jeden argument v nepovinném seznamu argument_list má typ určený při kompilaci
dynamic.
V tomto případě je invocation_expression klasifikována jako hodnota typu dynamic. Níže uvedená pravidla pro určení významu invocation_expression se pak použijí za běhu, přičemž se používá typ za běhu místo typu za doby kompilace pro ty z primary_expression a argumentů, které mají typ kompilace dynamic. Pokud primary_expression nemá typ kompilace dynamic, provede vyvolání metody omezenou kontrolu doby kompilace, jak je popsáno v §12.6.5.
primární výrazvýraz vyvolání musí být skupina metod nebo hodnota typu delegát . Je-li primární_výraz skupinou metod, výraz_volání je vyvoláním metody (§12.8.10.2). Je-li výraz primary_expression hodnotou typu delegate_type, pak je výraz invocation_expression vyvoláním delegáta (§12.8.10.4). Pokud primary_expression není skupinou metod ani hodnotou typu delegate_type, dojde k chybě v době vazby.
Nepovinný argument_list (§12.6.2) poskytuje hodnoty nebo proměnné odkazy na parametry metody.
Výsledek vyhodnocení invocation_expression je klasifikován takto:
- Pokud invocation_expression vyvolá metodu vracející žádnou hodnotu (§15.6.1) nebo delegáta vracejícího žádnou hodnotu, výsledkem je nic. Výraz, který je klasifikován jako nic, je povolen pouze v kontextu statement_expression (§13.7) nebo jako tělo lambda_expression (§12.21). V opačném případě dojde k chybě časování vazby.
- Jinak pokud invocation_expression vyvolá metodu s návratem podle odkazu (§15.6.1) nebo delegáta s návratem podle odkazu, výsledek je proměnná, jejíž typ je roven návratovému typu metody nebo delegáta. Pokud je vyvolána metoda instance a příjemce je typu třídy
T, přidružený typ je vybrán z první deklarace nebo přepsání metody, které se najde při začátku sTa prohledávání jeho základních tříd. - V opačném případě invocation_expression vyvolá metodu s návratem hodnotou (§15.6.1) nebo delegáta s návratem hodnotou a výsledek je hodnota s odpovídajícím typem návratového typu metody nebo delegáta. Pokud je vyvolána metoda instance a příjemce je typu třídy
T, přidružený typ je vybrán z první deklarace nebo přepsání metody, které se najde při začátku sTa prohledávání jeho základních tříd.
12.8.10.2 Vyvolání metody
Pro vyvolání metody musí být primary_expression v rámci invocation_expression skupinou metod. Skupina metod identifikuje jednu metodu, která se má vyvolat, nebo sadu přetížených metod, ze kterých zvolit konkrétní metodu, která se má vyvolat. V druhém případě je určení konkrétní metody vyvolání založeno na kontextu poskytovaném typy argumentů v argument_list.
Zpracování času vazby vyvolání metody tvaru M(A), kde M je skupina metod (pravděpodobně včetně type_argument_list) a A je volitelná argument_list, se skládá z následujících kroků:
- Sada kandidátských metod pro vyvolání metody je vytvořena. Pro každou metodu
Fpřidružené ke skupině metodM:- Pokud
Fnení obecný,Fje kandidátem v následujících případech:-
Mnemá žádný seznam argumentů typu a -
Fse vztahuje naA(§ 12.6.4.2).
-
- Pokud je
Fobecný aMneobsahuje seznam argumentů typu,Fje kandidátem v následujících případech: - Pokud je
Fobecný aMobsahuje seznam argumentů typu,Fje kandidátem v následujících případech:
- Pokud
- Sada kandidátských metod je snížena tak, aby obsahovala pouze metody z nejvíce odvozených typů: Pro každou metodu
C.Fv sadě, kdeCje typ, ve kterém je metodaFdeklarována, všechny metody deklarované v základním typuCjsou ze sady odebrány. Navíc pokudCje jiný typ třídy nežobject, všechny metody deklarované v typu rozhraní jsou ze sady odebrány.Poznámka: Toto druhé pravidlo má účinek pouze v případě, že skupina metod byla výsledkem vyhledávání člena u parametru typu, který má efektivní základní třídu jinou než
objecta neprázdnou efektivní sadu rozhraní. koncová poznámka - Pokud je výsledná sada kandidátských metod prázdná, další zpracování podle následujících kroků je opuštěno a místo toho je proveden pokus o zpracování vyvolání metody rozšíření (§12.8.10.3). Pokud se to nezdaří, neexistují žádné použitelné metody a dojde k chybě při určování doby vazby.
- Nejlepší způsob sady kandidátských metod je identifikován pomocí pravidel řešení přetížení §12.6.4. Pokud nelze identifikovat jednu nejlepší metodu, vyvolání metody je nejednoznačné a dojde k chybě při vazbě. Při provádění řešení přetížení se parametry obecné metody zohledňují po nahrazení odpovídajících parametrů typu metody argumenty typu (zadanými nebo odvozenými).
Jakmile je metoda vybrána a ověřena v době vazby výše uvedeným postupem, skutečné vyvolání za běhu se zpracuje podle pravidel volání člena funkce popsaného v §12.6.6.
Poznámka: Intuitivní účinek výše popsaných pravidel řešení je následující: Chcete-li vyhledat konkrétní metodu vyvolanou vyvoláním metody, začněte typem označeným vyvoláním metody a pokračujte v řetězu dědičnosti, dokud nenajdete alespoň jednu dostupnou deklaraci metody bez přepsání. Pak proveďte odvození typu a rozlišení přetížení u sady použitelných, přístupných, nepřekrývaných metod deklarovaných v daném typu, a takto vybranou metodu vyvolejte. Pokud nebyla nalezena žádná metoda, zkuste místo toho zpracovat vyvolání jako vyvolání metody rozšíření. koncová poznámka
12.8.10.3 Vyvolání metody rozšíření
Vyvolání metody (§12.6.6.2) jedné z forem
«expr» . «identifier» ( )
«expr» . «identifier» ( «args» )
«expr» . «identifier» < «typeargs» > ( )
«expr» . «identifier» < «typeargs» > ( «args» )
Pokud normální zpracování vyvolání nenajde žádné použitelné metody, pokusí se zpracovat konstruktor jako vyvolání rozšiřující metody. Pokud má «výraz» nebo některý z «args» typ určený při kompilaci dynamic, metody rozšíření se neuplatní.
Cílem je najít nejlepší type_nameC, aby mohlo dojít k odpovídajícímu vyvolání statické metody.
C . «identifier» ( «expr» )
C . «identifier» ( «expr» , «args» )
C . «identifier» < «typeargs» > ( «expr» )
C . «identifier» < «typeargs» > ( «expr» , «args» )
Metoda rozšíření Cᵢ.Mₑ je způsobilá, pokud:
-
Cᵢje negenerická třída, která není vnořená. - Název
Mₑje identifikátor -
Mₑje přístupná a použitelná při použití na argumenty jako statická metoda, jak je znázorněno výše. - Implicitní převod identity, odkazu nebo boxování existuje z výrazu na typ prvního parametru
Mₑ.
Hledání C pokračuje následujícím způsobem:
- Počínaje nejbližší uzavřenou deklarací oboru názvů, pokračuje se každou další uzavřenou deklarací oboru názvů a končí u nadřazené kompilační jednotky. Postupně se pokoušíme najít kandidátskou sadu rozšiřujících metod.
- Pokud daná jednotka oboru názvů nebo kompilační jednotka přímo obsahuje ne generické deklarace typu
Cᵢs způsobilými rozšiřujícími metodamiMₑ, pak sada těchto rozšiřujících metod je kandidátskou sadou. - Pokud obory názvů importované pomocí direktiv oboru názvů v daném oboru názvů nebo kompilační jednotce přímo obsahují deklarace ne generického typu
Cᵢs způsobilými rozšiřujícími metodamiMₑ, pak je sada těchto rozšiřujících metod kandidátskou sadou.
- Pokud daná jednotka oboru názvů nebo kompilační jednotka přímo obsahuje ne generické deklarace typu
- Pokud se v žádné uzavřené deklaraci oboru názvů nebo kompilační jednotce nenajde žádná kandidátská sada, dojde k chybě v době kompilace.
- Jinak se rozlišení přetížení použije na kandidátskou sadu, jak je popsáno v §12.6.4. Pokud se nenajde žádná jedna nejlepší metoda, dojde k chybě v době kompilace.
-
Cje typ, ve kterém je nejlepší metoda deklarována jako rozšiřující metoda.
Při použití C jako cíle se volání metody zpracuje jako volání statické metody (§12.6.6).
Poznámka: Na rozdíl od vyvolání metody instance není vyvolána žádná výjimka, pokud je výraz vyhodnocen jako odkaz null. Místo toho je hodnota
nullpředána metodě rozšíření, stejně jako by byla předána prostřednictvím běžného volání statické metody. Je na implementaci metody rozšíření rozhodnout, jak reagovat na takové volání. koncová poznámka
Předchozí pravidla znamenají, že metody instance mají přednost před rozšiřujícími metodami, že metody rozšíření dostupné v deklaraci vnitřního oboru názvů mají přednost před rozšiřujícími metodami dostupnými v deklaracích vnějšího oboru názvů a že metody rozšíření deklarované přímo v oboru názvů mají přednost před rozšiřujícími metodami importovanými do stejného oboru názvů s použitím direktivy oboru názvů.
Příklad :
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) } }V příkladu má metoda
Bpřednost před první metodou rozšíření a metodaCmá přednost před oběma metodami rozšíření.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(); } } }Výstupem tohoto příkladu je:
E.F(1) D.G(2) C.H(3)
D.Gmá přednost předC.G, aE.Fmá přednost před oběmaD.FaC.F.koncového příkladu
12.8.10.4 Vyvolání delegáta
Pro vyvolání delegáta musí primary_expression výrazu invocation_expression být hodnotou typu delegate_type. Vzhledem k tomu, že delegate_type je členem funkce se stejným seznamem parametrů jako delegate_type, použitelnost delegate_type se určuje podle§12.6.4.2s ohledem na argument_list v kontextu výrazu invocation_expression.
Zpracování za běhu vyvolání delegáta ve tvaru D(A), kde D je výraz typu delegáta a A je volitelný seznam argumentů, se skládá z následujících kroků:
-
Dse vyhodnotí. Pokud toto vyhodnocení způsobí výjimku, neprojdou žádné další kroky. - Vyhodnocuje se seznam argumentů
A. Pokud toto vyhodnocení způsobí výjimku, neprojdou žádné další kroky. - Hodnota
Dje kontrolována tak, aby byla platná. Pokud je hodnotaDnull, vyvolá seSystem.NullReferenceExceptiona nebude provedeno žádné další kroky. - V opačném případě je
Dodkazem na instanci delegáta. Vyvolání členů funkce (§12.6.6) se provádí na každém z volatelných entit v seznamu vyvolání delegáta. U volatelných entit, které se skládají z instance a metody instance, je instance volání instance obsažená v volatelné entitě.
Podrobnosti o více seznamech vyvolání bez parametrů najdete v části §21.6 .
12.8.11 Výraz podmíněného vyvolání s hodnotou Null
null_conditional_invocation_expression je syntakticky buď null_conditional_member_access (§12.8.8) nebo null_conditional_element_access (§12.8.13), kde poslední dependent_access je výraz vyvolání (§12.8.10).
K null_conditional_invocation_expression dochází v rámci statement_expression (§13.7), anonymous_function_body (§12.21.1) nebo method_body (§15.6.1).
Na rozdíl od syntakticky ekvivalentní null_conditional_member_access nebo null_conditional_element_accessmůže být null_conditional_invocation_expression klasifikována jako nic.
null_conditional_invocation_expression
: null_conditional_member_access null_forgiving_operator? '(' argument_list? ')'
| null_conditional_element_access null_forgiving_operator? '(' argument_list? ')'
;
Volitelný null_forgiving_operator může být zahrnut pouze tehdy a jen tehdy, pokud má null_conditional_member_access nebo null_conditional_element_accessdelegate_type.
Výraz null_conditional_invocation_expression je E formuláře P?A; pokud A je zbytek syntakticky ekvivalentní null_conditional_member_access nebo null_conditional_element_access, A proto začne s . nebo [. Let PA podepsat zřetězení P a A.
Pokud E nastane jako statement_expression význam E je stejný jako význam příkazu :
if ((object)P != null) PA
s tím rozdílem, že P se vyhodnotí pouze jednou.
Pokud E nastane jako anonymous_function_body nebo method_body význam E závisí na jeho klasifikaci:
Pokud je
Eklasifikována jako nic, jeho význam je stejný jako význam bloku:{ if ((object)P != null) PA; }s tím rozdílem, že
Pse vyhodnotí pouze jednou.Jinak je význam
Estejný jako význam bloku:{ return E; }význam tohoto bloku
závisí na tom, zda je syntakticky ekvivalentní null_conditional_member_access (§12.8.8 ) nebonull_conditional_element_access (§12.8.13 ).
12.8.12 Přístup k elementům
12.8.12.1 Obecné
Element_access se skládá z primární_výraz, který je následován tokenem "[", poté seznam_argumentů, a tokenem "]".
argument_list se skládá z jednoho nebo více argumentů , oddělené čárkami.
element_access
: primary_expression '[' argument_list ']'
;
Při uznání primary_expression , pokud jsou alternativy element_access i pointer_element_access (§24.6.4) použitelné, je-li vložený primary_expression typu ukazatele (§24.3).
Primary_expressionelement_access nesmí být výrazem array_creation_expression, pokud neobsahuje array_initializer, nebo výrazem stackalloc_expression, pokud neobsahuje stackalloc_initializer.
Poznámka: Toto omezení existuje, pokud chcete zakázat potenciálně matoucí kód, například:
var a = new int[3][1];který by jinak byl interpretován jako:
var a = (new int[3])[1];Podobné omezení platí pro null_conditional_element_access (§12.8.13). koncová poznámka
element_access je dynamicky svázaný (§12.3.3) v případě, že platí alespoň jeden z následujících:
-
primary_expression má typ kompilace
dynamic. - Nejméně jeden výraz argument_list má typ
dynamickompilačního času .
V tomto případě typ kompilace element_access závisí na typu kompilace jeho primary_expression: pokud má typ pole, typ kompilátoru je typ prvku tohoto typu pole; v opačném případě je dynamic typ kompilace a element_access je klasifikována jako hodnota typu dynamic. Níže uvedená pravidla určují význam element_access, který se pak použije za běhu pomocí typu za běhu místo typu době kompilace z výrazů primary_expression a argument_list, které mají kompilovaný typ dynamic. Pokud primary_expression neobsahuje typ dynamickompilace , pak přístup k prvku prochází omezenou kontrolou doby kompilace, jak je popsáno v §12.6.5.
Příklad :
var index = (dynamic)1; // index has compile-time type dynamic int[] a = {0, 1, 2}; var a_elem = a[index]; // dynamically bound, a_elem has compile-time type int string s = "012"; var s_elem = s[index]; // dynamcially bound, s_elem has compile-time type dynamickoncového příkladu
Pokud je primary_expressionelement_access :
- hodnota typu matice, element_access je maticový přístup (§12.8.12.2);
- hodnota
stringtypu, element_access je řetězcový přístup (§12.8.12.3); - jinak musí být primary_expression proměnnou nebo hodnotou třídy, struktury nebo typu rozhraní, který má jeden nebo více členů indexeru, v takovém případě je element_access přístup indexeru (§12.8.12.4).
12.8.12.2 Přístup k poli
Pro maticový přístup argument_list nesmí obsahovat pojmenované argumenty ani argumenty podle odkazu (§15.6.2.3).
Počet výrazů v argument_list musí být stejný jako pořadí array_type a každý výraz musí být:
- typu
int, ,uintnebolong; neboulong - pouze pro přístup k poli s jedním pořadím, typu
IndexneboRange; nebo - být implicitně konvertibilní na jeden nebo více výše uvedených typů.
Zpracování běhu pole přístupu formuláře P[A], kde P je primary_expression array_type a A je argument_list výrazů indexu, se skládá z následujících kroků:
-
Pse vyhodnotí. Pokud toto vyhodnocení způsobí výjimku, neprojdou žádné další kroky. - Pro každý výraz indexu v argument_list v pořadí zleva doprava:
- Výraz indexu se vyhodnotí, nechte typ výsledné hodnoty T;
- Tato hodnota se pak převede na první z typů:
int,uint,long,ulongnebo pouze pro přístup k poli s jedním pořadím,IndexneboRange; pro který existuje implicitní převod (§10.2) z T . - Pokud vyhodnocení výrazu indexu nebo následné implicitní převody způsobí výjimku, nevyhodnotí se žádné další výrazy indexu a neprojdou žádné další kroky.
- Hodnota
Pje kontrolována tak, aby byla platná. Pokud je hodnotaPnull, vyvolá seSystem.NullReferenceExceptiona nebude provedeno žádné další kroky. - Pokud předchozí kroky vytvořily jednu hodnotu indexu typu
Range, pak:- Let L být délkou pole odkazovaného
Pna . -
Aje kontrolována, aby byla platná s ohledem na L (§18.3). Pokud tomu tak není,System.ArgumentOutOfRangeExceptionvyvolá se a neprojdou žádné další kroky. - Počáteční posun, S a počet položek, N, pro
As ohledem na L, jsou určeny způsobem popsaným proGetOffsetAndLength(§18.3). - Výsledkem přístupu k poli je matice obsahující mělkou kopii N prvků začínající indexem
PS. Pokud n je nula, matice má nula prvků.
- Let L být délkou pole odkazovaného
Poznámka:S i N může být nula (24,3 USD). Indexování prázdného pole je obvykle neplatné, ale indexování s prázdnou oblastí začínající nulou je platné a vrátí prázdnou matici. Definice také umožňuje , aby S byla L, poslední koncový index (§18.1), v takovém případě N bude nula a vrátí se prázdné pole. koncová poznámka
Poznámka: Rozsah prvků pole nelze přiřadit pomocí přístupu k poli. To se liší od přístupů indexerů (§12.8.12.4), které mohou, ale nemusí, podporovat přiřazení k rozsahu indexů určených
Rangehodnotou. koncová poznámka
- Jinak:
- Výsledkem vyhodnocení přístupu k matici je proměnná odkaz (§9.5) typu prvku pole.
- Hodnota každého výrazu v argument_list je kontrolována na základě skutečných hranic každé dimenze instance pole, na kterou odkazuje
P. Pokud je jedna nebo více hodnot mimo rozsah, vyvolá seSystem.IndexOutOfRangeExceptiona neprojdou žádné další kroky. - Vypočítá se odkaz na proměnnou elementu pole zadaného výrazy indexu a stane se výsledkem přístupu k poli.
12.8.12.3 Přístup k řetězci
Pro řetězcový přístup k argument_listelement_access musí obsahovat jediný nepojmenovaný argument hodnoty (§15.6.2.2), který musí být:
- typu
int,Indexnebo ; neboRange - implicitně se konvertibilní na jeden nebo více výše uvedených typů.
Zpracování běhu řetězce přístupu formuláře P[A], kde P je primary_expressionstring typu a A je jedním výrazem, který se skládá z následujících kroků:
-
Pse vyhodnotí. Pokud toto vyhodnocení způsobí výjimku, neprojdou žádné další kroky. - Výraz indexu se vyhodnotí, nechte typ výsledné hodnoty T;
- Tato hodnota se pak převede na první z typů:
int,IndexneboRange; pro které existuje implicitní převod (§10.2) z T . - Pokud vyhodnocení výrazu indexu nebo následné implicitní převody způsobí výjimku, nevyhodnotí se žádné další výrazy indexu a neprojdou žádné další kroky.
- Hodnota
Pje kontrolována tak, aby byla platná. Pokud je hodnotaPnull, vyvolá seSystem.NullReferenceExceptiona nebude provedeno žádné další kroky. - Pokud předchozí kroky vytvořily hodnotu indexu typu
Range, pak:- Výsledkem vyhodnocení přístupu k řetězci je hodnota
stringtypu. -
Nechejte L délku řetězce, na který
Podkazuje . -
Aje kontrolována, aby byla platná s ohledem na L (§18.3), pokud tomu tak neníSystem.ArgumentOutOfRangeException, je vyvolán a nejsou provedeny žádné další kroky. - Počáteční posun, S a počet položek, N, pro
As ohledem na L, jsou určeny způsobem popsaným proGetOffsetAndLength(§18.3). - Výsledkem přístupu k řetězci je řetězec vytvořený zkopírováním N znaků začínajících
Pod S, pokud N je nula, řetězec je prázdný.
- Výsledkem vyhodnocení přístupu k řetězci je hodnota
Poznámka:S i N mohou být nulové (§18.3). Indexování prázdného řetězce je obvykle neplatné, ale indexování s prázdným rozsahem začínajícím na nule je platné a vrátí prázdný řetězec. Defince také umožňuje , aby S byla L, poslední-koncový index (§18.1), v takovém případě N bude nula a vrácen prázdný řetězec. koncová poznámka
- Jinak:
- Výsledkem vyhodnocení přístupu k řetězci je hodnota
chartypu. - Hodnota převedeného indexového výrazu je kontrolována na skutečných hranicích instance řetězce, na kterou
Podkazuje . Pokud je hodnota mimo rozsah,System.IndexOutOfRangeExceptionvyvolá se a neprojdou žádné další kroky. - Hodnota znaku na posunu převedeného indexového výrazu s řetězcem
Pse stane výsledkem přístupu k řetězci.
- Výsledkem vyhodnocení přístupu k řetězci je hodnota
12.8.12.4 Přístup k indexeru
Pro indexový přístup musí primary_expressionelement_access být proměnnou nebo hodnotou typu třídy, struktury, nebo rozhraní a tento typ musí implementovat jeden nebo více indexerů, které jsou použitelné vzhledem k argument_listelement_access.
Argument_list nesmí obsahovat out ani ref argumenty.
Zpracování časové vazby přístupu k indexeru ve formě P[A], kde P je primary_expression třídy, struktury nebo typu rozhraní T a A je argument_list, tvoří následující kroky:
- Sada indexerů, kterou poskytuje
T, byla vytvořena. Sada se skládá ze všech indexerů deklarovaných vTnebo v základním typuT, které nejsou deklaracemi přepsání a jsou přístupné v aktuálním kontextu (§7.5). - Sada je omezena na ty indexery, které jsou použitelné a nejsou skryty jinými indexery. Následující pravidla se použijí pro každý indexer
S.Iv sadě, kdeSje typ, ve kterém je deklarován indexerI:- Pokud se
Inevztahuje naA(§12.6.4.2),Ise ze sady odebere. - Je-li
Ipoužitelná proA(§12.6.4.2), všechny indexery deklarované v základním typuSjsou ze sady odebrány. - Je-li
Ipoužitelná proA(§ 12.6.4.2) aSje typ třídy jiný nežobject, všechny indexery deklarované v rozhraní jsou ze sady odebrány.
- Pokud se
- Pokud je výsledná sada kandidátských indexerů prázdná, neexistují žádné použitelné indexery a dojde k chybě vazby.
- Nejlepší indexer sady kandidátských indexerů je identifikován pomocí pravidel řešení přetížení §12.6.4. Pokud nelze identifikovat jediný optimální indexer, je přístup k indexeru nejednoznačný a dojde k chybě času vazby.
- Přístupové objekty nejlepšího indexeru jsou kontrolovány:
- Je-li přístup k indexeru cílem přiřazení, bude mít indexer nastavenou nebo referenční metodu get, jinak dojde k chybě doby vazby;
- Jinak indexer musí mít přístupové objekty get nebo ref get, jinak dojde k chybě doby vazby.
Zpracování modulu runtime přístupu indexeru se skládá z následujících kroků:
- Cílový primary_expression
Pse vyhodnotí. - Výrazy indexu argument_list
Ase vyhodnocují v pořadí zleva doprava. - Použití nejlepšího indexeru určeného v době vazby:
12.8.13 Přístup k podmíněnému elementu null
Null_conditional_element_access se skládá z primary_expression následovaných dvěma tokeny "?" a "[", načež následuje argument_list, poté token "]", a následně nula nebo více dependent_access, z nichž každé může být předcházeno null_forgiving_operator.
null_conditional_element_access
: primary_expression '?' '[' argument_list ']'
(null_forgiving_operator? dependent_access)*
;
Argument_listnull_conditional_element_access nesmí obsahovat out ani ref argumenty.
Výraz primary_expression v null_conditional_element_access nesmí být array_creation_expression, pokud nezahrnuje array_initializer, ani stackalloc_expression, pokud nezahrnuje stackalloc_initializer.
Poznámka: Toto omezení existuje, pokud chcete zakázat potenciálně matoucí kód. Podobné omezení platí pro element_access (§12.8.12), kde lze nalézt příklad vyloučeného omezení. koncová poznámka
null_conditional_element_access je podmíněná verze element_access (§12.8.12) a jedná se o chybu doby vazby, pokud je typ výsledku void. Pro podmíněný výraz s hodnotou null, kde může být typ výsledku void viz (§12.8.11).
Výraz null_conditional_element_accessE má formu P?[A]B; kde jsou Bes , pokud existují. Význam E se určuje takto:
Pokud typ
Pje typ hodnoty nullable:Nechte
Tbýt typem výrazuP.Value[A]B.Pokud
Tje parametr typu, o který není známo, že se jedná o odkazový typ nebo nenulový typ hodnoty, dojde k chybě v době kompilace.Pokud je
Tnenulový typ hodnoty, typEjeT?a významEje stejný jako význam:((object)P == null) ? (T?)null : P.Value[A]BKromě toho, že
Pse vyhodnotí pouze jednou.V opačném případě je typ
ETa významEje stejný jako význam:((object)P == null) ? null : P.Value[A]BKromě toho, že
Pse vyhodnotí pouze jednou.
Jinak:
Nechte
Tbýt typem výrazuP[A]B.Pokud
Tje parametr typu, o který není známo, že se jedná o odkazový typ nebo nenulový typ hodnoty, dojde k chybě v době kompilace.Pokud je
Tnenulový typ hodnoty, typEjeT?a významEje stejný jako význam:((object)P == null) ? (T?)null : P[A]BKromě toho, že
Pse vyhodnotí pouze jednou.V opačném případě je typ
ETa významEje stejný jako význam:((object)P == null) ? null : P[A]BKromě toho, že
Pse vyhodnotí pouze jednou.
Poznámka: Ve výrazu formuláře:
P?[A₀]?[A₁]pokud se
Pvyhodnotí jakonull, nevyhodnotí se aniA₀aniA₁. Totéž platí, pokud je výraz sekvencí operací null_conditional_element_access nebo null_conditional_member_access§12.8.8.koncová poznámka
12.8.14 Tento přístup
this_access se skládá z klíčového slova this.
this_access
: 'this'
;
this_access je povolena pouze v bloku konstruktoru instance, metody instance, přístupového objektu instance (§12.2.1) nebo finalizátoru. Má jeden z následujících významů:
- Pokud se
thispoužívá v primary_expression v konstruktoru instance třídy, je klasifikovaný jako hodnota. Typ hodnoty je typ instance (§15.3.2) třídy, ve které dochází k použití, a hodnota je odkazem na objekt, který je vytvořen. - Pokud je
thispoužito v rámci primary_expression v metodě instance nebo jako přístupový objekt instance třídy, je klasifikováno jako hodnota. Typ hodnoty je typ instance (§15.3.2) třídy, ve které dochází k použití, a hodnota je odkazem na objekt, pro který byla vyvolána metoda nebo příslušenství. - Pokud se
thispoužívá v primárním výrazu v konstruktoru instance struktury, je klasifikováno jako proměnná. Typ proměnné je typ instance (§15.3.2) struktury, ve které dochází k použití, a proměnná představuje strukturu, která je vytvořena.- Pokud deklarace konstruktoru nemá žádný inicializátor konstruktoru,
thisproměnná se chová přesně stejně jako výstupní parametr typu struktury. Konkrétně to znamená, že proměnná musí být rozhodně přiřazena v každé cestě provádění konstruktoru instance. - V opačném případě se proměnná
thischová přesně stejně jakorefparametr typu struktury. Konkrétně to znamená, že proměnná se považuje za původně přiřazenou.
- Pokud deklarace konstruktoru nemá žádný inicializátor konstruktoru,
- Pokud se
thispoužívá v primárním výrazu v rámci instanční metody nebo instančního přístupového objektu struktury, je klasifikován jako proměnná. Typ proměnné je typ instance (§15.3.2) struktury, ve které dochází k použití.- Pokud metoda nebo příslušenství není iterátorem (§15.15) nebo asynchronní funkcí (§15.14),
thispředstavuje proměnná strukturu, pro kterou byla vyvolána metoda nebo příslušenství.- Pokud je struktura
readonly struct,thisproměnná se chová úplně stejně jako vstupní parametr typu struktury. - Jinak se proměnná
thischová přesně stejně jako parametrreftypu struktury.
- Pokud je struktura
- Pokud je metoda nebo přístupová funkce iterátor nebo asynchronní funkce, představuje proměnná
thiskopírování struktury, pro kterou byla vyvolána metoda nebo příslušenství, a chová se přesně stejně jako hodnota parametru typu struktury.
- Pokud metoda nebo příslušenství není iterátorem (§15.15) nebo asynchronní funkcí (§15.14),
Použití this v primary_expression v jiném kontextu, než je uvedeno výše, je chyba v době kompilace. Konkrétně není možné odkazovat na this ve statické metodě, přístupovém objektu statické vlastnosti nebo při inicializaci proměnné při deklaraci pole.
12.8.15 Základní přístup
base_access se skládá z klíčového slova base následovaného tokenem.a identifikátorem s volitelným type_argument_list, nebo argument_list uzavřeným v hranatých závorkách:
base_access
: 'base' '.' identifier type_argument_list?
| 'base' '[' argument_list ']'
;
base_access slouží k přístupu k členům základní třídy, které jsou skryty podobnými pojmenovanými členy v aktuální třídě nebo struktuře.
base_access je povoleno pouze v těle konstruktoru instance, instanční metody, instančního přístupového objektu (§12.2.1) nebo ve finalizátoru. Pokud base.I nastane ve třídě nebo struktuře, I označí člen základní třídy této třídy nebo struktury. Stejně tak platí, že pokud base[E] nastane ve třídě, musí příslušný indexer existovat v základní třídě.
V době vazby se base_access výrazy formuláře base.I a base[E] vyhodnotí přesně tak, jako kdyby byly zapsány ((B)this).I a ((B)this)[E], kde B je základní třída třídy nebo struktury, ve které se konstruktor vyskytuje. Proto base.I a base[E] odpovídají this.I a this[E], s výjimkou this se zobrazuje jako instance základní třídy.
Pokud base_access odkazuje na člena virtuální funkce (metoda, vlastnost nebo indexer), je změněno určení, který člen funkce se má vyvolat za běhu (§12.6.6). Člen funkce, který je vyvolán, je určen nalezením nejodvozenější implementace (§15.6.4) člena funkce s ohledem na B (místo vzhledem k běhovému typu this, jak by bylo obvyklé při přístupu bez základového typu). Proto lze během přepsání virtuálního funkčního člena použít base_access k vyvolání zděděné implementace tohoto člena. Pokud je člen funkce odkazovaný base_access abstraktní, nastane chyba během vazby.
Poznámka: Na rozdíl od
thisneníbasevýraz sám o sobě. Je to klíčové slovo pouze v kontextu base_access nebo constructor_initializer (§15.11.2). koncová poznámka
12.8.16 Operátory přírůstku a dekrementace
post_increment_expression
: primary_expression '++'
;
post_decrement_expression
: primary_expression '--'
;
Operand postfixové operace přiřazení nebo dekrementace může být výraz klasifikovaný jako proměnná, přístup k vlastnosti, nebo přístup indexeru. Výsledkem operace je hodnota stejného typu jako operand.
Pokud primary_expression má dynamic typ kompilace, je operátor dynamicky vázán (§12.3.3), post_increment_expression nebo post_decrement_expression má typ kompilace dynamic a následující pravidla se použijí za běhu pomocí typu běhu primary_expression.
Pokud je operandem postfixní inkrementace nebo dekrementace přístup k vlastnosti nebo indexeru, vlastnost nebo indexer musí mít jak get, tak set přístupový prvek. Pokud tomu tak není, dojde k chybě při určování doby vazby.
Rozlišení přetížení unárního operátoru (§12.4.4) se použije k výběru konkrétní implementace operátoru. Předdefinované operátory ++ a -- existují pro následující typy: sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimala libovolný typ výčtu. Předdefinované operátory ++ vrátí hodnotu vytvořenou přidáním 1 do operandu a předdefinované operátory -- vrátí hodnotu vytvořenou odečtením 1 z operandu. Pokud je výsledek tohoto sčítání nebo odčítání mimo rozsah typu výsledku a typ výsledku je celočíselný typ nebo výčtový typ, vyvolá se System.OverflowException.
Existuje implicitní převod z návratového typu vybraného unárního operátoru na typ primary_expression, jinak dojde k chybě v době kompilace.
Zpracování běhu operace přírůstku nebo dekrementace formuláře x++ nebo x-- se skládá z následujících kroků:
- Pokud se
xklasifikuje jako proměnná:-
xse vyhodnotí tak, aby vznikla proměnná. - Hodnota
xse uloží. - Uložená hodnota
xje převedena na typ operandu vybraného operátoru a operátor je vyvolán s touto hodnotou jako argument. - Hodnota vrácená operátorem je převedena na typ
xa uložena na místo určené dřívějším vyhodnocenímx. - Uložená hodnota
xse stane výsledkem operace.
-
- Pokud je
xklasifikován jako přístup k vlastnosti nebo indexeru:- Výraz instance (pokud
xnenístatic) a seznam argumentů (pokudxje přístup k indexeru) přidružený kxse vyhodnotí a výsledky se použijí v následných vyvolání get a set. - Vyvolá se přístupový objekt get
xa vrácená hodnota se uloží. - Uložená hodnota
xje převedena na typ operandu vybraného operátoru a operátor je vyvolán s touto hodnotou jako argument. - Hodnota vrácená operátorem je převedena na typ
xa nastavovací přístupová funkcexje vyvolána s touto hodnotou jako argument pro hodnotu. - Uložená hodnota
xse stane výsledkem operace.
- Výraz instance (pokud
Operátory ++-- také podporují zápis předpon (§12.9.7). Výsledkem x++ nebo x-- je hodnota xpřed operace, zatímco výsledek ++x nebo --x je hodnota xpo operace. V obou případech má x stejnou hodnotu po operaci.
Implementaci operátoru ++ nebo operátoru -- lze vyvolat pomocí postfixové nebo prefixové notace. Pro dva různé zápisy není možné mít samostatné implementace operátorů.
12.8.17 Nový operátor
12.8.17.1 Obecné
Operátor new slouží k vytvoření nových instancí typů.
Existují tři formy nových výrazů:
- Výrazy pro vytváření objektů se používají k vytváření nových instancí typů tříd a hodnotových typů.
- Výrazy pro vytváření polí se používají k vytváření nových instancí typů polí.
- Výrazy vytváření delegátů se používají k získání instancí typů delegátů.
Operátor new znamená vytvoření instance typu, ale nemusí nutně znamenat přidělení paměti. Konkrétně instance hodnotových typů nevyžadují žádnou další paměť nad rámec proměnných, ve kterých se nacházejí, a při použití new k vytváření instancí typů hodnot nedojde k žádným přidělením.
Poznámka: Výrazy vytváření delegátů nevytvoří vždy nové instance. Při zpracování výrazu stejným způsobem jako převod skupiny metod (§10.8) nebo anonymní převod funkce (§10.7) může dojít k opakovanému použití existující instance delegáta. koncová poznámka
12.8.17.2 Výrazy vytváření objektů
12.8.17.2.1 Obecné
object_creation_expression slouží k vytvoření nové instance class_type nebo value_type.
object_creation_expression
: 'new' type '(' argument_list? ')' object_or_collection_initializer?
| 'new' type object_or_collection_initializer
;
object_or_collection_initializer
: object_initializer
| collection_initializer
;
Typ object_creation_expression musí být class_type, value_typenebo type_parameter. Typ nemůže být tuple_type ani abstraktním ani statickým class_type.
Nepovinný argument_list (§ 12.6.2) je povolen pouze v případě, že typ je class_type nebo struct_type.
Výraz pro vytvoření objektu může vynechat seznam argumentů konstruktoru a uzavřít závorky, pokud obsahuje inicializátor objektů nebo inicializátor kolekce. Vynechání seznamu argumentů konstruktoru a uzavření závorek je ekvivalentní zadání prázdného seznamu argumentů.
Zpracování výrazu pro vytvoření objektu, který obsahuje inicializátor objektu nebo inicializátor kolekce, se skládá z prvního zpracování konstruktoru instance a následného zpracování inicializace členů nebo prvků určených inicializátorem objektu (§12.8.17.2.2) nebo inicializátoru kolekce (§12.8.17.2.3).
Pokud některý z argumentů ve volitelném argument_list má typ při kompilaci dynamic, je object_creation_expression dynamicky vázán (§12.3.3) a následující pravidla se použijí za běhu pomocí běhového typu těchto argumentů z argument_list, které mají typ při kompilaci dynamic. Vytvoření objektu však prochází omezenou kontrolou doby kompilace, jak je popsáno v §12.6.5.
Zpracování vázání object_creation_expression ve formě new T(A), kde T je class_typenebo value_typea A je volitelný argument_list, sestává z těchto kroků:
- Pokud je
Tvalue_type aAneexistuje:-
object_creation_expression je vyvolání výchozího konstruktoru. Výsledkem object_creation_expression je hodnota typu
T, konkrétně výchozí hodnota proTdefinovaná v §8.3.3.
-
object_creation_expression je vyvolání výchozího konstruktoru. Výsledkem object_creation_expression je hodnota typu
- Jinak, pokud je
Ttypu type_parameter aAnení přítomen:- Není-li pro zadáno žádné omezení typu hodnoty, ani omezení konstruktoru (
T), dojde k chybě při době vazby. - Výsledkem object_creation_expression je hodnota typu runtime, ke kterému byl parametr typu vázán, a to konkrétně výsledek vyvolání výchozího konstruktoru tohoto typu. Typ běhu může být referenčním typem nebo typem hodnoty.
- Není-li pro zadáno žádné omezení typu hodnoty, ani omezení konstruktoru (
- Jinak, pokud je
Ttypu class_type nebo typu struct_type:- Pokud
Tje abstraktní nebo statický class_type, dojde k chybě v době kompilace. - Konstruktor instance, který se má vyvolat, je určen pomocí pravidel rozlišení přetížení §12.6.4. Sada konstruktorů instancí kandidáta se skládá ze všech přístupných konstruktorů instancí deklarovaných v
T, které se vztahují kA(§12.6.4.2). Pokud je sada kandidátních konstruktorů instance prázdná nebo pokud nelze identifikovat jeden nejlepší konstruktor instance, dojde k chybě při vazbě. - Výsledkem object_creation_expression je hodnota typu
T, konkrétně hodnota vytvořená vyvoláním konstruktoru instance určeného v kroku výše. - V opačném případě je object_creation_expression neplatný a dojde k chybě při vazbě.
- Pokud
I když je object_creation_expression dynamicky vázán, typ kompilace je stále T.
Zpracování za běhu object_creation_expression tvaru new T(A), kde T je class_type nebo struct_type a A je volitelný argument_list, se skládá z následujících kroků:
- Pokud je
třída typu : - Je přidělena nová instance třídy
T. Pokud není k dispozici dostatek paměti pro přidělení nové instance, vyvolá seSystem.OutOfMemoryExceptiona neprojdou žádné další kroky. - Všechna pole nové instance jsou inicializována na výchozí hodnoty (§9.3).
- Konstruktor instance je vyvolán podle pravidel vyvolání člena funkce (§12.6.6). Odkaz na nově přidělenou instanci se automaticky předá konstruktoru instance a instance může být přístupná z tohoto konstruktoru.
- Je přidělena nová instance třídy
- Pokud je
Tstruktura typu:- Instance typu
Tje vytvořena přidělením dočasné místní proměnné. Protože instance konstruktoru typu struktury musí jednoznačně přiřadit hodnotu každému poli vytvářené instance, není nutné žádné inicializování pomocné proměnné. - Konstruktor instance je vyvolán podle pravidel vyvolání člena funkce (§12.6.6). Odkaz na nově přidělenou instanci se automaticky předá konstruktoru instance a instance může být přístupná z tohoto konstruktoru.
- Instance typu
12.8.17.2.2 Inicializátory objektů
Inicializátor objektů určuje hodnoty pro nula nebo více polí, vlastností nebo indexovaných prvků objektu.
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
;
Inicializátor objektu se skládá z posloupnosti inicializátorů členů, které jsou uzavřeny do { a } tokenů a odděleny čárkami. Každý member_initializer určí cíl inicializace. Identifikátor pojmenuje přístupné pole nebo vlastnost inicializovaného objektu, zatímco argument_list uzavřená v hranatých závorkách určuje argumenty pro přístupný indexer inicializovaného objektu. Jedná se o chybu, kdy inicializátor objektů obsahuje více než jeden inicializátor členu pro stejné pole nebo vlastnost.
Poznámka: I když inicializátor objektů není povoleno nastavit stejné pole nebo vlastnost více než jednou, neexistují žádná taková omezení pro indexery. Inicializátor objektů může obsahovat více cílů inicializátoru odkazujících na indexery a může dokonce používat stejné argumenty indexeru vícekrát. koncová poznámka
Za každým initializer_target následuje znaménko rovná se a buď výraz, inicializátor objektu, nebo inicializátor kolekce. Není možné, aby výrazy v inicializátoru objektu odkazovaly na nově vytvořený objekt, který inicializují.
V argument_listinitializer_target neexistuje implicitní podpora argumentů typu Index (§18.4.2) nebo Range (§18.4.3).
Inicializátor člena, který určuje výraz za symbolem rovná se, se zpracuje stejným způsobem jako přiřazení (§12.23.2) cíli.
Inicializátor členu, který určuje inicializátor objektu nacházející se za znaménkem rovnosti, je vnořený inicializátor objektu, tj. inicializace vloženého objektu. Namísto přiřazení nové hodnoty k poli nebo vlastnosti se přiřazení v inicializátoru vnořeného objektu považují za přiřazení členům pole nebo vlastnosti. Inicializátory vnořených objektů nelze použít u vlastností s typem hodnoty ani u polí jen pro čtení s typem hodnoty.
Inicializátor člena, který určuje inicializátor kolekce za znaménkem rovná se je inicializace vložené kolekce. Místo přiřazení nové kolekce k cílovému poli, vlastnosti nebo indexeru se prvky uvedené v inicializátoru přidají do kolekce odkazované cílem. Cílem je typ sběru, který splňuje požadavky stanovené v §12.8.17.2.3.
Pokud cíl inicializátoru odkazuje na indexer, musí být argumenty indexeru vždy vyhodnoceny přesně jednou. Proto, i když se argumenty nikdy nepoužijí (např. kvůli prázdnému vnořenému inicializátoru), zvažovány jsou kvůli svým vedlejším efektům.
Příklad: Následující třída představuje bod se dvěma souřadnicemi:
public class Point { public int X { get; set; } public int Y { get; set; } }Instanci
Pointlze vytvořit a inicializovat následujícím způsobem:Point a = new Point { X = 0, Y = 1 };To má stejný účinek jako
Point __a = new Point(); __a.X = 0; __a.Y = 1; Point a = __a;kde
__aje jinak neviditelná a nepřístupná dočasná proměnná.Následující třída ukazuje obdélník vytvořený ze dvou bodů a vytvoření a inicializaci instance
Rectangle:public class Rectangle { public Point P1 { get; set; } public Point P2 { get; set; } }Instanci
Rectanglelze vytvořit a inicializovat následujícím způsobem:Rectangle r = new Rectangle { P1 = new Point { X = 0, Y = 1 }, P2 = new Point { X = 2, Y = 3 } };To má stejný účinek jako
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;kde
__r,__p1a__p2jsou dočasné proměnné, které jsou jinak neviditelné a nepřístupné.Pokud konstruktor
Rectanglepřiděluje dvě vloženéPointinstance, je možné je použít k inicializaci vloženýchPointinstancí místo přiřazování nových instancí:public class Rectangle { public Point P1 { get; } = new Point(); public Point P2 { get; } = new Point(); }Pomocí následujícího konstruktoru lze inicializovat vložené
Pointinstance místo přiřazování nových instancí:Rectangle r = new Rectangle { P1 = { X = 0, Y = 1 }, P2 = { X = 2, Y = 3 } };To má stejný účinek jako
Rectangle __r = new Rectangle(); __r.P1.X = 0; __r.P1.Y = 1; __r.P2.X = 2; __r.P2.Y = 3; Rectangle r = __r;koncového příkladu
12.8.17.2.3 Inicializátory kolekce
Inicializátor kolekce určuje prvky kolekce.
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)*
;
Inicializátor kolekce se skládá z posloupnosti inicializátorů prvků ohraničených tokeny { a } a oddělených čárkami. Každý inicializátor elementu určuje prvek, který se má přidat do inicializovaného objektu kolekce, a skládá se ze seznamu výrazů uzavřených { a } tokeny a oddělené čárkami. Inicializátor elementu s jedním výrazem lze zapsat bez závorek, ale nesmí být přiřazovacím výrazem, aby nedocházelo k nejednoznačnosti s inicializátory členů. Výroba non_assignment_expression je definována v §12.24.
Příklad: Následuje příklad výrazu pro vytvoření objektu, který obsahuje inicializátor kolekce:
List<int> digits = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };koncového příkladu
Objekt kolekce, na který se použije inicializátor kolekce, musí být typu, který implementuje System.Collections.IEnumerable nebo dojde k chybě v době kompilace. Pro každý zadaný prvek v pořadí odleva doprava se normální vyhledávání členů použije k vyhledání členu s názvem Add. Pokud výsledek vyhledávání člena není skupina metod, dojde k chybě v době kompilace. V opačném případě je vyhodnocení přetížení aplikováno s výrazovým seznamem inicializátoru prvku jako seznamem argumentů a inicializátor kolekce vyvolá výslednou metodu. Objekt kolekce tedy musí obsahovat platnou instanci nebo rozšiřující metodu s názvem Add pro každý inicializátor prvku.
Příklad: Následující text ukazuje třídu, která představuje kontakt se jménem a seznamem telefonních čísel a dále pak vytvoření a inicializaci
List<Contact>:public class Contact { public string Name { get; set; } public List<string> PhoneNumbers { get; } = new List<string>(); } class A { static void M() { var contacts = new List<Contact> { new Contact { Name = "Chris Smith", PhoneNumbers = { "206-555-0101", "425-882-8080" } }, new Contact { Name = "Bob Harris", PhoneNumbers = { "650-555-0199" } } }; } }který má stejný účinek jako
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;kde
__clist,__c1a__c2jsou dočasné proměnné, které jsou jinak neviditelné a nepřístupné.koncového příkladu
12.8.17.3 Výrazy pro vytváření anonymních objektů
anonymous_object_creation_expression se používá k vytvoření objektu anonymního typu.
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
;
Inicializátor anonymního objektu deklaruje anonymní typ a vrátí instanci tohoto typu. Anonymní typ je beznázvový typ třídy, který dědí přímo z object. Členy anonymního typu jsou posloupnost vlastností jen pro čtení odvozených z inicializátoru anonymního objektu použitého k vytvoření instance typu. Konkrétně anonymní inicializátor objektu formuláře
new {
p₁=e₁,p₂=e₂, ...
pv=ev}
deklaruje anonymní typ formuláře.
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() { ... }
}
kde každý «Tx» je typ odpovídajícího výrazu «ex». Výraz použitý v member_declarator musí mít typ. Jedná se tedy o chybu při kompilaci, pokud je výraz v member_declaratornull nebo anonymní funkcí.
Názvy anonymního typu a parametru pro jeho Equals metodu jsou automaticky generovány kompilátorem a nelze na to odkazovat v textu programu.
Ve stejném programu dva inicializátory anonymních objektů, které určují posloupnost vlastností stejných názvů a typů kompilátoru ve stejném pořadí, vytvoří instance stejného anonymního typu.
Příklad: V příkladu
var p1 = new { Name = "Lawnmower", Price = 495.00 }; var p2 = new { Name = "Shovel", Price = 26.95 }; p1 = p2;přiřazení na posledním řádku je povoleno, protože
p1ap2jsou stejného anonymního typu.koncového příkladu
Metody Equals a GetHashcode u anonymních typů přepíší metody zděděné z objecta jsou definovány z hlediska Equals a GetHashcode vlastností, aby byly dvě instance stejného anonymního typu stejné, a to pouze v případě, že jsou všechny jejich vlastnosti stejné.
Deklarátor člena lze zkrátit na jednoduchý název (§12.8.4), přístup člena (§12.8.7), inicializátor podmíněné projekce null §12.8.8 nebo základní přístup (§12.8.15). To se nazývá inicializátor projekce a je zkratka pro deklaraci a přiřazení vlastnosti se stejným názvem. Konkrétně členské deklarátory formulářů
«identifier», «expr» . «identifier» a «expr» ? . «identifier»
jsou přesně ekvivalentní následujícímu:
«identifier» = «identifier», «identifier» = «expr» . «identifier» a «identifier» = «expr» ? . «identifier»
Proto v inicializátoru projekce identifikátor vybere hodnotu i pole nebo vlastnost, ke které je hodnota přiřazena. Intuitivně inicializátor projekce projektuje nejen hodnotu, ale také název hodnoty.
12.8.17.4 Výrazy vytváření polí
array_creation_expression se používá k vytvoření nové instance array_type.
array_creation_expression
: 'new' non_array_type '[' expression_list ']' rank_specifier*
array_initializer?
| 'new' array_type array_initializer
| 'new' rank_specifier array_initializer
;
Výraz vytvoření pole prvního tvaru přidělí instanci pole toho typu, který vznikne odstraněním každého z jednotlivých výrazů ze seznamu výrazů.
Příklad: Výraz pro vytvoření pole
new int[10,20]vytvoří instanci pole typuint[,]a nový výraz pro vytvoření poleint[10][,]vytvoří instanci pole typuint[][,]. koncového příkladu
Každý výraz v seznamu výrazů musí být typu int, uint, longnebo ulongnebo implicitně konvertibilní na jeden nebo více těchto typů. Hodnota každého výrazu určuje délku odpovídající dimenze v nově přidělené instanci pole. Vzhledem k tomu, že délka rozměru pole musí být nezáporná, je v době kompilace chybou mít v seznamu výrazů konstantní výraz se zápornou hodnotou.
S výjimkou nebezpečného kontextu (§24.2) není určeno rozložení polí.
Pokud výraz vytvoření pole prvního formuláře obsahuje inicializátor pole, musí být každý výraz v seznamu výrazů konstantou a délkami pořadí a dimenzí určenými seznamem výrazů musí odpovídat hodnotám inicializátoru pole.
Ve výrazu vytvoření pole druhé nebo třetí formy se pořadí zadaného typu pole nebo specifikátoru pořadí musí shodovat s inicializátorem pole. Jednotlivé délky dimenzí jsou odvozeny z počtu prvků v každé z odpovídajících úrovní vnoření inicializátoru pole. Výraz inicializátoru tedy v následující deklaraci
var a = new int[,] {{0, 1}, {2, 3}, {4, 5}};
přesně odpovídá
var a = new int[3, 2] {{0, 1}, {2, 3}, {4, 5}};
Výraz vytvoření pole třetího typu se označuje jako implicitně zadaný výraz pro vytvoření pole. Podobá se druhé formě s tím rozdílem, že typ prvku pole není explicitně uveden, ale je určen jako nejlepší společný typ (§12.6.3.16) sady výrazů v inicializátoru pole. Pro multidimenzionální pole, tj. jedno, kde rank_specifier obsahuje aspoň jednu čárku, se tato sada skládá ze všech výrazů nalezených ve vnořených array_initializers.
Inicializátory pole jsou popsány dále v §17.7.
Výsledek vyhodnocení výrazu vytvoření pole je klasifikován jako hodnota, konkrétně odkaz na nově přidělenou instanci pole. Zpracování za běhu výrazu pro vytvoření pole se skládá z následujících kroků:
- Výrazy délky dimenzí expression_list se vyhodnocují v pořadí zleva doprava. Po vyhodnocení každého výrazu se provádí implicitní převod (§10.2) na jeden z následujících typů:
int,uint,long,ulong. První typ v tomto seznamu, pro který existuje implicitní převod, je vybrán. Pokud vyhodnocení výrazu nebo následné implicitní převody způsobí výjimku, nevyhodnotí se žádné další výrazy a nebudou provedeny žádné další kroky. - Vypočítané hodnoty délky dimenzí se ověřují následujícím způsobem: Pokud je jedna nebo více hodnot menší než nula, vyvolá se
System.OverflowExceptiona neprojdou žádné další kroky. - Je přidělena instance pole s danou délkou dimenze. Pokud není k dispozici dostatek paměti pro přidělení nové instance, vyvolá se
System.OutOfMemoryExceptiona neprojdou žádné další kroky. - Všechny prvky nové instance pole jsou inicializovány na výchozí hodnoty (§9.3).
- Pokud výraz pro vytvoření pole obsahuje inicializátor pole, je každý výraz v inicializátoru pole vyhodnocen a přiřazen k příslušnému prvku pole. Vyhodnocení a přiřazení se provádějí v pořadí, v jakém se výrazy zapisují v inicializátoru pole – jinými slovy, prvky se inicializují v rostoucím pořadí indexu, přičemž první dimenze úplně vpravo se zvyšuje. Pokud vyhodnocení daného výrazu nebo následného přiřazení k odpovídajícímu prvku pole způsobí výjimku, nebudou inicializovány žádné další prvky (a zbývající prvky tak budou mít výchozí hodnoty).
Výraz vytvoření pole umožňuje vytvořit instanci pole s prvky typu pole, ale prvky takového pole musí být inicializovány ručně.
Příklad: Příkaz
int[][] a = new int[100][];vytvoří jednorozměrné pole s 100 prvky typu
int[]. Počáteční hodnota každého prvku jenull. Není možné, aby stejný výraz pro vytvoření pole také instancoval dílčí pole.int[][] a = new int[100][5]; // Errorvýsledkem je chyba v době kompilace. Vytvoření instance dílčích polí je možné místo toho provést ručně, například v
int[][] a = new int[100][]; for (int i = 0; i < 100; i++) { a[i] = new int[5]; }koncového příkladu
Poznámka: Pokud má matice polí obdélníkový tvar, je to v případě, že jsou podmatice všechny stejné délky, je efektivnější použít vícerozměrné pole. V předchozím příkladu instanciace pole polí vytvoří 101 objektů – jedno vnější pole a 100 podpolí. Na rozdíl
int[,] a = new int[100, 5];vytvoří pouze jeden objekt, dvojrozměrné pole a provede přidělení v jednom příkazu.
koncová poznámka
Příklad: Následující příklady implicitně zadaných výrazů vytváření pole:
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" }; // ErrorPoslední výraz způsobí chybu v době kompilace, protože
intanistringnejsou implicitně konvertibilní na druhý, a proto neexistuje nejlepší společný typ. V tomto případě musí být použit explicitně zadaný výraz pro vytvoření pole, například určení typu, který má býtobject[]. Alternativně lze jeden z prvků přetypovat na běžný základní typ, který by se pak stal odvozeným typem elementu.koncového příkladu
Implicitně typované výrazy vytváření pole lze kombinovat s anonymními inicializátory objektů (§12.8.17.3) k vytvoření anonymně typovaných datových struktur.
Příklad :
var contacts = new[] { new { Name = "Chris Smith", PhoneNumbers = new[] { "206-555-0101", "425-882-8080" } }, new { Name = "Bob Harris", PhoneNumbers = new[] { "650-555-0199" } } };koncového příkladu
Výrazy pro vytváření delegátů
K získání instance delegate_typese používá delegate_creation_expression .
delegate_creation_expression
: 'new' delegate_type '(' expression ')'
;
Argumentem výrazu vytvoření delegáta musí být skupina metod, anonymní funkce, nebo hodnota buď typu stanoveného v době kompilace dynamic, nebo typ delegáta . Pokud je argumentem skupina metod, identifikuje metodu a v případě metody instance objekt, pro který se má vytvořit delegát. Pokud je argument anonymní funkcí, přímo definuje parametry a tělo metody cíle delegáta. Pokud je argument hodnotou, identifikuje instanci delegáta, ze které se má vytvořit kopie.
Pokud má výraz typ dynamickompilace, je delegate_creation_expression dynamicky vázán (§12.8.17.5) a následující pravidla se použijí za běhu pomocí typu běhu výrazu. V opačném případě se pravidla použijí v době kompilace.
Zpracování vazby delegate_creation_expression ve tvaru new D(E), kde D je delegate_type a E je výraz, se skládá z následujících kroků:
Pokud
Eje skupina metod, výraz vytvoření delegáta se zpracuje stejným způsobem jako převod skupiny metod (§10,8) zEnaD.Je-li
Eanonymní funkcí, výraz vytvoření delegáta se zpracuje stejným způsobem jako anonymní převod funkce (§10,7) zEnaD.Je-li
Ehodnota,Emusí být kompatibilní (§21.2) sDa výsledkem je odkaz na nově vytvořený delegát se seznamem vyvolání s jednou položkou, která vyvoláE.
Zpracování běhu delegate_creation_expression ve tvaru new D(E), kde D je delegate_type a E je výraz, se skládá z následujících kroků:
- Pokud
Eje skupina metod, výraz vytvoření delegáta se vyhodnotí jako převod skupiny metod (§10.8) zEnaD. - Pokud
Eje anonymní funkce, je vytvoření delegáta vyhodnoceno jako anonymní převod funkce zEnaD(§10,7). - Pokud je
Ehodnotou typu delegáta :-
Ese vyhodnotí. Pokud toto vyhodnocení způsobí výjimku, neprojdou žádné další kroky. - Pokud je hodnota
Enull, vyvolá seSystem.NullReferenceExceptiona nebude provedeno žádné další kroky. - Je přidělena nová instance typu delegáta
D. Pokud není k dispozici dostatek paměti pro přidělení nové instance, vyvolá seSystem.OutOfMemoryExceptiona neprojdou žádné další kroky. - Nová instance delegáta se inicializuje s jednopoložkovým seznamem volání, který vyvolá
E.
-
Seznam vyvolání delegáta se určí, když se delegát instancuje, a zůstane konstantní po celou dobu existence delegáta. Jinými slovy, po vytvoření delegáta není možné změnit cílové volatelné entity delegáta.
Poznámka: Nezapomeňte, že pokud jsou dva delegáti zkombinováni nebo jeden z nich je odebrán z jiného, vznikne nový delegát; žádný existující delegát nemá změněný obsah. koncová poznámka
Není možné vytvořit delegát, který odkazuje na vlastnost, indexer, uživatelem definovaný operátor, konstruktor instance, finalizátor nebo statický konstruktor.
Příklad: Jak je popsáno výše, když je delegát vytvořen ze skupiny metod, seznam parametrů a návratový typ delegáta určují, které z přetížených metod se mají vybrat. V příkladu
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; }pole
A.fje inicializováno delegátem, který odkazuje na druhou metoduSquare, protože tato metoda přesně odpovídá seznamu parametrů a návratovému typuDoubleFunc. Pokud druháSquaremetoda nebyla přítomna, došlo k chybě v době kompilace.koncového příkladu
12.8.18 Operátor typeof
Operátor typeof slouží k získání System.Type objektu pro typ.
typeof_expression
: 'typeof' '(' type ')'
| 'typeof' '(' unbound_type_name ')'
| 'typeof' '(' 'void' ')'
;
unbound_type_name
: identifier generic_dimension_specifier?
('.' identifier generic_dimension_specifier?)*
| unbound_qualified_alias_member
('.' identifier generic_dimension_specifier?)*
;
unbound_qualified_alias_member
: identifier '::' identifier generic_dimension_specifier?
;
generic_dimension_specifier
: '<' comma* '>'
;
comma
: ','
;
První forma typeof_expression se skládá z typeof klíčového slova následovaného typem uzavřeným v závorkách. Výsledkem výrazu tohoto formuláře je System.Type objekt pro zadaný typ. Pro každý daný typ existuje pouze jeden objekt System.Type. To znamená, že pro typ Tje typeof(T) == typeof(T) vždy pravdivý. Typ nemůže být dynamic.
Druhá forma typeof_expression se skládá z typeof klíčového slova následovaného závorkou unbound_type_name.
Poznámka: Gramatiky unbound_type_name a unbound_qualified_alias_member odpovídají gramatikám type_name (§7.8) a qualified_alias_member (§14.8.1) s tím rozdílem, že generic_dimension_specifier jsou nahrazeny type_argument_list. koncová poznámka
Při rozpoznávání operandu typeof_expression, pokud jsou použitelné obě možnosti, unbound_type_name i type_name, a to v případě, že neobsahuje generic_dimension_specifier ani type_argument_list, type_name bude vybrán.
Poznámka: ANTLR nastaví zadanou volbu automaticky z důvodu řazení alternativ typeof_expression. koncová poznámka
Význam unbound_type_name je určen jako:
- Posloupnost tokenů se převede na type_name tím, že se každý generic_dimension_specifier nahradí type_argument_list, který má stejný počet čárek a klíčové slovo
objectjako každý type_argument. - Výsledný type_name se přeloží na konstruovaný typ (§7.8).
- Unbound_type_name je pak nevázaný obecný typ spojený s vyřešeným konstruovaným typem (§8.4).
Poznámka: Neexistuje žádný požadavek na to, aby implementace transformovala posloupnost tokenů nebo vytvořila zprostředkující konstruovaný typ; pouze že nevázaný obecný typ, který je určen, by měl být "jako by" tento proces byl dodržen. koncová poznámka
Je chybou, aby název typu byl nulovatelným referenčním typem.
Výsledkem typeof_expression je objekt System.Type pro výsledný nevázaný obecný typ.
Třetí forma typeof_expression se skládá z typeof klíčového slova následovaného vloženým do závorek void klíčovým slovem. Výsledkem výrazu této formy je objekt System.Type, který představuje nepřítomnost typu. Objekt typu vrácený typeof(void) se liší od objektu typu vráceného pro libovolný typ.
Poznámka: Tento speciální objekt
System.Typeje užitečný v knihovnách tříd, které umožňují reflexi na metody v daném jazyce. Tyto metody chtějí mít způsob, jak reprezentovat návratový typ jakékoli metody, včetně metodvoid, s instancíSystem.Type. koncová poznámka
Operátor typeof lze použít u parametru typu. Jedná se o chybu v době kompilace, pokud je název typu známý jako typ odkazu s možnou hodnotou null. Výsledkem je System.Type objekt pro typ běhu, který byl vázán na parametr typu. Pokud je běhový typ typu odkaz s možnou hodnotou null, je výsledkem odpovídající typ odkazu, který není nulovatelný. Operátor typeof lze použít také u typu konstrukce nebo nevázaného obecného typu (§8.4.4). Objekt System.Type pro nevázaný obecný typ není stejný jako objekt System.Type typu instance (§15.3.2). Typ instance je vždy uzavřený typ za běhu, takže jeho System.Type objekt závisí na argumentech typu za běhu. Nevázaný obecný typ na druhé straně nemá žádné argumenty typu a poskytuje stejný System.Type objekt bez ohledu na argumenty typu modulu runtime.
Příklad: Příklad
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(); } }vytvoří následující výstup:
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]Všimněte si, že
intaSystem.Int32jsou stejného typu. Výsledektypeof(X<>)nezávisí na argumentu typu, ale výsledektypeof(X<T>)ano.koncového příkladu
12.8.19 Operátor sizeof
Operátor sizeof vrátí počet 8bitových bajtů obsazených proměnnou daného typu. Typ určený jako operand pro velikost musí být unmanaged_type (§8.8).
sizeof_expression
: 'sizeof' '(' unmanaged_type ')'
;
U některých předdefinovaných typů operátor sizeof poskytuje konstantní int hodnotu, jak je znázorněno v následující tabulce:
| výraz | Výsledek |
|---|---|
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 |
Pro typ výčtu Tje výsledek výrazu sizeof(T) konstantní hodnota, která se rovná velikosti jeho základního typu, jak je uvedeno výše. Pro všechny ostatní typy operandů sizeof je operátor uveden v §24.6.9.
12.8.20 Zaškrtnuté a nezaškrtnuté operátory
Operátory checked a unchecked slouží k řízení kontextu kontroly přetečení pro aritmetické operace a převody celočíselného typu.
checked_expression
: 'checked' '(' expression ')'
;
unchecked_expression
: 'unchecked' '(' expression ')'
;
Operátor checked vyhodnotí obsažený výraz v kontrolovaném kontextu a operátor unchecked vyhodnotí obsažený výraz v nezaškrtnutém kontextu.
checked_expression nebo unchecked_expression se přesně shoduje s parenthesized_expression (§12.8.5), s tím rozdílem, že obsažený výraz je hodnocen v daném kontextu kontroly přetečení.
Kontext kontroly přetečení lze také řídit prohlášeními checked a unchecked (§13.12).
Následující operace jsou ovlivněny kontextem kontroly přetečení vytvořeným kontrolovanými a nekontrolovanými operátory a příkazy:
- Předdefinované a
++operátory--(§12.8.16 a §12.9.7), pokud je operand celočíselný nebo výčtový typ. - Předdefinovaný
-unární operátor (§12.9.3), pokud je operand celočíselného typu. - Předdefinované
+, ,-*a/binární operátory (§12.12), pokud oba operandy jsou celočíselné nebo výčtové typy. - Explicitní číselné převody (§10.3.2) z jednoho integrálního nebo výčtového typu na jiný integrální nebo výčtový typ, nebo z
floatnebodoublena celočíselný nebo výčtový typ.
Pokud jedna z výše uvedených operací vytvoří výsledek, který je příliš velký, aby byl reprezentován v cílovém typu, kontext, ve kterém je operace prováděna, řídí výsledného chování:
-
checkedPokud je operace konstantním výrazem (§12.25), dojde k chybě v době kompilace. Jinak se při provedení operace za běhu vyvoláSystem.OverflowException. - V kontextu
uncheckedse výsledek zkrátí tak, že zahodí všechny bity s vysokým pořadím, které se nevejdou do cílového typu.
Pro ne constantní výrazy (§12.25) (výrazy vyhodnocené za běhu), které nejsou uzavřeny žádnými checked nebo operátory nebo unchecked příkazy, není výchozí kontext kontroly přetečení nezaškrtnut, pokud externí faktory (například přepínače kompilátoru a konfigurace spouštěcího prostředí) volají ke kontrole vyhodnocení.
U konstantních výrazů (§12.25) (výrazy, které lze plně vyhodnotit v době kompilace), je vždy kontrolován výchozí kontext kontroly přetečení. Pokud není konstantní výraz explicitně umístěn v kontextu unchecked, přetečení, ke kterým dochází při vyhodnocování doby kompilace výrazu, vždy způsobí chyby kompilace.
Tělo anonymní funkce není ovlivněno checked nebo unchecked kontexty, ve kterých se anonymní funkce vyskytuje.
příklad: V následujícím kódu
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 }Nejsou hlášeny žádné chyby v době kompilace, protože ani jeden z výrazů nelze vyhodnotit v době kompilace. Při běhu programu vyvolá metoda
FSystem.OverflowException, a metodaGvrátí –727379968 (32 nejnižších bitů výsledku mimo rozsah). Chování metodyHzávisí na výchozím kontextu kontroly přetečení pro kompilaci, ale je to buď stejné jakoF, nebo stejné jakoG.koncového příkladu
příklad: V následujícím kódu
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 }přetečení, která nastávají při hodnocení konstantních výrazů v
FaH, způsobují nahlášení chyb v době kompilace, protože výrazy se hodnotí v kontextuchecked. K přetečení dochází také při vyhodnocování konstantního výrazu vG, ale protože vyhodnocení probíhá v kontextuunchecked, přetečení není hlášeno.koncového příkladu
Operátory checked a unchecked mají vliv pouze na kontext kontroly přetečení pro operace, které jsou textově obsažené v tokenech(a). Operátory nemají žádný vliv na členy funkce, které jsou vyvolány v důsledku vyhodnocení obsaženého výrazu.
příklad: V následujícím kódu
class Test { static int Multiply(int x, int y) => x * y; static int F() => checked(Multiply(1000000, 1000000)); }použití
checkedv F nemá vliv na vyhodnoceníx * yvMultiply, takžex * yje vyhodnocen ve výchozím kontextu kontroly přetečení.koncového příkladu
Operátor unchecked je vhodný při psaní konstant celočíselných typů se znaménkovým zápisem.
Příklad :
class Test { public const int AllBits = unchecked((int)0xFFFFFFFF); public const int HighBit = unchecked((int)0x80000000); }Oba šestnáctkové konstanty výše jsou typu
uint. Vzhledem k tomu, že konstanty jsou mimo rozsahint, bez operátoruuncheckedby přetypování naintvytvářelo chyby v době kompilace.koncového příkladu
Poznámka: Operátory a příkazy
checkedauncheckedumožňují programátorům řídit určité aspekty některých číselných výpočtů. Chování některých číselných operátorů ale závisí na datových typech operandů. Například vynásobením dvou desetinných čísel vždy dojde k výjimce při přetečení, i v rámci explicitně neověřené konstrukce. Podobně při vynásobení dvou plovoucích desetinných čísel nedochází k výjimce při přetečení, a to ani v rámci explicitně kontrolované konstrukce. Kromě toho nejsou ostatní operátory nikdy ovlivněny kontrolou, ať už výchozí nebo explicitní. koncová poznámka
12.8.21 Výrazy výchozí hodnoty
Výchozí výraz hodnoty slouží k získání výchozí hodnoty (§9.3) typu.
default_value_expression
: explicitly_typed_default
| default_literal
;
explicitly_typed_default
: 'default' '(' type ')'
;
default_literal
: 'default'
;
default_literal představuje výchozí hodnotu (§9,3). Nemá typ, ale lze jej převést na jakýkoli typ pomocí výchozího literálového převodu (§10.2.16).
Výsledkem default_value_expression je výchozí hodnota (§9.3) explicitního typu v explicitly_typed_default nebo cílový typ převodu default_value_expression.
Default_value_expression je konstantní výraz (§12.25), pokud je typ jedním z:
- referenční typ
- parametr typu, který je znám jako referenční typ (§8.2);
- jeden z následujících typů hodnot:
sbyte,byte,short,ushort,int,uint,long,ulong,char,float,double,decimal,bool,; nebo - libovolný typ výčtu.
12.8.22 Alokace zásobníku
Výraz přidělení zásobníku přiděluje blok paměti ze zásobníku spouštění. Zásobník spouštění je oblast paměti, ve které jsou uloženy místní proměnné. Vykonávací zásobník není součástí spravované haldy. Paměť používaná pro místní úložiště proměnných se automaticky obnoví při vrácení aktuální funkce.
Pravidla bezpečného kontextu výrazu přidělování zásobníku jsou popsána v §16.4.15.7.
stackalloc_expression
: 'stackalloc' unmanaged_type '[' expression ']'
| 'stackalloc' unmanaged_type? '[' constant_expression? ']' stackalloc_initializer
;
stackalloc_initializer
: '{' stackalloc_initializer_element_list '}'
;
stackalloc_initializer_element_list
: stackalloc_element_initializer (',' stackalloc_element_initializer)* ','?
;
stackalloc_element_initializer
: expression
;
unmanaged_type (§8.8) označuje typ položek, které budou uloženy v nově přiděleném umístění, a výraz označuje počet těchto položek. Společně určují požadovanou velikost přidělení. Typ výrazu musí být implicitně konvertibilní na typ int.
Vzhledem k tomu, že velikost přidělení zásobníku nemůže být záporná, jedná se o chybu v době kompilace, která určuje počet položek jako constant_expression, která se vyhodnotí jako záporná hodnota.
Za běhu, pokud počet položek, které se mají přidělit, je záporná hodnota, pak chování není definováno. Pokud je nula, pak se neprovedou žádné přidělení a vrácená hodnota je definována implementací. Pokud není k dispozici dostatek paměti pro přidělení položek, vyvolá se System.StackOverflowException.
Když je k dispozici stackalloc_initializer:
- Je-li unmanaged_type vynechán, je odvozeno podle pravidel pro nejlepší společný typ (§12.6.3.16) pro sadu stackalloc_element_initializers.
- Pokud constant_expression vynecháte, je odvozeno, že se jedná o počet stackalloc_element_initializers.
- Je-li constant_expression přítomna, rovná se počtu stackalloc_element_initializers.
Každý stackalloc_element_initializer má implicitní převod na unmanaged_type (§10.2). stackalloc_element_initializerinicializuje prvky v přidělené paměti v rostoucím pořadí, počínaje prvkem na indexu nula. V nepřítomnosti stackalloc_initializernení definován obsah nově přidělené paměti.
Pokud se stackalloc_expression vyskytuje přímo jako inicializační výraz local_variable_declaration (§13.6.2), kde local_variable_type je typ ukazatele (§24.3) nebo odvozený (var), je výsledek stackalloc_expression ukazatelem typu T* (§24.9). V takovém případě se stackalloc_expression musí objevit v nebezpečném kódu. V opačném případě je výsledkem stackalloc_expression instance typu Span<T>, kde T je unmanaged_type:
-
Span<T>(§C.3) je typ ref struktury (§16.2.3), který reprezentuje blok paměti, zde blok přidělený stackalloc_expression, jako indexovatelnou kolekci položek (T). - Vlastnost
Lengthvýsledku vrátí počet přidělených položek. - Indexer výsledku (§15,9) vrátí variable_reference (§9,5) k položce přiděleného bloku a je kontrolován rozsah.
Inicializátory přidělování zásobníku nejsou povoleny v blocích catch nebo finally (§13.11).
Poznámka: Neexistuje způsob, jak explicitně uvolnit paměť přidělenou pomocí
stackalloc. koncová poznámka
Všechny bloky paměti přidělené zásobníkem vytvořené během provádění člena funkce se automaticky zahodí, když tento člen funkce vrátí.
Kromě operátoru stackalloc jazyk C# neposkytuje žádné předdefinované konstrukce pro správu paměti, která není spravována docházkovým sběračem. Tyto služby jsou obvykle poskytovány podporou knihoven tříd nebo importovány přímo ze základního operačního systému.
Příklad :
// 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; } }V případě
span8mástackallocza následekSpan<int>, který je převeden implicitním operátorem naReadOnlySpan<int>. Podobně uspan9se výslednáSpan<double>převede na uživatelem definovaný typWidget<double>pomocí převodu, jak je znázorněno. koncového příkladu
12.8.23 Operátor nameof
nameof_expression se používá pro získání názvu programové entity jako konstantního řetězce.
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
;
Protože nameof není klíčové slovo, nameof_expression je vždy syntakticky nejednoznačné s voláním jednoduchého jména nameof. Z důvodu kompatibility, pokud je vyhledání názvu (§12.8.4) jména nameof úspěšné, je výraz považován za invocation_expression – bez ohledu na to, zda je vyvolání platné. Jinak je to nameof_expression.
Na named_entity v době kompilace se provádějí jednoduché vyhledávání a přístup členů podle pravidel popsaných v §12.8.4 a §12.8.7. Pokud však vyhledávání popsané v §12.8.4 a §12.8.7 vede k chybě, protože člen instance byl nalezen ve statickém kontextu, nameof_expression taková chyba nevyvolá.
Jedná se o chybu v době kompilace pro named_entity označující skupinu metod tak, aby měla type_argument_list. Je to chyba v době kompilace, když named_entity_target má typ dynamic.
nameof_expression je konstantní výraz typu stringa nemá žádný účinek za běhu. Konkrétně se jeho named_entity nevyhodnocuje a je ignorována pro účely analýzy jednoznačného přiřazení (§9.4.4.22). Jeho hodnota je poslední identifikátor named_entity před volitelným konečným seznamem typových argumentů , transformována následujícím způsobem:
- Předpona "
@", pokud se používá, je odebrána. - Každý unicode_escape_sequence se transformuje na odpovídající znak Unicode.
- Odeberou se všechny formátovací znaky.
Jedná se o stejné transformace použité ve §6.4.3 při testování rovnosti mezi identifikátory.
Příklad: Následující ilustruje výsledky různých výrazů
nameofza předpokladu, že je deklarován obecný typList<T>v rámci oboru názvůSystem.Collections.Generic: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 { } }Potenciálně překvapivými částmi tohoto příkladu jsou rozlišení
nameof(System.Collections.Generic)na pouze "Generic" místo úplného oboru názvů, a rozlišenínameof(TestAlias)na "TestAlias" místo "String". koncového příkladu
12.8.24 Výrazy anonymní metody
anonymous_method_expression je jeden ze dvou způsobů, jak definovat anonymní funkci. Dále jsou popsány v §12.21.
12.9 Unární operátory
12.9.1 Obecné
+, ( -! logická negace §12.9.4), ~, , ^, ++, přetypování --a await operátory se nazývají unární operátory.
Poznámka: Operátor null-odpustit (§12.8.9),
!, je z výše uvedeného seznamu vyloučen kvůli své povaze, která je pouze pro kompilaci a nelze ji přetížit. koncová poznámka
unary_expression
: primary_expression
| '+' unary_expression
| '-' unary_expression
| logical_negation_operator unary_expression
| '~' unary_expression
| '^' unary_expression
| pre_increment_expression
| pre_decrement_expression
| cast_expression
| await_expression
| pointer_indirection_expression // unsafe code support
| addressof_expression // unsafe code support
;
pointer_indirection_expression (§24.6.2) a addressof_expression (§24.6.5) jsou k dispozici pouze v nebezpečném kódu (§24).
Je-li operand unary_expression v době kompilace typu dynamic, je dynamicky vázán (§12.3.3). V tomto případě:
- typ kompilace unary_expression je:
-
Indexrejstříku od koncového^operátora (§12.9.6) -
dynamicpro všechny ostatní unární operátory; a
-
- řešení popsané níže se provede za běhu pomocí typu běhu operandu.
12.9.2 Unární operátor plus
Pro operaci ve tvaru +xse použije přetížení unárního operátoru (§12.4.4) pro výběr konkrétní implementace operátoru. Operand je převeden na typ parametru vybraného operátoru a typ výsledku je návratový typ operátoru. Unární operátory plus jsou předdefinované:
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);
Pro každý z těchto operátorů je výsledkem jednoduše hodnota operandu.
Zvedané (§12.4.8) formy předdefinovaných předdefinovaných unárních operátorů plus definovaných výše jsou také předdefinovány.
12.9.3 Unární operátor minus
Pro operaci ve tvaru –xse použije přetížení unárního operátoru (§12.4.4) pro výběr konkrétní implementace operátoru. Operand je převeden na typ parametru vybraného operátoru a typ výsledku je návratový typ operátoru. Předdefinované unární minusové operátory jsou:
Celočíselná negace:
int operator –(int x); long operator –(long x);Výsledek se vypočítá odečtením
Xod nuly. Je-li hodnotaXnejmenší reprezentovatelnou hodnotou typu operandu (−2³¹ prointnebo −2⁶³ prolong), není matematická negaceXreprezentovatelná v rámci typu operandu. Pokud k tomu dojde v kontextuchecked, vyvolá seSystem.OverflowException; pokud dojde vuncheckedkontextu, je výsledkem hodnota operandu a přetečení není hlášeno.Je-li operand operátoru negace typu
uint, je převeden na typlonga typ výsledku jelong. Výjimkou je pravidlo, které povoluje zápis hodnotyint−2147483648(−2³¹) jako celočíselná literál (§6.4.5.3).Pokud je operand operátoru negace typu
ulong, dojde k chybě v době kompilace. Výjimkou je pravidlo, které povoluje zápis hodnotylong−9223372036854775808(−2⁶³) jako celočíselné literály (§6.4.5.3)Negace plovoucí desetinné čárky:
float operator –(float x); double operator –(double x);Výsledkem je hodnota
Xs invertovaným znakem. Pokud jexNaN, výsledek je takéNaN.Desetinná negace:
decimal operator –(decimal x);Výsledek se vypočítá odečtením
Xod nuly. Desetinná negace je ekvivalentní použití unárního operátoru minus typuSystem.Decimal.
Liftované (§12.4.8) tvary ne-liftovaných předdefinovaných unárních operátorů minus definovaných výše jsou také předdefinovány.
12.9.4 Logický operátor negace
Pro operaci ve tvaru !xse použije přetížení unárního operátoru (§12.4.4) pro výběr konkrétní implementace operátoru. Operand je převeden na typ parametru vybraného operátoru a typ výsledku je návratový typ operátoru. Existuje pouze jeden předdefinovaný logický operátor negace:
bool operator !(bool x);
Tento operátor vypočítá logickou negaci operandu: Pokud je operand true, výsledek je false. Pokud je operand false, výsledek je true.
Pozvednuté (§12.4.8) formy neliftovaných předdefinovaných logických operátorů negace uvedených výše jsou také předdefinovány.
Poznámka: Operátory prefixového logického záporu a postfixového null-odpouštějícího (§12.8.9), přestože jsou reprezentovány stejným lexikálním znakem (!), jsou odlišné.
koncová poznámka
12.9.5 Operátor bitového doplňku
Pro operaci ve tvaru ~xse použije přetížení unárního operátoru (§12.4.4) pro výběr konkrétní implementace operátoru. Operand je převeden na typ parametru vybraného operátoru a typ výsledku je návratový typ operátoru. Předdefinované bitové doplňkové operátory jsou:
int operator ~(int x);
uint operator ~(uint x);
long operator ~(long x);
ulong operator ~(ulong x);
Výsledkem operace pro každý z těchto operátorů je bitový doplněk x.
Každý typ výčtu E implicitně poskytuje následující operátor bitového doplňku:
E operator ~(E x);
Výsledek vyhodnocení ~x, kde X je výraz typu výčtu E s podkladovým typem U, je naprosto stejný jako vyhodnocení (E)(~(U)x), s tím rozdílem, že převod na E je vždy proveden jako v kontextu unchecked (§12.8.20).
Pozvedané (§12.4.8) formy od nepozvednutých předdefinovaných bitových doplňkových operátorů definovaných výše jsou také předdefinovány.
12.9.6 Index od koncového operátoru
Unární ^ operátor se nazývá index od koncového operátoru (který se označuje jako operátor hat). Tento operátor není přetížitelný (§12.4.3) a existuje jeden předdefinovaný operátor:
Index operator ^(int x);
Výsledkem operace formuláře ^x je hodnota od konce Index (§18.2) ekvivalentní výsledku výrazu:
new Index(x, true)
Stejně jako u ostatních unary_expressions operand může mít typ dynamic kompilační doby (§12.9.1) a být dynamicky vázán (§12.3.3). Typ kompilace výsledku je vždy Index.
Předdefinovaná je rovněž předdefinovaná forma indexu od koncového operátoru (§12.4.8).
12.9.7 Operátory inkrementace a dekrementace předpon
pre_increment_expression
: '++' unary_expression
;
pre_decrement_expression
: '--' unary_expression
;
Operand operace s předponovým přírůstkem či dekrementací musí být výraz klasifikovaný jako proměnná, přístup k vlastnosti nebo přístup indexeru. Výsledkem operace je hodnota stejného typu jako operand.
Pokud je operandem operace předřazené inkrementace nebo dekrementace vlastnost nebo přístup indexeru, pak musí mít vlastnost nebo indexer přístup get i přístup set. Pokud tomu tak není, dojde k chybě při určování doby vazby.
Rozlišení přetížení unárního operátoru (§12.4.4) se použije k výběru konkrétní implementace operátoru. Předdefinované operátory ++ a -- existují pro následující typy: sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimala libovolný typ výčtu. Předdefinované operátory ++ vrátí hodnotu vytvořenou přidáním 1 do operandu a předdefinované operátory -- vrátí hodnotu vytvořenou odečtením 1 z operandu. V kontextu checked je-li výsledek tohoto sčítání nebo odčítání mimo rozsah typu výsledku a typ výsledku je celočíselný typ nebo výčtový typ, je vyvolán System.OverflowException.
Existuje implicitní převod z návratového typu vybraného unárního operátoru na typ unary_expression, jinak dojde k chybě v době kompilace.
Zpracování za běhu operace předpony přírůstku nebo dekrementace formuláře ++x nebo --x se skládá z následujících kroků:
- Pokud se
xklasifikuje jako proměnná:-
xse vyhodnotí tak, aby vznikla proměnná. - Hodnota
xje převedena na typ operandu vybraného operátoru a operátor je vyvolán s touto hodnotou jako argument. - Hodnota vrácená operátorem je převedena na typ
x. Výsledná hodnota je uložena v umístění zadaném vyhodnocenímxa stane se výsledkem operace.
-
- Pokud je
xklasifikován jako přístup k vlastnosti nebo indexeru:- Výraz instance (pokud
xnenístatic) a seznam argumentů (pokudxje přístup k indexeru) přidružený kxse vyhodnotí a výsledky se použijí v následných vyvolání get a set. - Vyvolá se přístupový objekt get
x. - Hodnota vrácená akcesorem get je převedena na typ operandu vybraného operátoru a operátor je vyvolán s touto hodnotou jako argument.
- Hodnota vrácená operátorem je převedena na typ
x. Nastavovací přístupxje volán s touto hodnotou jako argumentem. - Tato hodnota se také stane výsledkem operace.
- Výraz instance (pokud
Operátory ++ a -- podporují také postfixovou notaci (§12.8.16). Výsledkem x++ nebo x-- je hodnota x před operací, zatímco výsledek ++x nebo --x je hodnota x po operaci. V obou případech má x stejnou hodnotu po operaci.
Implementaci operátoru ++ nebo operátoru -- lze vyvolat pomocí postfixové nebo prefixové notace. Pro dva různé zápisy není možné mít samostatné implementace operátorů.
Zdvižené (§12.4.8) tvary u nezdvižených předem definovaných operátorů předpony inkrementace a dekrementace definovaných výše jsou také předurčeny.
12.9.8 Přetypování výrazů
cast_expression se používá k explicitnímu převodu výrazu na daný typ.
cast_expression
: '(' type ')' unary_expression
;
cast_expression ve formě (T)E, kde T je typ a E je unary_expression, provede explicitní převod (§10.3) hodnoty E na typ T. Pokud neexistuje žádný explicitní převod z E na T, dojde k chybě doby vazby. V opačném případě je výsledkem hodnota vytvořená explicitním převodem. Výsledek je vždy klasifikován jako hodnota, i když E označuje proměnnou.
Gramatika cast_expression vede k určitým syntaktickým nejednoznačnostem.
Příklad: Výraz
(x)–ylze interpretovat buď jako přetypovací výraz (přetypování–yk typux), nebo jako sčítací výraz v kombinaci s výrazem v závorkách (který vypočítá hodnotux – y). koncového příkladu
K vyřešení cast_expression nejednoznačností existuje následující pravidlo: Posloupnost jednoho nebo více tokenů (§6.4) uzavřených v závorkách se považuje za začátek cast_expression pouze v případě, že platí alespoň jedna z následujících hodnot:
- Posloupnost tokenů je správná gramatika pro typ, ale ne pro výraz.
- Posloupnost tokenů je správná gramatika pro typ a token bezprostředně za pravou závorkou je token "
~", token "!", token "(", identifikátor (§6.4.3), literál (§6.4.5) nebo jakékoli klíčové slovo (§6.4.4) s výjimkouasais.
Termín "správná gramatika" výše znamená pouze to, že posloupnost tokenů odpovídá konkrétní gramatické produkci. Konkrétně nebere v úvahu skutečný význam žádných identifikátorů prvků.
příklad: Pokud
xayjsou identifikátory,x.yje pro typ správná gramatika, i kdyžx.yve skutečnosti neoznačuje typ. koncového příkladu
Poznámka: Z pravidla nejednoznačnosti platí, že pokud
xayjsou identifikátory,(x)y,(x)(y)a(x)(-y)jsou cast_expressions, ale(x)-ynení, i kdyžxidentifikuje typ. Pokud je všakxklíčovým slovem, které identifikuje předdefinovaný typ (napříkladint), jsou všechny čtyři formuláře cast_expressions (protože takové klíčové slovo by pravděpodobně nemohlo být výraz sám). koncová poznámka
12.9.9 Výrazy Await
12.9.9.1 Obecné
Operátor await slouží k pozastavení vyhodnocení ohraničující asynchronní funkce, dokud se nedokončí asynchronní operace reprezentovaná operandem.
await_expression
: 'await' unary_expression
;
Await_expression je povolena pouze v těle asynchronní funkce (§15.14). V nejbližší obalující asynchronní funkci se await_expression nesmí vyskytovat na těchto místech:
- Uvnitř vnořené (nesynchronní) anonymní funkce
- Uvnitř bloku lock_statement
- Při převodu anonymní funkce na typ výrazového stromu (§10.7.3)
- V nebezpečném kontextu
Poznámka: await_expression se nemůže vyskytovat na většině míst v rámci query_expression, protože jsou syntakticky transformovány tak, aby používaly neasynchronní lambda výrazy. koncová poznámka
Uvnitř asynchronní funkce se await nepoužije jako available_identifier, i když lze použít doslovný identifikátor @await. Neexistuje proto syntaktická nejednoznačnost mezi await_expressions a různými výrazy zahrnujícími identifikátory. Mimo asynchronní funkce await funguje jako normální identifikátor.
Operand await_expression se nazývá úkol. Představuje asynchronní operaci, která může nebo nemusí být dokončena v době vyhodnocení await_expression . Účelem operátoru await je pozastavit provádění ohraničující asynchronní funkce, dokud není dokončena očekávaná úloha, a pak získat její výsledek.
12.9.9.2 Výrazy Awaitable
Úkol await_expression musí být čekaný. Výraz t lze očekávat, pokud platí jedna z následujících podmínek:
-
tje typ kompilacedynamic -
tmá přístupnou metodu instance nebo rozšíření nazvanouGetAwaiter, která nemá žádné parametry ani typové parametry, a návratový typA, pro který platí všechny následující podmínky:-
AimplementujeSystem.Runtime.CompilerServices.INotifyCompletionrozhraní (dále označované jakoINotifyCompletionpro stručnost). -
Amá přístupnou, čitelnou vlastnost instanceIsCompletedtypubool -
Amá přístupnou metodu instanceGetResultbez parametrů a parametrů typu
-
Účelem metody GetAwaiter je získat awaiter pro úkol. Typ A se nazývá typem awaiteru pro výraz await.
Účelem vlastnosti IsCompleted je určit, zda je úkol již dokončen. Pokud ano, není nutné pozastavit hodnocení.
Účelem metody INotifyCompletion.OnCompleted je registrace "pokračování" úkolu; tj. delegát (typu System.Action), který bude vyvolán po dokončení úkolu.
Účelem GetResult metody je získat výsledek úkolu po jeho dokončení. Tento výsledek může být úspěšné dokončení, případně s hodnotou výsledku, nebo může být výjimkou, která je vyvolána metodou GetResult.
12.9.9.3 Klasifikace výrazů await
Výraz await t je klasifikován stejným způsobem jako výraz (t).GetAwaiter().GetResult(). Pokud je tedy návratový typ GetResultvoid, await_expression se klasifikuje jako žádný. Pokud má voidnávratový typ, který neníT , await_expression se klasifikuje jako hodnota typu T.
12.9.9.4 Vyhodnocení výrazů await za běhu
Za běhu se výraz await t vyhodnotí takto:
- Awaiter
aje získán vyhodnocením výrazu(t).GetAwaiter(). -
boolbse získá vyhodnocením výrazu(a).IsCompleted. - Pokud je
bfalse, vyhodnocení závisí na tom, jestliaimplementuje rozhraníSystem.Runtime.CompilerServices.ICriticalNotifyCompletion(dále jenICriticalNotifyCompletionpro stručnost). Tato kontrola se provádí v době vazby; tj. za běhu, pokud máatyp v době kompilacedynamic, a jinak při kompilaci. Označmerdelegáta pokračování (§15.14):- Pokud
aneimplementujeICriticalNotifyCompletion, vyhodnotí se výraz((a) as INotifyCompletion).OnCompleted(r). - Pokud
aimplementujeICriticalNotifyCompletion, vyhodnotí se((a) as ICriticalNotifyCompletion).UnsafeOnCompleted(r). - Vyhodnocení se pak pozastaví a řízení se vrátí k aktuálnímu volajícímu asynchronní funkce.
- Pokud
- Buď ihned po (pokud
bbyltrue), nebo po pozdějším vyvolání delegáta obnovení (pokudbbylfalse), vyhodnotí se výraz(a).GetResult(). Pokud vrátí hodnotu, je tato hodnota výsledkem await_expression. Jinak výsledek není žádný.
Implementace metod rozhraní INotifyCompletion.OnCompleted a ICriticalNotifyCompletion.UnsafeOnCompleted by měla způsobit vyvolání delegáta r nejvýše jednou. V opačném případě je chování nadřazené asynchronní funkce nedefinované.
12.10 Operátor rozsahu
Operátor .. se nazývá operátor rozsahu .
range_expression
: unary_expression
| unary_expression? '..' unary_expression?
;
Předdefinovaný operátor rozsahu je:
Range operator ..(Index x, Index y);
Operátor rozsahu není přetížitelný (§12.4.3).
Všechny výrazy rozsahu jsou považovány za formulář x..y, kde:
-
xje levý operand, pokud je k dispozici, jinak výraz0; a -
yje pravý operand, pokud je k dispozici, jinak výraz^0.
Výsledkem operace je Range hodnota (§18.3), která odpovídá výsledku výrazu:
new Range(x, y)
Pokud výraz rozsahu má buď operandy nebo oba operandy typ dynamickompilačního času , je výraz dynamicky vázán (§12.3.3). Typ kompilace výsledku je vždy Range.
Předdefinovaná je rovněž předdefinovaná forma operátoru rozsahu (§12.4.8).
Operátor rozsahu není asociativní (§12.4.2).
12.11 Výraz Switch
Switch_expression poskytuje switchsémantika výrazu v kontextu výrazu.
switch_expression
: range_expression
| switch_expression 'switch' '{' switch_expression_arms? '}'
;
switch_expression_arms
: switch_expression_arm (',' switch_expression_arm)* ','?
;
switch_expression_arm
: pattern case_guard? '=>' switch_expression_arm_expression
;
switch_expression_arm_expression
: expression
;
Existuje převod výrazu přepínače (§10.2.18) z výrazu přepínače na typ T , pokud existuje implicitní převod z každého switch_expression_arm_expression každého výrazu přepínače switch_expression_armna T.
Pokud výraz switch není předmětem převodu výrazu switch, pak
- Typ switch_expression je nejlepším běžným typem §12.6.3.16) switch_expression_arm_expressionswitch_expression_arms, pokud takový typ existuje, a každý switch_expression_arm_expression lze implicitně převést na tento typ.
- Pokud takový typ neexistuje, jedná se o chybu.
Jedná se o chybu, pokud určitý vzor switch_expression_arm nemůže ovlivnit výsledek, protože některý předchozí vzor a ochrana budou vždy odpovídat.
Výraz switch se říká, že je vyčerpávající , pokud každá hodnota jeho vstupu je zpracována alespoň jedním ramenem výrazu switch. Kompilátor vygeneruje upozornění, pokud výraz přepínače není vyčerpávající.
Za běhu je výsledkem switch_expression hodnota výrazu prvního switch_expression_arm , pro který výraz na levé straně switch_expression odpovídá vzoru switch_expression_arm a pro který se case_guardswitch_expression_arm, pokud existuje, vyhodnotí jako true. Pokud neexistuje taková switch_expression_arm, switch_expression vyvolá instanci výjimky System.InvalidOperationException (nebo třídy odvozené z této).
Příklad: Následující funkce převede hodnoty výčtu představující vizuální směry na online mapě na odpovídající směry mohutnosti:
static Orientation ToOrientation(Direction direction) => direction switch { Direction.Up => Orientation.North, Direction.Right => Orientation.East, Direction.Down => Orientation.South, Direction.Left => Orientation.West, _ => throw new ArgumentOutOfRangeException(direction.ToString()), }; public enum Direction { Up, Down, Right, Left } public enum Orientation { North, South, East, West }koncového příkladu
12.12 Aritmetické operátory
12.12.1 Obecné
Operátory *, /, %, +a - se nazývají aritmetické operátory.
multiplicative_expression
: switch_expression
| multiplicative_expression '*' switch_expression
| multiplicative_expression '/' switch_expression
| multiplicative_expression '%' switch_expression
;
additive_expression
: multiplicative_expression
| additive_expression '+' multiplicative_expression
| additive_expression '-' multiplicative_expression
;
Pokud má operand aritmetického operátoru typ kompilace dynamic, je výraz dynamicky vázán (§12.3.3). V tomto případě je typ kompilátoru výrazu dynamica níže popsané řešení proběhne za běhu pomocí typu běhu těchto operandů, které mají typ kompilace dynamic.
12.12.2 Operátor násobení
Pro operaci ve tvaru x * yse rozlišení přetížení binárního operátoru (§12.4.5) používá k určení specifické implementace operátoru. Operandy jsou převedeny na typy parametrů vybraného operátoru a typ výsledku je návratový typ operátoru.
Předdefinované operátory násobení jsou uvedeny níže. Všechny operátory vypočítají součin mezi x a y.
Celočíselné násobení:
int operator *(int x, int y); uint operator *(uint x, uint y); long operator *(long x, long y); ulong operator *(ulong x, ulong y);V kontextu
checked, pokud je produkt mimo rozsah typu výsledku, je vyvolánaSystem.OverflowException. V kontextuuncheckednejsou přetečení hlášena a žádné významné bity s vysokým pořadím mimo rozsah typu výsledku jsou zahozena.Násobení s plovoucí desetinnou čárkou:
float operator *(float x, float y); double operator *(double x, double y);Produkt se vypočítá podle pravidel aritmetik IEC 60559. Následující tabulka uvádí výsledky všech možných kombinací nenulových konečných hodnot, nul, infinit a sítí NAN. V tabulce jsou
xaykladné konečné hodnoty.zje výsledkemx * y, zaokrouhleno na nejbližší reprezentovatelnou hodnotu. Pokud je velikost výsledku pro cílový typ příliš velká,zje nekonečno. Vzhledem k zaokrouhlování může býtznula, i kdyžxaniynení nula.+y-y+0-0+∞-∞NaN+x+z-z+0-0+∞-∞NaN-x-z+z-0+0-∞+∞NaN+0+0-0+0-0NaNNaNNaN-0-0+0-0+0NaNNaNNaN+∞+∞-∞NaNNaN+∞-∞NaN-∞-∞+∞NaNNaN-∞+∞NaNNaNNaNNaNNaNNaNNaNNaNNaN(Pokud není uvedeno jinak, znamená použití "" v tabulkách s plovoucí desetinnou čárkou v §12.12.2–§12.6 použití "
+" znamená, že hodnota je záporná; a nedostatek znaménka znamená, že hodnota může být kladná-nebo záporná nebo nemá žádné znaménko (NaN).)Násobení desetinných čísel
decimal operator *(decimal x, decimal y);Pokud je velikost výsledné hodnoty příliš velká na to, aby ji bylo možné reprezentovat v desítkovém formátu, je generována chyba
System.OverflowException. Vzhledem k zaokrouhlování může být výsledek nula, i když žádný operand není nula. Měřítko výsledku před zaokrouhlováním je součet měřítka dvou operandů. Násobení desetinných čísel je ekvivalentní použití operátoru násobení typuSystem.Decimal.
Zvedané (§12.4.8) tvary nezvednutých předdefinovaných operátorů násobení definovaných výše jsou také předdefinovány.
12.12.3 Operátor dělení
Pro operaci ve tvaru x / yse rozlišení přetížení binárního operátoru (§12.4.5) používá k určení specifické implementace operátoru. Operandy jsou převedeny na typy parametrů vybraného operátoru a typ výsledku je návratový typ operátoru.
Níže jsou uvedeny předdefinované operátory dělení. Všichni operátoři vypočítají podíl mezi x a y.
Celočíselné dělení:
int operator /(int x, int y); uint operator /(uint x, uint y); long operator /(long x, long y); ulong operator /(ulong x, ulong y);Pokud je hodnota pravého operandu nula, vyvolá se
System.DivideByZeroException.Dělení zaokrouhlí výsledek na nulu. Absolutní hodnota výsledku je tedy největší možné celé číslo, které je menší nebo rovno absolutní hodnotě podílu obou operandů. Výsledek je nulový nebo kladný, pokud dva operandy mají stejné znaménko a nula nebo záporné, pokud mají dva operandy opačné znaménka.
Pokud je levý operand nejmenší hodnota, kterou lze reprezentovat jako
intnebolong, a pravý operand je–1, dojde k přetečení. V kontextucheckedto způsobí vyvoláníSystem.ArithmeticException(nebo jeho podtřídy). V kontextuuncheckedzáleží na implementaci, zda je vyvolána výjimkaSystem.ArithmeticException(nebo její podtřída), nebo zda je přetečení ignorováno a výsledná hodnota odpovídá levému operandu.Dělení s plovoucí desetinnou čárkou:
float operator /(float x, float y); double operator /(double x, double y);Podíl se vypočítá podle pravidel aritmetiky IEC 60559. Následující tabulka uvádí výsledky všech možných kombinací nenulových konečných hodnot, nul, infinit a sítí NAN. V tabulce jsou
xaykladné konečné hodnoty.zje výsledkemx / y, zaokrouhleno na nejbližší reprezentovatelnou hodnotu.+y-y+0-0+∞-∞NaN+x+z-z+∞-∞+0-0NaN-x-z+z-∞+∞-0+0NaN+0+0-0NaNNaN+0-0NaN-0-0+0NaNNaN-0+0NaN+∞+∞-∞+∞-∞NaNNaNNaN-∞-∞+∞-∞+∞NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNDělení desetinných čísel
decimal operator /(decimal x, decimal y);Pokud je hodnota pravého operandu nula, vyvolá se
System.DivideByZeroException. Pokud je velikost výsledné hodnoty příliš velká na to, aby ji bylo možné reprezentovat v desítkovém formátu, je generována chybaSystem.OverflowException. Vzhledem k zaokrouhlování může být výsledek nula, i když první operand není nula. Měřítko výsledku před zaokrouhlováním je nejblíže k upřednostňované škále, která zachová výsledek, který se rovná přesnému výsledku. Upřednostňované měřítko je měřítkoxs odečtením měřítkay.Desetinné dělení je ekvivalentní použití operátoru dělení typu
System.Decimal.
Zdvižené (§12.4.8) formy nezdvižených předdefinovaných operátorů dělení definovaných výše jsou také předdefinovány.
12.12.4 Operátor zbytku
Pro operaci ve tvaru x % yse rozlišení přetížení binárního operátoru (§12.4.5) používá k určení specifické implementace operátoru. Operandy jsou převedeny na typy parametrů vybraného operátoru a typ výsledku je návratový typ operátoru.
Předdefinované operátory zbytku jsou uvedeny níže. Všechny operátory vypočítají zbytek dělení mezi x a y.
Zbytek po dělení celými čísly:
int operator %(int x, int y); uint operator %(uint x, uint y); long operator %(long x, long y); ulong operator %(ulong x, ulong y);Výsledkem
x % yje hodnota vytvořenáx – (x / y) * y. Pokud jeynula, vyvolá seSystem.DivideByZeroException.Pokud je levý operand nejmenší
intnebolonga pravý operand je–1, vyvolá seSystem.OverflowException, pouze pokudx / yvyvolá výjimku.Zbytek čísla s plovoucí řádovou čárkou:
float operator %(float x, float y); double operator %(double x, double y);Následující tabulka uvádí výsledky všech možných kombinací nenulových konečných hodnot, nul, infinit a sítí NAN. V tabulce jsou
xaykladné konečné hodnoty.zje výsledkemx % ya vypočítá se jakox – n * y, kde n je největší možné celé číslo, které je menší nebo rovnox / y. Tato metoda výpočtu zbytku je podobná té, která se používá pro celočíselné operandy, ale liší se od definice IEC 60559 (ve které jencelé číslo nejblížex / y).+y-y+0-0+∞-∞NaN+x+z+zNaNNaN+x+xNaN-x-z-zNaNNaN-x-xNaN+0+0+0NaNNaN+0+0NaN-0-0-0NaNNaN-0-0NaN+∞NaNNaNNaNNaNNaNNaNNaN-∞NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNDesetinný zbytek:
decimal operator %(decimal x, decimal y);Pokud je hodnota pravého operandu nula, vyvolá se
System.DivideByZeroException. Je určeno implementací, kdy je vyvolánaSystem.ArithmeticException(nebo její podtřída). Odpovídající implementace nevyvolá výjimku prox % yv každém případě, kdyx / ynevyvolá výjimku. Škála výsledku před zaokrouhlením je větší z měřítek dvou operandů a znamení výsledku, pokud není nula, je stejné jako ux.Desetinný zbytek je ekvivalentní použití operátoru zbytku typu
System.Decimal.Poznámka: Tato pravidla zajišťují, aby výsledek pro všechny typy nikdy neměl opačné znaménko levého operandu. koncová poznámka
Zvednuté (§12.4.8) verze výše definovaných předdefinovaných zbytkových operátorů jsou také předdefinovány.
12.12.5 Operátor sčítání
Pro operaci ve tvaru x + yse rozlišení přetížení binárního operátoru (§12.4.5) používá k určení specifické implementace operátoru. Operandy jsou převedeny na typy parametrů vybraného operátoru a typ výsledku je návratový typ operátoru.
Předdefinované operátory sčítání jsou uvedeny níže. Předdefinované operátory sčítání u číselných a výčtových typů vypočítávají součet dvou operandů. Pokud je jeden nebo oba operandy typu string, předdefinované operátory sčítání zřetězí jejich řetězcovou reprezentaci.
Celočíselné sčítání:
int operator +(int x, int y); uint operator +(uint x, uint y); long operator +(long x, long y); ulong operator +(ulong x, ulong y);V kontextu
checked, pokud je součet mimo rozsah typu výsledku, je vyvolánSystem.OverflowException. V kontextuuncheckednejsou přetečení hlášena a žádné významné bity s vysokým pořadím mimo rozsah typu výsledku jsou zahozena.Sčítání s plovoucí řádovou čárkou
float operator +(float x, float y); double operator +(double x, double y);Součet se vypočítá podle pravidel aritmetik IEC 60559. Následující tabulka uvádí výsledky všech možných kombinací nenulových konečných hodnot, nul, infinit a sítí NAN. V tabulce jsou
xaynenulové konečné hodnoty azje výsledkemx + y. Pokudxaymají stejnou velikost, ale opačné znaky,zje kladná nula. Pokud jex + ypříliš velký na zobrazení v cílovém typu,zje nekonečno se stejným znaménkem jakox + y.y+0-0+∞-∞NaNxzxx+∞-∞NaN+0y+0+0+∞–∞NaN-0y+0-0+∞-∞NaN+∞+∞+∞+∞+∞NaNNaN-∞-∞-∞-∞NaN-∞NaNNaNNaNNaNNaNNaNNaNNaNSčítání desetinných čísel
decimal operator +(decimal x, decimal y);Pokud je velikost výsledné hodnoty příliš velká na to, aby ji bylo možné reprezentovat v desítkovém formátu, je generována chyba
System.OverflowException. Měřítko výsledku před zaokrouhlováním je větší z měřítek dvou operandů.Desetinné sčítání je ekvivalentní použitím operátoru sčítání typu
System.Decimal.Přidávání k výčtu. Každý typ výčtu implicitně poskytuje následující předdefinované operátory, kde
Eje typ výčtu aUje základní typE:E operator +(E x, U y); E operator +(U x, E y);Během běhu se tyto operátory vyhodnocují přesně způsobem jako
(E)((U)x + (U)y).Zřetězení řetězců:
string operator +(string x, string y); string operator +(string x, object y); string operator +(object x, string y);Tato přetížení binárního operátoru
+provádí zřetězení řetězců. Pokud je operand zřetězení řetězcenull, nahradí se prázdný řetězec. V opačném případě se jakýkoli operand, který nenístring, převede na řetězcovou reprezentaci vyvoláním virtuálníToStringmetody zděděné z typuobject. PokudToStringvrátínull, nahradí se prázdný řetězec.Příklad :
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 } }Výstup zobrazený v komentářích je typickým výsledkem US-English systému. Přesný výstup může záviset na místním nastavení spouštěcího prostředí. Operátor zřetězení řetězců se v každém případě chová stejným způsobem, ale místní nastavení můžou mít vliv na metody
ToStringimplicitně volané během provádění.koncového příkladu
Výsledkem operátoru zřetězení řetězce je
string, který se skládá ze znaků levého operandu, po kterých následují znaky pravého operandu. Operátor zřetězení řetězců nikdy nevrátí hodnotunull.System.OutOfMemoryExceptionmůže být vyhozena, pokud není k dispozici dostatek paměti pro přidělení výsledného řetězce.Kombinace delegátů Každý typ delegáta implicitně poskytuje následující předdefinovaný operátor, kde
Dje typ delegáta:D operator +(D x, D y);Pokud je první operand
null, je výsledkem operace hodnota druhého operandu (i když je to takénull). Jinak, pokud je druhý operandnull, je výsledkem operace hodnota prvního operandu. V opačném případě je výsledkem operace nová instance delegáta, jejíž seznam vyvolání se skládá z prvků v seznamu vyvolání prvního operandu, následované prvky v seznamu vyvolání druhého operandu. To znamená, že seznam vyvolání výsledného delegáta je spojením seznamů vyvolání dvou operandů.Poznámka: Příklady kombinace delegátů naleznete v článcích §12.12.6 a §21.6. Vzhledem k tomu, že
System.Delegatenení typu delegáta, operátor + není pro něj definován. koncová poznámka
Zvedané (§12.4.8) tvary nezdvižených předdefinovaných operátorů sčítání definované výše jsou také předdefinovány.
12.12.6 Operátor odčítání
Pro operaci ve tvaru x – yse rozlišení přetížení binárního operátoru (§12.4.5) používá k určení specifické implementace operátoru. Operandy jsou převedeny na typy parametrů vybraného operátoru a typ výsledku je návratový typ operátoru.
Předdefinované operátory odčítání jsou uvedeny níže. Všechny operátory odečítají y od x.
Odčítání celého čísla:
int operator –(int x, int y); uint operator –(uint x, uint y); long operator –(long x, long y); ulong operator –(ulong x, ulong yPokud je v kontextu
checkedrozdíl mimo rozsah typu výsledku, vyvolá seSystem.OverflowException. V kontextuuncheckednejsou přetečení hlášena a žádné významné bity s vysokým pořadím mimo rozsah typu výsledku jsou zahozena.Odčítání s plovoucí desetinou čárkou:
float operator –(float x, float y); double operator –(double x, double y);Rozdíl se vypočítá podle pravidel aritmetik IEC 60559. Následující tabulka uvádí výsledky všech možných kombinací nenulových konečných hodnot, nul, infinit a sítí NAN. V tabulce jsou
xaynenulové konečné hodnoty azje výsledkemx – y. Pokud jsouxaystejné,zje kladná nula. Pokud jex – ypříliš velký na zobrazení v cílovém typu,zje nekonečno se stejným znaménkem jakox – y.y+0-0+∞-∞NaNxzxx-∞+∞NaN+0-y+0+0-∞+∞NaN-0-y-0+0-∞+∞NaN+∞+∞+∞+∞NaN+∞NaN-∞-∞-∞-∞-∞NaNNaNNaNNaNNaNNaNNaNNaNNaN(Ve výše uvedené tabulce položky
-yoznačují negaci uy, nikoli že jde o zápornou hodnotu.)Odčítání desetinných čísel:
decimal operator –(decimal x, decimal y);Pokud je velikost výsledné hodnoty příliš velká na to, aby ji bylo možné reprezentovat v desítkovém formátu, je generována chyba
System.OverflowException. Měřítko výsledku před zaokrouhlováním je větší z měřítek dvou operandů.Odčítání desetinných čísel je ekvivalentní k použití operátoru odčítání typu
System.Decimal.Odčítání výčtu Každý typ výčtu implicitně poskytuje následující předdefinovaný operátor, kde
Eje typ výčtu aUje základní typE:U operator –(E x, E y);Tento operátor je vyhodnocen přesně jako
(U)((U)x – (U)y). Jinými slovy, operátor vypočítá rozdíl mezi pořadovými hodnotamixaya typem výsledku je základní typ výčtu.E operator –(E x, U y);Tento operátor je vyhodnocen přesně jako
(E)((U)x – y). Jinými slovy, operátor odečte hodnotu od základního typu výčtu a získá hodnotu výčtu.Odebrání delegáta Každý typ delegáta implicitně poskytuje následující předdefinovaný operátor, kde
Dje typ delegáta:D operator –(D x, D y);Sémantika je následující:
- Pokud je první operand
null, výsledek operace jenull. - Jinak, pokud je druhý operand
null, je výsledkem operace hodnota prvního operandu. - V opačném případě oba operandy představují neprázdné seznamy volání (§21.2).
- Pokud se seznamy porovnávají stejně, jak je určeno operátorem rovnosti delegáta (§12.14.9), je
nullvýsledkem operace . - V opačném případě je výsledkem operace nový seznam vyvolání, který se skládá ze seznamu prvního operandu s odebranými položkami druhého operandu, za předpokladu, že seznam druhého operandu je dílčí seznam prvního operandu. (Pokud chcete zjistit rovnost dílčího seznamu, porovnávají se odpovídající položky stejně jako u operátoru rovnosti delegáta.) Pokud seznam druhého operandu odpovídá více dílčím seznamům souvislých položek v seznamu prvního operandu, poslední odpovídající dílčí seznam souvislých položek je odstraněn.
- V opačném případě je výsledkem operace hodnota levého operandu.
- Pokud se seznamy porovnávají stejně, jak je určeno operátorem rovnosti delegáta (§12.14.9), je
Ani jeden ze seznamů operandů (pokud existuje) se v procesu nezmění.
Příklad :
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 } }koncového příkladu
- Pokud je první operand
Liftované (§12.4.8) tvary nenaliftovaných předdefinovaných operátorů odčítání definovaných výše jsou také předdefinovány.
12.13 Operátory shift
Operátory << a >> slouží k provádění operací posunu bitů.
shift_expression
: additive_expression
| shift_expression '<<' additive_expression
| shift_expression right_shift additive_expression
;
Pokud má operand shift_expression typ kompilace dynamic, je výraz dynamicky vázán (§12.3.3). V tomto případě je typ kompilátoru výrazu dynamica níže popsané řešení proběhne za běhu pomocí typu běhu těchto operandů, které mají typ kompilace dynamic.
Pro operaci formuláře x << count nebo x >> countse použije rozlišení přetížení binárního operátoru (§12.4.5) pro výběr konkrétní implementace operátoru. Operandy jsou převedeny na typy parametrů vybraného operátoru a typ výsledku je návratový typ operátoru.
Při deklarování přetíženého operátoru posunu musí typ prvního operandu vždy být třída či struktura obsahující deklaraci operátoru a typ druhého operandu musí být vždy int.
Předdefinované operátory posunu jsou uvedeny níže.
Posuňte doleva.
int operator <<(int x, int count); uint operator <<(uint x, int count); long operator <<(long x, int count); ulong operator <<(ulong x, int count);Operátor
<<posunexdoleva o počet bitů vypočítaný, jak je popsáno níže.Bity s vysokým pořadím mimo rozsah typu výsledku
xse zahodí, zbývající bity se posunou doleva a pozice prázdných bitů s nízkým pořadím se nastaví na nulu.Posunout doprava:
int operator >>(int x, int count); uint operator >>(uint x, int count); long operator >>(long x, int count); ulong operator >>(ulong x, int count);Operátor
>>posunexdoprava o počet bitů, jak je popsáno níže.Pokud je
xtypuintnebolong, zahodí se bityxs nízkým pořadím, zbývající bity se posunou doprava a pozice prázdných bitů s vysokým pořadím se nastaví na nulu, pokud jexnezáporná a nastaví se na jeden, pokud jexzáporná.Pokud je
xtypuuintneboulong, zahodí se bityxs nízkým pořadím, zbývající bity se posunou doprava a pozice prázdných bitů s vysokým pořadím se nastaví na nulu.
U předdefinovaných operátorů se počet bitů, které se mají posunout, vypočítá takto:
- Pokud je typ
xintnebouint, počet posunů je dán pěti bity nižšího řáducount. Jinými slovy, počet směn se vypočítá zcount & 0x1F. - Pokud je typ
xlongneboulong, počet posunů je dán nízkými řádovými šesti bitycount. Jinými slovy, počet směn se vypočítá zcount & 0x3F.
Pokud je výsledný počet směn nula, operátory směn jednoduše vrátí hodnotu x.
Operace směny nikdy nezpůsobí přetečení a vytvoří stejné výsledky v nezaškrtnutých kontextech.
Pokud je levý operand operátoru >> znaménkový celočíselný typ, operátor provede aritmetický posun doprava, kde se hodnota nejvýznamnějšího bitu (znaménkového bitu) operandu rozšíří do volných pozic bitů s vysokým pořadím. Pokud levý operand operátoru >> je celočíselný typ bez znaménka, operátor provede logickou posunu doprava, kde jsou pozice prázdných bitů ve vysokém pořadí vždy nastaveny na nulu. Chcete-li provést opačnou operaci odvozenou z typu operandu, lze použít explicitní přetypování.
Příklad: Pokud je
xproměnnou typuint, operaceunchecked ((int)((uint)x >> y))provede logický posun vpravo odx. koncového příkladu
Zvedané (§12.4.8) tvary níže definovaných nepředdefinovaných operátorů posunu jsou také předdefinovány.
12.14 Relační operátory a operátory testování typu
12.14.1 Obecné
Operátory ==, !=, <, >, <=, >=, isa as se nazývají relační operátory a operátory pro testování typů.
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
;
Poznámka: Vyhledání správného operandu operátoru
ismusí nejprve být testováno jako typ, pak jako výraz , který může se rozprostírat přes více tokenů. V případě, že je operand výrazem, musí mít výraz vzoru precedenci alespoň tak vysokou jako shift_expression. koncová poznámka
Poznámka: Existuje gramatická nejednoznačnost mezi typem a constant_pattern na
relational_expressionpravé straněis; může být platná analýza kvalifikovaného identifikátoru. V takovém případě se pouze v případě, že se nepodaří vytvořit vazbu jako typ (kvůli kompatibilitě s předchozími verzemi jazyka), je vyřešena jako první nalezená věc (která musí být buď konstanta, nebo typ). Tato nejednoznačnost je přítomna pouze na pravé straně takového výrazu.
Operátor is je popsán v §12.14.12 a as operátor je popsán v §12.14.13.
Operátory ==, !=, <, >, <= a >= jsou operátory porovnání.
Pokud se default_literal (§ 12.8.21) používá jako operand operátoru <, >, <=nebo >=, dojde k chybě při kompilaci.
Pokud se default_literal používá jako oba operandy operátoru == nebo !=, dojde k chybě v době kompilace. Pokud se default_literal použije jako levý operand operátoru is nebo as, dojde k chybě v době kompilace.
Pokud má operand porovnávacího operátoru typ při kompilaci dynamic, je výraz dynamicky vázán (§12.3.3). V tomto případě je typ kompilace výrazu dynamica rozlišení popsané níže proběhne za běhu pomocí typu běhu těchto operandů, které mají typ kompilačního času dynamic.
Pro operaci ve tvaru x «op» y, kde «op» je porovnávací operátor, je použito přetížení rozlišení (§12.4.5) pro výběr konkrétní implementace operátoru. Operandy jsou převedeny na typy parametrů vybraného operátoru a typ výsledku je návratový typ operátoru. Pokud jsou oba operandy výrazu equality_expression literály null, pak se neprovádí rozlišení přetížení a výraz se vyhodnotí jako konstantní hodnota true nebo false, podle toho, zda je operátor == nebo !=.
Předdefinované porovnávací operátory jsou popsány v následujících pododstavcích. Všechny předdefinované srovnávací operátory vrátí výsledek typu bool, jak je uvedeno v následující tabulce.
| operace | Výsledek |
|---|---|
x == y |
true, pokud se x rovná y, false jinak |
x != y |
true, pokud se x nerovná y, false jinak |
x < y |
true, pokud je x menší než y, false jinak |
x > y |
true, pokud je x větší než y, false jinak |
x <= y |
true, pokud je x menší nebo roven y, false jinak |
x >= y |
true, pokud je x větší než nebo roven y, false jinak |
12.14.2 Celočíselné relační operátory
Předdefinované celočíselné relační operátory jsou:
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);
Každý z těchto operátorů porovnává číselné hodnoty dvou celočíselných operandů a vrací hodnotu bool, která určuje, zda je konkrétní vztah true nebo false.
Lifted (§12.4.8) tvary předdefinovaných předdefinovaných integerových relačních operátorů definovaných výše jsou také předdefinovány.
12.14.3 Relační operátory s plovoucí desetinou čárkou
Předdefinované operátory porovnání pro plovoucí desetinnou čárku jsou:
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);
Operátory porovnávají operandy podle pravidel standardu IEC 60559:
Pokud je jeden operand NaN, výsledek je false pro všechny operátory kromě !=, pro které je výsledek true. U všech dvou operandů x != y vždy vytvoří stejný výsledek jako !(x == y). Pokud jsou však jeden nebo oba operandy NaN, operátory <, >, <=a >=nevygenerují stejné výsledky, jako logická negace oproti opačnému operátoru.
příklad: Pokud některý z
xayje NaN,x < yjefalse, ale!(x >= y)jetrue. koncového příkladu
Pokud ani jeden z operandů není NaN, operátory porovnávají hodnoty dvou operandů s plovoucím desetinným bodem s ohledem na řazení.
–∞ < –max < ... < –min < –0.0 == +0.0 < +min < ... < +max < +∞
kde min a max jsou nejmenší a největší kladné konečné hodnoty, které lze reprezentovat v daném formátu s plovoucí desetinou čárkou. Mezi efekty tohoto řazení patří:
- Záporné a kladné nuly jsou považovány za stejné.
- Záporné nekonečno je považováno za menší než všechny ostatní hodnoty, ale rovná se jinému zápornému nekonečnu.
- Kladné nekonečno se považuje za větší než všechny ostatní hodnoty, ale rovná se jinému kladnému nekonečnu.
Lifted (§12.4.8) formy výše definovaných nepředdefinovaných relačních operátorů s plovoucí desetinnou čárkou jsou také předdefinovány.
12.14.4 Operátory porovnání desetinných míst
Předdefinované relační operátory desetinných míst jsou:
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);
Každý z těchto operátorů porovnává číselné hodnoty dvou desetinných operandů a vrací hodnotu bool, která označuje, zda je konkrétní vztah true nebo false. Každé porovnání desetinných míst odpovídá použití odpovídajícího relačního operátoru nebo operátoru rovnosti typu System.Decimal.
Přenesené (§12.4.8) formy neprodloužených předdefinovaných desetinových porovnávacích operátorů definovaných výše jsou také předdefinovány.
12.14.5 Logické operátory rovnosti
Předdefinované logické operátory rovnosti jsou:
bool operator ==(bool x, bool y);
bool operator !=(bool x, bool y);
Výsledek == je true, pokud jsou x i ytrue nebo pokud jsou x i yfalse. V opačném případě je výsledek false.
Výsledek != je false, pokud jsou x i ytrue nebo pokud jsou x i yfalse. V opačném případě je výsledek true. Pokud jsou operandy typu bool, operátor != vytvoří stejný výsledek jako operátor ^.
Zvýšené (§12.4.8) formy nezvýšených předdefinovaných Booleovských operátorů rovnosti definovaných výše jsou také předdefinovány.
12.14.6 Operátory porovnání výčtu
Každý typ výčtu implicitně poskytuje následující předdefinované relační operátory.
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);
Výsledek vyhodnocení x «op» y, kde x a y jsou výrazy typu výčtu E s podkladovým typem U, a «op» je jedním z relačních operátorů, je přesně stejný jako vyhodnocení ((U)x) «op» ((U)y). Jinými slovy, operátory porovnání typů výčtu jednoduše porovnávají základní celočíselné hodnoty dvou operandů.
Vyzdvižené formy (§12.4.8) nevyzdvižených předdefinovaných relačních operátorů výčtu definovaných výše jsou také předdefinovány.
12.14.7 Operátory rovnosti typů odkazů
Každý typ třídy C implicitně poskytuje následující předdefinované operátory rovnosti typů odkazů:
bool operator ==(C x, C y);
bool operator !=(C x, C y);
pokud neexistují jiné předdefinované operátory rovnosti pro C (například pokud je Cstring nebo System.Delegate).
Operátory vrátí výsledek porovnání dvou referencí na rovnost nebo nerovnost.
operator == vrátí true, pokud a pouze pokud x a y odkazují na stejnou instanci nebo jsou oba null, zatímco operator != vrátí true pokud a pouze pokud operator == se stejnými operandy vrátí false.
Kromě běžných pravidel použitelnosti (§ 12.6.4.2), předdefinované operátory rovnosti typů odkazů vyžadují, aby bylo možné použít jednu z následujících možností:
- Oba operandy jsou hodnotou typu známého jako reference_type nebo literál
null. Kromě toho existuje převod identity nebo explicitního odkazu (§10.3.5) z jednoho operandu na typ druhého operandu. - Jeden operand je literál
nulla druhý operand je hodnota typuT, kdeTje typový parametr, u kterého není známo, že by šlo o typ hodnoty a nemá omezení typem hodnoty.- Pokud je za běhu
Tnenulový typ hodnoty, výsledek==jefalsea výsledek!=jetrue. - Pokud je za běhu
Ttyp hodnoty null, výsledek se vypočítá zHasValuevlastnosti operandu, jak je popsáno v §12.14.10). - Pokud je za běhu
Ttyp odkazu, výsledek jetrue, pokud je operandnull, a jinak jefalse.
- Pokud je za běhu
Není-li splněna některá z těchto podmínek, dojde k chybě při vázání času.
Poznámka: Důležité důsledky těchto pravidel:
- Jedná se o chybu doby vazby při použití předdefinovaných operátorů rovnosti typu odkazu k porovnání dvou odkazů, o kterých je známo, že se v době vazby liší. Pokud jsou například typy času vazby operandů dva typy tříd a ani jeden není odvozený od toho druhého, pak by nebylo možné, aby dva operandy odkazovaly na stejný objekt. Operace je tedy považována za chybu v době vazby.
- Předdefinované operátory rovnosti typu odkazu nepovolují porovnání operandů typu hodnot (s výjimkou případů, kdy jsou parametry typu porovnány s
null, který je zpracován speciálně).- Operandy operátorů rovnosti pro referenční typy, které jsou předem definovány, nejsou nikdy boxovány. Nemá smysl provádět takové operace zaobalování, protože odkazy na nově přidělené zaobalené instance se nutně budou lišit od všech ostatních odkazů.
Pro operaci tvaru
x == ynebox != y, je-li k dispozici uživatelsky definovanáoperator ==nebooperator !=, pravidla řešení přetížení operátoru (§12.4.5) vyberou tenhle operátor namísto předdefinovaného operátoru rovnosti typu odkazu. Vždy je možné vybrat předdefinovaný operátor rovnosti pro referenční typy explicitním přetypováním jednoho nebo obou operandů na typobject.koncová poznámka
Příklad: Následující příklad zkontroluje, zda je argument typu parametrické, který není omezený,
null.class C<T> { void F(T x) { if (x == null) { throw new ArgumentNullException(); } ... } }Konstrukce
x == nullje povolena, i kdyžTmůže být nenulový typ hodnoty a výsledek je jednoduše určen jakofalse, kdyžTpředstavuje nenulový typ hodnoty.koncového příkladu
Pro operaci ve formě x == y nebo x != y, pokud existuje jakýkoli platný operator == nebo operator !=, pravidla rozlišení přetížení operátoru (§12.4.5) vyberou tento operátor místo předdefinovaného operátoru rovnosti typu odkazu.
Poznámka: Vždy je možné vybrat předdefinovaný operátor rovnosti typu odkazu explicitním přetypováním obou operandů na typ
object. koncová poznámka
Příklad: Příklad
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); } }vytvoří výstup.
True False False FalseProměnné
satodkazují na dvě různé instance řetězců obsahující stejné znaky. První porovnání výstupyTrue, protože předdefinovaný operátor rovnosti řetězců (§12.14.8) je vybrán, pokud jsou oba operandy typustring. Zbývající porovnání všech mají výstupFalse, protože přetíženíoperator ==typustringnení relevantní, pokud má některý operand typ přiřazení časuobject.Všimněte si, že výše uvedená technika není pro typy hodnot smysluplná. Příklad
class Test { static void Main() { int i = 123; int j = 123; Console.WriteLine((object)i == (object)j); } }výstup je
False, protože přetypování vytváří odkazy na dvě samostatné instance boxovanýchinthodnot.koncového příkladu
12.14.8 Operátory rovnosti řetězců
Předdefinované operátory rovnosti řetězců jsou:
bool operator ==(string x, string y);
bool operator !=(string x, string y);
Dvě string hodnoty jsou považovány za stejné, pokud platí jedna z následujících hodnot:
- Obě hodnoty jsou
null. - Obě hodnoty jsou ne-
nullodkazy na instance řetězců, které mají identické délky a identické znaky v každé pozici znaku.
Operátory rovnosti řetězců porovnávají řetězcové hodnoty místo odkazů na řetězce. Pokud dvě samostatné instance řetězců obsahují přesně stejnou sekvenci znaků, hodnoty řetězců jsou stejné, ale odkazy se liší.
Poznámka: Jak je popsáno v §12.14.7, lze operátory rovnosti referenčního typu použít k porovnání řetězcových odkazů namísto řetězcových hodnot. koncová poznámka
12.14.9 Delegování operátorů rovnosti
Předdefinované operátory rovnosti delegáta jsou:
bool operator ==(System.Delegate x, System.Delegate y);
bool operator !=(System.Delegate x, System.Delegate y);
Dvě instance delegátů se považují za stejné:
- Pokud je některá z instancí delegáta
null, jsou stejné, pokud a pouze pokud jsou obanull. - Pokud mají delegáti různé běhové typy, nejsou nikdy stejné.
- Pokud obě instance delegáta mají seznam vyvolání (§21.2), jsou tyto instance stejné, pouze pokud jejich seznamy vyvolání mají stejnou délku a každá položka v seznamu vyvolání je stejná (jak je definováno níže) odpovídající položky v seznamu vyvolání druhého.
Následující pravidla určují rovnost položek seznamu volání:
- Pokud dvě položky seznamu vyvolání oba odkazují na stejnou statickou metodu, položky jsou stejné.
- Pokud dvě položky seznamu volání odkazují na stejnou nestatickou metodu na stejném cílovém objektu (jak je definováno operátory rovnosti referencí), položky jsou si rovny.
- Vyvolání položek seznamu vytvořených z vyhodnocení sémanticky identických anonymních funkcí (§12.21) se stejnou (případně prázdnou) sadou zachycených instancí vnější proměnné jsou povoleny (ale nejsou vyžadovány).
Pokud se překlad přetížení operátoru přeloží na operátor rovnosti delegáta a typy vazeb obou operandů jsou typy delegátů, jak je popsáno v §21 , a System.Delegateneexistuje žádný převod identity mezi typy operandu typu vazby, dojde k chybě doby vazby.
Poznámka: Toto pravidlo zabraňuje porovnáním, která nemohou považovat hodnoty odlišné od
nullza rovné, protože jsou odkazy na instance různých typů delegátů. koncová poznámka
12.14.10 Operátory rovnosti mezi typy hodnot s možnou hodnotou null a literálem null
Operátory == a != umožňují, aby jeden operand byl hodnotou nulovatelného typu hodnoty a druhý operand byl literálem null, a to i v případě, že pro operaci neexistuje žádný předdefinovaný nebo uživatelem definovaný operátor (v nezvednuté nebo zvednuté formě).
Pro jednu z forem operace
x == null null == x x != null null != x
Pokud je x výrazem typu hodnoty null a rozlišení přetížení operátoru (§12.4.5) nenajde použitelný operátor, výsledek se místo toho vypočítá z vlastnosti HasValue objektu x. Konkrétně první dvě formuláře jsou přeloženy do !x.HasValuea poslední dvě formuláře jsou přeloženy do x.HasValue.
12.14.11 Operátory rovnosti řazené kolekce členů
Operátory rovnosti u n-tic jsou aplikovány na prvky operandů n-tic ve dvojicích a v lexikálním pořadí.
Pokud je každý operand x a y operátoru == nebo != klasifikován buď jako n-tice, nebo jako hodnota s typem n-tice (§8.3.11), je operátor operátor rovnosti n-tic.
Pokud je operand e klasifikován jako řazená kolekce členů, jsou prvky, e1...en výsledkem vyhodnocení výrazů prvků výrazu řazené kolekce členů. Jinak je-li e hodnotou typu n-tice, musí být prvky t.Item1...t.Itemn, kde t je výsledkem vyhodnocení e.
Operandy x a y operátoru rovnosti n-tice musí mít stejný počet prvků, jinak dojde k chybě při kompilaci. Pro každou dvojici prvků xi a yise použije stejný operátor rovnosti, který poskytne výsledek typu bool, dynamic, typ, který má implicitní převod na bool, nebo typ, který definuje operátory true a false.
Operátor rovnosti n-tice x == y se vyhodnotí takto:
- Operand na levé straně
xje vyhodnocen. - Vyhodnocuje se pravý operand
y. - Pro každou dvojici prvků
xiayiv lexikálním pořadí:- Operátor
xi == yise vyhodnotí a výsledek typuboolse získá následujícím způsobem:- Pokud porovnání přineslo
bool, pak je to výsledek. - V opačném případě, pokud porovnání přineslo
dynamicpak je operátorfalsedynamicky vyvolán a výsledná hodnotaboolje negovaná s logickým operátorem negace (!). - Jinak platí, že pokud typ porovnání má implicitní převod na
bool, použije se tento převod. - V opačném případě, pokud typ porovnání má operátor
false, tento operátor je vyvolán a výslednáboolhodnota je negována s logickým operátorem negace (!).
- Pokud porovnání přineslo
- Pokud je výsledná
boolfalse, nedojde k žádnému dalšímu vyhodnocení a výsledek operátoru rovnosti n-tic jefalse.
- Operátor
- Pokud všechna porovnání prvků přinesla
true, výsledek operátoru rovnosti n-tice jetrue.
Operátor rovnosti n-tice x != y se vyhodnotí takto:
- Operand na levé straně
xje vyhodnocen. - Vyhodnocuje se pravý operand
y. - Pro každou dvojici prvků
xiayiv lexikálním pořadí:- Operátor
xi != yise vyhodnotí a výsledek typuboolse získá následujícím způsobem:- Pokud porovnání přineslo
bool, pak je to výsledek. - Jinak pokud porovnání přineslo
dynamic, operátortrueje na něm dynamicky vyvolán a výslednáboolhodnota je výsledkem. - Jinak platí, že pokud typ porovnání má implicitní převod na
bool, použije se tento převod. - V opačném případě, pokud typ porovnání má operátor
true, tento operátor je vyvolán a výslednáboolhodnota je výsledek.
- Pokud porovnání přineslo
- Pokud je výsledná
booltrue, nedojde k žádnému dalšímu vyhodnocení a výsledek operátoru rovnosti n-tic jetrue.
- Operátor
- Pokud všechna porovnání prvků přinesla
false, výsledek operátoru rovnosti n-tice jefalse.
12.14.12 Operátor is
Existují dvě formy operátoru is. Jedním je operátor typu, který má typ na pravé straně. Druhý je operátor typu "is-pattern", který má vzor na pravé straně.
12.14.12.1 Operátor is-type
je operátor typu, který se používá ke kontrole, zda je typ objektu za běhu kompatibilní s daným typem. Kontrola se provádí za běhu. Výsledek operace E is T, kde E je výraz a T je typ jiný než dynamic, je logická hodnota označující, zda E je nenulová a lze ji úspěšně převést na typ T pomocí převodu podle odkazu, převodu boxingu, převodu unboxingu, převodu wrappingu nebo převodu unwrappingu.
Operace E is T se vyhodnotí takto:
- Pokud
Eje anonymní funkce nebo skupina metod, dojde k chybě v době kompilace. - Pokud
Tje odkazový typ s možnou hodnotou null (§8.9.3), dojde k chybě v době kompilace. - Jestliže je
Eliterálemnull, nebo pokud je hodnotaErovnanull, pak je výsledekfalse. - Jinak:
- Nechte
Rbýt typem modulu runtimeE. - Pojďme
Dse odvozovatRtakto:- Pokud je
Rtypem hodnoty, která může být null, pak jeDzákladním typemR. - V opačném případě
DjeR.
- Pokud je
- Výsledek závisí na
Dnásledujícím výsledkuT:- Pokud je
Treferenční typ, výsledek jetrue, pokud:- existuje převod identity mezi
DaTnebo nebo -
Dje typ odkazu a implicitní převod odkazu zDnaTexistuje nebo -
Dje typ hodnoty a převod boxingu zDtoho, naTkterý existuje.
- existuje převod identity mezi
- Pokud je
Tnulovatelný typ hodnoty, výsledek jetrue, pokud jeDzákladním typemT. - Pokud je
Thodnotový typ, který nepřipouští hodnotu null, výsledek jetrue, pokud jsouDaTstejného typu. - V opačném případě je výsledek
false.
- Pokud je
- Nechte
Operátor is nebere v úvahu uživatelsky definované převody.
Poznámka: Při vyhodnocování operátoru
isza běhu byly nahrazeny všechny argumenty typu a neexistují žádné otevřené typy (§8.4.3). koncová poznámka
Poznámka: Operátor
islze pochopit z hlediska typů kompilace a převodů následujícím způsobem, kdeCje typEkompilace:
- Je-li typ
ekompilace stejný jakoT, nebo pokud implicitní převod odkazu (§10.2.8), převod boxingu (§10.2.9), převod obtékání (§10.6) nebo explicitní převod přebalování (E) existuje z typuTkompilace na :
- Pokud je typ
Chodnotový a nelze ho nullovat, výsledkem operace jetrue.- V opačném případě je výsledek operace ekvivalentní vyhodnocení
E != null.- V opačném případě, pokud existuje explicitní převod odkazu (§10.3.5) nebo zrušení převodu (§10.3.7) z
CTdo nebo je-liCTotevřený typ (§8.4.3), musí být provedeny kontroly za běhu, jak je uvedeno výše.- Jinak není možné žádné odkazování, zabalení, rozbalení nebo zrušení převodu
Ena typTa výsledek operace jefalse. Kompilátor může implementovat optimalizace na základě typu kompilace.koncová poznámka
12.14.12.2 Operátor is-pattern
Operátor is-pattern slouží ke kontrole, jestli hodnota vypočítaná výrazem odpovídá danému vzoru (§11). Kontrola se provádí za běhu. Výsledek operátoru is-pattern je true, pokud hodnota odpovídá vzoru; jinak je false.
Pro výraz tvaru E is P, kde E je relační výraz typu T a P je vzorec, jedná se o chybu při kompilaci, pokud platí některá z následujících podmínek:
-
Eneurčil hodnotu nebo nemá typ. - Vzor
Pnelze použít (§11.2) na typT.
Každý single_variable_designation vzoru zavádí novou místní proměnnou, která je rozhodně přiřazena (§9.4), pokud odpovídající relational_expression testy true.
12.14.13 Operátor as
Operátor as se používá k explicitnímu převodu hodnoty na daný odkazový typ nebo nulovatelný typ hodnoty. Na rozdíl od výrazu přetypování (§12.9.8) as operátor nikdy nevyvolá výjimku. Pokud není uvedený převod možný, výsledná hodnota je null.
Při operaci ve formě E as Tmusí být E výrazem a T referenčním typem, parametrem typu známým jako referenční, nebo nulovatelným typem hodnoty. Kromě toho musí být splněna alespoň jedna z následujících podmínek, jinak dojde k chybě v době kompilace:
- Identita (§10.2.2), implicitní nulovatelnost (§10.2.6), implicitní reference (§10.2.8), zabalení (§10.2.9), explicitní nulovatelnost (§10.3.4), explicitní reference (§10.3.5) nebo obtékání (§8.3.12) se provádí z
EnaT. - Typ
EneboTje otevřený typ. -
Eje doslovný výraznull.
Pokud typ za běhu kompilace E není dynamic, operace E as T vede ke stejnému výsledku jako
E is T ? (T)(E) : (T)null
kromě toho, že E se vyhodnotí pouze jednou. Kompilátor může být očekáváno, že optimalizuje E as T tak, aby prováděl maximálně jednu kontrolu typu modulu runtime, na rozdíl od dvou kontrol typu modulu runtime odvozených z výše uvedeného rozšíření.
Pokud je typ E kompilace dynamic, na rozdíl od operátoru přetypování není operátor as dynamicky vázán (§12.3.3). Proto rozšíření v tomto případě je:
E is T ? (T)(object)(E) : (T)null
Všimněte si, že některé převody, jako například uživatelsky definované převody, nejsou možné s operátorem as a místo toho by se měly provést pomocí výrazů přetypování.
Příklad: V příkladu
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 } }parametr typu
TGje známý jako odkazový typ, protože má omezení třídy. Parametr typuUuHvšak nesplňuje požadované podmínky; proto je použití operátoruasvHzakázáno.koncového příkladu
12.15 Logické operátory
12.15.1 Obecné
Operátory &, ^a | se nazývají logické operátory.
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
;
Pokud má operand logického operátoru typ kompilace dynamic, je výraz dynamicky vázán (§12.3.3). V tomto případě je typ kompilace výrazu dynamica rozlišení popsané níže proběhne za běhu pomocí typu běhu těchto operandů, které mají typ kompilačního času dynamic.
Pro operaci ve tvaru x «op» y, kde «op» je jedním z logických operátorů, je použito přetížení rozlišení (§12.4.5) pro výběr konkrétní implementace operátoru. Operandy jsou převedeny na typy parametrů vybraného operátoru a typ výsledku je návratový typ operátoru.
Předdefinované logické operátory jsou popsány v následujících dílčích kapitolách.
12.15.2 Celočíselné logické operátory
Předdefinované celočíselné logické operátory jsou:
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);
Operátor & vypočítá bitovou logickou hodnotu AND dvou operandů, operátor | vypočítá bitovou logickou hodnotu OR obou operandů a operátor ^ vypočítá bitovou logickou výhradní hodnotu OR obou operandů. Z těchto operací nejsou možné přetečení.
Zvednuté (§12.4.8) tvary předdefinovaných celočíselných logických operátorů definovaných výše jsou také předdefinovány.
12.15.3 – logické operátory výčtu
Každý typ výčtu E implicitně poskytuje následující předdefinované logické operátory:
E operator &(E x, E y);
E operator |(E x, E y);
E operator ^(E x, E y);
Výsledek vyhodnocení x «op» y, kde x a y jsou výrazy typu výčtu E s podkladovým typem U, a «op» je jeden z logických operátorů, je naprosto stejný jako vyhodnocení (E)((U)x «op» (U)y). Jinými slovy, logické operátory typu výčtu jednoduše provádějí logickou operaci u základního typu dvou operandů.
Zvednuté (§12.4.8) formy nezvednutých předdefinovaných logických operátorů výčtu definovaných výše jsou rovněž předdefinovány.
12.15.4 Logické operátory
Předdefinované Booleovské logické operátory jsou:
bool operator &(bool x, bool y);
bool operator |(bool x, bool y);
bool operator ^(bool x, bool y);
Výsledek x & y je true, pokud jsou jak x, tak y rovny true. V opačném případě je výsledek false.
Výsledek x | y je true, pokud je buď x, nebo y rovno true. V opačném případě je výsledek false.
Výsledek x ^ y je true, pokud je xtrue a y je falsenebo x je false a y je true. V opačném případě je výsledek false. Pokud jsou operandy typu bool, operátor ^ vypočítá stejný výsledek jako operátor !=.
12.15.5 Boolean s možnou hodnotou Null a | operátoři
Booleanový typ s možnou hodnotou null bool? může představovat tři hodnoty, true, falsea null.
Stejně jako u ostatních binárních operátorů jsou rovněž předdefinované formy logických operátorů & a | (§12.15.4):
bool? operator &(bool? x, bool? y);
bool? operator |(bool? x, bool? y);
Sémantika zdvižených operátorů & a | jsou definována v následující tabulce:
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 |
Poznámka: Typ
bool?se koncepčně podobá typu se třemi hodnotami používaným pro logické výrazy v SQL. Výše uvedená tabulka se řídí stejnou sémantikou jako SQL, zatímco použití pravidel §12.4.8 na operátory&a|by tomu neodpovídalo. Pravidla §12.4.8 již poskytují SQL podobnou sémantiku pro rozšířený operátor^. koncová poznámka
12.16 Podmíněné logické operátory
12.16.1 Obecné
Operátory && a || se nazývají podmíněné logické operátory. označují se také jako „logické operátory s krátkým obvodem“.
conditional_and_expression
: inclusive_or_expression
| conditional_and_expression '&&' inclusive_or_expression
;
conditional_or_expression
: conditional_and_expression
| conditional_or_expression '||' conditional_and_expression
;
Operátory && a || jsou podmíněné verze operátorů & a |:
- Operace
x && yodpovídá operacix & ys tím rozdílem, žeyje vyhodnocena pouze v případě, žexnenífalse. - Operace
x || yodpovídá operacix | ys tím rozdílem, žeyje vyhodnocena pouze v případě, žexnenítrue.
Poznámka: Důvodem, proč zkratování používá podmínky „pravdivý“ a „nepravdivý“, je umožnit uživatelsky definovaným podmíněným operátorům určit, kdy se má zkratování aplikovat. Uživatelem definované typy můžou být ve stavu, kdy
operator truevracífalseaoperator falsevracífalse. V takových případech by&&ani||nezkratovaly. koncová poznámka
Pokud má operand podmíněného logického operátoru typ kompilace dynamic, je výraz dynamicky vázán (§12.3.3). V tomto případě je typ kompilace výrazu dynamica rozlišení popsané níže proběhne za běhu pomocí typu běhu těchto operandů, které mají typ kompilačního času dynamic.
Operace formuláře x && y nebo x || y je zpracována použitím rozlišení přetížení (§12.4.5), jako by byla operace zapsána x & y nebo x | y. Potom
- Pokud se rozlišení přetížení nepodaří najít jeden nejlepší operátor, nebo pokud rozlišení přetížení vybere jeden z předdefinovaných logických operátorů celého čísla nebo logické operátory s možnou hodnotou null (§12.15.5), dojde k chybě v době vazby.
- V opačném případě, pokud je vybraný operátor jedním z předdefinovaných logických logických operátorů (§12.15.4), je operace zpracována, jak je popsáno v §12.16.2.
- V opačném případě je vybraným operátorem operátor definovaný uživatelem a operace se zpracuje podle §12.16.3.
Podmíněné logické operátory není možné přímo přetěžovat. Vzhledem k tomu, že se podmíněné logické operátory vyhodnocují z hlediska běžných logických operátorů, přetížení běžných logických operátorů jsou s určitými omezeními také považována za přetížení podmíněných logických operátorů. Toto je popsáno dále v §12.16.3.
12.16.2 Logické logické operátory
Pokud jsou operandy && nebo || typu boolnebo pokud jsou operandy typu, které nedefinují použitelné operator & nebo operator |, ale definují implicitní převody na bool, operace se zpracuje takto:
- Operace
x && yse vyhodnotí jakox ? y : false. Jinými slovy,xje nejprve vyhodnocen a převeden na typbool. Pokud jextrue,yse vyhodnotí a převede na typboola stane se výsledkem operace. V opačném případě je výsledek operacefalse. - Operace
x || yse vyhodnotí jakox ? true : y. Jinými slovy,xje nejprve vyhodnocen a převeden na typbool. Pokud jextrue, výsledek operace jetrue. V opačném případě seyvyhodnotí a převede na typboola stane se výsledkem operace.
12.16.3 Uživatelem definované podmíněné logické operátory
Jsou-li operandy && nebo || typy, které deklarují použitelné uživatelem definované operator & nebo operator |, platí obě následující podmínky, pokud T je typ, ve kterém je vybraný operátor deklarován:
- Návratový typ a typ každého parametru vybraného operátoru musí být
T. Jinými slovy, operátor vypočítá logický operátor AND nebo logický operátor OR dvou operandů typuTa vrátí výsledek typuT. -
Tmá obsahovat prohlášeníoperator trueaoperator false.
K chybě času vazby dochází v případě, že jakýkoli z těchto požadavků není splněn. Jinak se operace && nebo || vyhodnotí kombinací uživatelem definovaného operator true nebo operator false s vybraným uživatelem definovaným operátorem:
- Operace
x && yje vyhodnocena jakoT.false(x) ? x : T.&(x, y), kdeT.false(x)je vyvoláníoperator falsedeklarovaného vTaT.&(x, y)je vyvoláním vybranéhooperator &. Jinými slovy,xse nejprve vyhodnotí aoperator falsese vyvolá na výsledku, aby se zjistilo, jestli jexrozhodně nepravdivý. Pokudxje rozhodně nepravda, výsledek operace je hodnota, která byla dříve vypočtena prox. V opačném případě seyvyhodnotí a vybranáoperator &se vyvolá na hodnotě dříve vypočítané proxa hodnotě vypočítané proypro získání výsledku operace. - Operace
x || yje vyhodnocena jakoT.true(x) ? x : T.|(x, y), kdeT.true(x)je vyvoláníoperator truedeklarovaného vTaT.|(x, y)je vyvoláním vybranéhooperator |. Jinými slovy,xse nejprve vyhodnotí aoperator truese vyvolá na výsledek, který určí, jestlixje rozhodně pravdivá. Pokud jexrozhodně pravdivý, výsledek operace je hodnota, která byla dříve vypočtena prox. V opačném případě seyvyhodnotí a vybranáoperator |se vyvolá na hodnotě dříve vypočítané proxa hodnotě vypočítané proypro získání výsledku operace.
V obou těchto operacích se výraz zadaný x vyhodnotí pouze jednou a výraz zadaný y se buď nevyhodnotí, nebo se vyhodnotí přesně jednou.
12.17 Operátor sjednocení null
Operátor ?? se nazývá operátor nulového sjednocení.
null_coalescing_expression
: conditional_or_expression
| conditional_or_expression '??' null_coalescing_expression
| throw_expression
;
Ve výrazu nulového slučování a ?? b, jestliže a nenínull, výsledek je a; jinak je výsledek b. Operace vyhodnotí b pouze v případě, že je anull.
Operátor nulového sjednocení je asociativní zprava, což znamená, že operace jsou seskupeny zprava doleva.
Příklad: Výraz tvaru
a ?? b ?? cse vyhodnotí jakoa ?? (b ?? c). Obecně řečeno, výraz formulářeE1 ?? E2 ?? ... ?? ENvrátí první z operandů, které nejsounull, nebonull, pokud jsou všechny operandynull. koncového příkladu
Typ výrazu a ?? b závisí na tom, které implicitní převody jsou k dispozici na operandech. V pořadí podle priority je typ a ?? bA₀, Anebo B, kde A je typem a (za předpokladu, že a má typ), B je typem b(za předpokladu, že b má typ) a A₀ je podkladovým typem A, pokud je A typ nullovatelné hodnoty, nebo A jinak. Konkrétně se a ?? b zpracuje takto:
- Pokud
Aexistuje a je nespravovaným typem (§8.8) nebo je známo, že se jedná o nenulový typ hodnoty, dojde k chybě v době kompilace. - V opačném případě pokud
Aexistuje abje dynamický výraz, typ výsledku jedynamic. V době běhu se nejprve vyhodnotía. Pokudanenínull,ase převede nadynamica stane se výsledkem. V opačném případě sebvyhodnotí a stane se výsledkem. - Jinak pokud
Aexistuje a je typ hodnoty null a implicitní převod existuje zbnaA₀, je typ výsledkuA₀. V době běhu se nejprve vyhodnotía. Pokudanenínull,ase rozbalí na typA₀a to se stane výsledkem. V opačném případě sebvyhodnotí a převede na typA₀a stane se výsledkem. - V opačném případě, pokud
Aexistuje a implicitní převod existuje zbnaA, je typ výsledkuA. V době běhu se nejprve vyhodnotía. Pokudanenínull,ase stane výsledkem. V opačném případě sebvyhodnotí a převede na typAa stane se výsledkem. - V opačném případě, pokud
Aexistuje a je nulovatelný typ hodnoty,bmá typBa existuje implicitní převod zA₀naB, pak je typ výsledkuB. V době běhu se nejprve vyhodnotía. Pokudanenínull,aje rozbalený na typA₀, převeden na typBa stane se výsledkem. V opačném případě sebvyhodnotí a stane se výsledkem. - V opačném případě, pokud
bmá typBa implicitní převod existuje zanaB, je typ výsledkuB. V době běhu se nejprve vyhodnotía. Pokudanenínull,ase převede na typBa stane se výsledkem. V opačném případě sebvyhodnotí a stane se výsledkem. - V opačném případě jsou
aabnekompatibilní a dojde k chybě v době kompilace.
Příklad :
T M<T>(T a, T b) => a ?? b; string s = M(null, "text"); int i = M(19, 23);Parametr typu
Tpro metoduMje neomezený. Argument typu proto může být referenční typ nebo nulovatelný typ hodnoty, jak je znázorněno při prvním voláníM. Argument typu může být také nenulový typ hodnoty, jak je znázorněno ve druhém voláníM. Pokud je argumentem typu nenulový typ hodnoty, je hodnota výrazua ?? ba.koncového příkladu
12.18 Operátor výrazu throw
throw_expression
: 'throw' null_coalescing_expression
;
throw_expression vyhodí hodnotu vytvořenou vyhodnocením null_coalescing_expression. Výraz se implicitně převede na System.Exceptiona výsledek vyhodnocení výrazu se před vyvoláním převede na System.Exception. Chování při vyhodnocování výrazu throw je stejné jako u příkazu throw (§13.10.6).
throw_expression nemá žádný typ. throw_expression je převoditelný na každý typ implicitní vyvolá převod.
Výraz throw nastane pouze v následujících syntaktických kontextech:
- Jako druhý nebo třetí operand ternárního podmíněného operátoru (
?:). - Jako druhý operand operátoru nulového sjednocení (
??). - Jako tělo lambda výrazu nebo členu.
12.19 Výrazy deklarace
Výraz deklarace deklaruje místní proměnnou.
declaration_expression
: local_variable_type identifier
;
local_variable_type
: type
| 'var'
;
simple_name_ se také považuje za výraz deklarace, pokud vyhledávání jednoduchých názvů nenašlo přidruženou deklaraci (§12.8.4). Při použití jako výraz deklarace se _ nazývá jednoduché zahození. Je séanticky ekvivalentní var _, ale je povolen na více místech.
Výraz deklarace se provádí pouze v následujících syntaktických kontextech:
- Jako
outargument_value v argument_list. - Jednoduché zahození
_, které tvoří levou stranu jednoduchého zadání (§12.23.2). - Jako tuple_element v jednom nebo více rekurzivně vnořených tuple_expressions, které se skládají z levé strany dekonstrukčního přiřazení. deconstruction_expression vyvolává výrazy deklarace v této pozici, i když výrazy deklarace nejsou syntakticky přítomny.
Poznámka: To znamená, že deklarativní výraz nelze dávat do závorek. koncová poznámka
Jedná se o chybu pro implicitně typovanou proměnnou deklarovanou s declaration_expression, na které se má odkazovat v argument_list, kde je deklarována.
Je chybou, pokud je proměnná deklarovaná s declaration_expression odkazována v rámci dekonstrukčního přiřazení, ve kterém se nachází.
Výraz deklarace, který představuje jednoduché zahození, nebo kde local_variable_type je identifikátor var, je klasifikován jako implicitně typovaná proměnná. Výraz nemá žádný typ a typ místní proměnné je odvozen na základě syntaktického kontextu následujícím způsobem:
- V argument_list odvozený typ proměnné je deklarovaný typ odpovídajícího parametru.
- Jako levá strana jednoduchého přiřazení je odvozený typ proměnné stejný jako typ pravé strany přiřazovacího výrazu.
- V n-tice na levé straně jednoduchého přiřazení je odvozený typ proměnné typem odpovídajícího prvku n-tice na pravé straně přiřazení (po dekonstrukci).
V opačném případě se výraz deklarace klasifikuje jako explicitně typovaná proměnná a typ výrazu i deklarované proměnné musí být ten, který je určen local_variable_type.
Výraz deklarace s identifikátorem _ je zahození (§9.2.9.2) a nezavádí název proměnné. Výraz deklarace s identifikátorem jiným než _ zavádí dané jméno do nejbližšího obklopujícího prostoru deklarace místní proměnné (§7.3).
Příklad :
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 _);Deklarace
s1ukazuje explicitně i implicitně zadané výrazy deklarace. Odvozený typb1jebool, protože je to typ odpovídajícího výstupního parametru vM1. NásledujícíWriteLinemá přístup ki1ab1, které byly zavedeny v uzavřeném oboru.Deklarace
s2ukazuje pokus o použitíi2ve vnořeném voláníM, což je zakázáno, protože odkaz se vyskytuje v seznamu argumentů, kdei2byl deklarován. Na druhou stranu je povolen odkaz nab2v posledním argumentu, protože k němu dochází po konci vnořeného seznamu argumentů, kdeb2byl deklarován.Deklarace
s3ukazuje použití implicitně i explicitně zadaných deklarativních výrazů, které se ignorují. Vzhledem k tomu, že zahození neeklarují pojmenovanou proměnnou, je povoleno více výskytů identifikátoru_.(int i1, int _, (var i2, var _), _) = (1, 2, (3, 4), 5);Tento příklad ukazuje použití implicitně a explicitně typovaných deklarací výrazů pro proměnné i zahození v rámci dekonstruujícího přiřazení. simple_name
_je ekvivalentnívar _, pokud se nenajde žádná deklarace_.void M1(out int i) { ... } void M2(string _) { M1(out _); // Error: `_` is a string M1(out var _); }Tento příklad ukazuje použití
var _k poskytnutí implicitně zadaného zahození, pokud_není k dispozici, protože označuje proměnnou v nadřazeném oboru.koncového příkladu
12.20 Podmíněný operátor
Operátor ?: se nazývá podmíněný operátor. Někdy se také nazývá ternární operátor.
conditional_expression
: null_coalescing_expression
| null_coalescing_expression '?' expression ':' expression
| null_coalescing_expression '?' 'ref' variable_reference ':'
'ref' variable_reference
;
Výraz throw (§12.18) není v podmíněném operátoru povolen, pokud ref je k dispozici.
Podmíněný výraz formuláře b ? x : y nejprve vyhodnotí podmínku b. Pokud je btrue, x se vyhodnotí a stane se výsledkem operace. V opačném případě se y vyhodnotí a stane se výsledkem operace. Podmíněný výraz nikdy nevyhodnocuje x i y.
Podmíněný operátor má pravou asociativitu, což znamená, že operace jsou seskupené zprava doleva.
Příklad: Výraz tvaru
a ? b : c ? d : ese vyhodnotí jakoa ? b : (c ? d : e). koncového příkladu
Prvním operandem operátoru ?: musí být výraz, který lze implicitně převést na boolnebo výraz typu, který implementuje operator true. Pokud není splněna žádná z těchto požadavků, dojde k chybě v době kompilace.
Pokud je k dispozici ref:
- Převod identity musí existovat mezi typy dvou variable_references a typem výsledku může být jeden typ. Pokud je některý typ
dynamic, odvození typu dává přednostdynamic(§8.7). Pokud je některý z typů typu n-tice (§8.3.11), zahrnuje typová inferenci názvy prvků, pokud se názvy prvků ve stejné pořadové pozici shodují v obou n-ticích. - Výsledkem je odkaz na proměnnou, která je zapisovatelná, pokud je možné zapisovat oba variable_references.
Poznámka: Pokud
refexistuje, vrátí conditional_expression odkaz na proměnnou, která se dá přiřadit k referenční proměnné pomocí operátoru= refnebo předán jako parametr reference/input/output. koncová poznámka
Pokud ref není k dispozici, druhý a třetí operand, x a y, operátoru ?: řídí typ podmíněného výrazu:
- Pokud má
xtypXaytypY,- Pokud existuje převod identity mezi
XaY, pak je výsledkem nejlepší společný typ sady výrazů (§12.6.3.16). Pokud je některý typdynamic, odvození typu dává přednostdynamic(§8.7). Pokud je některý z typů typu n-tice (§8.3.11), zahrnuje typová inferenci názvy prvků, pokud se názvy prvků ve stejné pořadové pozici shodují v obou n-ticích. - Jinak, pokud implicitní převod (§10.2) existuje z
XnaY, ale ne zYnaX, pakYje typ podmíněného výrazu. - Jinak, pokud existuje implicitní převod výčtu (§10.2.4) z
XnaY, pak jeYtypem podmíněného výrazu. - Jinak, pokud existuje implicitní převod výčtu (§10.2.4) z
YnaX, pak jeXtypem podmíněného výrazu. - Jinak, pokud implicitní převod (§10.2) existuje z
YnaX, ale ne zXnaY, pakXje typ podmíněného výrazu. - V opačném případě nelze určit žádný typ výrazu a dojde k chybě v době kompilace.
- Pokud existuje převod identity mezi
- Pokud má typ jenom jeden z
xayaxiyjsou implicitně konvertibilní na tento typ, pak se jedná o typ podmíněného výrazu. - V opačném případě nelze určit žádný typ výrazu a dojde k chybě v době kompilace.
Zpracování podmíněného výrazu ref formuláře b ? ref x : ref y se skládá z následujících kroků:
- Nejprve se vyhodnotí
ba určí se hodnotaboolb:- Pokud existuje implicitní převod z typu
bnabool, tento implicitní převod se provede a vytvoří hodnotubool. - V opačném případě je vyvolána
operator truedefinovaná typemb, aby se vytvořila hodnotabool.
- Pokud existuje implicitní převod z typu
- Pokud
boolhodnota vytvořená výše uvedeným krokem jetrue,xse vyhodnotí a výsledný odkaz na proměnnou se stane výsledkem podmíněného výrazu. - V opačném případě se vyhodnotí
ya výsledný odkaz na proměnnou se stane výsledkem podmíněného výrazu.
Zpracování podmíněného výrazu ve tvaru b ? x : y za běhu se skládá z následujících kroků:
- Nejprve se vyhodnotí
ba určí se hodnotaboolb:- Pokud existuje implicitní převod z typu
bnabool, tento implicitní převod se provede a vytvoří hodnotubool. - V opačném případě je vyvolána
operator truedefinovaná typemb, aby se vytvořila hodnotabool.
- Pokud existuje implicitní převod z typu
- Pokud
boolhodnota vytvořená výše uvedeným krokem jetrue,xse vyhodnotí a převede na typ podmíněného výrazu a stane se výsledkem podmíněného výrazu. - V opačném případě se
yvyhodnotí a převede na typ podmíněného výrazu a stane se výsledkem podmíněného výrazu.
12.21 Anonymní výrazy funkce
12.21.1 Obecné
anonymní funkce je výraz, který představuje definici metody in-line. Anonymní funkce sama o sobě nemá hodnotu ani typ, ale je převoditelná na kompatibilní typ delegátu nebo stromu výrazů. Vyhodnocení převodu anonymní funkce závisí na cílovém typu převodu: Pokud se jedná o typ delegáta, převod se vyhodnotí na hodnotu delegáta odkazující na metodu, kterou anonymní funkce definuje. Pokud se jedná o typ stromu výrazů, převod se vyhodnotí na strom výrazu, který představuje strukturu metody jako strukturu objektu.
Poznámka: Z historických důvodů existují dvě syntaktické varianty anonymních funkcí, konkrétně lambda_expressions a anonymous_method_expressions. Pro téměř všechny účely jsou lambda_expressions stručnější a výstižnější než anonymous_method_expressions, které zůstávají v jazyce pro zpětnou kompatibilitu. koncová poznámka
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
;
Při rozpoznání anonymous_function_body, pokud jsou použitelné jak null_conditional_invocation_expression, tak výraz jako alternativy, zvolí se ta první.
Note: Překrývání a prioritizace mezi alternativami je zde určeno pouze pro popisné pohodlí; gramatická pravidla by mohla být propracovaná k odstranění tohoto překrývání. ANTLR a další gramatické systémy přijímají stejné pohodlí, takže anonymous_function_body má zadanou sémantiku automaticky. koncová poznámka
Poznámka: Pokud se považuje za výraz , syntaktická forma, například
x?.M(), by byla chyba, pokud je typ výsledkuMvoid(§12.8.13). Pokud se však považuje za null_conditional_invocation_expression, je povolen typ výsledkuvoid. koncová poznámka
Příklad: Typ výsledku
List<T>.Reversejevoid. V následujícím kódu je tělo anonymního výrazu null_conditional_invocation_expression, takže se nejedná o chybu.Action<List<int>> a = x => x?.Reverse();koncového příkladu
Operátor => má stejnou prioritu jako přiřazení (=) a je pravostranně asociativní.
Anonymní funkce s modifikátorem async je asynchronní funkce a dodržuje pravidla popsaná v §15.14.
Parametry anonymní funkce ve formě lambda_expression mohou být explicitně nebo implicitně zadány. V seznamu explicitně zadaných parametrů je explicitně uveden typ každého parametru. V seznamu implicitně zadaných parametrů jsou typy parametrů odvozeny z kontextu, ve kterém se vyskytuje anonymní funkce – konkrétně, když je anonymní funkce převedena na kompatibilní typ delegáta nebo typ stromu výrazů, tento typ poskytuje typy parametrů (§10,7).
V lambda_expression s jedním implicitně zadaným parametrem mohou být závorky ze seznamu parametrů vynechány. Jinými slovy, anonymní funkce formuláře
( «param» ) => «expr»
lze zkrátit na
«param» => «expr»
Seznam parametrů anonymní funkce ve formě anonymous_method_expression je volitelný. Jsou-li zadány, parametry musí být explicitně zadány. Pokud ne, anonymní funkce je převeditelná na delegáta, který může mít libovolný seznam parametrů neobsahujících výstupní parametry.
Blok těla anonymní funkce je vždy dosažitelný (§13.2).
Příklad: Některé příklady anonymních funkcí následují níže:
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 omittedkoncového příkladu
Chování lambda_expressions a anonymous_method_expressions je stejné s výjimkou následujících bodů:
- anonymous_method_expressionumožňuje, aby byl seznam parametrů zcela vynechán, což umožňuje převoditelnost na delegátové typy libovolného seznamu parametrů hodnot.
- lambda_expressionumožňují vynechání typů parametrů a jejich odvození, zatímco anonymous_method_expressionvyžadují explicitní uvedení typů parametrů.
- Tělo lambda_expression může být výraz nebo blok, zatímco tělo anonymous_method_expression musí být blokem.
- Pouze lambda_expressionmají převody na kompatibilní typy stromu výrazů (§8.6).
12.21.2 Anonymní podpisy funkcí
signatura anonymní funkce definuje názvy a volitelně typy parametrů pro anonymní funkci. Rozsah parametrů anonymní funkce je anonymous_function_body (§7.7). Spolu se seznamem parametrů (pokud je zadán) anonymní tělo metody představuje prostor deklarace (§7.3). Jedná se tedy o chybu v době kompilace pro název parametru anonymní funkce tak, aby odpovídal názvu místní proměnné, místní konstanty nebo parametru, jehož obor zahrnuje anonymous_method_expression nebo lambda_expression.
Pokud má anonymní funkce explicit_anonymous_function_signature, je sada kompatibilních typů delegátů a typů stromu výrazů omezena na ty, které mají stejné typy parametrů a modifikátory ve stejném pořadí (§10.7). Na rozdíl od převodů skupin metod (§10,8), není podporována kontra variance anonymních typů parametrů funkce. Pokud anonymní funkce nemá anonymous_function_signature, je sada kompatibilních typů delegátů a typů stromu výrazů omezena na ty, které nemají žádné výstupní parametry.
Všimněte si, že anonymous_function_signature nemůže obsahovat atributy ani pole parametrů. Nicméně anonymous_function_signature může být kompatibilní s typem delegáta, jehož seznam parametrů obsahuje parametrické pole.
Všimněte si také, že převod na typ stromu výrazů, i když je kompatibilní, může v době kompilace selhat (§8.6).
12.21.3 Anonymní orgány funkce
Tělo anonymní funkce (výraz nebo blok) podléhá následujícím pravidlům:
- Pokud anonymní funkce obsahuje podpis, jsou parametry zadané v podpisu k dispozici v textu. Pokud anonymní funkce nemá podpis, lze ji převést na typ delegáta nebo typ výrazu s parametry (§10.7), ale k parametrům nelze v těle funkce přistupovat.
- S výjimkou parametrů podle reference zadaných v podpisu (pokud nějaké jsou) nejbližší uzavírající anonymní funkce se jedná o chybu při kompilaci, pokud tělo přistupuje k parametru podle reference.
- Kromě parametrů uvedených v podpisu (pokud nějaké existují) nejbližší obalující anonymní funkce je chyba při kompilaci, pokud se tělo snaží přistoupit k parametru typu
ref struct. - Pokud je typ
thistypu struktury, jedná se o chybu kompilace, když se tělo pokusí přistoupit kthis. To platí, zda je přístup explicitní (jako vthis.x) nebo implicitní (stejně jako vx, kdexje členem instance struktury). Toto pravidlo jednoduše zakáže takový přístup a nemá vliv na to, zda vyhledávání členů vede k členu struktury. - Tělo má přístup k vnějším proměnným (§12.21.6) anonymní funkce. Přístup vnější proměnné bude odkazovat na instanci proměnné, která je aktivní v době vyhodnocení lambda_expression nebo anonymous_method_expression (§12.21.7).
- Jedná se o chybu v době kompilace, pokud tělo obsahuje příkaz
goto, příkazbreaknebo příkazcontinue, jehož cíl je mimo tělo nebo uvnitř těla anonymní funkce. - Příkaz
returnv těle vrátí ovládací prvek z vyvolání nejbližší anonymní funkce, ne z nadřazeného členu funkce.
Není výslovně stanoveno, zda existuje nějaký způsob, jak spustit blok anonymní funkce jinak než prostřednictvím vyhodnocení a vyvolání lambda_expression nebo anonymous_method_expression. Konkrétně se kompilátor může rozhodnout implementovat anonymní funkci tím, že syntetizuje jednu nebo více pojmenovaných metod nebo typů. Názvy všech těchto syntetizovaných prvků mají formu vyhrazenou pro použití kompilátoru (§6.4.3).
12.21.4 Rozlišení přetížení
Anonymní funkce v seznamu argumentů se účastní odvozování typů a rozlišení přetížení. Konkrétní pravidla najdete v §12.6.3 a §12.6.4.
Příklad: Následující příklad ukazuje vliv anonymních funkcí na rozlišení přetížení.
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; } }Třída
ItemList<T>má dvěSummetody. Každá z nich přebíráselectorargument, který extrahuje hodnotu, která se má sečíst z položky seznamu. Extrahovaná hodnota může být buďint, nebodoublea výsledný součet je podobněintnebodouble.Metody
Sumlze například použít k výpočtu součtů ze seznamu řádků podrobností v pořadí.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( ... ) { ... } }Při prvním vyvolání
orderDetails.Sumplatí obě metodySum, protože anonymníd => d.UnitCountfunkce je kompatibilní sFunc<Detail,int>i sFunc<Detail,double>. Řešení přetížení však vybere prvníSummetodu, protože převod naFunc<Detail,int>je lepší než převod naFunc<Detail,double>.Při druhém vyvolání
orderDetails.Sumlze použít pouze druhou metoduSum, protože anonymní funkced => d.UnitPrice * d.UnitCountvytvoří hodnotu typudouble. Rozlišení přetížení proto vybere druhouSummetodu pro toto vyvolání.koncového příkladu
12.21.5 Anonymní funkce a dynamické vazby
Anonymní funkce nemůže být přijímačem, argumentem nebo operandem dynamicky vázané operace.
12.21.6 Vnější proměnné
12.21.6.1 Obecné
Jakákoli místní proměnná, parametr hodnoty nebo pole parametrů, jejichž obor zahrnuje lambda_expression nebo anonymous_method_expression, se nazývá vnější proměnná anonymní funkce. V instanci funkce člen třídy je tato hodnota považována za parametr hodnoty a je vnější proměnnou jakékoli anonymní funkce obsažené v členu funkce.
12.21.6.2 Zachycené vnější proměnné
Když na vnější proměnnou odkazuje anonymní funkce, říká se, že vnější proměnná byla zachycena anonymní funkcí. Obvykle je životnost místní proměnné omezena na provádění bloku nebo příkazu, ke kterému je přidružen (§9.2.9.1). Životnost zachycené vnější proměnné je však rozšířena alespoň dokud se delegát nebo strom výrazu vytvořený z anonymní funkce nestane způsobilým pro garbage collection.
Příklad: V příkladu
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()); } }místní proměnná
xje zachycena anonymní funkcí a doba životnostixje rozšířena alespoň tak dlouho, dokud delegát vrácený zFnebude způsobilý pro uvolňování paměti. Vzhledem k tomu, že každé vyvolání anonymní funkce funguje na stejné instancix, výstupem tohoto příkladu je:1 2 3koncového příkladu
Pokud je místní proměnná nebo parametr hodnoty zachycen anonymní funkcí, místní proměnná nebo parametr se už nepovažuje za pevnou proměnnou (§24.4), ale považuje se za přesunoutelnou proměnnou. Zachycené vnější proměnné však nelze použít v fixed příkazu (§24.7), takže adresu zachycené vnější proměnné nelze vzít.
Poznámka: Na rozdíl od nezachycené proměnné může být zachycená místní proměnná současně vystavena více vláknům provádění. koncová poznámka
12.21.6.3 Vytvoření instance místních proměnných
Místní proměnná se považuje za inicializovanou, když se provádění dostane do oboru platnosti proměnné.
Příklad: Například při vyvolání následující metody se místní proměnná
xvytvoří instance a inicializuje třikrát – jednou pro každou iteraci smyčky.static void F() { for (int i = 0; i < 3; i++) { int x = i * 2 + 1; ... } }Přesunutím deklarace
xmimo smyčku však vznikne jedna instancex:static void F() { int x; for (int i = 0; i < 3; i++) { x = i * 2 + 1; ... } }koncového příkladu
Pokud se nezachytí, neexistuje způsob, jak přesně sledovat, jak často se vytváří instance místní proměnné – protože životnosti instancí jsou oddělené, je možné, aby každá instance jednoduše používala stejné umístění úložiště. Když ale anonymní funkce zachytí místní proměnnou, důsledky vytváření instance se stanou zjevné.
Příklad: Příklad
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(); } } }vytvoří výstup:
1 3 5Pokud se však deklarace
xpřesune mimo smyčku: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(); } } }výstup je:
5 5 5Všimněte si, že kompilátor má povoleno (ale není povinen) optimalizovat tři instancí do jednoho objektu delegáta (§10.7.2).
koncového příkladu
Pokud smyčka for-loop deklaruje proměnnou iterace, je tato proměnná považována za deklarovanou mimo smyčku.
příklad: Pokud se tedy příklad změní tak, aby zachytil samotnou iterační proměnnou:
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(); } } }Zachytí se pouze jedna instance proměnné iterace, která vytvoří výstup:
3 3 3koncového příkladu
Anonymní delegáti funkce mohou sdílet některé zachycené proměnné, ale mají samostatné instance jiných.
Příklad: Pokud se například
Fzmění nastatic 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; }tři delegáti zachycují stejnou instanci
xale samostatné instanceya výstup je:1 1 2 1 3 1koncového příkladu
Samostatné anonymní funkce mohou zachytit stejnou instanci vnější proměnné.
příklad: V příkladu:
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()); } }tyto dvě anonymní funkce zachytávají stejnou instanci místní proměnné
xa mohou tak "komunikovat" prostřednictvím této proměnné. Výstupem příkladu je:5 10koncového příkladu
12.21.7 Vyhodnocení anonymních výrazů funkce
Anonymní funkce F musí být vždy převedena na typ delegáta D nebo typ stromové struktury výrazu E, a to buď přímo, nebo prostřednictvím výrazu vytvoření delegáta new D(F). Tento převod určuje výsledek anonymní funkce, jak je popsáno v §10.7.
12.21.8 Příklad implementace
Tato dílčí položka je informativní.
Tato dílčí část popisuje možnou implementaci převodů anonymních funkcí z hlediska jiných konstruktorů jazyka C#. Implementace popsaná zde je založena na stejných principech používaných komerčním kompilátorem jazyka C#, ale není to žádným způsobem pověřená implementace, ani není jedinou možnou implementací. Jenom stručně se zmiňuje o převodech na stromy výrazů, protože jejich přesná sémantika je mimo rozsah této specifikace.
Zbývající část tohoto dílčího seznamu obsahuje několik příkladů kódu, který obsahuje anonymní funkce s různými vlastnostmi. Pro každý příklad je k dispozici odpovídající překlad kódu, který používá pouze jiné konstruktory jazyka C#. V příkladech se předpokládá, že identifikátor D představuje následující typ delegáta:
public delegate void D();
Nejjednodušší forma anonymní funkce je ta, která nezachytí žádné vnější proměnné:
delegate void D();
class Test
{
static void F()
{
D d = () => Console.WriteLine("test");
}
}
To lze přeložit na instanci delegáta, která odkazuje na statickou metodu vygenerovanou kompilátorem, ve které je umístěn kód anonymní funkce:
delegate void D();
class Test
{
static void F()
{
D d = new D(__Method1);
}
static void __Method1()
{
Console.WriteLine("test");
}
}
V následujícím příkladu anonymní funkce odkazuje na členy instance this:
delegate void D();
class Test
{
int x;
void F()
{
D d = () => Console.WriteLine(x);
}
}
Dá se přeložit na metodu instance vygenerovanou kompilátorem obsahující kód anonymní funkce:
delegate void D();
class Test
{
int x;
void F()
{
D d = new D(__Method1);
}
void __Method1()
{
Console.WriteLine(x);
}
}
V tomto příkladu anonymní funkce zachycuje místní proměnnou:
delegate void D();
class Test
{
void F()
{
int y = 123;
D d = () => Console.WriteLine(y);
}
}
Životnost místní proměnné se teď musí rozšířit alespoň na dobu životnosti delegáta anonymní funkce. Toho lze dosáhnout "nahoistováním" místní proměnné do pole třídy generované kompilátorem. Vytvoření instance místní proměnné (§12.21.6.3) pak odpovídá vytvoření instance třídy generované kompilátorem a přístup k místní proměnné odpovídá přístupu k poli v instanci třídy generované kompilátorem. Anonymní funkce se navíc stane metodou instance třídy generované kompilátorem:
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);
}
}
}
A konečně následující anonymní funkce zachycuje this a také dvě místní proměnné s různými životnostmi:
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);
}
}
}
Zde se vytvoří třída generovaná kompilátorem pro každý blok, ve kterém jsou místní hodnoty zachyceny tak, aby místní hodnoty v různých blocích mohly mít nezávislé životnosti. Instance __Locals2, kompilátor-vygenerovaná třída pro vnitřní blok, obsahuje místní proměnnou z a pole, které odkazuje na instanci __Locals1. Instance __Locals1, kompilátor-vygenerovaná třída pro vnější blok, obsahuje místní proměnnou y a pole, které odkazuje na this ohraničujícího členu funkce. S těmito datovými strukturami je možné dosáhnout všech zachycených vnějších proměnných prostřednictvím instance __Local2a kód anonymní funkce lze tedy implementovat jako metodu instance této třídy.
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);
}
}
}
Stejnou techniku, která se zde používá k zachycení místních proměnných, lze použít také při převodu anonymních funkcí na stromy výrazů: odkazy na objekty generované kompilátorem mohou být uloženy ve stromu výrazů a přístup k místním proměnným lze reprezentovat jako přístup k polím u těchto objektů. Výhodou tohoto přístupu je, že umožňuje sdílení "lifted" místních proměnných mezi delegáty a stromy výrazů.
Konec informativního textu.
12.22 Výrazy dotazů
12.22.1 Obecné
Výrazy dotazů poskytují jazykově integrovanou syntaxi pro dotazy, která je podobná relačním a hierarchickým dotazovacím jazykům, jako jsou SQL a XQuery.
query_expression
: from_clause query_body
;
from_clause
: 'from' type? identifier 'in' expression
;
query_body
: query_body_clause* select_or_group_clause query_continuation?
;
query_body_clause
: from_clause
| let_clause
| where_clause
| join_clause
| join_into_clause
| orderby_clause
;
let_clause
: 'let' identifier '=' expression
;
where_clause
: 'where' boolean_expression
;
join_clause
: 'join' type? identifier 'in' expression 'on' expression
'equals' expression
;
join_into_clause
: 'join' type? identifier 'in' expression 'on' expression
'equals' expression 'into' identifier
;
orderby_clause
: 'orderby' orderings
;
orderings
: ordering (',' ordering)*
;
ordering
: expression ordering_direction?
;
ordering_direction
: 'ascending'
| 'descending'
;
select_or_group_clause
: select_clause
| group_clause
;
select_clause
: 'select' expression
;
group_clause
: 'group' expression 'by' expression
;
query_continuation
: 'into' identifier query_body
;
Výraz dotazu začíná klauzulí from a končí klauzulí select nebo group. Za počáteční klauzulí from může následovat nula nebo více from, let, where, join nebo orderby klauzulí. Každá klauzule from je generátor, který zavádí proměnnou rozsahu , která iteruje přes prvky sekvence . Každá klauzule let zavádí proměnnou rozsahu představující hodnotu vypočítanou pomocí předchozích proměnných rozsahu. Každá klauzule where je filtr, který z výsledku vylučuje položky. Každá klauzule join porovnává zadané klíče zdrojové sekvence s klíči jiné sekvence a poskytuje odpovídající páry. Každá klauzule orderby mění pořadí položek podle zadaných kritérií. Konečná select nebo group klauzule určuje tvar výsledku z hlediska proměnných rozsahu. Nakonec lze klauzuli into použít k „splice“ dotazů tím, že v následném dotazu zachází s výsledky jednoho dotazu jako s generátorem.
12.22.2 Nejednoznačnosti ve výrazech dotazů
Výrazy dotazu používají několik kontextových klíčových slov (§6.4.4): ascending, by, descending, equals, from, group, into, join, let, on, orderby, select a where.
Aby nedocházelo k nejednoznačnostem, které by mohly vzniknout z použití těchto identifikátorů jako klíčových slov i jednoduchých názvů, jsou tyto identifikátory považovány za klíčová slova kdekoli ve výrazu dotazu, pokud nejsou předponou "@" (§6.4.4) v takovém případě se považují za identifikátory. Pro tento účel je výraz dotazu libovolný výraz začínající výrazem "fromidentifikátor" následovaný libovolným tokenem kromě ";", "=" nebo ",".
12.22.3 Překlad výrazů dotazu
12.22.3.1 Obecné
Jazyk C# neurčí sémantiku spouštění výrazů dotazu. Výrazy dotazu se spíše překládají do vyvolání metod, které odpovídají vzoru výrazu dotazu (§12.22.4). Konkrétně se výrazy dotazu překládají do vyvolání metod pojmenovaných Where, Select, SelectMany, Join, GroupJoin, OrderBy, OrderByDescending, ThenBy, ThenByDescending, GroupBya Cast. U těchto metod se očekává, že budou mít určité podpisy a návratové typy, jak je popsáno v §12.22.4. Tyto metody mohou být metody instance objektu dotazované nebo rozšiřující metody, které jsou externí objektu. Tyto metody implementují skutečné vykonání dotazu.
Překlad dotazovacích výrazů na volání metod je syntaktické mapování, ke kterému dochází před provedením jakéhokoli přiřazení typu nebo řešení přetížení. Po překladu výrazů dotazu se výsledné vyvolání metody zpracovávají jako vyvolání běžných metod, což může zase odhalit chyby v čase kompilace. Mezi tyto chybové stavy patří metody, které neexistují, argumenty nesprávných typů a obecné metody, ve kterých se odvozování typu nezdaří.
Výraz dotazu se zpracuje opakovaným použitím následujících překladů, dokud nebude možné žádné další snížení. Překlady jsou uvedeny v pořadí použití: každý oddíl předpokládá, že překlady v předchozích oddílech byly provedeny vyčerpávajícím způsobem a po vyčerpání se oddíl později znovu nezobrazí při zpracování stejného výrazu dotazu.
Jedná se o chybu v době kompilace, kdy výraz dotazu zahrnuje přiřazení proměnné rozsahu nebo použití proměnné rozsahu jako argumentu pro odkaz nebo výstupní parametr.
Některé překlady vkládají proměnné rozsahu s transparentními identifikátory , označenými *. Jsou popsány dále v §12.22.3.8.
12.22.3.2 Dotazovací výrazy s pokračováními
Výraz dotazu s pokračováním za textem dotazu
from «x1» in «e1» «b1» into «x2» «b2»
je přeloženo do
from «x2» in ( from «x1» in «e1» «b1» ) «b2»
Překlady v následujících částech předpokládají, že dotazy nemají žádné pokračování.
příklad: Příklad:
from c in customers group c by c.Country into g select new { Country = g.Key, CustCount = g.Count() }je přeložen do:
from g in (from c in customers group c by c.Country) select new { Country = g.Key, CustCount = g.Count() }konečný překlad:
customers. GroupBy(c => c.Country). Select(g => new { Country = g.Key, CustCount = g.Count() })koncového příkladu
12.22.3.3 Explicitní typy proměnných rozsahu
Klauzule from, která explicitně určuje typ proměnné rozsahu
from «T» «x» in «e»
je přeloženo do
from «x» in ( «e» ) . Cast < «T» > ( )
Klauzule join, která explicitně určuje typ proměnné rozsahu
join «T» «x» in «e» on «k1» equals «k2»
je přeloženo do
join «x» in ( «e» ) . Cast < «T» > ( ) on «k1» equals «k2»
Překlady v následujících částech předpokládají, že dotazy nemají žádné explicitní typy proměnných rozsahu.
Příklad: Příklad
from Customer c in customers where c.City == "London" select cje přeloženo do
from c in (customers).Cast<Customer>() where c.City == "London" select cjehož konečný překlad je
customers. Cast<Customer>(). Where(c => c.City == "London")koncového příkladu
Poznámka: Explicitní typy proměnných rozsahu jsou užitečné pro dotazování kolekcí, které implementují neobecné
IEnumerablerozhraní, ale ne obecnéIEnumerable<T>rozhraní. V předchozím příkladu by to byl případ, kdy zákazníci byli typuArrayList. koncová poznámka
12.22.3.4 Degenerovat výrazy dotazu
Výraz dotazu ve formě
from «x» in «e» select «x»
je přeloženo do
( «e» ) . Select ( «x» => «x» )
Příklad: Příklad
from c in customers select cje přeloženo do
(customers).Select(c => c)koncového příkladu
Degenerační výraz dotazu je výraz, který triviálně vybere prvky zdroje.
Poznámka: Pozdější fáze překladu (§12.22.3.6 a §12.22.3.7) odstraňují degenerované dotazy zavedené jinými kroky překladu nahrazením jejich zdroje. Je však důležité zajistit, aby výsledek výrazu dotazu nikdy nebyl zdrojovým objektem samotným. V opačném případě může vrácení výsledku takového dotazu neúmyslně vystavit privátní data (např. pole elementů) volajícímu. Tento krok proto chrání degenerované dotazy napsané přímo ve zdrojovém kódu tím, že explicitně volá
Selectve zdroji. Pak je až na implementátorySelecta dalších operátorů dotazů, aby se zajistilo, že tyto metody nikdy nevracely samotný zdrojový objekt. koncová poznámka
12.22.3.5 From, let, where, join and orderby klauzule
Výraz dotazu s druhou klauzulí from následovanou klauzulí select
from «x1» in «e1»
from «x2» in «e2»
select «v»
je přeloženo do
( «e1» ) . SelectMany( «x1» => «e2» , ( «x1» , «x2» ) => «v» )
Příklad: Příklad
from c in customers from o in c.Orders select new { c.Name, o.OrderID, o.Total }je přeloženo do
(customers). SelectMany(c => c.Orders, (c,o) => new { c.Name, o.OrderID, o.Total } )koncového příkladu
Výraz dotazu s druhou klauzulí from následovanou textem dotazu Q obsahující neprázdnou sadu klauzulí textu dotazu:
from «x1» in «e1»
from «x2» in «e2»
Q
je přeloženo do
from * in («e1») . SelectMany( «x1» => «e2» ,
( «x1» , «x2» ) => new { «x1» , «x2» } )
Q
Příklad: Příklad
from c in customers from o in c.Orders orderby o.Total descending select new { c.Name, o.OrderID, o.Total }je přeloženo do
from * in (customers). SelectMany(c => c.Orders, (c,o) => new { c, o }) orderby o.Total descending select new { c.Name, o.OrderID, o.Total }jehož konečný překlad je
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 })kde
xje identifikátor vygenerovaný kompilátorem, který je jinak neviditelný a nepřístupný.koncového příkladu
Výraz let spolu s předchozí klauzulí from:
from «x» in «e»
let «y» = «f»
...
je přeloženo do
from * in ( «e» ) . Select ( «x» => new { «x» , «y» = «f» } )
...
Příklad: Příklad
from o in orders let t = o.Details.Sum(d => d.UnitPrice * d.Quantity) where t >= 1000 select new { o.OrderID, Total = t }je přeloženo do
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 }jehož konečný překlad je
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 })kde
xje identifikátor vygenerovaný kompilátorem, který je jinak neviditelný a nepřístupný.koncového příkladu
Výraz where spolu s předchozí klauzulí from:
from «x» in «e»
where «f»
...
je přeloženo do
from «x» in ( «e» ) . Where ( «x» => «f» )
...
Klauzule join bezprostředně následovaná klauzulí select
from «x1» in «e1»
join «x2» in «e2» on «k1» equals «k2»
select «v»
je přeloženo do
( «e1» ) . Join( «e2» , «x1» => «k1» , «x2» => «k2» , ( «x1» , «x2» ) => «v» )
Příklad: Příklad
from c in customers join o in orders on c.CustomerID equals o.CustomerID select new { c.Name, o.OrderDate, o.Total }je přeloženo do
(customers).Join( orders, c => c.CustomerID, o => o.CustomerID, (c, o) => new { c.Name, o.OrderDate, o.Total })koncového příkladu
Klauzule join následovaná klauzulí textu dotazu:
from «x1» in «e1»
join «x2» in «e2» on «k1» equals «k2»
...
je přeloženo do
from * in ( «e1» ) . Join(
«e2» , «x1» => «k1» , «x2» => «k2» ,
( «x1» , «x2» ) => new { «x1» , «x2» })
...
Klauzule join-into bezprostředně následovaná klauzulí select
from «x1» in «e1»
join «x2» in «e2» on «k1» equals «k2» into «g»
select «v»
je přeloženo do
( «e1» ) . GroupJoin( «e2» , «x1» => «k1» , «x2» => «k2» ,
( «x1» , «g» ) => «v» )
Klauzule join into následovaná klauzulí textu dotazu
from «x1» in «e1»
join «x2» in «e2» on «k1» equals «k2» into *g»
...
je přeloženo do
from * in ( «e1» ) . GroupJoin(
«e2» , «x1» => «k1» , «x2» => «k2» , ( «x1» , «g» ) => new { «x1» , «g» })
...
Příklad: Příklad
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 }je přeloženo do
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 }jehož konečný překlad je
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 })kde
xayjsou identifikátory generované kompilátorem, které jsou jinak neviditelné a nepřístupné.koncového příkladu
Klauzule orderby a její předchozí klauzule from:
from «x» in «e»
orderby «k1» , «k2» , ... , «kn»
...
je přeloženo do
from «x» in ( «e» ) .
OrderBy ( «x» => «k1» ) .
ThenBy ( «x» => «k2» ) .
... .
ThenBy ( «x» => «kn» )
...
Pokud klauzule ordering určuje sestupný ukazatel směru, vytvoří se místo toho vyvolání OrderByDescending nebo ThenByDescending.
Příklad: Příklad
from o in orders orderby o.Customer.Name, o.Total descending select omá konečný překlad.
(orders) .OrderBy(o => o.Customer.Name) .ThenByDescending(o => o.Total)koncového příkladu
Následující překlady předpokládají, že neexistují žádné let, where, join nebo orderby klauzule a ne více než jedna počáteční from klauzule v každém výrazu dotazu.
12.22.3.6 Klauzule Select
Výraz dotazu ve formě
from «x» in «e» select «v»
je přeloženo do
( «e» ) . Select ( «x» => «v» )
s výjimkou «v» je identifikátor «x», překlad je jednoduše
( «e» )
Příklad: Příklad
from c in customers.Where(c => c.City == "London") select cje jednoduše přeložen do
(customers).Where(c => c.City == "London")koncového příkladu
12.22.3.7 Klauzule Group
Klauzule group
from «x» in «e» group «v» by «k»
je přeloženo do
( «e» ) . GroupBy ( «x» => «k» , «x» => «v» )
Kromě případu, kdy je «v» identifikátor «x», je překlad
( «e» ) . GroupBy ( «x» => «k» )
Příklad: Příklad
from c in customers group c.Name by c.Countryje přeloženo do
(customers).GroupBy(c => c.Country, c => c.Name)koncového příkladu
12.22.3.8 Transparentní identifikátory
Některé překlady vloží proměnné rozsahu s transparentním identifikátoremoznačeným *. Transparentní identifikátory existují pouze v přechodném kroku procesu překladu výrazů dotazu.
Když překlad dotazu vloží transparentní identifikátor, další kroky překladu rozšíří transparentní identifikátor do anonymních funkcí a inicializátorů anonymních objektů. V těchto kontextech mají transparentní identifikátory následující chování:
- Pokud se transparentní identifikátor objeví jako parametr v anonymní funkci, členové přidruženého anonymního typu jsou automaticky v dosahu v těle anonymní funkce.
- Pokud je člen s průhledným identifikátorem dostupný, členové tohoto člena jsou také dostupní.
- Když se jako deklarátor člena v inicializátoru anonymního objektu vyskytuje transparentní identifikátor, zavádí člena s transparentním identifikátorem.
V krocích překladu popsaných výše jsou transparentní identifikátory vždy zavedeny společně s anonymními typy s záměrem zachycení více proměnných rozsahu jako členů jednoho objektu. Implementace jazyka C# umožňuje použít jiný mechanismus než anonymní typy k seskupení více proměnných rozsahu. Následující příklady překladu předpokládají, že se používají anonymní typy a ukazuje jeden z možných překladů transparentních identifikátorů.
Příklad: Příklad
from c in customers from o in c.Orders orderby o.Total descending select new { c.Name, o.Total }je přeloženo do
from * in (customers).SelectMany(c => c.Orders, (c,o) => new { c, o }) orderby o.Total descending select new { c.Name, o.Total }která je dále přeložena do
customers .SelectMany(c => c.Orders, (c,o) => new { c, o }) .OrderByDescending(* => o.Total) .Select(\* => new { c.Name, o.Total })které, pokud jsou průhledné identifikátory vymazány, je ekvivalentní
customers .SelectMany(c => c.Orders, (c,o) => new { c, o }) .OrderByDescending(x => x.o.Total) .Select(x => new { x.c.Name, x.o.Total })kde
xje identifikátor vygenerovaný kompilátorem, který je jinak neviditelný a nepřístupný.Příklad
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 }je přeloženo do
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 }který je dále omezen na
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 })jehož konečný překlad je
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 })kde
xayjsou identifikátory generované kompilátorem, které jsou jinak neviditelné a nepřístupné. koncového příkladu
12.22.4 Vzor výrazu dotazu
Vzor výrazu dotazu vytvoří vzor metod, které typy mohou implementovat pro podporu výrazů dotazů.
Obecný typ C<T> podporuje model výrazů dotazu, pokud by jeho metody veřejného člena a veřejně přístupné rozšiřující metody mohly být nahrazeny následující definicí třídy. Členy a metody rozšíření s podporou přístupnosti se označují jako "tvar" obecného typu C<T>. Obecný typ se používá k ilustraci správných vztahů mezi parametry a návratovými typy, ale je možné implementovat i vzor pro ne generické typy.
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; }
}
Výše uvedené metody používají obecné typy delegátů Func<T1, R> a Func<T1, T2, R>, ale mohly by stejně dobře použít jiné typy delegáta nebo stromu výrazů se stejnými relacemi v parametrech a návratových typech.
Poznámka: Doporučený vztah mezi
C<T>aO<T>, který zajišťuje, že metodyThenByaThenByDescendingjsou k dispozici pouze na základě výsledkuOrderByneboOrderByDescending. koncová poznámka
Poznámka: Doporučený tvar výsledku
GroupBy– posloupnost sekvencí, kde má každá vnitřní sekvence další vlastnostKey. koncová poznámka
Poznámka: Vzhledem k tomu, že výrazy dotazu se překládají na vyvolání metod pomocí syntaktického mapování, mají typy značnou flexibilitu při implementaci libovolného nebo všech vzorů výrazů dotazu. Například metody vzoru lze implementovat jako metody instance nebo jako rozšiřující metody, protože tyto dvě mají stejnou syntaxi vyvolání a metody mohou požadovat delegáty nebo stromy výrazů, protože anonymní funkce jsou převoditelné na obě. Typy, které implementují pouze některé vzory výrazů dotazu, podporují pouze překlady těchto výrazů, které lze namapovat na metody, jež tento typ podporuje. koncová poznámka
Poznámka: Obor názvů
System.Linqposkytuje implementaci vzoru výrazu dotazu pro libovolný typ, který implementuje rozhraníSystem.Collections.Generic.IEnumerable<T>. koncová poznámka
12.23 Operátory přiřazení
12.23.1 Obecné
Všechny operátory přiřazení kromě jednoho z operátorů přiřazení přiřadí novou hodnotu proměnné, vlastnosti, události nebo prvku indexeru. Výjimka, = ref, přiřadí referenci na proměnnou (§9.5) referenční proměnné (§9.7).
assignment
: unary_expression assignment_operator expression
;
assignment_operator
: '=' 'ref'? | '+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' |
'<<=' | '??='
| right_shift_assignment
;
Levý operand přiřazení musí být výraz klasifikovaný jako proměnná nebo, s výjimkou = ref, přístup k vlastnosti, indexeru, události nebo n-tice. Výraz deklarace není přímo povolen jako levá operanda, ale může se objevit jako krok při evaluaci dekonstruujícího přiřazení.
Operátor = se nazývá jednoduchý operátor přiřazení . Přiřadí hodnotu nebo hodnoty pravého operandu proměnné, vlastnosti, prvku indexeru nebo prvkům n-tice určeným levým operandem. Levý operand operátoru jednoduchého přiřazení nesmí být přístupem k události (s výjimkou případů popsaných v §15.8.2). Operátor jednoduchého přiřazení je popsán v §12.23.2.
Operátor = ref se nazývá operátor přiřazení odkazu . Určuje pravý operand, který je variable_reference (§9.5), jako referent referenční proměnné, kterou určuje levý operand. Operátor přiřazení odkazu je popsán v §12.23.3.
Operátory přiřazení jiné než = operátor a = ref operátor se nazývají složené operátory přiřazení. Tyto operátory se zpracovávají takto:
- U operátoru
??=, pouze pokud je hodnota levého operandunull, je pravý operand vyhodnocen a výsledek je přiřazen proměnné, vlastnosti nebo prvku indexeru, který je určen levým operandem. - V opačném případě se uvedená operace provádí na dvou operandech a výsledná hodnota je přiřazena proměnné, vlastnosti nebo indexeru prvku zadanému levým operandem. Operátory složeného přiřazení jsou popsány v §12.23.4.
Operátory a += operátory -= s výrazem pro přístup k události jako levý operand se nazývají operátory přiřazení událostí. Žádný jiný operátor přiřazení není platný s přístupem k události jako levým operandem. Operátory přiřazení události jsou popsány v §12.23.5.
Operátory přiřazení mají pravoasociativní povahu, což znamená, že operace jsou seskupovány zprava doleva.
Příklad: Výraz tvaru
a = b = cse vyhodnotí jakoa = (b = c). koncového příkladu
12.23.2 Jednoduché přiřazení
Operátor = se nazývá jednoduchý operátor přiřazení.
Je-li levý operand jednoduchého přiřazení ve tvaru E.P nebo E[Ei], a E má kompilovaný typ dynamic, pak je přiřazení dynamicky vázáno (§12.3.3). V tomto případě je typ výrazu přiřazení určen za kompilace jako dynamica rozlišení, popsané níže, bude provedeno za běhu na základě typu E. Je-li levý operand formuláře E[Ei], kde alespoň jeden prvek Ei má dynamictyp kompilace a typ doby kompilace E není pole, výsledný přístup indexeru je dynamicky vázán, ale s omezenou kontrolou času kompilace (§12.6.5).
Jednoduché přiřazení, kde je levý operand klasifikován jako n-tice, se také nazývá dekonstrukční přiřazení. Pokud některý z elementů řazené kolekce členů levého operandu má název elementu, dojde k chybě v době kompilace. Pokud některý z prvků řazené kolekce členů levého operandu je declaration_expression a jakýkoli jiný prvek není declaration_expression nebo jednoduchý zahození, dojde k chybě v době kompilace.
Typ jednoduchého přiřazení x = y je typ přiřazení k x z y, který je určován rekurzivním způsobem následovně:
- Je-li
xvýraz řazené kolekce členů(x1, ..., xn)aylze dekonstruovat na výraz řazené kolekce členů(y1, ..., yn)s prvkyn(§12.7) a každé přiřazeníxiyimá typTi, pak přiřazení má typ(T1, ..., Tn). - Jinak pokud je
xklasifikována jako proměnná, proměnná neníreadonly,xmá typTaymá implicitní převod naT, pak přiřazení má typT. - V opačném případě je
xklasifikována jako implicitně typovaná proměnná (tj. implicitně zadaný výraz deklarace) aymá typT, odvozený typ proměnné jeTa přiřazení má typT. - Jinak pokud je
xklasifikován jako vlastnost nebo přístup indexeru, a má přístupný nastavovací přístupový člen,xmá typTaymá implicitní převod naT, pak přiřazení má typT. - V opačném případě není přiřazení platné a dojde k chybě při vyhodnocování času vazby.
Zpracování za běhu jednoduchého přiřazení formuláře x = y s typem T se provádí jako přiřazení k xy s typem T, který se skládá z následujících rekurzivních kroků:
-
xse vyhodnotí, pokud to ještě nebylo. - Pokud je
xklasifikována jako proměnná,yse vyhodnotí a v případě potřeby se převede naTprostřednictvím implicitního převodu (§10,2).- Pokud je proměnná daná
xprvkem pole odkazového typu, provede se kontrola za běhu, která zajistí, že hodnota vypočítaná proyje kompatibilní s instancí pole, jehožxje prvkem. Kontrola proběhne úspěšně, pokudyjenull, nebo pokud implicitní převod odkazu (§10.2.8) existuje z typu instance odkazovanéyna skutečný typ prvku instance pole obsahujícíx. V opačném případě se vyvoláSystem.ArrayTypeMismatchException. - Hodnota, která vznikla vyhodnocením a převodem
y, je uložena na umístění, které je dáno vyhodnocenímx, a je předána jako výsledek přiřazení.
- Pokud je proměnná daná
- Pokud je
xklasifikován jako přístup k vlastnosti nebo indexeru:-
yse vyhodnotí a v případě potřeby se převede naTprostřednictvím implicitního převodu (§10,2). - Sada přístupového objektu
xje vyvolána s hodnotou, která je výsledkem vyhodnocení a převoduyjako argument hodnoty. - Hodnota vyplývající z vyhodnocení a převodu
yje výsledkem přiřazení.
-
- Pokud je
xklasifikován jako n-tice(x1, ..., xn)s aritoun:-
yse dekonstruktivně konstruuje s prvkynna n-ticový výraze. - Výsledná řazená kolekce členů
tse vytvoří převodemenaTpomocí implicitního převodu řazené kolekce členů. - pro každou
xizleva doprava se provede přiřazeníxidot.Itemis tím rozdílem, žexise znovu nevyhodnocují. -
tje výsledkem přiřazení.
-
Poznámka: Pokud je typ času kompilace
xdynamica existuje implicitní převod z typu času kompilaceynadynamic, nevyžaduje se žádné řešení za běhu. koncová poznámka
Poznámka: Pravidla společné odchylky matice (§17.6) umožňují, aby hodnota typu matice
A[]byla odkazem na instanci typu maticeB[], pokud existuje implicitní převod odkazu zBnaA. Vzhledem k těmto pravidlům je přiřazení prvku pole reference_type vyžaduje kontrolu za běhu, která zajistí, že přiřazená hodnota je kompatibilní s instancí pole. V příkladustring[] sa = new string[10]; object[] oa = sa; oa[0] = null; // OK oa[1] = "Hello"; // OK oa[2] = new ArrayList(); // ArrayTypeMismatchExceptionPoslední přiřazení způsobí vyvolání
System.ArrayTypeMismatchException, protože odkaz naArrayListnelze uložit do prvkustring[].koncová poznámka
Pokud je vlastnost nebo indexer deklarovaný v struct_type cílem přiřazení, výraz instance přidružený k vlastnosti nebo přístupu indexeru se klasifikuje jako proměnná. Pokud je výraz instance klasifikován jako hodnota, dojde k chybě vyhodnocení času vazby.
Poznámka: Vzhledem k §12.8.7platí stejné pravidlo i pro pole. koncová poznámka
Příklad: Vzhledem k deklaracím:
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; } } }v příkladu
Point p = new Point(); p.X = 100; p.Y = 100; Rectangle r = new Rectangle(); r.A = new Point(10, 10); r.B = p;přiřazení
p.X,p.Y,r.Aar.Bjsou povolená, protožeparjsou proměnné. V příkladu všakRectangle r = new Rectangle(); r.A.X = 10; r.A.Y = 10; r.B.X = 100; r.B.Y = 100;Přiřazení jsou všechna neplatná, protože
r.Aar.Bnejsou proměnné.koncového příkladu
12.23.3 Přiřazení odkazu
Operátor = ref je známý jako operátor přiřazení ref.
Levý operand musí být výraz, který je vázán na referenční proměnnou (§9.7), parametr odkazu (jiný než this), výstupní parametr nebo vstupní parametr. Pravý operand musí být výraz, který poskytuje variable_reference označující hodnotu stejného typu jako levý operand.
Jedná se o chybu doby kompilace, pokud je kontext ref-safe-context (§9.7.2) levého operandu širší než kontext ref-safe-kontext pravého operandu.
Pravý operand musí být jistě přiřazen v okamžiku přiřazování reference.
Když levý operand vytvoří vazbu s výstupním parametrem, jedná se o chybu, pokud tento výstupní parametr nebyl určitě přiřazen na začátku operátoru přiřazení s ref.
Je-li levý operand zapisovatelným odkazem (tj. označuje cokoli jiného než ref readonly místní nebo vstupní parametr), bude pravý operand zapisovatelným variable_reference. Pokud je pravá proměnná operandu zapisovatelná, může být levý operand referencí, která je zapisovatelná nebo jen pro čtení.
Operace dělá z levého operandu alias pravé proměnné operandu. Alias může být jen pro čtení, i když je správná proměnná operandu zapisovatelná.
Operátor přiřazení ref poskytuje variable_reference přiřazeného typu. Je zapisovatelný, pokud je levý operand zapisovatelný.
Operátor přiřazení odkazu nepřečte umístění úložiště, na které odkazuje správný operand.
příklad: Tady je několik příkladů použití
= ref:public static int M1() { ... } public static ref int M2() { ... } public static ref uint M2u() { ... } public static ref readonly int M3() { ... } public static void Test() { int v = 42; ref int r1 = ref v; // OK, r1 refers to v, which has value 42 r1 = ref M1(); // Error; M1 returns a value, not a reference r1 = ref M2(); // OK; makes an alias r1 = ref M2u(); // Error; lhs and rhs have different types r1 = ref M3(); // error; M3 returns a ref readonly, which r1 cannot honor ref readonly int r2 = ref v; // OK; make readonly alias to ref r2 = ref M2(); // OK; makes an alias, adding read-only protection r2 = ref M3(); // OK; makes an alias and honors the read-only r2 = ref (r1 = ref M2()); // OK; r1 is an alias to a writable variable, // r2 is an alias (with read-only access) to the same variable }koncového příkladu
Poznámka: Při čtení kódu pomocí operátoru
= refmůže být lákavé čístrefčást jako součást operandu. To je obzvláště matoucí, pokud je operand podmíněným výrazem?:. Například při čteníref int a = ref b ? ref x : ref y;je důležité to interpretovat jako že= refje operátor ab ? ref x : ref yje pravý operand:ref int a = ref (b ? ref x : ref y);. Důležité je, že výrazref bje není součástí tohoto příkazu, i když se může na první pohled objevit. koncová poznámka
12.23.4 Složené přiřazení
Je-li levý operand složeného přiřazení ve tvaru E.P nebo E[Ei], kde E má kompilaci časový typ dynamic, pak je přiřazení dynamicky vázáno (§12.3.3). V tomto případě je typ výrazu přiřazení určen za kompilace jako dynamica rozlišení, popsané níže, bude provedeno za běhu na základě typu E. Je-li levý operand formuláře E[Ei], kde alespoň jeden prvek Ei má dynamictyp kompilace a typ doby kompilace E není pole, výsledný přístup indexeru je dynamicky vázán, ale s omezenou kontrolou času kompilace (§12.6.5).
a ??= b je ekvivalentní hodnotě (T) (a ?? (a = b)), s výjimkou toho, že je a vyhodnocen pouze jednou, kde T je typ a , pokud je typ b dynamického a jinak T je typ a ?? b.
V opačném případě je operace formuláře x «op»= y zpracována použitím rozlišení přetížení binárního operátoru (§12.4.5), jako by byla operace zapsána x «op» y. Pak
- Pokud je návratový typ vybraného operátoru implicitně konvertibilní na typ
x, operace se vyhodnotí jakox = x «op» ys tím rozdílem, žexse vyhodnotí pouze jednou. - V opačném případě, pokud je vybraný operátor předdefinovaným operátorem, je-li návratový typ vybraného operátoru explicitně konvertibilní na typ
xa pokud jeyimplicitně konvertibilní na typxnebo operátor je operátor směny, operace se vyhodnotí jakox = (T)(x «op» y), kdeTje typx, kromě toho, žexse vyhodnotí pouze jednou. - V opačném případě je složené přiřazení neplatné a dojde k chybě určení času vazby.
Pojem "vyhodnocen pouze jednou" znamená, že při vyhodnocování x «op» yse výsledky všech základních výrazů x dočasně uloží a pak znovu použijí při provádění přiřazení k x.
Příklad: V
A()[B()] += C()přiřazení, kdeAje metoda vracejícíint[]aBaCjsou metody vracejícíint, metody jsou vyvolány pouze jednou, v pořadíA,B,C. koncového příkladu
Pokud je levý operand složeného přiřazení přístupem k vlastnosti nebo indexeru, musí mít tato vlastnost nebo indexer přístupový prvek get i set. Pokud tomu tak není, dojde k chybě při určování doby vazby.
Druhé pravidlo výše umožňuje vyhodnotit x «op»= y jako x = (T)(x «op» y) v určitých kontextech. Pravidlo existuje tak, aby předdefinované operátory bylo možné použít jako složené operátory, pokud je levý operand typu sbyte, byte, short, ushortnebo char. I když jsou oba argumenty jednoho z těchto typů, předdefinované operátory vytvoří výsledek typu int, jak je popsáno v §12.4.7.3. Bez přetypování by tedy nebylo možné přiřadit výsledek levému operandu.
Intuitivním účinkem pravidla pro předdefinované operátory je jednoduše to, že x «op»= y je povoleno, pokud jsou povoleny obě x «op» y i x = y.
příklad: V následujícím kódu
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; // OKintuitivním důvodem každé chyby je, že odpovídající jednoduché přiřazení by také bylo chybou.
koncového příkladu
Poznámka: To také znamená, že operace složeného přiřazení podporují zvedané operátory. Vzhledem k tomu, že složené přiřazení
x «op»= yje vyhodnoceno jakox = x «op» ynebox = (T)(x «op» y), pravidla vyhodnocení implicitně pokrývají operátory lifted. koncová poznámka
12.23.5 Přiřazení události
Pokud je levý operand operátoru a += or -= klasifikovaný jako přístup k události, vyhodnotí se výraz následujícím způsobem:
- Vyhodnocuje se, pokud existuje, výraz instance pro přístup k události.
- Pravý operand operátoru
+=nebo-=se vyhodnotí, a v případě potřeby se převede na typ levého operandu pomocí implicitní konverze (§10.2). - Je vyvolán přístupový člen události se seznamem argumentů, který se skládá z hodnoty vypočítané v předchozím kroku. Pokud byl operátor
+=, je vyvolán přístupový objekt pro přidání; pokud byl operátor-=, je vyvolán přístupový objekt pro odstranění.
Výraz přiřazení události nevyvolá hodnotu. Výraz přiřazení události je tedy platný pouze v kontextu statement_expression (§13.7).
Výraz 12.24
Výraz je non_assignment_expression nebo přiřazení.
expression
: non_assignment_expression
| assignment
;
non_assignment_expression
: declaration_expression
| conditional_expression
| lambda_expression
| query_expression
;
12.25 Konstantní výrazy
Konstantní výraz je výraz, který se v době kompilace plně vyhodnotí.
constant_expression
: expression
;
Konstantní výraz musí mít hodnotu null nebo jeden z následujících typů:
-
sbyte,byte,short,ushort,int,uint,long,ulong,char,float,double,decimal,bool,string - výčtový typ; nebo
- výchozí hodnota výrazu (§12.8.21) pro referenční typ.
Ve výrazech konstant jsou povoleny pouze následující konstrukce:
- Literály (včetně literálu
null). - Odkazy na
constčleny tříd, struktur a typů rozhraní. - Odkazy na členy typů výčtu.
- Odkazy na místní konstanty.
- Závorkované podvýrazy, které jsou samy o sobě konstantními výrazy.
- Přetypování výrazů
- výrazy
checkedaunchecked. -
nameofvýrazy. - Předdefinované
+,-,!(logická negace) a~unární operátory. - Předdefinované
+,-,*,/,%,<<,>>,&,|,^,&&,||,==,!=,<,>,<=a>=binární operátory. - Podmíněný operátor
?:. - Operátor
!odpouštějící hodnotu null (§12.8.9). -
sizeofvýrazy, za předpokladu, že nespravovaný typ je jedním z typů zadaných v §24.6.9 , pro kterésizeofse vrátí konstantní hodnota. - Výchozí výrazy hodnot za předpokladu, že typ je jedním z výše uvedených typů.
V konstantních výrazech jsou povoleny následující převody:
- Převody identit
- Číselné převody
- Výčtové převody
- Převody konstantních výrazů
- Implicitní a explicitní převody odkazů za předpokladu, že zdrojem převodů je konstantní výraz, který se vyhodnotí jako hodnota
null.
Poznámka: Jiné převody, včetně krabicování, rozbalení a implicitních převodů odkazů bez
nullhodnot, nejsou ve výrazech konstanty povoleny. koncová poznámka
příklad: V následujícím kódu
class C { const object i = 5; // error: boxing conversion not permitted const object str = "hello"; // error: implicit reference conversion }inicializace
ije chyba, protože je vyžadován převod boxingu. Inicializacestrje chyba, protože je vyžadován implicitní převod odkazu z hodnoty, která nenínull.koncového příkladu
Kdykoli výraz splňuje výše uvedené požadavky, výraz se vyhodnotí v době kompilace. To platí i v případě, že výraz je dílčí výraz většího výrazu, který obsahuje nekontinutní konstrukce.
Vyhodnocení doby kompilace konstantních výrazů používá stejná pravidla jako vyhodnocení ne constantních výrazů, s tím rozdílem, že pokud by vyhodnocení za běhu vyvolalo výjimku, vyhodnocení v době kompilace způsobí, že dojde k chybě kompilace.
Pokud není konstantní výraz explicitně umístěn v kontextu unchecked, přetečení, ke kterým dochází v aritmetických operacích a převodech celočíselného typu během vyhodnocení výrazu v době kompilace, vždy způsobují chyby v době kompilace (§12.8.20).
Konstantní výrazy jsou vyžadovány v kontextech uvedených níže a to je uvedeno v gramatikě pomocí constant_expression. V těchto kontextech dojde k chybě v době kompilace, pokud výraz nelze plně vyhodnotit v době kompilace.
- Konstantní deklarace (§15.4)
- Prohlášení členů výčtu (§20.4)
- Výchozí argumenty seznamů parametrů (§15.6.2)
-
casepopiskyswitchprohlášení (§13.8.3). -
goto caseprohlášení (§13.10.4) - Délky dimenzí ve výrazu pro vytvoření pole (§12.8.17.4), který obsahuje inicializátor.
- Atributy (§23)
- V konstantní_vzor (§ 11.2.3)
Implicitní převod konstantního výrazu (§10.2.11) umožňuje, aby konstantní výraz typu int byl převeden na sbyte, byte, short, ushort, uintnebo ulong, za předpokladu, že hodnota konstantního výrazu je v rozsahu cílového typu.
12.26 Logické výrazy
boolean_expression je výraz, který poskytuje výsledek typu bool; buď přímo, nebo prostřednictvím použití operator true v určitých kontextech, jak je uvedeno níže:
boolean_expression
: expression
;
Řídící podmínkový výraz u if_statement (§13.8.2), while_statement (§13.9.2), do_statement (§13.9.3) nebo for_statement (§13.9.4) je boolean_expression. Řídicí podmíněný výraz operátoru ?: (§12.20) se řídí stejnými pravidly jako boolean_expression, ale z důvodů priority operátoru je klasifikován jako null_coalescing_expression.
Aby bylo možné vytvořit hodnotu typu , je nutné Ebool následujícím způsobem:
- Pokud je E implicitně konvertibilní na
boolpak se použije implicitní převod za běhu. - V opačném případě se rozlišení přetížení unárního operátoru (§12.4.4) používá k nalezení jedinečné nejlepší implementace
operator truenaEa tato implementace se použije za běhu. - Pokud se nenajde žádný takový operátor, dojde k chybě při určování vázacího času.
ECMA C# draft specification