12 Výrazy
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 je platný pouze v kontextu statement_expression (§13.7) nebo jako tělo lambda_expression (§12.19).
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.6) a lze jej 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-=
(§12.21.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. 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řístup je určen kontextem vlastnosti nebo přístupu indexeru: Pokud je přístup cílem přiřazení, je vyvolán přístupový objekt sady pro přiřazení nové hodnoty (§12.21.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.WriteLine
je 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.WriteLine
je 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 můžou být objekty z jiných programovacích jazyků s různými typy systémů nebo se mohou jednat o objekty, které jsou programově nastaveny tak, aby implementovaly vlastní sémantiku vazeb 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 nemusí být samy o sobě dynamickými objekty, ale jsou stále typem neznámým pro programátora v době kompilace. 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. konec 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á metodaF
pomocí staré hodnotyi
, metodaG
je volána se starou hodnotoui
a nakonec metodaH
je volána s novou hodnotou i. Toto je oddělené od a nesouvisí s pořadím operátoru. konec 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 * z
se 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.y
x?.y
f(x)
a[x]
a?[x]
x++
x--
x!
new
typeof
default
checked
unchecked
delegate
stackalloc
§12.9 Unární +
-
!x
~
++x
--x
(T)x
await x
§12.10 Multiplikativní *
/
%
§12.10 Aditivní +
-
§12.11 Směna <<
>>
§12.12 Relační a typové testování <
>
<=
>=
is
as
§12.12 Rovnost ==
!=
§12.13 Logická operace AND &
§12.13 Logický XOR ^
§12.13 Logické NEBO \|
§12.14 Podmíněný operátor AND &&
§12.14 Podmíněné OR \|\|
§12.15 a §12.16 Spojení pro null a výraz throw ??
throw x
§12.18 Podmíněný ?:
§12.21 a §12.19 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í:
- Kromě operátorů přiřazení a operátoru nulového sjednocení jsou všechny binární operátory asociativnívlevo, což znamená, že operace se provádějí zleva doprava.
Příklad:
x + y + z
se vyhodnotí jako(x + y) + z
. konec 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 = z
se vyhodnotí jakox = (y = z)
. koncového příkladu
Prioritu a asociativitu lze řídit pomocí závorek.
Příklad:
x + y * z
nejprve vynásobíy
z
a potom přidá výsledek kx
, ale(x + y) * z
nejprve přidáx
ay
a výsledek pak vynásobíz
. konec 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átory jsou:
+ - !
(pouze logická negace) ~ ++ -- true false
Poznámka: I když
true
afalse
nejsou ve výrazech explicitně používány (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ů.: Booleovské výrazy (§12.24) a výrazy zahrnující podmíněné (§12.18) a podmíněné logické operátory (§12.14). koncová poznámka
Poznámka: Operátor odpouštějící null (příponu
!
, §12.8.9) není operátorem, který lze přetížit. koncová poznámka
přetížitelné binární operátory jsou:
+ - * / % & | ^ << >> == != > < <= >=
Přetížit můžou být pouze výše uvedené operátory. Není možné přetížit přístup ke členům, vyvolání metody ani operátory =
, &&
, ||
, ??
, ?:
, =>
, checked
, unchecked
, new
, typeof
, default
, as
a is
.
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.21. konec 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.21.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ů
is
neboas
. 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í. konec 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íbool
výsledek. koncová poznámka
Popisy jednotlivých operátorů v §12.9 až §12.21 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á
X
pro 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 X
a y
je výraz typu Y
, je zpracován takto:
- Sada kandidátních operátorů definovaných uživatelem, poskytovaných
X
aY
pro operacioperator «op»(x, y)
, je určena. Sada je tvořena sjednocením kandidátských operátorů poskytovanýchX
a 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
X
aY
zaměnitelné identity nebo pokud jsouX
aY
odvozeny 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
X
aY
, operátor«op»Y
poskytovanýY
má stejný návratový typ jako«op»X
poskytovanéX
a typy operandů«op»Y
mají 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í
operator «op»
implementace, včetně jejich zdvižených formulářů, stanou sadou kandidátských operátorů pro 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 při řešení přetížení nepodaří vybrat jediný nejlepší operátor, dojde k chybě při vazbě.
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₀
. PokudT
je 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ůA
použitelný alespoň jeden operátor (§12.6.4.2), 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.10), relační operátory (§12.12) a integrální logické operátory (§12.13.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
, kdeb
jebyte
as
jeshort
, rozlišení přetížení vybereoperator *(int, int)
jako nejlepší operátor. Účinek je tedy, žeb
as
jsou převedeny naint
a typ výsledku jeint
. Stejně tak pro operacii * d
, kdei
jeint
ad
jedouble
,overload
rozlišení vybereoperator *(double, double)
jako nejlepší operátor. konec 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
, ushort
nebo 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 typufloat
nebodouble
. - 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
,int
nebolong
. - 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
uint
a druhý operand je typusbyte
,short
neboint
, 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
decimal
s typydouble
afloat
. Pravidlo vyplývá ze skutečnosti, že mezi typemdecimal
a typydouble
afloat
neexistují 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 hodnotulong
stejně 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
decimal
nelze vynásobitdouble
. Tato chyba je vyřešena explicitním převodem druhého operandu nadecimal
ná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
Lifted operátory umožňují, aby předdefinované a uživatelsky definované operátory, které pracují s nenulovými hodnotovými typy, mohly být také použity s nulovatelnými tvary těchto typů. 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~
, existuje vyvýžená forma operátoru, pokud operand a typy výsledků 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>>
, existuje nadzvednutá forma operátoru, pokud operand a typy výsledků 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 typu "zvednutý" vytváří hodnotunull
, pokud je jeden nebo oba operandynull
(výjimku tvoří operátory&
a|
typubool?
, jak je popsáno v §12.13.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ěnull
hodnoty za stejné anull
hodnotu za nerovnou s libovolnou ne-null
hodnotou. Pokud oba operandy nejsounull
, operátor zvednutím rozbalí operandy a použije podkladový operátor k vytvořeníbool
vý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í, pole nebo vlastnost buď typu delegáta (§20) nebo typ dynamic
(§8.2.4), pak je člen 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
T
parametr typu, pak je sada sjednocení sad přístupných členů pojmenovanýchN
v 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ýchN
vobject
. - V opačném případě se sada skládá ze všech přístupných (§7.5) členů pojmenovaných
N
vT
, včetně zděděných členů a přístupných členů pojmenovanýchN
vobject
. Je-liT
konstruovaný 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
K
nula, odeberou se všechny vnořené typy, jejichž deklarace zahrnují parametry typu. PokudK
není nula, odeberou se všechny členy s jiným počtem parametrů typu. Pokud jeK
nula, 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.M
v sadě, kdeS
je typ, ve kterém je členM
deklarován, jsou použita následující pravidla:- Pokud je
M
konstanta, pole, vlastnost, událost nebo člen výčtu, odeberou se ze sady všechny členy deklarované v základním typuS
. - Je-li
M
deklarace typu, všechny typy, které nejsou deklarovány v základním typuS
jsou ze sady odebrány a všechny deklarace typů se stejným počtem parametrů typu jakoM
deklarované v základním typuS
jsou ze sady odebrány. - Pokud
M
je 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
T
je parametr typu aT
má efektivní základní třídu jinou nežobject
a neprázdnou efektivní sadu rozhraní (§15.2.5). Pro každý členS.M
v sadě, kdeS
je typ, ve kterém jeM
člen deklarován, použijí se následující pravidla, pokudS
je deklarace třídy jiná nežobject
:- Pokud
M
je konstanta, pole, vlastnost, událost, člen výčtu nebo deklarace typu, všechny členy deklarované v deklaraci rozhraní jsou ze sady odebrány. - Pokud
M
je metoda, odeberou se ze sady všechny členy bez metody deklarované v deklaraci rozhraní a všechny metody se stejným podpisem jakoM
deklarované 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 §18.4.6.
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
T
object
nebodynamic
,T
nemá žádný základní typ. - Pokud je
T
enum_type, základní typyT
jsou typy třídSystem.Enum
,System.ValueType
aobject
. - Pokud je
T
struktura typu , základní typyT
jsou typy třídSystem.ValueType
aobject
.Poznámka: nullable_value_type je struct_type (§8.3.1). koncová poznámka
- Je-li
T
typ třídy, základní typyT
jsou základními třídamiT
, včetně typu třídyobject
. - Je-li
T
interface_type, základní typyT
jsou základní rozhraníT
a typ třídyobject
. - Pokud
T
je array_type, základní typyT
jsou typy třídSystem.Array
aobject
. - Je-li
T
delegate_type, základní typyT
jsou typy třídSystem.Delegate
aobject
.
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
,y
avalue
označují výrazy klasifikované jako proměnné nebo hodnoty,T
označuje výraz klasifikovaný jako typ,F
je jednoduchý název metody aP
je 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 F
v 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 F
ve třídě nebo struktuřeT
se 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 F
ve třídě, struktuře nebo rozhraní zadaném typeme
. K chybě doby vazby dojde, pokud je metodastatic
. Metoda je vyvolána pomocí výrazu instancee
a seznamu argumentů(x, y)
.Přístup k vlastnostem P
Přístupový prvek typu get vlastnosti P
ve vnitřní třídě nebo struktuře je vyvolán. Pokud jeP
jen pro zápis, dojde k chybě v době kompilace. PokudP
nenístatic
, výraz instance jethis
.P = value
Nastavovací metoda vlastnosti P
v obsahující třídě nebo struktuře je vyvolána se seznamem argumentů(value)
. Pokud jeP
jen pro čtení, dojde k chybě v době kompilace. PokudP
nenístatic
, výraz instance jethis
.T.P
Přístupový objekt get vlastnosti P
ve třídě nebo struktuřeT
je vyvolán. K chybě v době kompilace dochází, pokudP
nenístatic
nebo jeP
jen pro zápis.T.P = value
Set akcesor vlastnosti P
ve třídě nebo struktuřeT
je vyvolán se seznamem argumentů(value)
. K chybě v době kompilace dochází, pokudP
nenístatic
nebo jeP
jen pro čtení.e.P
Get přístup k vlastnosti P
v třídě, struktuře nebo rozhraní zadaném typemE
je vyvolán výrazem instancee
. K chybě při vazbě dochází, pokud jeP
static
nebo jeP
určeno pouze pro zápis.e.P = value
Nastavovací metoda vlastnosti P
ve třídě, struktuře nebo rozhraní určeném typemE
se vyvolává s výrazem instancee
a seznamem argumentů(value)
. Dochází k chybě doby vazby, pokud jeP
static
, nebo pokud jeP
určeno pouze pro čtení.Přístup k událostem E += value
Vyvolá se doplněk události E
v obsahující třídě nebo struktuře. PokudE
nenístatic
, výraz instance jethis
.E -= value
Přístup k odebrání události E
ve včleněné třídě nebo struktuře je vyvolán. PokudE
nenístatic
, výraz instance jethis
.T.E += value
Přidání přístupového členu události E
ve třídě nebo struktuřeT
je vyvoláno. K chybě při vazbě dochází, pokudE
nenístatic
.T.E -= value
Odebrání přístupového objektu události E
ve třídě nebo struktuřeT
je vyvoláno. K chybě doby vazby dochází, pokudE
nenístatic
.e.E += value
Přidávací přístupový prvek události E
ve třídě, struktuře nebo rozhraní daném typemE
je vyvolán s výrazem instancee
. Dochází k chybě doby vazby, pokud jeE
static
.e.E -= value
Odebrání přístupového objektu události E
ve třídě, struktuře nebo rozhraní zadaném typemE
je vyvoláno výrazem instancee
. K chybě při vazbě dochází, pokud jeE
static
.Přístup k indexeru e[x, y]
Výběr nejlepšího indexeru ve třídě, struktuře nebo rozhraní určených typem e
se provádí pomocí rozlišení přetížení. Přístupový objekt get indexeru je vyvolán výrazem instancee
a seznamem argumentů(x, y)
. Chyba při vazebním čase nastane, pokud je indexer pouze pro zápis.e[x, y] = value
Rozlišení přetížení se použije k výběru nejlepšího indexeru ve třídě, struktuře nebo rozhraní zadaném typem e
. Objekt set indexeru se vyvolá pomocí výrazu instancee
a seznamu argumentů(x, y, value)
. Pokud je indexer pouze pro čtení, nastane chyba při určování doby vazby.Vyvolání operátoru -x
Rozliš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 + y
Rozliš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 x
ay
. 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
in
ná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
ref
ná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
out
ná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 nemusí být považováno 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
value
parametru 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ázvemc
je 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 argument
Ve volání metody
M1(i)
sei
předává jako vstupní argument, protože je klasifikován jako proměnná a má stejný typint
jako vstupní parametr. Ve volání metodyM1(i + 5)
se vytvoří nepojmenovanáint
proměnná, inicializuje se s hodnotou argumentu a pak se předá jako vstupní argument. Viz §12.6.4.2 a §12.6.4.4.závěr 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í
F
způsobí vyvoláníSystem.ArrayTypeMismatchException
, protože skutečný typ prvkub
jestring
a nikoliobject
.ukončit příklad
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 = 3
konec příkladu
Když je člen funkce s polem parametrů vyvolán ve své 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.5) 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 });
konec 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
int
astring
určeny z argumentů metody.konec 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.14) a nalezení nejlepšího společného typu sady výrazů (§12.6.3.15). koncová poznámka
12.6.3.2 První fáze
Pro každý argument metody Eᵢ
:
- Je-li
anonymní funkcí, je odvození typu explicitního typu parametru ( §12.6.3.8 )z do - V opačném případě, pokud
má typ a odpovídající parametr je parametr hodnoty ( §15.6.2.2 ) pakdolní (§12.6.3.10 ) se vytvoříz do . - Jinak, pokud
Eᵢ
má typU
a odpovídající parametr je referenčním parametrem (§15.6.2.3.3) nebo výstupní parametr (§15.6.2 .3.4) je přesný odvození (§12.6.3.9) odU
doTᵢ
. - Jinak, pokud
Eᵢ
má typU
a odpovídajícím parametrem je vstupní parametr (§15.6.2.3.2) aEᵢ
je vstupním argumentem, přesný odvození (§12.6.3.9) je zU
naTᵢ
. - V opačném případě, pokud
Eᵢ
má typU
a odpovídající parametr je vstupní parametr (§15.6.2.3.2) pak dolní mez odvození (§12.6.3.10) se zU
Tᵢ
. - Jinak se pro tento argument nevyvozuje žádná odvození.
12.6.3.3 Druhá fáze
Druhá fáze pokračuje takto:
- Všechny nefixované proměnné typu
Xᵢ
, které nejsou závislé na (§12.6.3.6), jsou všechnyXₑ
pevné (§12.6.3.12). - 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ě.
- Jinak pro všechny argumenty
Eᵢ
s odpovídajícím typem parametruTᵢ
, kde výstupní typy (§12.6.3.5) obsahují nepřipravené proměnné typuXₑ
, ale vstupní typy (§12.6.3.4) ne, odvození typu výstupu (§12.6.3.7) je zEᵢ
doTᵢ
. 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ů T
vstupní typyE
s 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ýstupuE
o 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í výstupního typu
odvození výstupního typu se z výrazu E
typu T následujícím způsobem:
- Je-li
anonymní funkcí s odvozeným návratovým typem ( §12.6.3.13 ) aje typ delegování nebo stromový typ výrazu s návratovým typem odvození ( § 12.6.3.10 ) jez do . - 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.8 Explicitní odvození typu parametru
explicitní odvození typu parametru sez výrazu E
typ T
následujícím způsobem:
- Pokud je
E
explicitně zadaná anonymní funkce s typy parametrůU₁...Uᵥ
aT
je typ delegovaného typu nebo stromu výrazů s typy parametrůV₁...Vᵥ
pak pro každýUᵢ
přesný odvozování (§12.6.3.9) se zUᵢ
odpovídajícíVᵢ
.
12.6.3.9 Přesné odvozy
Následujícím způsobem je provedeno přesné odvozeníz typu U
na typ V
:
- Pokud je
V
jedním z neopravenýchXᵢ
,U
se 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ů:-
V
je typ poleV₁[...]
aU
je typ poleU₁[...]
stejného pořadí. -
V
je typV₁?
aU
je typU₁
-
V
je konstruovaný typC<V₁...Vₑ>
aU
je 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 nevyvodí žádné závěry.
12.6.3.10 Odvození dolní meze
Odvození dolní mez z typu U
na typ V
je provedeno takto:
- Pokud je
V
jedním z neopravenýchXᵢ
,U
se přidá do sady dolních mezí proXᵢ
. - V opačném případě pokud je
V
typV₁?
aU
je typU₁?
pak se odvozuje dolní mez zU₁
doV₁
. - V opačném případě se sady
U₁...Uₑ
aV₁...Vₑ
určují kontrolou, jestli platí některý z následujících případů:-
V
je typ poleV₁[...]
aU
je typ poleU₁[...]
stejného pořadí. -
V
je jedním zIEnumerable<V₁>
,ICollection<V₁>
,IReadOnlyList<V₁>>
,IReadOnlyCollection<V₁>
neboIList<V₁>
aU
je jednorozměrný typ poleU₁[]
-
V
je vytvořenýclass
,struct
,interface
nebodelegate
typC<V₁...Vₑ>
a existuje jedinečný typC<U₁...Uₑ>
tak, abyU
(nebo pokudU
je typparameter
, jeho efektivní základní třída nebo jakýkoli člen jeho efektivní sady rozhraní) je identický,inherits
z (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í zU
doC<T>
, neprobíhá žádné odvozování, protožeU₁
může býtX
neboY
.)
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
U
typ pole, provede se odvození dolní meze . - Jinak pokud je
V
C<V₁...Vₑ>
, odvozování závisí na parametru typui-th
C
:- 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 nevyvozují žádné závěry.
12.6.3.11 Odvození horní hranice
Horní mez vyplývající z typu U
na typ V
je stanovena následujícím způsobem:
- Pokud je
V
jedním z neopravenýchXᵢ
, přidá seU
do sady horních hranic proXᵢ
. - V opačném případě se sady
V₁...Vₑ
aU₁...Uₑ
určují ověřením, jestli platí některý z následujících případů:-
U
je typ poleU₁[...]
aV
je typ poleV₁[...]
stejného pořadí. -
U
je jedním zIEnumerable<Uₑ>
,ICollection<Uₑ>
,IReadOnlyList<Uₑ>
,IReadOnlyCollection<Uₑ>
neboIList<Uₑ>
aV
je jednorozměrný typ poleVₑ[]
-
U
je typU1?
aV
je typV1?
-
U
je vytvořený typ třídy, struktury, rozhraní nebo delegátaC<U₁...Uₑ>
aV
je typclass, struct, interface
nebodelegate
, který jeidentical
na,inherits
z (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 typ odkazu, provede se přesná odvození - Jinak, pokud je
V
typ pole, vytvoří se odvozování horní hranice pro . - Jinak pokud je
U
C<U₁...Uₑ>
, odvozování závisí na parametru typui-th
C
:- Pokud je kovariantní, provede se odvození horní hranice .
- Pokud je kontravariantní, provádí se odvození dolní hranice .
- Pokud je invariantní, provede se přesně odvození.
-
- Jinak se nedělají žádné závěry.
12.6.3.12 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í mezU
zXᵢ
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.13 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
F
výrazem , který má typ, odvozený efektivní návratový typF
je typ tohoto výrazu. - Pokud tělo
F
je blokem typu a sada výrazů v příkazech blokureturn
má nejvhodnější společný typT
(§12.6.3.15), pak je odvozený efektivní návratový typF
T
. - V opačném případě nelze efektivní návratový typ odvodit pro
F
.
odvozený návratový typ je určen takto:
- Je-li
F
asynchronní a těloF
je buď výraz klasifikovaný jako nic (§12.2), nebo blok, kde žádnéreturn
příkazy nemají výrazy, odvozený návratový typ je«TaskType»
(§15.15.1). - Pokud je
F
asynchronní a má odvozený účinný návratový typT
, odvozený návratový typ je«TaskType»<T>»
(§15.15.1). - Pokud
F
není 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í
Select
deklarovanou 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.Linq
byl importován pomocí direktivyusing namespace
a je dána třídaCustomer
s vlastnostíName
typustring
, lze použít metoduSelect
k 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)
Select
se 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
TSource
býtCustomer
. Poté, pomocí výše popsaného procesu odvozování typu anonymní funkce, jec
přiřazen typCustomer
, a výrazc.Name
souvisí s návratovým typem parametru selektoru, přičemž se odvozuje, žeTResult
jestring
. 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ásledovně: Nejprve je argument „1:15:30“ přičítán k parametru hodnoty, což vede k odvození, že
X
je řetězec. Parametr první anonymní funkce,s
, má odvozený typstring
a výrazTimeSpan.Parse(s)
souvisí s návratovým typemf1
, odvozujeY
jakoSystem.TimeSpan
. Nakonec, parametr druhé anonymní funkce,t
, má odvozený typSystem.TimeSpan
, a výrazt.TotalHours
souvisí s návratovým typemf2
, a odvozuje se, žeZ
jedouble
. Výsledkem vyvolání je tedy typdouble
.konec příkladu
12.6.3.14 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ᵥ>
je kompatibilní (§20.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.15 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
Ei
výstupní typ odvození (§12.6.3.7) se provádí z něj doX
. -
X
je pevná (§12.6.3.12), pokud je 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
A
odpoví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
A
je 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á
in
modifiká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ů
A
odpovídal celkovému počtu parametrů. PokudA
obsahuje 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 shodný s režimem předávání parametrů odpovídajícího parametru a
- Pro parametr s pevnou hodnotou nebo parametr 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.
- Způsob předávání parametrů argumentu je hodnota a způsob předávání pro odpovídající parametr je vstup. Existuje implicitní konverze (§10.2) z výrazu argumentu na typ odpovídajícího parametru.
- režim předávání parametrů argumentu je shodný 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: Vzhledem k následujícím deklaracím a voláním 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 }
ukončit příklad
- 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
this
pří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 parametruRx
není méně specifický nežSx
, a u alespoň jednoho parametru jeRx
specifič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í:
-
E
přesně odpovídáT₁
aE
přesně neodpovídáT₂
(§12.6.4.6) -
E
přesně odpovídá buď oběma, nebo žádnému zT₁
aT₂
, aT₁
je lepším cílem konverze nežT₂
(§12.6.4.7) -
E
je skupina metod (§12.2),T₁
je kompatibilní (§20.4) s jednou nejlepší metodou ze skupiny metod pro převodC₁
aT₂
není kompatibilní s jedinou nejlepší metodou ze skupiny metod pro převodC₂
12.6.4.6 Přesně odpovídající výraz
Při zadání výrazu E
a typu T
E
přesně odpovídáT
, pokud platí některá z následujících možností:
-
E
má typS
a převod identity existuje zS
naT
-
E
je anonymní funkce,T
je buď typ delegátaD
nebo typ stromu výrazůExpression<D>
a platí jedna z následujících podmínek:- Odvozený návratový typ
X
existuje proE
v kontextu seznamu parametrůD
(§12.6.3.12) a převod identity existuje zX
na návratový typD
-
E
jeasync
lambda bez návratové hodnoty aD
má návratový typ, kterým je negenerický«TaskType»
-
E
je nesynchronní aD
má návratový typY
, neboE
je asynchronní aD
má návratový typ«TaskType»<Y>
(§15.15.1), a platí jedno z následujícího:- Tělo
E
je výraz, který přesně odpovídajícíY
- Tělo
E
je 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.15.1),T₂
je«TaskType»<S₂>
aS₁
je lepším cílem převodu nežS₂
-
T₁
je«TaskType»<S₁>
(§15.15.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₁
jesbyte
aS₂
jebyte
,ushort
,uint
neboulong
-
S₁
jeshort
aS₂
jeushort
,uint
neboulong
-
S₁
jeint
aS₂
jeuint
neboulong
-
S₁
jelong
aS₂
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); }
konec 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.3) 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
F
je 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ě. - Je pak vyloučen jakýkoli parametr, jehož typ je otevřený (tj. obsahuje parametr typu; viz §8.4.3), spolu se svými odpovídajícími parametry.
Aby F
prošel kontrolou, musí platit všechny následující podmínky:
- Upravený seznam parametrů pro
F
se 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
F
v předchozím kroku nahrazeny, jsou jejich omezení splněna. - Je-li
F
statickou 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
F
metodou 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
M
je členem statické funkce:- Seznam argumentů je vyhodnocen tak, jak je popsáno v §12.6.2.
-
M
je vyvolána.
V opačném případě, pokud je typ
E
hodnotového typuV
, aM
je deklarován nebo přepsán vV
:-
E
se 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ě seE
klasifikuje jako proměnná. - Pokud
E
není klasifikovaný jako proměnná nebo pokudV
není typ struktury jen pro čtení (§16.2.2) aE
je jedním z:- vstupní parametr (§ 15.6.2.3.2), nebo
-
readonly
pole (§ 15.5.3), nebo -
readonly
referenční proměnná nebo návrat (§9.7),
pak se vytvoří dočasná místní proměnná typu
E
a k této proměnné se přiřadí hodnotaE
.E
je pak překlasifikováno jako odkaz na tuto dočasnou místní proměnnou. Dočasná proměnná je přístupná jakothis
v rámciM
, ale ne jiným způsobem. Proto pouze v případě, žeE
lze zapsat, je pro volajícího možné sledovat změny, kteréM
provedethis
.- Seznam argumentů je vyhodnocen tak, jak je popsáno v §12.6.2.
-
M
je vyvolána. Proměnná, na kterou se odkazujeE
, se stane proměnnou, na kterou se odkazujethis
.
-
Jinak:
-
E
se 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
E
value_type, provede se převod boxingu (§10.2.9) pro převodE
na class_typeaE
se 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
E
je kontrolována tak, aby byla platná. Pokud je hodnotaE
null, vyvolá seSystem.NullReferenceException
a neprojdou žádné další kroky. - Je určena implementace člena funkce, která se má vyvolat:
- Pokud je typ vazby
E
rozhraní, člen funkce, který se má vyvolat, je implementaceM
poskytované typem běhu instance odkazovanéE
. Tento člen funkce je určen použitím pravidel mapování rozhraní (§18.6.5) k určení implementaceM
poskytnutého typem běhu instance, na kterou odkazujeE
. - V opačném případě, pokud
M
je členem virtuální funkce, člen funkce, který se má vyvolat, je implementaceM
poskytované 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)M
s ohledem na typ běhu instance odkazovanéE
. - V opačném případě je
M
ne virtuálním členem funkce a člen funkce, který se má vyvolat, jeM
sá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.
-
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.ValueType
neboSystem.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
E
výraz n-tice sn
prvky, výsledkem dekonstrukce je výrazE
samotný. - Jinak, pokud má
E
typ n-tice(T1, ..., Tn)
sn
prvky, pak seE
vyhodnotí do dočasné proměnné__v
a 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
E
dekonstruovat.
Tady __v
a __v1, ..., __vn
odkazují na jinak neviditelné a nepřístupné dočasné proměnné.
Poznámka: Výraz typu
dynamic
nelze 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
: primary_no_array_creation_expression
| array_creation_expression
;
primary_no_array_creation_expression
: literal
| interpolated_string_expression
| simple_name
| parenthesized_expression
| tuple_expression
| member_access
| null_conditional_member_access
| invocation_expression
| element_access
| null_conditional_element_access
| this_access
| base_access
| post_increment_expression
| post_decrement_expression
| null_forgiving_expression
| object_creation_expression
| delegate_creation_expression
| anonymous_object_creation_expression
| typeof_expression
| sizeof_expression
| checked_expression
| unchecked_expression
| default_value_expression
| nameof_expression
| anonymous_method_expression
| pointer_member_access // unsafe code support
| pointer_element_access // unsafe code support
| stackalloc_expression
;
Poznámka: Tato gramatická pravidla nejsou připravená na ANTLR, protože jsou součástí sady vzájemně rekurzivních pravidel (
primary_expression
,primary_no_array_creation_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. To se neprovádí, 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 (§ 23.6.3) a pointer_element_access (§23.6.4) jsou k dispozici pouze v nebezpečném kódu (§23).
Primární výrazy se dělí na výrazy vytvářející polea primární výrazy nevytvářející pole. Tímto způsobem zacházení s array_creation_expression, namísto jeho uvedení společně s jinými formami jednoduchého výrazu, umožňuje gramatice zakázat potenciálně matoucí kód, jako například
object o = new int[3][1];
který by jinak byl interpretován jako
object o = (new int[3])[1];
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_FormatInterpolated_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 ≥ 1
pro každé čísloI
od0
doN-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
string
hodnotu 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" |
konec 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
e
nula 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
e
nula 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
e
nula a deklaraceT
obsahuje 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)
I
vT
s argumenty typue
vytvoří shodu:- Pokud
T
je 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
T
typem 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ě, žee
je nula. - V opačném případě je výsledek stejný jako přístup člena (§12.8.7) formuláře
T.I
neboT.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
e
nula aI
je název oboru názvů vN
, pak:- Pokud je umístění, kde dojde k simple_name, uzavřeno deklarací oboru názvů pro
N
a deklarace oboru názvů obsahuje extern_alias_directive nebo using_alias_directive, které přidruží názevI
k 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ý
I
vN
.
- Pokud je umístění, kde dojde k simple_name, uzavřeno deklarací oboru názvů pro
- Pokud
N
obsahuje přístupný typ s názvemI
a parametry typue
, pak:- Pokud je
e
nula 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ázevI
k 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
e
nula a deklarace oboru názvů obsahuje extern_alias_directive nebo using_alias_directive, které přidruží názevI
k 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
I
a 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
I
a 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
- V opačném případě, pokud je
e
nula aI
je identifikátor_
, simple_name je jednoduché zahození, což je forma výrazu deklarace (§12.17). - 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 proto zavádí výraz deklarace (§12.17). V důsledku toho může k deconstruction_expression dojít pouze na levé straně jednoduchého přiřazení.
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
Ei
formulářeNi
neboE.Ni
neboE?.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.Ni
neboE?.Ni
, nebo -
Ni
má tvarItemX
, kdeX
je posloupnost desítkových číslic, které nejsou zahájeny0
a které by mohly představovat pozici prvku n-tice, aX
nepř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 n-tice lze získat z výrazu n-tice tak, že ji převedete na typ n-tice (§10.2.13), přeřazením jako hodnotu (§12.2.2) nebo jejím určením jako cíle dekonstrukčního přiřazení (§12.21.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 type
V tomto příkladu jsou všechny čtyři výrazy n-tic platné. První dva,
t1
at2
, nepoužívají typ návratové hodnoty n-tice, místo toho použijí implicitní převod n-tice. V případět2
se implicitní převod n-tice spoléhá na implicitní převody z2
nalong
a znull
nastring
. Třetí výraz n-tice má typ(int i, string)
, a lze proto překlasifikovat na hodnotu tohoto typu. Deklaracet4
je 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.
konec 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ě kompilátor klasifikuje přístup člena jako přístup k vlastnosti 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
e
je nula aE
je obor názvů aE
obsahuje vnořený obor názvů s názvemI
, výsledek je tento obor názvů. - Pokud je
E
obor názvů aE
obsahuje přístupný typ s názvemI
a parametry typuK
, je výsledkem tento typ vytvořený s danými argumenty typu. - Pokud je
E
klasifikovaný jako typ, pokudE
není parametr typu, a pokud vyhledávání členů (§12,5)I
vE
s parametry typuK
vytvoří shodu,E.I
se vyhodnotí a klasifikuje takto:Poznámka: Pokud je výsledkem takového vyhledávání člena skupina metod a
K
je 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
I
identifikuje typ, je výsledkem tento typ vytvořený s libovolnými zadanými argumenty typu. - Pokud
I
identifikuje jednu nebo více metod, je výsledkem skupina metod bez přidruženého výrazu instance. - Pokud
I
identifikuje statickou vlastnost, je výsledkem přístup k vlastnosti bez přidruženého výrazu instance. - Pokud
I
identifikuje 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
I
vE
. - V opačném případě je výsledkem proměnná, konkrétně statické pole
I
vE
.
- 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
I
identifikuje 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.I
je zpracována přesně tak, jako byI
byla 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
I
identifikuje konstantu, je výsledkem hodnota, konkrétně hodnota této konstanty. - Pokud
I
identifikuje člen výčtu, je výsledkem hodnota, konkrétně hodnota tohoto člena výčtu. - V opačném případě je
E.I
neplatným odkazem na člena a dojde k chybě v době kompilace.
- Pokud
- Pokud je
E
přístupem k vlastnosti, indexeru, proměnnou nebo hodnotou, jehož typ jeT
, a vyhledávání člena (§12.5)I
vT
s argumenty typuK
vytvoří shodu, pak seE.I
vyhodnotí a klasifikuje takto:- Nejprve, pokud je
E
vlastnost 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
I
identifikuje jednu nebo více metod, je výsledkem skupina metod s přidruženým výrazem instanceE
. - Pokud
I
identifikuje vlastnost instance, je výsledkem přístup k vlastnosti s přidruženým výrazem instanceE
a přidruženým typem, který je typem vlastnosti. Je-liT
typ třídy, je přidružený typ vybrán z první deklarace nebo přepsání vlastnosti, které se najde při zahájení odT
a vyhledávání v jejích základních třídách. - Pokud je
T
class_type aI
identifikuje instanční pole daného class_type:- Pokud je hodnota
E
null
, 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
I
v objektu odkazovanémE
. - Jinak je výsledkem proměnná, konkrétně pole
I
v objektu odkazovanémE
.
- Pokud je hodnota
- Pokud je
T
struktura typu aI
identifikuje pole instance této struktury typu :- Pokud
E
je 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 poleI
v instanci struktury danéE
. - V opačném případě je výsledkem proměnná, konkrétně pole
I
v instanci struktury poskytnutéE
.
- Pokud
- Pokud
I
identifikuje 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.I
zpracuje přesně tak, jako byI
bylo 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.I
jako vyvolání rozšiřující metody (§12.8.10.3). Pokud se to nezdaří,E.I
je 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
A
ty výskyty identifikátoruColor
, které odkazují na typColor
, odděleny pomocí«...»
, zatímco ty, které odkazují na poleColor
, nejsou.konec 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ého dvěma tokeny "?
" a ".
", následované identifikátorem s volitelným type_argument_list, následovaným nulou nebo více dependent_access, kterým lze 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
P
je typ hodnoty nullable:Nechť je
T
typemP.Value.A
.Pokud
T
je 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
T
nenulový typ hodnoty, typE
jeT?
a významE
je stejný jako význam:((object)P == null) ? (T?)null : P.Value.A
Kromě toho, že
P
se vyhodnotí pouze jednou.V opačném případě je typ
E
T
a významE
je stejný jako význam:((object)P == null) ? (T)null : P.Value.A
Kromě toho, že
P
se vyhodnotí pouze jednou.
Jinak:
Nechte
T
být typem výrazuP.A
.Pokud
T
je 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
T
nenulový typ hodnoty, typE
jeT?
a významE
je stejný jako význam:((object)P == null) ? (T?)null : P.A
Kromě toho, že
P
se vyhodnotí pouze jednou.V opačném případě je typ
E
T
a významE
je stejný jako význam:((object)P == null) ? (T)null : P.A
Kromě toho, že
P
se vyhodnotí pouze jednou.
Poznámka: Ve výrazu formuláře:
P?.A₀?.A₁
pokud se
P
vyhodnotí 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.7).
12.8.9 Výrazy ignorující hodnotu Null
12.8.9.1 Obecné
Hodnota, typ, klasifikace výrazu s hodnotou null, typem, klasifikací (§12.2) a bezpečným kontextem (§16.4.12) je hodnota, typ, klasifikace a bezpečný kontext jeho primary_expression.
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 ta poslední může být přepsána (§15.10), definice operátoru odpouštějícího hodnoty null je pevně daná.
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)
konec 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
IsValid
vrátítrue
,p
může být bezpečně dereferencováno pro přístup ke svéName
vlastnosti a upozornění na "dereferencování potenciálně nulové hodnoty" lze potlačit pomocí!
.konec 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šakx
null
během běhu programu, dojde k výjimce, protoženull
nelze přetypovat naint
.konec 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žlv
je výstupní parametr, a provádí jednoduché přiřazení.Metoda
M
předává proměnnous
, typustring
, jako výstupní parametrAssign
, a kompilátor vydává upozornění, protožes
není nullovatelná proměnná. Vzhledem k tomu, že druhý argumentAssign
nemůže být null, operátor null-forgiving se používá k potlačení upozornění.konec 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ě kompilátor klasifikuje invocation_expression jako hodnotu 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.19). 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 sT
a 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ání metody instance a příjemce je typu třídy
T
, přidružený typ je vybrán z první deklarace nebo přepsání metody nalezen při zahájeníT
a vyhledávání v jeho základních třídách.
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
F
přidružené ke skupině metodM
:- Pokud
F
není obecný,F
je kandidátem v následujících případech:-
M
nemá žádný seznam argumentů typu a -
F
se vztahuje naA
(§ 12.6.4.2).
-
- Pokud je
F
obecný aM
neobsahuje seznam argumentů typu,F
je kandidátem v následujících případech: - Pokud je
F
obecný aM
obsahuje seznam argumentů typu,F
je 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.F
v sadě, kdeC
je typ, ve kterém je metodaF
deklarována, všechny metody deklarované v základním typuC
jsou ze sady odebrány. Navíc pokudC
je 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ž
object
a 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.
-
C
je 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
null
př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
B
přednost před první metodou rozšíření a metodaC
má 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.G
má přednost předC.G
, aE.F
má přednost předD.F
iC.F
.konec 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ů:
-
D
se 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
D
je kontrolována tak, aby byla platná. Pokud je hodnotaD
null
, vyvolá seSystem.NullReferenceException
a nebude provedeno žádné další kroky. - V opačném případě je
D
odkazem 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 volání bez parametrů najdete v §20.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 kontextu statement_expression (§ 13.7), anonymous_function_body (§12.19.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 [
. Nechť PA
znamená spojení 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
E
klasifikována jako nic, jeho význam je stejný jako význam bloku:{ if ((object)P != null) PA; }
s tím rozdílem, že
P
se vyhodnotí pouze jednou.Jinak je význam
E
stejný 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 primary_no_array_creation_expression, po kterém následuje token „[
“, poté argument_list, a nakonec token „]
“.
argument_list se skládá z jednoho nebo více argumentů , oddělené čárkami.
element_access
: primary_no_array_creation_expression '[' argument_list ']'
;
seznam_argumentů u přístupu_k_prvku nesmí obsahovat argumenty out
ani ref
.
element_access je dynamicky svázaný (§12.3.3) v případě, že platí alespoň jeden z následujících:
-
primary_no_array_creation_expression má typ pro dobu kompilace
dynamic
. - Nejméně jeden výraz argument_list má typ kompilace
dynamic
a primary_no_array_creation_expression nemá typ pole.
V tomto případě kompilátor klasifikuje element_access jako hodnotu typu dynamic
. Níže uvedená pravidla pro určení významu element_access se aplikují za běhu programu, přičemž se místo typu doby kompilace těchto výrazů primary_no_array_creation_expression a argument_list, které mají typ kompilace dynamic
, použije typ za běhu. Pokud primary_no_array_creation_expression nemá typ kompilace dynamic
, projde přístup k prvku omezenou kontrolou doby kompilace, jak je popsáno v §12.6.5.
Je-li primary_no_array_creation_expressionelement_access hodnotou typu pole, pak element_access představuje přístup k prvku pole (§12.8.12.2). V opačném případě musí být primary_no_array_creation_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.3).
12.8.12.2 Přístup k poli
Pro přístup k poli musí být výraz primary_no_array_creation_expression u přístupu k prvkům element_access hodnotou typu array_type. Kromě toho argument_list přístupu k poli nesmí obsahovat pojmenované argumenty. Počet výrazů v argument_list musí být stejný jako pořadí array_typea každý výraz musí být typu int
, uint
, long
nebo ulong,
nebo musí být implicitně konvertibilní na jeden nebo více těchto typů.
Výsledkem vyhodnocení přístupu k poli je proměnná typu prvku pole, konkrétně prvek pole vybraný hodnotami výrazů v argument_list.
Zpracování přístupu k poli ve tvaru P[A]
, kde P
je primary_no_array_creation_expression typu array_type a A
je argument_list, se skládá z následujících kroků:
-
P
se vyhodnotí. Pokud toto vyhodnocení způsobí výjimku, neprojdou žádné další kroky. - Výrazy indexu argument_list se vyhodnocují v pořadí zleva doprava. Při vyhodnocování každého výrazu indexu 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 je například výraz indexu typushort
pak se provede implicitní převod naint
, protože je možné implicitní převody zshort
naint
a zshort
nalong
. 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
P
je kontrolována tak, aby byla platná. Pokud je hodnotaP
null
, vyvolá seSystem.NullReferenceException
a nejsou provedeny žádné další kroky. - 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.IndexOutOfRangeException
a neprojdou žádné další kroky. - Umístění prvku pole zadaného výrazy indexu se vypočítá a toto umístění se stane výsledkem přístupu k poli.
12.8.12.3 Přístup k indexeru
Pro přístup k indexeru musí být primary_no_array_creation_expressionelement_access proměnnou nebo hodnotou třídy, struktury nebo typu rozhraní a tento typ musí implementovat jeden nebo více indexerů, které se vztahují k argument_listelement_access.
Zpracování přístupu k indexeru P[A]
ve formě, kde P
je primary_no_array_creation_expression typu třídy, struktury nebo rozhraní T
, a A
je argument_list, se skládá z následujících kroků:
- Sada indexerů, kterou poskytuje
T
, byla vytvořena. Sada se skládá ze všech indexerů deklarovaných vT
nebo 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.I
v sadě, kdeS
je typ, ve kterém je deklarován indexerI
:- Pokud se
I
nevztahuje naA
(§12.6.4.2),I
se ze sady odebere. - Je-li
I
použitelná proA
(§12.6.4.2), všechny indexery deklarované v základním typuS
jsou ze sady odebrány. - Je-li
I
použitelná proA
(§ 12.6.4.2) aS
je 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.
- Výrazy indexu argument_list se vyhodnocují v pořadí zleva doprava. Výraz získaný zpracováním přístupu k indexeru je klasifikován jako přístup indexeru. Výraz přístupu indexeru odkazuje na indexer určený v kroku výše a má přidružený výraz instance
P
a přidružený seznam argumentůA
a přidružený typ, který je typem indexeru. PokudT
je typ třídy, je přidružený typ vybrán z první deklarace nebo přepsání indexeru nalezen při spuštěníT
a vyhledávání v jeho základních třídách.
V závislosti na kontextu, ve kterém se používá, způsobí přístup indexeru vyvolání přístupového objektu get nebo sady přístupového objektu indexeru. Pokud je přístup k indexeru cílem přiřazení, je vyvolán objekt set pro přiřazení nové hodnoty (§12.21.2). Ve všech ostatních případech je vyvolán get accessor k získání aktuální hodnoty (§12.2.2).
12.8.13 Přístup k podmíněnému elementu null
null_conditional_element_access se skládá z primary_no_array_creation_expression následovaných dvěma tokeny "?
" a "[
", následované argument_list, následovaným tokenem "]
" následovaným nulou nebo více dependent_accesses, které může předcházet null_forgiving_operator.
null_conditional_element_access
: primary_no_array_creation_expression '?' '[' argument_list ']'
(null_forgiving_operator? dependent_access)*
;
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 dependent_accesses B
, pokud existují. Význam E
se určuje takto:
Pokud typ
P
je typ hodnoty nullable:Nechte
T
být typem výrazuP.Value[A]B
.Pokud
T
je 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
T
nenulový typ hodnoty, typE
jeT?
a významE
je stejný jako význam:((object)P == null) ? (T?)null : P.Value[A]B
Kromě toho, že
P
se vyhodnotí pouze jednou.V opačném případě je typ
E
T
a významE
je stejný jako význam:((object)P == null) ? null : P.Value[A]B
Kromě toho, že
P
se vyhodnotí pouze jednou.
Jinak:
Nechte
T
být typem výrazuP[A]B
.Pokud
T
je 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
T
nenulový typ hodnoty, typE
jeT?
a významE
je stejný jako význam:((object)P == null) ? (T?)null : P[A]B
Kromě toho, že
P
se vyhodnotí pouze jednou.V opačném případě je typ
E
T
a významE
je stejný jako význam:((object)P == null) ? null : P[A]B
Kromě toho, že
P
se vyhodnotí pouze jednou.
Poznámka: Ve výrazu formuláře:
P?[A₀]?[A₁]
pokud se
P
vyhodnotí 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
this
použí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
this
použ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
this
použí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,
this
promě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á
this
chová přesně stejně jakoref
parametr 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
this
použí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átor (§15.14) nebo asynchronní funkce (§15.15), představuje proměnná
this
strukturu, pro kterou byla metoda nebo příslušenství vyvolána.- Pokud je struktura
readonly struct
,this
proměnná se chová úplně stejně jako vstupní parametr typu struktury. - Jinak se proměnná
this
chová přesně stejně jako parametrref
typu struktury.
- Pokud je struktura
- Pokud je metoda nebo přístupová funkce iterátor nebo asynchronní funkce, představuje proměnná
this
kopí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átor (§15.14) nebo asynchronní funkce (§15.15), představuje proměnná
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á ze základu klíčových slov následovaného tokenem ".
" a identifikátorem a volitelným type_argument_list nebo argument_list uzavřenými 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, označím č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
this
neníbase
vý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
, decimal
a 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
x
klasifikuje jako proměnná:-
x
se vyhodnotí tak, aby vznikla proměnná. - Hodnota
x
se uloží. - Uložená hodnota
x
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
a uložena na místo určené dřívějším vyhodnocenímx
. - Uložená hodnota
x
se stane výsledkem operace.
-
- Pokud je
x
klasifikován jako přístup k vlastnosti nebo indexeru:- Výraz instance (pokud
x
nenístatic
) a seznam argumentů (pokudx
je přístup k indexeru) přidružený kx
se vyhodnotí a výsledky se použijí v následných vyvolání get a set. - Vyvolá se přístupový objekt get
x
a vrácená hodnota se uloží. - Uložená hodnota
x
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
a nastavovací přístupová funkcex
je vyvolána s touto hodnotou jako argument pro hodnotu. - Uložená hodnota
x
se stane výsledkem operace.
- Výraz instance (pokud
Operátory ++
a --
také podporují zápis předpon (§12.9.6). Výsledkem x++
nebo x--
je hodnota x
před operace, zatímco výsledek ++x
nebo --x
je hodnota x
po 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ů a výrazy pro vytváření anonymních 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ů
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 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é zpracování inicializace člena nebo prvku určené inicializátorem objektu (§12.8.17.17.3) nebo inicializátoru kolekce (§12.8.17.4).
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í vazby výrazu pro vytvoření objektu ve formátu nový T(A)
, kde T
je třídního typunebo hodnotového typua A
je volitelný seznam argumentů, sestává z následujících kroků:
- Pokud je
T
value_type aA
neexistuje:-
object_creation_expression je vyvolání výchozího konstruktoru. Výsledkem object_creation_expression je hodnota typu
T
, konkrétně výchozí hodnota proT
definovaná 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
T
typu type_parameter aA
není přítomen:- Není-li pro
T
zadáno žádné omezení typu hodnoty, ani omezení konstruktoru (§15.2.5), 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
- Jinak, pokud je
T
typu class_type nebo typu struct_type:- Pokud
T
je 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ů instance kandidáta se skládá ze všech přístupných konstruktorů instancí deklarovaných v
T
, které se vztahují k A (§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.OutOfMemoryException
a 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
T
struktura typu:- Instance typu
T
je 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.3 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í.
Inicializátor členu určuje výraz, který následuje po rovnítku, a je zpracován stejným způsobem, jako by se jednalo o přiřazení (§12.21.2) na cíl.
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.4.
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
Point
lze 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
__a
je 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
Rectangle
lze 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
,__p1
a__p2
jsou dočasné proměnné, které jsou jinak neviditelné a nepřístupné.Pokud konstruktor
Rectangle
přiděluje dvě vloženéPoint
instance, je možné je použít k inicializaci vloženýchPoint
instancí 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é
Point
instance 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.4 Inicializátory kolekcí
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_list ',' 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ů. Produkce non_assignment_expression je definována v §12.22.
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 };
konec 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í příklad ukazuje třídu, která představuje kontakt se jménem a seznamem telefonních čísel, a jak vytvořit a inicializovat objekt
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
,__c1
a__c2
jsou dočasné proměnné, které jsou jinak neviditelné a nepřístupné.konec příkladu
12.8.17.5 Výrazy pro 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[][,]
. konec příkladu
Každý výraz v seznamu výrazů musí být typu int
, uint
, long
nebo ulong
nebo 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 (§ 23.2), rozložení polí není určeno.
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 určen jako nejlepší společný typ (§12.6.3.15) 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.OverflowException
a 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.OutOfMemoryException
a 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]; // Error
vý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]; }
konec 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" }; // Error
Poslední výraz způsobí chybu v době kompilace, protože
int
anistring
nejsou 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.konec příkladu
Implicitně typované výrazy vytváření pole lze kombinovat s anonymními inicializátory objektů (§12.8.17.7) 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" } } };
konec příkladu
12.8.17.6 Výrazy 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 výraz má typ kompilace dynamic
, je výraz delegate_creation dynamicky vázán (§12.8.17.6), a následující pravidla se použijí za běhu s použitím běhového typu 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
E
je skupina metod, výraz vytvoření delegáta se zpracuje stejným způsobem jako převod skupiny metod (§10,8) zE
naD
.Je-li
E
anonymní funkcí, výraz vytvoření delegáta se zpracuje stejným způsobem jako anonymní převod funkce (§10,7) zE
naD
.Je-li
E
hodnotou,E
musí být kompatibilní (§20.2) sD
a výsledkem je odkaz na nově vytvořený delegát s jedním vstupním seznamem vyvolání, 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
E
je skupina metod, výraz vytvoření delegáta se vyhodnotí jako převod skupiny metod (§10.8) zE
naD
. - Pokud
E
je anonymní funkce, je vytvoření delegáta vyhodnoceno jako anonymní převod funkce zE
naD
(§10,7). - Pokud je
E
hodnotou typu delegáta :-
E
se vyhodnotí. Pokud toto vyhodnocení způsobí výjimku, neprojdou žádné další kroky. - Pokud je hodnota
E
null
, vyvolá seSystem.NullReferenceException
a neprovedou se žá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.OutOfMemoryException
a 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.f
je 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áSquare
metoda nebyla přítomna, došlo k chybě v době kompilace.konec příkladu
12.8.17.7 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
p1
ap2
jsou stejného anonymního typu.konec příkladu
Metody Equals
a GetHashcode
u anonymních typů přepíší metody zděděné z object
a 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:
«identifer» = «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.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 '::' identifier generic_dimension_specifier?
| unbound_type_name '.' 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 T
je 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: unbound_type_name se velmi podobá type_name (§7.8) s tím rozdílem, že unbound_type_name obsahuje generic_dimension_specifiers místem, kde type_name obsahuje type_argument_lists. koncová poznámka
Pokud je operand typeof_expression posloupnost tokenů, která splňuje gramatiky unbound_type_name i type_name, a to v případě, že neobsahuje generic_dimension_specifier ani type_argument_list, je posloupnost tokenů považována za type_name. Význam unbound_type_name se určuje takto:
- Převeďte posloupnost tokenů na type_name tím, že nahradíte každý generic_dimension_specifier s type_argument_list, který má stejný počet čárek a klíčové slovo
object
jako každý type_argument. - Vyhodnoťte výslednou type_namea současně ignorujte všechna omezení parametrů typu.
- unbound_type_name se odkazuje na nevázaný obecný typ přidružený k výslednému konstruovanému typu (§8.4).
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.Type
je 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
int
aSystem.Int32
jsou 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 T
je 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ů je operátor sizeof
uveden v §23.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é operátory
++
a--
(§12.8.16 a §12.9.6), pokud je operand celočíselného typu nebo výčtu. - Předdefinovaný
-
unární operátor (§12.9.3), pokud je operand celočíselného typu. - Předdefinované
+
,-
,*
a/
binární operátory (§12.10), pokud jsou oba operandy 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
float
nebodouble
na 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í:
- V kontextu
checked
, pokud je operace konstantním výrazem (§12.23), nastane chyba při kompilaci. Jinak se při provedení operace za běhu vyvoláSystem.OverflowException
. - V kontextu
unchecked
se 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.23) (výrazy vyhodnocené za běhu), které nejsou uzavřeny žádnými checked
nebo unchecked
operátory nebo příkazy, není výchozí kontext kontroly přetečení nezaškrtnut, pokud nejsou zaškrtnuté externí faktory (například přepínače kompilátoru a konfigurace prostředí spouštění) pro kontrolované vyhodnocení.
U konstantních výrazů (§12.23) (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
F
System.OverflowException
, a metodaG
vrátí –727379968 (32 nejnižších bitů výsledku mimo rozsah). Chování metodyH
závisí na výchozím kontextu kontroly přetečení pro kompilaci, ale je to buď stejné jakoF
, nebo stejné jakoG
.konec 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
F
aH
, 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.konec 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í
checked
v F nemá vliv na vyhodnoceníx * y
vMultiply
, takžex * y
je vyhodnocen ve výchozím kontextu kontroly přetečení.konec 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átoruunchecked
by přetypování naint
vytvářelo chyby v době kompilace.konec příkladu
Poznámka: Operátory a příkazy
checked
aunchecked
umožň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
: explictly_typed_default
| default_literal
;
explictly_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 explicitně_typovaném_výchozímnebo cílový typ převodu pro default_value_expression.
default_value_expression je konstantní výraz (§12,23) 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 pro alokaci zásobníku jsou popsána v §16.4.12.7.
stackalloc_expression
: 'stackalloc' unmanaged_type '[' expression ']'
| 'stackalloc' unmanaged_type? '[' constant_expression? ']' stackalloc_initializer
;
stackalloc_initializer
: '{' stackalloc_initializer_element_list '}'
;
stackalloc_initializer_element_list
: stackalloc_element_initializer (',' stackalloc_element_initializer)* ','?
;
stackalloc_element_initializer
: expression
;
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:
- Pokud unmanaged_type vynecháte, je odvozeno podle pravidel pro nejlepší společný typ (§12.6.3.15) 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 stackalloc_expression se vyskytne přímo jako inicializační výraz local_variable_declaration (§13.6.2), kde local_variable_type je buď ukazatel typu (§23.3) nebo odvozený (var
), výsledkem výrazu stackalloc_expression je ukazatel typu T*
(§23.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
Length
vý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ě
span8
mástackalloc
za následekSpan<int>
, který je převeden implicitním operátorem naReadOnlySpan<int>
. Podobně uspan9
se výslednáSpan<double>
převede na uživatelem definovaný typWidget<double>
pomocí převodu, jak je znázorněno. konec 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 string
a 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ů
nameof
za 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ý příklad
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.19.
12.9 Unární operátory
12.9.1 Obecné
+
, -
, !
(logická negace pouze §12.9.4), ~
, ++
, --
, přetypování a await
jsou nazývány unárními 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
| pre_increment_expression
| pre_decrement_expression
| cast_expression
| await_expression
| pointer_indirection_expression // unsafe code support
| addressof_expression // unsafe code support
;
pointer_indirection_expression (§ 23.6.2) a addressof_expression (§23.6.5) jsou k dispozici pouze v nebezpečném kódu (§23).
Je-li operand unary_expression v době kompilace typu dynamic
, je dynamicky vázán (§12.3.3). V tomto případě je typ kompilace unary_expressiondynamic
a řešení popsané níže proběhne za běhu pomocí typu běhu operandu.
12.9.2 Unární operátor plus
Pro operaci ve tvaru +x
se 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 tvaru –x
se použije přetížení unárního operátoru (§12.4.4) k výběru 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
X
od nuly. Je-li hodnotaX
nejmenší reprezentovatelnou hodnotou typu operandu (−2³¹ proint
nebo −2⁶³ prolong
), není matematická negaceX
reprezentovatelná v rámci typu operandu. Pokud k tomu dojde v kontextuchecked
, vyvolá seSystem.OverflowException
; pokud dojde vunchecked
kontextu, je výsledkem hodnota operandu a přetečení není hlášeno.Je-li operand operátoru negace typu
uint
, je převeden na typlong
a 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
X
s invertovaným znakem. Pokud jex
NaN
, výsledek je takéNaN
.Desetinná negace:
decimal operator –(decimal x);
Výsledek se vypočítá odečtením
X
od 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 typu !x
se přetížení unárního operátoru (§12.4.4) použije k výběru specifické 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 tvaru ~x
se použije rozlišení přetíženého unárního operátoru (§12.4.4) k výběru 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 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ýsledek 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ě 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
, decimal
a 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
x
klasifikuje jako proměnná:-
x
se vyhodnotí tak, aby vznikla proměnná. - Hodnota
x
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
. Výsledná hodnota je uložena v umístění určeném vyhodnocenímx
. - a stává se výsledkem operace.
-
- Pokud je
x
klasifikováno jako vlastnost nebo přístup indexeru:- Výraz instance (pokud
x
nenístatic
) a seznam argumentů (pokudx
je přístup k indexeru) přidružený kx
se 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řístupx
je 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.
Operátor ++
nebo implementace operátoru --
může být vyvolána použitím buď postfixové, nebo prefixové notace. Pro tyto dva 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.7 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)–y
lze interpretovat buď jako přetypovací výraz (přetypování–y
k 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ýjimkouas
ais
.
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
x
ay
jsou identifikátory,x.y
je pro typ správná gramatika, i kdyžx.y
ve skutečnosti neoznačuje typ. konec příkladu
Poznámka: Z pravidla nejednoznačnosti platí, že pokud
x
ay
jsou identifikátory,(x)y
,(x)(y)
a(x)(-y)
jsou cast_expressions, ale(x)-y
není, i kdyžx
identifikuje typ. Pokud je všakx
klíč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.8 Výrazy Await
12.9.8.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.15). 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 okamžiku 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.8.2 Čekatelné výrazy
Úkol await_expression musí být čekaný. Výraz t
lze očekávat, pokud platí jedna z následujících podmínek:
-
t
je typ kompilacedynamic
-
t
má 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:-
A
implementujeSystem.Runtime.CompilerServices.INotifyCompletion
rozhraní (dále označované jakoINotifyCompletion
pro stručnost). -
A
má přístupnou, čitelnou vlastnost instanceIsCompleted
typubool
-
A
má přístupnou metodu instanceGetResult
bez 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.8.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 GetResult
void
, await_expression se klasifikuje jako žádný. Pokud má T
návratový typ, který nenívoid
, await_expression se klasifikuje jako hodnota typu T
.
12.9.8.4 Vyhodnocení výrazů await za běhu
Za běhu se výraz await t
vyhodnotí takto:
- Awaiter
a
je získán vyhodnocením výrazu(t).GetAwaiter()
. -
bool
b
se získá vyhodnocením výrazu(a).IsCompleted
. - Pokud je
b
false
, vyhodnocení závisí na tom, jestlia
implementuje rozhraníSystem.Runtime.CompilerServices.ICriticalNotifyCompletion
(dále jenICriticalNotifyCompletion
pro stručnost). Tato kontrola se provádí v době vazby; tj. za běhu, pokud máa
typ v době kompilacedynamic
, a jinak při kompilaci. Nechťr
označuje delegáta obnovení (§ 15.15):- Pokud
a
neimplementujeICriticalNotifyCompletion
, vyhodnotí se výraz((a) as INotifyCompletion).OnCompleted(r)
. - Pokud
a
implementujeICriticalNotifyCompletion
, 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
b
byltrue
), nebo po pozdějším vyvolání delegáta obnovení (pokudb
bylfalse
), 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 Aritmetické operátory
12.10.1 Obecné
Operátory *
, /
, %
, +
a -
se nazývají aritmetické operátory.
multiplicative_expression
: unary_expression
| multiplicative_expression '*' unary_expression
| multiplicative_expression '/' unary_expression
| multiplicative_expression '%' unary_expression
;
additive_expression
: multiplicative_expression
| additive_expression '+' multiplicative_expression
| additive_expression '-' multiplicative_expression
;
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 dynamic
a níže popsané řešení proběhne za běhu pomocí typu běhu těchto operandů, které mají typ kompilace dynamic
.
12.10.2 Operátor násobení
Pro operaci ve tvaru x * y
se 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 kontextuunchecked
nejsou 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
x
ay
kladné konečné hodnoty.z
je výsledkemx * y
, zaokrouhleno na nejbližší reprezentovatelnou hodnotu. Pokud je velikost výsledku pro cílový typ příliš velká,z
je nekonečno. Vzhledem k zaokrouhlování může býtz
nula, i kdyžx
aniy
není nula.+y
-y
+0
-0
+∞
-∞
NaN
+x
+z
-z
+0
-0
+∞
-∞
NaN
-x
-z
+z
-0
+0
-∞
+∞
NaN
+0
+0
-0
+0
-0
NaN
NaN
NaN
-0
-0
+0
-0
+0
NaN
NaN
NaN
+∞
+∞
-∞
NaN
NaN
+∞
-∞
NaN
-∞
-∞
+∞
NaN
NaN
-∞
+∞
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
(S výjimkou případů, kde je uvedeno jinak, v tabulkách s plovoucí čárkou v §12.10.2–§12.10.6 použití "
+
" znamená, že hodnota je kladná; použití "-
" znamená, že hodnota je záporná; a absence znaménka znamená, že hodnota může být kladná nebo záporná, nebo nemá 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.10.3 Operátor dělení
Pro operaci ve formě x / y
se použije rozlišení přetížení binárního operátoru (§12.4.5) k výběru 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.
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
int
nebolong
, a pravý operand je–1
, dojde k přetečení. V kontextuchecked
to způsobí vyvoláníSystem.ArithmeticException
(nebo jeho podtřídy). V kontextuunchecked
zá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
x
ay
kladné konečné hodnoty.z
je výsledkemx / y
, zaokrouhleno na nejbližší reprezentovatelnou hodnotu.+y
-y
+0
-0
+∞
-∞
NaN
+x
+z
-z
+∞
-∞
+0
-0
NaN
-x
-z
+z
-∞
+∞
-0
+0
NaN
+0
+0
-0
NaN
NaN
+0
-0
NaN
-0
-0
+0
NaN
NaN
-0
+0
NaN
+∞
+∞
-∞
+∞
-∞
NaN
NaN
NaN
-∞
-∞
+∞
-∞
+∞
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
Dělení desetinných čísel
decimal operator /(decimal x, decimal y);
Pokud je hodnota pravého operandu nula, vyvolá se
System.DivideByZeroException
. Pokud je absolutní hodnota výsledné hodnoty příliš velká, aby mohla být vyjádřena v desítkovém formátu, je generovánaSystem.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ěřítkox
s 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.10.4 Operátor zbytku
Pro operaci tvaru x % y
se rozlišení přetížení binárního operátoru (§12.4.5) použije, aby se vybrala 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é 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 % y
je hodnota vytvořenáx – (x / y) * y
. Pokud jey
nula, vyvolá seSystem.DivideByZeroException
.Pokud je levý operand nejmenší
int
nebolong
a pravý operand je–1
, vyvolá seSystem.OverflowException
, pouze pokudx / y
vyvolá 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šechny možné kombinace nenulových konečných hodnot, nul, nekonečna a NaN. V tabulce jsou
x
ay
kladné konečné hodnoty.z
je výsledkemx % y
a 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é jen
celé číslo nejblížex / y
).+y
-y
+0
-0
+∞
-∞
NaN
+x
+z
+z
NaN
NaN
+x
+x
NaN
-x
-z
-z
NaN
NaN
-x
-x
NaN
+0
+0
+0
NaN
NaN
+0
+0
NaN
-0
-0
-0
NaN
NaN
-0
-0
NaN
+∞
NaN
NaN
NaN
NaN
NaN
NaN
NaN
-∞
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
Desetinný 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 % y
v každém případě, kdyx / y
nevyvolá 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.10.5 Operátor sčítání
Pro operaci ve tvaru x + y
se 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ř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 kontextuunchecked
nejsou přetečení hlášena a významné vysokořádové bity mimo rozsah typu výsledku jsou zahazovány.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, nekonečen a NaN. V tabulce jsou
x
ay
nenulové konečné hodnoty az
je výsledkemx + y
. Pokudx
ay
mají stejnou velikost, ale opačné znaky,z
je kladná nula. Pokud jex + y
příliš velký na zobrazení v cílovém typu,z
je nekonečno se stejným znaménkem jakox + y
.y
+0
-0
+∞
-∞
NaN
x
z
x
x
+∞
-∞
NaN
+0
y
+0
+0
+∞
–∞
NaN
-0
y
+0
-0
+∞
-∞
NaN
+∞
+∞
+∞
+∞
+∞
NaN
NaN
-∞
-∞
-∞
-∞
NaN
-∞
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
Sčítání desetinných čísel
decimal operator +(decimal x, decimal y);
Pokud je velikost výsledné hodnoty příliš velká na to, aby měla reprezentaci v desítkovém formátu, je vyvolá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
E
je typ výčtu aU
je 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íToString
metody zděděné z typuobject
. PokudToString
vrá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
ToString
implicitně volané během provádění.konec 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.OutOfMemoryException
můž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
D
je 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 §12.10.6 a §20.6. Vzhledem k tomu, že
System.Delegate
není 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.10.6 Operátor odčítání
Pro operaci tvaru x – y
je použita přetížení binárního operátoru (§12.4.5) k výběru 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é 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 y
Pokud je v kontextu
checked
rozdíl mimo rozsah typu výsledku, vyvolá seSystem.OverflowException
. V kontextuunchecked
nejsou přetečení hlášena a jakékoli 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, nekonečen a NaN. V tabulce jsou
x
ay
nenulové konečné hodnoty az
je výsledkemx – y
. Pokud jsoux
ay
stejné,z
je kladná nula. Pokud jex – y
příliš velký na reprezentaci v cílovém typu,z
je nekonečno se stejným znaménkem jakox – y
.y
+0
-0
+∞
-∞
NaN
x
z
x
x
-∞
+∞
NaN
+0
-y
+0
+0
-∞
+∞
NaN
-0
-y
-0
+0
-∞
+∞
NaN
+∞
+∞
+∞
+∞
NaN
+∞
NaN
-∞
-∞
-∞
-∞
-∞
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
(Ve výše uvedené tabulce položky
-y
označ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á, aby mohla být vyjádřena v desítkovém formátu, vyvolá se
System.OverflowException
. Měřítko výsledku před zaokrouhlováním je větší ze dvou měřítek 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
E
je typ výčtu aU
je 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 hodnotamix
ay
a 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
D
je 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í (§20.2).
- Pokud jsou seznamy shodné, jak určuje delegovaný operátor rovnosti (§12.12.9), výsledek operace je
null
. - 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 jsou seznamy shodné, jak určuje delegovaný operátor rovnosti (§12.12.9), výsledek operace 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.
Operátory posunu 12.11
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 dynamic
a 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 >> count
se 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
<<
posunex
doleva o počet bitů vypočítaný, jak je popsáno níže.Bity s vysokým pořadím mimo rozsah typu výsledku
x
se 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
>>
posunex
doprava o počet bitů, jak je popsáno níže.Pokud je
x
typuint
nebolong
, zahodí se bityx
s 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 jex
nezáporná a nastaví se na jeden, pokud jex
záporná.Pokud je
x
typuuint
neboulong
, zahodí se bityx
s 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
x
int
nebouint
, 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
x
long
neboulong
, 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
x
proměnnou typuint
, operaceunchecked ((int)((uint)x >> y))
provede logický posun vpravo odx
. konec příkladu
Zvedané (§12.4.8) tvary níže definovaných nepředdefinovaných operátorů posunu jsou také předdefinovány.
12.12 Relační operátory a operátory pro testování typů
12.12.1 Obecné
Operátory ==
, !=
, <
, >
, <=
, >=
, is
a 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
is
musí 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 expreesion, musí mít výraz vzoru prioritu alespoň tak vysoký jako shift_expression. koncová poznámka
Operátor is
je popsán v §12.12.12 a operátor as
je popsán v §12.12.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 dynamic
a 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.12.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.12.3 Operátory pro porovnání s plovoucí desetinnou čá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
x
ay
je NaN,x < y
jefalse
, 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.12.4 Operátory porovnání desetinných čísel
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.12.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 y
true
nebo pokud jsou x
i y
false
. V opačném případě je výsledek false
.
Výsledek !=
je false
, pokud jsou x
i y
true
nebo pokud jsou x
i y
false
. 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.12.6 Porovnávací operátory výčtů
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.12.7 Operátory rovnosti referenčních typů
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 C
string
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
null
a druhý operand je hodnota typuT
, kdeT
je typový parametr, u kterého není známo, že by šlo o typ hodnoty a nemá omezení typem hodnoty.- Pokud je za běhu
T
nenulový typ hodnoty, výsledek==
jefalse
a výsledek!=
jetrue
. - Pokud je za běhu
T
nulovatelný typ hodnoty, výsledek se vypočítá z vlastnosti operanduHasValue
, jak je popsáno v (§12.12.10). - Pokud je za běhu
T
typ 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 == y
nebox != 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 == null
je povolena, i kdyžT
může být nenulový typ hodnoty a výsledek je jednoduše určen jakofalse
, kdyžT
představuje nenulový typ hodnoty.Konec 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); } }
produkuje výstup.
True False False False
Proměnné
s
at
odkazují na dvě různé instance řetězců obsahující stejné znaky. První porovnání dává výsledekTrue
, protože je zvolen předdefinovaný operátor rovnosti řetězce (§12.12.8), když jsou oba operandy typustring
. Zbývající porovnání všech mají výstupFalse
, protože přetíženíoperator ==
typustring
není 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ýchint
hodnot.konec příkladu
12.12.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-
null
odkazy 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.12.7, lze operátory rovnosti referenčního typu použít k porovnání řetězcových odkazů místo řetězcových hodnot. koncová poznámka
12.12.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 jiný běhový typ, nikdy nejsou stejní.
- Pokud mají obě instance delegáta seznam vyvolání (§20.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 rovna (jak je definováno níže) odpovídající položce 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.
- Položky seznamu vyvolání vytvořené z vyhodnocení sémanticky identických anonymních funkcí (§12.19) se stejnou (možná prázdnou) sadou zachycenými instancemi vnější proměnné mohou být (ale nemusí být) považovány za rovné.
Pokud řešení přetížení operátoru vyhodnotí některý z operátorů rovnosti delegáta a typy vazby obou operandů jsou typy delegátů, jak je popsáno v §20 místo v System.Delegate
, a neexistuje žádný převod identity mezi typy operandů určenými podle doby vazby, dojde k chybě v době vyhodnocování vazby.
Poznámka: Toto pravidlo zabraňuje porovnáním, která nemohou považovat hodnoty odlišné od
null
za rovné, protože jsou odkazy na instance různých typů delegátů. koncová poznámka
12.12.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.HasValue
a poslední dvě formuláře jsou přeloženy do x.HasValue
.
12.12.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 yi
se 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ě
x
je vyhodnocen. - Vyhodnocuje se pravý operand
y
. - Pro každou dvojici prvků
xi
ayi
v lexikálním pořadí:- Operátor
xi == yi
se vyhodnotí a výsledek typubool
se 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
dynamic
pak je operátorfalse
dynamicky vyvolán a výsledná hodnotabool
je 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ábool
hodnota je negována s logickým operátorem negace (!
).
- Pokud porovnání přineslo
- Pokud je výsledná
bool
false
, 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:
- Vyhodnocuje se levý operand
x
. - Pravý operand
y
se vyhodnocuje. - Pro každou dvojici prvků
xi
ayi
v lexikálním pořadí:- Operátor
xi != yi
se vyhodnotí a výsledek typubool
se získá následujícím způsobem:- Pokud porovnání přineslo výsledek
bool
, pak je to výsledek. - Jinak pokud porovnání přineslo
dynamic
, operátortrue
je na něm dynamicky vyvolán a výslednábool
hodnota 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ábool
hodnota je výsledek.
- Pokud porovnání přineslo výsledek
- Pokud je výsledná
bool
true
, nedojde k žádnému dalšímu vyhodnocení a výsledek operátoru rovnosti n-tice jetrue
.
- Operátor
- Pokud výsledkem všech porovnání prvků bylo
false
, výsledek operátoru rovnosti n-tice jefalse
.
12.12.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.12.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 se vyhodnotí takto:
- Pokud
E
je anonymní funkce nebo skupina metod, dojde k chybě v době kompilace. - Jestliže je
E
literálemnull
, nebo pokud je hodnotaE
rovnanull
, pak je výsledekfalse
. - Jinak:
- Nechte
R
být typem modulu runtimeE
. - Pojďme
D
odvozovat zR
následujícím způsobem: - Pokud je
R
typem hodnoty, která může být null, pak jeD
základním typemR
. - V opačném případě
D
jeR
. - Výsledek závisí na
D
aT
následujícím způsobem: - Pokud je
T
referenční typ, výsledek jetrue
, pokud:- mezi
D
aT
existuje převod identity , -
D
je typ odkazu a implicitní převod odkazu zD
naT
existuje nebo - Buď:
D
je typ hodnoty a existuje převod boxování zD
naT
.
Nebo:D
je typ hodnoty aT
je typ rozhraní implementovanýD
.
- mezi
- Pokud je
T
nulovatelný typ hodnoty, výsledek jetrue
, pokud jeD
základním typemT
. - Pokud je
T
hodnotový typ, který nepřipouští hodnotu null, výsledek jetrue
, pokud jsouD
aT
stejného typu. - V opačném případě je výsledek
false
.
Operátor is
nebere v úvahu uživatelsky definované převody.
Poznámka: Při vyhodnocování operátoru
is
za běhu byly nahrazeny všechny argumenty typu a neexistují žádné otevřené typy (§8.4.3). koncová poznámka
Poznámka: Operátor
is
lze pochopit z hlediska typů kompilace a převodů následujícím způsobem, kdeC
je typE
kompilace:
- Je-li typ
e
kompilace stejný jakoT
, nebo pokud implicitní převod odkazu (§10.2.8), převod boxingu (§10.2.9T
), převod obtékání (§10.6) nebo explicitní převod přebalování (§10.6) existuje z typuE
kompilace naT
:
- Pokud je typ
C
hodnotový 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 explicitní převod odkazu (§10.3.5) nebo převod rozbalení (§10.3.7) existuje od
C
doT
, nebo je-liC
neboT
otevřený typ (§8.4.3), budou provedeny kontroly za běhu, jak je popsáno výše.- Jinak není možné žádné odkazování, zabalení, rozbalení nebo zrušení převodu
E
na typT
a výsledek operace jefalse
. Kompilátor může implementovat optimalizace na základě typu kompilace.koncová poznámka
12.12.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:
-
E
neurčil hodnotu nebo nemá typ. - Vzor
P
nelze použít (§11.2) na typT
.
12.12.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 pro přetypování (§12.9.7) operátor as
nikdy nevyvolá výjimku. Pokud není uvedený převod možný, výsledná hodnota je null
.
Při operaci ve formě E as T
musí 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
E
naT
. - Typ
E
neboT
je otevřený typ. -
E
je 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
T
G
je známý jako odkazový typ, protože má omezení třídy. Parametr typuU
uH
však nesplňuje požadované podmínky; proto je použití operátoruas
vH
zakázáno.koncový příklad
12.13 Logické operátory
12.13.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 dynamic
a 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.13.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.13.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.13.4 Booleovské 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 x
true
a y
je false
nebo 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.13.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
, false
a null
.
Stejně jako u ostatních binárních operátorů jsou také předem definovány zvednuté tvary logických operátorů &
a |
(§12.13.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.14 Podmíněné logické operátory
12.14.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 && y
odpovídá operacix & y
s tím rozdílem, žey
je vyhodnocena pouze v případě, žex
nenífalse
. - Operace
x || y
odpovídá operacix | y
s tím rozdílem, žey
je vyhodnocena pouze v případě, žex
není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 true
vracífalse
aoperator false
vrací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 dynamic
a 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 rozlišení přetížení nenajde jediný nejlepší operátor nebo pokud rozlišení přetížení vybere jednoho z předdefinovaných logických operátorů pro celá čísla nebo logické operátory pro typ Nullable Boolean (§12.13.5), výsledkem je chyba vazby.
- V opačném případě, pokud je vybraný operátor jedním z předdefinovaných Booleových logických operátorů (§12.13.4), je operace zpracována dle popisu v §12.14.2.
- V opačném případě je vybraným operátorem operátor definovaný uživatelem a operace je zpracována, jak je popsáno v §12.14.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.14.3.
12.14.2 Booleovské podmínkové logické operátory
Pokud jsou operandy &&
nebo ||
typu bool
nebo pokud jsou operandy typu, které nedefinují použitelné operator &
nebo operator |
, ale definují implicitní převody na bool
, operace se zpracuje takto:
- Operace
x && y
se vyhodnotí jakox ? y : false
. Jinými slovy,x
je nejprve vyhodnocen a převeden na typbool
. Pokud jex
true
,y
se vyhodnotí a převede na typbool
a stane se výsledkem operace. V opačném případě je výsledek operacefalse
. - Operace
x || y
se vyhodnotí jakox ? true : y
. Jinými slovy,x
je nejprve vyhodnocen a převeden na typbool
. Pokud jex
true
, výsledek operace jetrue
. V opačném případě sey
vyhodnotí a převede na typbool
a stane se výsledkem operace.
12.14.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ů typuT
a vrátí výsledek typuT
. -
T
má obsahovat prohlášeníoperator true
aoperator 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 && y
je vyhodnocena jakoT.false(x) ? x : T.&(x, y)
, kdeT.false(x)
je vyvoláníoperator false
deklarovaného vT
aT.&(x, y)
je vyvoláním vybranéhooperator &
. Jinými slovy,x
se nejprve vyhodnotí aoperator false
se vyvolá na výsledku, aby se zjistilo, jestli jex
rozhodně nepravdivý. Pokudx
je rozhodně nepravda, výsledek operace je hodnota, která byla dříve vypočtena prox
. V opačném případě sey
vyhodnotí a vybranáoperator &
se vyvolá na hodnotě dříve vypočítané prox
a hodnotě vypočítané proy
pro získání výsledku operace. - Operace
x || y
je vyhodnocena jakoT.true(x) ? x : T.|(x, y)
, kdeT.true(x)
je vyvoláníoperator true
deklarovaného vT
aT.|(x, y)
je vyvoláním vybranéhooperator |
. Jinými slovy,x
se nejprve vyhodnotí aoperator true
se vyvolá na výsledek, který určí, jestlix
je rozhodně pravdivá. Pokud jex
rozhodně pravdivý, výsledek operace je hodnota, která byla dříve vypočtena prox
. V opačném případě se vyhodnotíy
a vybranáoperator |
se aplikuje na hodnotu dříve vypočítanou prox
a na hodnotu vypočítanou proy
, aby se vytvořil výsledek 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.15 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 a
null
.
Operátor nulového sjednocení je asociativní zprava, což znamená, že operace jsou seskupeny zprava doleva.
Příklad: Výraz ve tvaru
a ?? b ?? c
se vyhodnotí jako?? (b ?? c)
. Obecně řečeno, výraz formulářeE1 ?? E2 ?? ... ?? EN
vrátí první z operandů, které nejsounull
, nebonull
, pokud jsou všechny operandynull
. konec 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 ?? b
A₀
, A
nebo 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
A
existuje a není typ hodnoty null nebo odkazový typ, dojde k chybě v době kompilace. - V opačném případě pokud
A
existuje ab
je dynamický výraz, typ výsledku jedynamic
. V době běhu se nejprve vyhodnotía
. Pokuda
nenínull
,a
se převede nadynamic
a stane se výsledkem. V opačném případě seb
vyhodnotí a stane se výsledkem. - Jinak pokud
A
existuje a je typ hodnoty null a implicitní převod existuje zb
naA₀
, je typ výsledkuA₀
. Během běhu programu se nejprve vyhodnotía
. Pokuda
nenínull
,a
se rozbalí na typA₀
a to se stane výsledkem. V opačném případě seb
vyhodnotí a převede na typA₀
a stane se výsledkem. - V opačném případě, pokud
A
existuje a implicitní převod existuje zb
naA
, je typ výsledkuA
. Za běhu se nejprve vyhodnotí a. Pokud a není null, stane se výsledkem a. V opačném případě seb
vyhodnotí a převede na typA
a stane se výsledkem. - V opačném případě, pokud
A
existuje a je nulovatelný typ hodnoty,b
má typB
a existuje implicitní převod zA₀
naB
, pak je typ výsledkuB
. V době běhu se nejprve vyhodnotía
. Pokuda
nenínull
,a
je rozbalený na typA₀
, převeden na typB
a stane se výsledkem. V opačném případě seb
vyhodnotí a stane se výsledkem. - V opačném případě, pokud
b
má typB
a implicitní převod existuje za
naB
, je typ výsledkuB
. V době běhu se nejprve vyhodnotía
. Pokuda
nenínull
,a
se převede na typB
a stane se výsledkem. V opačném případě seb
vyhodnotí a stane se výsledkem.
V opačném případě jsou a
a b
nekompatibilní a a
dojde k chybě v době kompilace.
12.16 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.Exception
a 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.17 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
out
argument_value v argument_list. - Jako prosté zahození
_
, které tvoří levou stranu prostého přiřazení (§12.21.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
s1
ukazuje explicitně i implicitně zadané výrazy deklarace. Odvozený typb1
jebool
, protože je to typ odpovídajícího výstupního parametru vM1
. NásledujícíWriteLine
má přístup ki1
ab1
, které byly zavedeny v uzavřeném oboru.Deklarace
s2
ukazuje pokus o použitíi2
ve vnořeném voláníM
, což je zakázáno, protože odkaz se vyskytuje v seznamu argumentů, kdei2
byl deklarován. Na druhou stranu je povolen odkaz nab2
v posledním argumentu, protože k němu dochází po konci vnořeného seznamu argumentů, kdeb2
byl deklarován.Deklarace
s3
ukazuje 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.konec příkladu
12.18 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.16) není povolen v podmíněném operátoru, pokud je ref
.
Podmíněný výraz formuláře b ? x : y
nejprve vyhodnotí podmínku b
. Pokud je b
true
, 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 : e
se vyhodnotí jakoa ? b : (c ? d : e)
. konec příkladu
Prvním operandem operátoru ?:
musí být výraz, který lze implicitně převést na bool
nebo 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
ref
existuje, vrátí conditional_expression odkaz na proměnnou, která se dá přiřadit k referenční proměnné pomocí operátoru= ref
nebo 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á
x
typX
ay
typY
,- Pokud mezi
X
aY
existuje převod identity, je výsledkem nejlepší společný typ sady výrazů (§12.6.3.15). Pokud je některý typdynamic
, odvození typu dává přednostdynamic
(§8.7). Pokud je některý z typů typem n-tice (§8.3.11), odvození typu zahrnuje 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
X
naY
, ale ne zY
naX
, pakY
je typ podmíněného výrazu. - Jinak, pokud existuje implicitní převod výčtu (§10.2.4) z
X
naY
, pak jeY
typem podmíněného výrazu. - Pokud jinak existuje implicitní převod výčtu (§10.2.4) z
Y
naX
, pak typem podmíněného výrazu jeX
. - Jinak, pokud implicitní převod (§10.2) existuje z
Y
naX
, ale ne zX
naY
, pakX
je 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 mezi
- Pokud má typ jenom jeden z
x
ay
ax
iy
jsou 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í
b
a určí se hodnotabool
b
:- Pokud existuje implicitní převod z typu
b
nabool
, tento implicitní převod se provede a vytvoří hodnotubool
. - V opačném případě je vyvolána
operator true
definovaná typemb
, aby se vytvořila hodnotabool
.
- Pokud existuje implicitní převod z typu
- Pokud
bool
hodnota vytvořená výše uvedeným krokem jetrue
,x
se 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í
y
a 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í
b
a určí se hodnotabool
b
:- Pokud existuje implicitní převod z typu
b
nabool
, provede se tento implicitní převod a vytvoří hodnotubool
. - V opačném případě je vyvolána
operator true
definovaná typemb
, aby se vytvořila hodnotabool
.
- Pokud existuje implicitní převod z typu
- Pokud
bool
hodnota vytvořená výše uvedeným krokem jetrue
,x
se 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
y
vyhodnotí a převede na typ podmíněného výrazu a stane se výsledkem podmíněného výrazu.
12.19 Anonymní funkční výrazy
12.19.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ýsledkuM
void
(§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>.Reverse
jevoid
. 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ý příklad
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.15.
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 omitted
konec 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.19.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.19.3 Těla anonymních funkcí
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
this
typu 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
, kdex
je č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.19.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.19.7).
- Jedná se o chybu v době kompilace, pokud tělo obsahuje příkaz
goto
, příkazbreak
nebo příkazcontinue
, jehož cíl je mimo tělo nebo uvnitř těla anonymní funkce. - Příkaz
return
v 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.19.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ěSum
metody. Každá z nich přebíráselector
argument, který extrahuje hodnotu, která se má sečíst z položky seznamu. Extrahovaná hodnota může být buďint
, nebodouble
a výsledný součet je podobněint
nebodouble
.Metody
Sum
lze 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.Sum
platí obě metodySum
, protože anonymníd => d.UnitCount
funkce je kompatibilní sFunc<Detail,int>
i sFunc<Detail,double>
. Řešení přetížení však vybere prvníSum
metodu, protože převod naFunc<Detail,int>
je lepší než převod naFunc<Detail,double>
.Při druhém vyvolání
orderDetails.Sum
lze použít pouze druhou metoduSum
, protože anonymní funkced => d.UnitPrice * d.UnitCount
vytvoří hodnotu typudouble
. Rozlišení přetížení proto vybere druhouSum
metodu pro toto vyvolání.konec příkladu
12.19.5 Anonymní funkce a dynamické vazby
Anonymní funkce nemůže být přijímačem, argumentem nebo operandem dynamicky vázané operace.
12.19.6 Vnější proměnné
12.19.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.19.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á
x
je zachycena anonymní funkcí a doba životnostix
je rozšířena alespoň tak dlouho, dokud delegát vrácený zF
nebude 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 3
konec 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 (§23,4), ale považuje se za pohyblivou proměnnou. Zachycené vnější proměnné však nelze použít v příkazu fixed
(§23.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.19.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á
x
vytvoří 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
x
mimo smyčku však vznikne jedna instancex
:static void F() { int x; for (int i = 0; i < 3; i++) { x = i * 2 + 1; ... } }
ukončit příklad
Pokud není zachyceno, neexistuje způsob, jak přesně sledovat, jak často se vytváří instance místní proměnné, a protože životnosti instancí jsou oddělené, je možné, aby každá instance jednoduše používala stejné paměťové místo. 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 5
Pokud se však deklarace
x
př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 5
Všimněte si, že kompilátor má povoleno (ale není povinen) optimalizovat tři instancí do jednoho objektu delegáta (§10.7.2).
konec 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 3
konec 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
F
změ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
x
ale samostatné instancey
a výstup je:1 1 2 1 3 1
konec 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é
x
a mohou tak "komunikovat" prostřednictvím této proměnné. Výstupem příkladu je:5 10
konec příkladu
12.19.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.19.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");
}
}
Tuto instanci 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);
}
}
To lze 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.19.6.3) pak odpovídá vytvoření instance vygenerované třídy kompilátoru a přístup k místní proměnné odpovídá přístupu k poli v instanci generované třídy kompilátoru. 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 generované třídy pro vnitřní blok, obsahuje místní proměnnou z
a pole, které odkazuje na instanci __Locals1
. Instance __Locals1
, kompilátorem vygenerovaná třída pro vnější blok, obsahuje místní proměnnou y
a pole, které odkazuje na this
uzavírajícího člena 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 __Local2
a 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.20 Výrazy dotazů
12.20.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_clauses? select_or_group_clause query_continuation?
;
query_body_clauses
: query_body_clause
| query_body_clauses query_body_clause
;
query_body_clause
: from_clause
| let_clause
| where_clause
| join_clause
| join_into_clause
| orderby_clause
;
let_clause
: 'let' identifier '=' expression
;
where_clause
: 'where' boolean_expression
;
join_clause
: 'join' type? identifier 'in' expression 'on' expression
'equals' expression
;
join_into_clause
: 'join' type? identifier 'in' expression 'on' expression
'equals' expression 'into' identifier
;
orderby_clause
: 'orderby' orderings
;
orderings
: ordering (',' ordering)*
;
ordering
: expression ordering_direction?
;
ordering_direction
: 'ascending'
| 'descending'
;
select_or_group_clause
: select_clause
| group_clause
;
select_clause
: 'select' expression
;
group_clause
: 'group' expression 'by' expression
;
query_continuation
: 'into' identifier query_body
;
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.20.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 "from
identifikátor" následovaný libovolným tokenem kromě ";
", "=
" nebo ",
".
12.20.3 Překlad výrazů dotazu
12.20.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.20.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
, GroupBy
a Cast
. Tyto metody mají mít určité podpisy a návratové typy, jak je popsáno v §12.20.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.20.3.8.
12.20.3.2 Výrazy dotazů 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() })
konec příkladu
12.20.3.3 Explicitní typy proměnných rozsahu
Klauzule from
, která explicitně určuje typ proměnné rozsahu
from «T» «x» in «e»
se přeloží na
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 c
je přeloženo do
from c in (customers).Cast<Customer>() where c.City == "London" select c
jehož konečný překlad je
customers. Cast<Customer>(). Where(c => c.City == "London")
konec příkladu
Poznámka: Explicitní typy proměnných rozsahu jsou užitečné pro dotazování kolekcí, které implementují neobecné
IEnumerable
rozhraní, 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.20.3.4 Degenerované 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 c
překládá se do
(customers).Select(c => c)
příklad koncový
Degenerační výraz dotazu je výraz, který triviálně vybere prvky zdroje.
Poznámka: Pozdější fáze překladu (§12.20.3.6 a §12.20.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á
Select
ve zdroji. Pak je až na implementátorySelect
a dalších operátorů dotazů, aby se zajistilo, že tyto metody nikdy nevracely samotný zdrojový objekt. koncová poznámka
12.20.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»
se přeloží 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 }
překládá se do
(customers). SelectMany(c => c.Orders, (c,o) => new { c.Name, o.OrderID, o.Total } )
ukončit příklad
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 }
se přeloží 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 }
konečný překlad, který 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
x
je identifikátor vygenerovaný kompilátorem, který je jinak neviditelný a nepřístupný.konec 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 }
se přeloží 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 }
konečný překlad
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
x
je identifikátor vygenerovaný kompilátorem, který je jinak neviditelný a nepřístupný.konec 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»
se přeloží do
( «e1» ) . Join( «e2» , «x1» => «k1» , «x2» => «k2» , ( «x1» , «x2» ) => «v» )
Příklad: Příklad
from c in customersh join o in orders on c.CustomerID equals o.CustomerID select new { c.Name, o.OrderDate, o.Total }
se přeloží do
(customers).Join( orders, c => c.CustomerID, o => o.CustomerID, (c, o) => new { c.Name, o.OrderDate, o.Total })
konec příkladu
Klauzule join
následovaná klauzulí textu dotazu:
from «x1» in «e1»
join «x2» in «e2» on «k1» equals «k2»
...
se přeloží 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»
překládá se 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»
...
se přeloží 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 }
konečný překlad, jehož 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
x
ay
jsou vygenerované identifikátory kompilátoru, 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 o
má konečný překlad.
(orders) .OrderBy(o => o.Customer.Name) .ThenByDescending(o => o.Total)
ukončit příklad
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.20.3.6 Klauzule Select
Výraz dotazu formuláře
from «x» in «e» select «v»
se přeloží 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 c
je jednoduše přeložen do
(customers).Where(c => c.City == "London")
ukončit příklad
12.20.3.7 Skupinové klauzule
Klauzule group
from «x» in «e» group «v» by «k»
se přeloží 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.Country
se přeloží do
(customers).GroupBy(c => c.Country, c => c.Name)
konec příkladu
12.20.3.8 Transparentní identifikátory
Některé překlady vkládají proměnné rozsahu s transparentními identifikátory označené *
. 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 }
se přeloží 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
x
je 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 }
se přeloží 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 })
konečný překlad, jehož výsledkem 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
x
ay
jsou identifikátory generované kompilátorem, které jsou jinak neviditelné a nepřístupné. konec příkladu
12.20.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 přístupné metody extenson 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 metodyThenBy
aThenByDescending
jsou k dispozici pouze na základě výsledkuOrderBy
neboOrderByDescending
. 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.Linq
poskytuje implementaci vzoru výrazu dotazu pro libovolný typ, který implementuje rozhraníSystem.Collections.Generic.IEnumerable<T>
. koncová poznámka
12.21 Operátory přiřazení
12.21.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.21.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.21.3.
Operátory přiřazení, kromě operátorů =
a = ref
, se nazývají složené operátory přiřazení . Tyto operátory provádějí uvedenou operaci na dvou operandech a potom přiřazují výslednou hodnotu proměnné, vlastnosti nebo prvku indexeru zadanému levým operandem. Operátory složeného přiřazení jsou popsány v §12.21.4.
Operátory +=
a -=
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álostí jsou popsány v §12.21.5.
Operátory přiřazení mají pravoasociativní povahu, což znamená, že operace jsou seskupovány zprava doleva.
Příklad: Výraz ve tvaru
a = b = c
se vyhodnotí jakoa = (b = c)
. konec příkladu
12.21.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 dynamic
a 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á dynamic
typ 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
x
výraz řazené kolekce členů(x1, ..., xn)
ay
lze dekonstruovat na výraz řazené kolekce členů(y1, ..., yn)
s prvkyn
(§12.7) a každé přiřazeníxi
yi
má typTi
, pak přiřazení má typ(T1, ..., Tn)
. - Jinak pokud je
x
klasifikována jako proměnná, proměnná neníreadonly
,x
má typT
ay
má implicitní převod naT
, pak přiřazení má typT
. - V opačném případě je
x
klasifikována jako implicitně typovaná proměnná (tj. implicitně zadaný výraz deklarace) ay
má typT
, odvozený typ proměnné jeT
a přiřazení má typT
. - Jinak pokud je
x
klasifikován jako vlastnost nebo přístup indexeru, a má přístupný nastavovací přístupový člen,x
má typT
ay
má 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 x
y
s typem T
, který se skládá z následujících rekurzivních kroků:
-
x
se vyhodnotí, pokud to ještě nebylo. - Pokud je
x
klasifikována jako proměnná,y
se vyhodnotí a v případě potřeby se převede naT
prostřednictvím implicitního převodu (§10,2).- Pokud je proměnná daná
x
prvkem pole odkazového typu, provede se kontrola za běhu, která zajistí, že hodnota vypočítaná proy
je kompatibilní s instancí pole, jehožx
je prvkem. Kontrola proběhne úspěšně, pokudy
jenull
, nebo pokud implicitní převod odkazu (§10.2.8) existuje z typu instance odkazovanéy
na 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
x
klasifikován jako vlastnost nebo přístup indexeru:-
y
se vyhodnotí a v případě potřeby se převede naT
prostřednictvím implicitního převodu (§10,2). - Sada přístupového objektu
x
je vyvolána s hodnotou, která je výsledkem vyhodnocení a převoduy
jako argument hodnoty. - Hodnota vyplývající z vyhodnocení a převodu
y
je výsledkem přiřazení.
-
- Pokud je
x
klasifikován jako n-tice(x1, ..., xn)
s aritoun
:-
y
se dekonstruktivně konstruuje s prvkyn
na n-ticový výraze
. - Výsledná řazená kolekce členů
t
se vytvoří převodeme
naT
pomocí implicitního převodu řazené kolekce členů. - pro každou
xi
zleva doprava se provede přiřazenít.Itemi
doxi
s tím rozdílem, žexi
se znovu nevyhodnocují. -
t
je výsledkem přiřazení.
-
Poznámka: Pokud je typ času kompilace
x
dynamic
a existuje implicitní převod z typu času kompilacey
nadynamic
, 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 zB
naA
. 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(); // ArrayTypeMismatchException
Poslední přiřazení způsobí vyvolání
System.ArrayTypeMismatchException
, protože odkaz naArrayList
nelze 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.A
ar.B
jsou povolená, protožep
ar
jsou 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.A
ar.B
nejsou proměnné.konec příkladu
12.21.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
= ref
můž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= ref
je operátor ab ? ref x : ref y
je pravý operand:ref int a = ref (b ? ref x : ref y);
. Důležité je, že výrazref b
je není součástí tohoto příkazu, i když se může na první pohled objevit. koncová poznámka
12.21.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 kompilátoru výrazu přiřazení dynamic
a řešení popsané níže proběhne za běhu na základě typu E
. Je-li levý operand ve formě E[Ei]
, kde alespoň jeden prvek Ei
má typ kompilace dynamic
, a typ kompilace E
není pole, výsledný přístup indexeru je dynamicky vázán, ale s omezenou kontrolou při kompilaci (§12.6.5).
Operace formuláře x «op»= y
je zpracována použitím rozlišení přetížení binárního operátoru (§12.4.5) jako by operace byla zapsána x «op» y
. Potom
- Pokud je návratový typ vybraného operátoru implicitně konvertibilní na typ
x
, operace se vyhodnotí jakox = x «op» y
s tím rozdílem, žex
se 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
x
a pokud jey
implicitně konvertibilní na typx
nebo operátor je operátor směny, operace se vyhodnotí jakox = (T)(x «op» y)
, kdeT
je typx
, kromě toho, žex
se 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» y
se 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í, kdeA
je metoda vracejícíint[]
aB
aC
jsou metody vracejícíint
, metody jsou vyvolány pouze jednou, v pořadíA
,B
,C
. konec 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
, ushort
nebo 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; // OK
intuitivním důvodem každé chyby je, že odpovídající jednoduché přiřazení by také bylo chybou.
konec 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»= y
je vyhodnoceno jakox = x «op» y
nebox = (T)(x «op» y)
, pravidla vyhodnocení implicitně pokrývají operátory lifted. koncová poznámka
12.21.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.22
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.23 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 členy tříd a struktur typů
const
. - 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
checked
aunchecked
. -
nameof
vý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). - Výrazy
sizeof
, za předpokladu, že nespravovaný typ je jedním z typů uvedených v §23.6.9 , pro kterésizeof
vrací konstantní hodnotu. - 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
null
hodnot, 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
i
je chyba, protože je vyžadován převod boxingu. Inicializacestr
je chyba, protože je vyžadován implicitní převod odkazu z hodnoty, která nenínull
.konec 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 (§19.4)
- Výchozí argumenty seznamů parametrů (§15.6.2)
-
case
popiskyswitch
prohlášení (§13.8.3). -
goto case
prohlášení (§13.10.4) - Délky dimenzí ve výrazu pro vytvoření pole (§12.8.17.5), který obsahuje inicializátor.
- Atributy (§22)
- 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
, uint
nebo ulong
, za předpokladu, že hodnota konstantního výrazu je v rozsahu cílového typu.
12.24 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.18) se řídí stejnými pravidly jako boolean_expression, ale z důvodů přednosti operátoru je klasifikován jako null_coalescing_expression.
Aby bylo možné vytvořit hodnotu typu bool
, je nutné boolean_expressionE
následujícím způsobem:
- Pokud je E implicitně konvertibilní na
bool
pak 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 true
naE
a 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