13 Příkazy
13.1 Obecné
Jazyk C# poskytuje různé příkazy.
Poznámka: Většina těchto tvrzení bude známá vývojářům, kteří naprogramovali jazyk C a C++. koncová poznámka
statement
: labeled_statement
| declaration_statement
| embedded_statement
;
embedded_statement
: block
| empty_statement
| expression_statement
| selection_statement
| iteration_statement
| jump_statement
| try_statement
| checked_statement
| unchecked_statement
| lock_statement
| using_statement
| yield_statement
| unsafe_statement // unsafe code support
| fixed_statement // unsafe code support
;
unsafe_statement (§23.2) a fixed_statement (§23.7) jsou k dispozici pouze v nebezpečném kódu (§23).
Embedded_statement nonterminal se používá pro příkazy, které se zobrazují v jiných příkazech. Použití embedded_statement místo příkazu vylučuje použití deklarací příkazů a příkazů označených v těchto kontextech.
Příklad: Kód
void F(bool b) { if (b) int i = 44; }
výsledkem chyby v době kompilace, protože
if
příkaz vyžaduje embedded_statement místo příkazu pro jehoif
větev. Pokud by byl tento kód povolen, byla by proměnnái
deklarována, ale nikdy ji nelze použít. Všimněte si však, že uvedeními
deklarace do bloku je příklad platný.end example
13.2 Koncové body a dosažitelnost
Každý příkaz má koncový bod. Koncovým bodem příkazu je intuitivně umístění, které bezprostředně následuje za příkazem. Pravidla provádění pro složené příkazy (příkazy obsahující vložené příkazy) určují akci, která se provede, když ovládací prvek dosáhne koncového bodu vloženého příkazu.
Příklad: Když ovládací prvek dosáhne koncového bodu příkazu v bloku, ovládací prvek se přenese na další příkaz v bloku. end example
Pokud je možné dosáhnout příkazu spuštěním, říká se, že je dostupný. Naopak pokud neexistuje možnost, že se příkaz spustí, příkaz je nedostupný.
Příklad: V následujícím kódu
void F() { Console.WriteLine("reachable"); goto Label; Console.WriteLine("unreachable"); Label: Console.WriteLine("reachable"); }
Druhé vyvolání console.WriteLine je nedostupné, protože neexistuje možnost, že se příkaz spustí.
end example
Upozornění se hlásí, pokud není dostupný jiný příkaz než throw_statement, blokování nebo empty_statement . Konkrétně se nejedná o chybu pro nedostupný příkaz.
Poznámka: Chcete-li určit, zda je konkrétní příkaz nebo koncový bod dosažitelný, kompilátor provádí analýzu toku podle pravidel dostupnosti definovaných pro každý příkaz. Analýza toku bere v úvahu hodnoty konstantních výrazů (§12.23), které řídí chování příkazů, ale možné hodnoty ne constantních výrazů se nepovažují. Jinými slovy, pro účely analýzy toku řízení se považuje nekontuční výraz daného typu za možnou hodnotu tohoto typu.
V příkladu
void F() { const int i = 1; if (i == 2) Console.WriteLine("unreachable"); }
Logický výraz
if
příkazu je konstantní výraz, protože oba operandy operátoru==
jsou konstanty. Vzhledem k tomu, že konstantní výraz je vyhodnocen v době kompilace, vytvoření hodnotyfalse
,Console.WriteLine
vyvolání je považováno za nedostupné. Pokudi
se ale změní na místní proměnnouvoid F() { int i = 1; if (i == 2) Console.WriteLine("reachable"); }
Console.WriteLine
vyvolání je považováno za dosažitelné, i když ve skutečnosti se nikdy neprovede.koncová poznámka
Blok člena funkce nebo anonymní funkce je vždy považován za dosažitelný. Následným vyhodnocením pravidel dostupnosti každého příkazu v bloku lze určit dosažitelnost každého daného příkazu.
Příklad: V následujícím kódu
void F(int x) { Console.WriteLine("start"); if (x < 0) Console.WriteLine("negative"); }
dosažitelnost druhého
Console.WriteLine
se určuje takto:
- První
Console.WriteLine
výraz je dostupný, protože blokF
metody je dosažitelný (§13.3).- Koncový bod prvního
Console.WriteLine
výrazu je dosažitelný, protože tento výrok je dosažitelný (§13.7 a §13.3).- Prohlášení
if
je dosažitelné, protože koncový bod prvníhoConsole.WriteLine
výrazu je dosažitelný (§13.7 a §13.3).- Druhý
Console.WriteLine
příkaz výrazu je dostupný, protože logický výrazif
příkazu nemá konstantní hodnotufalse
.end example
Existují dvě situace, kdy se jedná o chybu v době kompilace pro koncový bod příkazu, aby byl dostupný:
switch
Vzhledem k tomu, že příkaz nepovoluje přechod oddílu přepínače na další oddíl přepínače, jedná se o chybu v době kompilace pro koncový bod seznamu příkazů oddílu switch, aby byl dostupný. Pokud k této chybě dojde, obvykle to značí, žebreak
chybí příkaz.Jedná se o chybu v době kompilace koncového bodu bloku člena funkce nebo anonymní funkce, která vypočítá hodnotu, která bude dosažitelná. Pokud k této chybě dojde, obvykle je to označení, že
return
chybí prohlášení (§13.10.5).
13.3 Bloky
13.3.1 Obecné
Blok umožňuje zápis více příkazů v kontextech, kde je povolený jeden příkaz.
block
: '{' statement_list? '}'
;
Blok se skládá z volitelného statement_list (§13.3.2) uzavřených ve složených závorkách. Pokud seznam příkazů vynecháte, bude blok prázdný.
Blok může obsahovat prohlášení (§13.6). Obor místní proměnné nebo konstanty deklarované v bloku je blok.
Blok se spustí takto:
- Pokud je blok prázdný, ovládací prvek se přenese na koncový bod bloku.
- Pokud blok není prázdný, ovládací prvek se přenese do seznamu příkazů. Když a pokud ovládací prvek dosáhne koncového bodu seznamu příkazů, ovládací prvek se přenese na koncový bod bloku.
Seznam příkazů bloku je dostupný, pokud je samotný blok dostupný.
Koncový bod bloku je dostupný, pokud je blok prázdný nebo pokud je koncový bod seznamu příkazů dostupný.
Blok, který obsahuje jeden nebo více yield
tvrzení (§13.15), se nazývá blok iterátoru. Bloky iterátoru se používají k implementaci členů funkce jako iterátorů (§15.14). Některá další omezení platí pro bloky iterátoru:
- Jedná se o chybu v době kompilace, kdy se
return
příkaz zobrazí v bloku iterátoru (aleyield return
příkazy jsou povolené). - Jedná se o chybu v době kompilace, kdy blok iterátoru obsahuje nebezpečný kontext (§23.2). Blok iterátoru vždy definuje bezpečný kontext, i když je jeho deklarace vnořena do nebezpečného kontextu.
13.3.2 Seznamy příkazů
Seznam příkazů se skládá z jednoho nebo více příkazů zapsaných v posloupnosti. Seznamy prohlášení se vyskytují v bloku s (§13.3) a v switch_blocks (§13.8.3).
statement_list
: statement+
;
Seznam příkazů se provádí přenosem ovládacího prvku na první příkaz. Když a pokud ovládací prvek dosáhne koncového bodu příkazu, ovládací prvek se přenese na další příkaz. Když a pokud ovládací prvek dosáhne koncového bodu posledního příkazu, ovládací prvek se přenese na koncový bod seznamu příkazů.
Příkaz v seznamu příkazů je dostupný, pokud platí alespoň jedna z následujících možností:
- Příkaz je první příkaz a samotný seznam příkazů je dostupný.
- Koncový bod předchozího příkazu je dostupný.
- Příkaz je označený příkazem a popisek je odkazován dosažitelným
goto
příkazem.
Koncový bod seznamu příkazů je dostupný, pokud je dostupný koncový bod posledního příkazu v seznamu.
13.4 Prázdný příkaz
Empty_statement nic nedělá.
empty_statement
: ';'
;
Prázdný příkaz se používá v případě, že neexistují žádné operace, které by bylo možné provést v kontextu, kde je příkaz povinný.
Spuštění prázdného příkazu jednoduše přenese řízení na koncový bod příkazu. Koncový bod prázdného příkazu je tak dostupný, pokud je prázdný příkaz dostupný.
Příklad: Prázdný příkaz lze použít při zápisu
while
příkazu s tělem null:bool ProcessMessage() {...} void ProcessMessages() { while (ProcessMessage()) ; }
Prázdný příkaz lze také použít k deklaraci popisku těsně před uzavřením
}
"" bloku:void F(bool done) { ... if (done) { goto exit; } ... exit: ; }
end example
13.5 Příkazy s popisky
Labeled_statement umožňuje, aby byl příkaz předponou popisku. Příkazy s popisky jsou povoleny v blocích, ale nejsou povoleny jako vložené příkazy.
labeled_statement
: identifier ':' statement
;
Příkaz s popiskem deklaruje popisek s názvem zadaným identifikátorem. Rozsah popisku je celý blok, ve kterém je popisek deklarován, včetně všech vnořených bloků. Jedná se o chybu v době kompilace pro dva popisky se stejným názvem, aby měly překrývající se obory.
Popisek lze odkazovat z goto
prohlášení (§13.10.4) v rámci popisku.
Poznámka: To znamená, že
goto
příkazy mohou přenášet řízení v blocích a mimo bloky, ale nikdy do bloků. koncová poznámka
Popisky mají vlastní prostor deklarací a neruší jiné identifikátory.
Příklad: Příklad
int F(int x) { if (x >= 0) { goto x; } x = -x; x: return x; }
je platný a používá název x jako parametr i popisek.
end example
Provedení příkazu označeného popiskem přesně odpovídá provedení příkazu za popiskem.
Kromě dosažitelnosti poskytované normálním tokem řízení je popisek dostupný, pokud je popisek odkazován dosažitelným goto
příkazem, pokud goto
není příkaz uvnitř try
bloku nebo catch
bloku try_statement , který obsahuje finally
blok, jehož koncový bod je nedostupný, a popisek je mimo try_statement.
13.6 Prohlášení
13.6.1 Obecné
Declaration_statement deklaruje jednu nebo více místních proměnných, jednu nebo více místních konstant nebo místní funkci. Příkazy deklarace jsou povoleny v blocích a blocích switch, ale nejsou povoleny jako vložené příkazy.
declaration_statement
: local_variable_declaration ';'
| local_constant_declaration ';'
| local_function_declaration
;
Místní proměnná je deklarována pomocí local_variable_declaration (§13.6.2). Místní konstanta je deklarována pomocí local_constant_declaration (§13.6.3). Místní funkce je deklarována pomocí local_function_declaration (§13.6.4).
Deklarované názvy se zavádějí do nejbližšího ohraničujícího prostoru deklarace (§7.3).
13.6.2 Deklarace místních proměnných
13.6.2.1 Obecné
Local_variable_declaration deklaruje jednu nebo více místních proměnných.
local_variable_declaration
: implicitly_typed_local_variable_declaration
| explicitly_typed_local_variable_declaration
| explicitly_typed_ref_local_variable_declaration
;
Implicitně zadané deklarace obsahují kontextové klíčové slovo (§6.4.4), var
což vede k syntaktické nejednoznačnosti mezi třemi kategoriemi, které jsou vyřešeny následujícím způsobem:
- Pokud neexistuje žádný typ pojmenovaný
var
v oboru a vstup odpovídá implicitly_typed_local_variable_declaration je zvolen; - Jinak pokud je název typu
var
v oboru, implicitly_typed_local_variable_declaration se nepovažuje za možnou shodu.
V rámci local_variable_declaration každá proměnná je zavedena deklarátorem, což je jeden z implicitly_typed_local_variable_declarator, explicitly_typed_local_variable_declarator nebo ref_local_variable_declarator pro implicitně napsané, explicitně zadané a ref místní proměnné. Deklarátor definuje název (identifikátor) a počáteční hodnotu zavedené proměnné( pokud existuje).
Pokud v deklaraci existuje více deklarátorů, zpracovávají se, včetně inicializačních výrazů, v pořadí odleva doprava (§9.4.4.5).
Poznámka: Pro local_variable_declaration nenastává jako for_initializer (§13.9.4) nebo resource_acquisition (§13.14) toto pořadí zleva doprava odpovídá každému deklarátoru, který je v samostatném local_variable_declaration. Příklad:
void F() { int x = 1, y, z = x * 2; }
odpovídá:
void F() { int x = 1; int y; int z = x * 2; }
koncová poznámka
Hodnota místní proměnné se získá ve výrazu pomocí simple_name (§12.8.4). Místní proměnná musí být rozhodně přiřazena (§9.4) v každém místě, kde je získána jeho hodnota. Každá místní proměnná zavedená local_variable_declaration je zpočátku nepřiřazena (§9.4.3). Pokud deklarátor má inicializační výraz, je zavedená místní proměnná klasifikována jako přiřazená na konci deklarátoru (§9.4.4.5).
Rozsah místní proměnné zavedené local_variable_declaration je definován takto (§7.7):
- Pokud k prohlášení dojde jako for_initializer pak je rozsahem for_initializer, for_condition, for_iterator a embedded_statement (§13.9.4);
- Pokud se prohlášení vyskytuje jako resource_acquisition pak je rozsahem nejkrajnější blok sémanticky ekvivalentního rozšíření using_statement (§13.14);
- V opačném případě je obor blokem, ve kterém se deklarace vyskytuje.
Jedná se o chybu odkazující na místní proměnnou podle názvu v textové pozici, která předchází deklarátoru nebo v rámci jakéhokoli inicializačního výrazu v deklarátoru. V oboru místní proměnné se jedná o chybu v době kompilace, která deklaruje jinou místní proměnnou, místní funkci nebo konstantu se stejným názvem.
Kontext ref-safe-context (§9.7.2) místní proměnné ref je kontext ref-safe jeho inicializace variable_reference. Kontext ref-safe-context místních proměnných bez odkazu je blok deklarace.
13.6.2.2 Implicitně zadané deklarace místních proměnných
implicitly_typed_local_variable_declaration
: 'var' implicitly_typed_local_variable_declarator
| ref_kind 'var' ref_local_variable_declarator
;
implicitly_typed_local_variable_declarator
: identifier '=' expression
;
Implicitly_typed_local_variable_declaration zavádí jednu místní proměnnou, identifikátor. Výraz nebo variable_reference musí mít typ kompilačního času . T
První alternativa deklaruje proměnnou s počáteční hodnotou výrazu; její typ je T?
v případě, že T
je nenulový odkazový typ, jinak je T
jeho typ . Druhá alternativa deklaruje proměnnou ref s počáteční hodnotou ref
variable_reference; její typ je ref T?
v případě, že T
se jedná o nenulový odkazový typ, jinak je ref T
jeho typ . (ref_kind je popsáno v §15.6.1.)
Příklad:
var i = 5; var s = "Hello"; var d = 1.0; var numbers = new int[] {1, 2, 3}; var orders = new Dictionary<int,Order>(); ref var j = ref i; ref readonly var k = ref i;
Výše uvedené deklarace místní proměnné implicitně odpovídají následujícím explicitně zadaným deklaracím:
int i = 5; string s = "Hello"; double d = 1.0; int[] numbers = new int[] {1, 2, 3}; Dictionary<int,Order> orders = new Dictionary<int,Order>(); ref int j = ref i; ref readonly int k = ref i;
Následující jsou nesprávné implicitně zadané deklarace místních proměnných:
var x; // Error, no initializer to infer type from var y = {1, 2, 3}; // Error, array initializer not permitted var z = null; // Error, null does not have a type var u = x => x + 1; // Error, anonymous functions do not have a type var v = v++; // Error, initializer cannot refer to v itself
end example
13.6.2.3 Explicitně zadané deklarace místních proměnných
explicitly_typed_local_variable_declaration
: type explicitly_typed_local_variable_declarators
;
explicitly_typed_local_variable_declarators
: explicitly_typed_local_variable_declarator
(',' explicitly_typed_local_variable_declarator)*
;
explicitly_typed_local_variable_declarator
: identifier ('=' local_variable_initializer)?
;
local_variable_initializer
: expression
| array_initializer
;
Explicity_typed_local_variable_declaration zavádí jednu nebo více místních proměnných se zadaným typem.
Je-li k dispozici local_variable_initializer, musí být jeho typ vhodný podle pravidel jednoduchého přiřazení (§12.21.2) nebo inicializace matice (§17.7) a jeho hodnota je přiřazena jako počáteční hodnota proměnné.
13.6.2.4 Explicitně zadané deklarace místních proměnných odkaz
explicitly_typed_ref_local_variable_declaration
: ref_kind type ref_local_variable_declarators
;
ref_local_variable_declarators
: ref_local_variable_declarator (',' ref_local_variable_declarator)*
;
ref_local_variable_declarator
: identifier '=' 'ref' variable_reference
;
Inicializační variable_reference musí mít typ a musí splňovat stejné požadavky jako pro přiřazení odkazu (§12.21.3).
Pokud ref readonly
, deklarované identifikátory jsou odkazy na proměnné, které jsou považovány za jen pro čtení. V opačném případě jsou ref_kind ref
deklarovány identifikátory odkazy na proměnné, které je možné zapisovat.
Jedná se o chybu v době kompilace deklarování místní proměnné odkazu nebo proměnné ref struct
typu v rámci metody deklarované pomocí async
nebo v rámci iterátoru (§15.14).
13.6.3 Deklarace místních konstant
Local_constant_declaration deklaruje jednu nebo více místních konstant.
local_constant_declaration
: 'const' type constant_declarators
;
constant_declarators
: constant_declarator (',' constant_declarator)*
;
constant_declarator
: identifier '=' constant_expression
;
Typ local_constant_declaration určuje typ konstant zavedených deklarací. Za typem následuje seznam constant_declarators, z nichž každá představuje novou konstantu. Constant_declarator se skládá z identifikátoru, který pojmenuje konstantu následovanou tokenem "=
", za kterým následuje constant_expression (§12,23), který dává hodnotu konstanty.
Typ a constant_expression místní deklarace konstanty se řídí stejnými pravidly jako deklarace konstantního členu (§15.4).
Hodnota místní konstanty je získána ve výrazu pomocí simple_name (§12.8.4).
Obor místní konstanty je blok, ve kterém se deklarace vyskytuje. Jedná se o chybu odkazující na místní konstantu v textové pozici, která předchází konci jeho constant_declarator.
Místní deklarace konstanty, která deklaruje více konstant, je ekvivalentní více deklarací jednoduchých konstant se stejným typem.
13.6.4 Deklarace místní funkce
Local_function_declaration deklaruje místní funkci.
local_function_declaration
: local_function_modifier* return_type local_function_header
local_function_body
| ref_local_function_modifier* ref_kind ref_return_type
local_function_header ref_local_function_body
;
local_function_header
: identifier '(' parameter_list? ')'
| identifier type_parameter_list '(' parameter_list? ')'
type_parameter_constraints_clause*
;
local_function_modifier
: ref_local_function_modifier
| 'async'
;
ref_local_function_modifier
: 'static'
| unsafe_modifier // unsafe code support
;
local_function_body
: block
| '=>' null_conditional_invocation_expression ';'
| '=>' expression ';'
;
ref_local_function_body
: block
| '=>' 'ref' variable_reference ';'
;
Gramatická poznámka: Při uznání local_function_body, pokud platí jak null_conditional_invocation_expression, tak alternativy výrazu, pak se zvolí první možnost. (§15.6.1)
Příklad: Pro místní funkce existují dva běžné případy použití: metody iterátoru a asynchronní metody. V metodách iterátoru jsou všechny výjimky pozorovány pouze při volání kódu, který vyčísluje vrácenou sekvenci. V asynchronníchmetodch Následující příklad ukazuje oddělení ověření parametru od implementace iterátoru pomocí místní funkce:
public static IEnumerable<char> AlphabetSubset(char start, char end) { if (start < 'a' || start > 'z') { throw new ArgumentOutOfRangeException(paramName: nameof(start), message: "start must be a letter"); } if (end < 'a' || end > 'z') { throw new ArgumentOutOfRangeException(paramName: nameof(end), message: "end must be a letter"); } if (end <= start) { throw new ArgumentException( $"{nameof(end)} must be greater than {nameof(start)}"); } return AlphabetSubsetImplementation(); IEnumerable<char> AlphabetSubsetImplementation() { for (var c = start; c < end; c++) { yield return c; } } }
end example
Pokud není uvedeno jinak, sémantika všech gramatických prvků je stejná jako u method_declaration (§15.6.1), přečteno v kontextu místní funkce místo metody.
Identifikátor local_function_declaration musí být v deklarovaném rozsahu bloku jedinečný, včetně všech uzavřených prostorů deklarace místních proměnných. Jedním z důsledků je to, že přetížené local_function_declarations nejsou povoleny.
Local_function_declaration může obsahovat jeden async
modifikátor (§15.15) a jeden unsafe
modifikátor (§23.1). Pokud prohlášení obsahuje async
modifikátor, musí být void
návratový «TaskType»
typ nebo typ (§15.15.1). Pokud deklarace obsahuje static
modifikátor, je to statická místní funkce, jinak se jedná o nestatické místní funkce. Jedná se o chybu v době kompilace pro type_parameter_list nebo parameter_list, která obsahuje atributy. Pokud je místní funkce deklarována v nebezpečném kontextu (§23.2), může místní funkce obsahovat nebezpečný kód, i když deklarace místní funkce neobsahuje unsafe
modifikátor.
Místní funkce je deklarována v oboru bloku. Nestatická místní funkce může zachytit proměnné z nadřazeného oboru, zatímco statická místní funkce nesmí (takže nemá přístup k uzavření místních hodnot, parametrů, nestatických místních funkcí nebo this
). Jedná se o chybu v době kompilace, pokud zachycená proměnná čte tělo nestatické místní funkce, ale není rozhodně přiřazena před každým voláním funkce. Kompilátor určí, které proměnné jsou jednoznačně přiřazeny při vrácení (§9.4.4.33).
Pokud je typ this
struktury typu, jedná se o chybu kompilace těla místní funkce pro přístup this
. To platí, zda je přístup explicitní (jako v this.x
) nebo implicitní (jako v případě, kde x
x
je členem instance struktury). Toto pravidlo zakáže pouze takový přístup a nemá vliv na to, zda vyhledávání členů vede k členu struktury.
Jedná se o chybu v době kompilace pro tělo místní funkce, která obsahuje goto
příkaz, break
příkaz nebo continue
příkaz, jehož cíl je mimo tělo místní funkce.
Poznámka: výše uvedená pravidla pro
this
anonymní funkce agoto
zrcadlí je v §12.19.3. koncová poznámka
Místní funkce může být volána z lexikálního bodu před jeho deklarací. Jedná se však o chybu v době kompilace, která má být deklarována lexicky před deklarací proměnné použité v místní funkci (§7.7).
Jedná se o chybu v době kompilace pro místní funkci, která deklaruje parametr, parametr typu nebo místní proměnnou se stejným názvem, který je deklarován v jakémkoli uzavřeném prostoru deklarace místní proměnné.
Místní těla funkcí jsou vždy dosažitelná. Koncový bod deklarace místní funkce je dostupný, pokud je dostupný počáteční bod deklarace místní funkce.
Příklad: V následujícím příkladu je tělo
L
dosažitelné, i když počáteční bodL
není dosažitelný. Vzhledem k tomu, že počáteční bodL
není dostupný, příkaz následující za koncovým bodemL
není dostupný:class C { int M() { L(); return 1; // Beginning of L is not reachable int L() { // The body of L is reachable return 2; } // Not reachable, because beginning point of L is not reachable return 3; } }
Jinými slovy, umístění deklarace místní funkce nemá vliv na dosažitelnost žádných příkazů v obsahující funkci. end example
Pokud je dynamic
typ argumentu pro místní funkci , funkce, která má být volána, musí být vyřešena v době kompilace, nikoli za běhu.
Místní funkce se nesmí používat ve stromu výrazů.
Statická místní funkce
- Může odkazovat na statické členy, parametry typu, definice konstant a statické místní funkce z nadřazeného oboru.
- Nesmí odkazovat
this
anibase
na členy instancí z implicitníhothis
odkazu ani z místních proměnných, parametrů nebo nestatických místních funkcí z nadřazeného oboru. Všechny tyto možnosti jsou však ve výrazunameof()
povolené.
13.7 Příkazy výrazů
Expression_statement vyhodnotí daný výraz. Hodnota vypočítaná výrazem (pokud existuje) se zahodí.
expression_statement
: statement_expression ';'
;
statement_expression
: null_conditional_invocation_expression
| invocation_expression
| object_creation_expression
| assignment
| post_increment_expression
| post_decrement_expression
| pre_increment_expression
| pre_decrement_expression
| await_expression
;
Ne všechny výrazy jsou povolené jako příkazy.
Poznámka: Zejména výrazy
x + y
jako ax == 1
, které pouze vypočítávají hodnotu (která bude zahozena), nejsou povoleny jako příkazy. koncová poznámka
Spuštění expression_statement vyhodnotí obsažený výraz a pak přenese řízení na koncový bod expression_statement. Koncový bod expression_statement je dostupný, pokud je tato expression_statement dosažitelná.
13.8 Příkazy výběru
13.8.1 Obecné
Příkazy výběru vyberou jeden z několika možných příkazů pro spuštění na základě hodnoty některého výrazu.
selection_statement
: if_statement
| switch_statement
;
13.8.2 Příkaz if
Příkaz if
vybere příkaz pro spuštění na základě hodnoty logického výrazu.
if_statement
: 'if' '(' boolean_expression ')' embedded_statement
| 'if' '(' boolean_expression ')' embedded_statement
'else' embedded_statement
;
Část else
je přidružena k lexicky nejbližšímu předchozímu if
, který je povolen syntaxí.
Příklad: Proto příkaz
if
formulářeif (x) if (y) F(); else G();
je ekvivalentem
if (x) { if (y) { F(); } else { G(); } }
end example
Příkaz if
se provede následujícím způsobem:
- Vyhodnocuje se boolean_expression (§12.24).
- Pokud logický výraz vrátí
true
hodnotu , ovládací prvek se přenese na první vložený příkaz. Když a pokud ovládací prvek dosáhne koncového bodu tohoto příkazu, ovládací prvek se přenese na koncový bodif
příkazu. - Pokud logický výraz získá
false
hodnotu a pokudelse
je součástí, ovládací prvek se přenese do druhého vloženého příkazu. Když a pokud ovládací prvek dosáhne koncového bodu tohoto příkazu, ovládací prvek se přenese na koncový bodif
příkazu. - Pokud logický výraz vrátí
false
hodnotu a pokudelse
část není k dispozici, ovládací prvek se převede na koncový bodif
příkazu.
První vložený příkaz if
příkazu je dostupný, pokud if
je příkaz dostupný a logický výraz nemá konstantní hodnotu false
.
Druhý vložený příkaz if
příkazu, pokud je k dispozici, je dosažitelný, pokud if
je příkaz dostupný a logický výraz nemá konstantní hodnotu true
.
Koncový bod if
příkazu je dosažitelný, pokud je dostupný koncový bod alespoň jednoho z jeho vložených příkazů. Koncový bod if
příkazu bez části else
je navíc dosažitelný, pokud if
je příkaz dostupný a logický výraz nemá konstantní hodnotu true
.
13.8.3 Příkaz switch
Příkaz switch
vybere pro spuštění seznam příkazů s přidruženým popiskem přepínače, který odpovídá hodnotě výrazu switch.
switch_statement
: 'switch' '(' expression ')' switch_block
;
switch_block
: '{' switch_section* '}'
;
switch_section
: switch_label+ statement_list
;
switch_label
: 'case' pattern case_guard? ':'
| 'default' ':'
;
case_guard
: 'when' expression
;
Switch_statement se skládá z klíčového slova switch
, následovaného závorkou výrazu (označovaného jako výraz switch), následovaného switch_block. Switch_block se skládá z nuly nebo více switch_sections uzavřených ve složených závorkách. Každý switch_section se skládá z jednoho nebo více switch_labelnásledovaných statement_list (§13.3.2). Každý switch_label obsahující case
má přidružený vzor (§11), proti kterému se testuje hodnota výrazu přepínače. Je-li case_guard přítomna, jeho výraz se implicitně konvertibilní na typ bool
a tento výraz se vyhodnotí jako další podmínka, aby byl případ považován za splněný.
Řídicí typswitch
příkazu je vytvořen výrazem switch.
- Pokud je
sbyte
typ výrazu switch , , ,byte
short
,ushort
int
uint
long
, ,ulong
,char
,bool
string
nebo enum_type, nebo pokud se jedná o typ hodnoty nullable odpovídající jednomu z těchto typů, pak se jedná o řídicí typswitch
příkazu. - V opačném případě, pokud přesně jeden uživatelem definovaný implicitní převod existuje z typu výrazu switch na jeden z následujících možných řídicích typů:
sbyte
, ,byte
short
,ushort
, ,int
, ,uint
,long
ulong
char
, ,string
nebo, nullable typ hodnoty odpovídající jednomu z těchto typů, pak převedený typ je řídí typswitch
příkazu. - V opačném případě je řídicím typem
switch
příkazu typ výrazu switch. Pokud takový typ neexistuje, jedná se o chybu.
Příkaz může obsahovat maximálně jeden default
popisek switch
.
Jedná se o chybu, pokud vzor jakéhokoli popisku přepínače není použitelný (§11.2.1) pro typ vstupního výrazu.
Jedná se o chybu, pokud je vzor jakéhokoli popisku přepínače dílčím součtem (§11.3) množinou vzorů dřívějších popisků přepínače příkazu přepínače, které nemají kryt případu nebo jejichž případová ochrana je konstantní výraz s hodnotou true.
Příklad:
switch (shape) { case var x: break; case var _: // error: pattern subsumed, as previous case always matches break; default: break; // warning: unreachable, all possible values already handled. }
end example
Příkaz switch
se provede následujícím způsobem:
- Výraz switch se vyhodnotí a převede na typ řízení.
- Ovládací prvek se přenese podle hodnoty převedeného výrazu přepínače:
- Lexicky první vzor v sadě
case
popisků ve stejnémswitch
příkazu, který odpovídá hodnotě výrazu switch, a pro který výraz stráže chybí nebo je vyhodnocen jako true, způsobí, že ovládací prvek se přenese do seznamu příkazů za odpovídajícímcase
popiskem. - Pokud je popisek k dispozici,
default
ovládací prvek se přenese do seznamu příkazů zadefault
popiskem. - Jinak se ovládací prvek přenese na koncový bod
switch
příkazu.
- Lexicky první vzor v sadě
Poznámka: Pořadí, ve kterém se vzory shodují za běhu, není definováno. Kompilátor je povolený (ale nevyžaduje) porovnávání vzorů mimo pořadí a opakované použití výsledků již spárovaných vzorů k výpočtu výsledku porovnávání jiných vzorů. Kompilátor je však nutný k určení prvního vzoru z hlediska lexikální, který odpovídá výrazu a pro nějž zabezpečovací podmínka buď chybí, nebo se vyhodnotí jako
true
. koncová poznámka
Pokud je koncový bod seznamu příkazů oddílu přepínače dostupný, dojde k chybě v době kompilace. To se označuje jako pravidlo "žádný pád".
Příklad: Příklad
switch (i) { case 0: CaseZero(); break; case 1: CaseOne(); break; default: CaseOthers(); break; }
je platný, protože žádný oddíl přepínače nemá dosažitelný koncový bod. Na rozdíl od jazyka C a C++ není spuštění oddílu přepínače povoleno "projít" do dalšího oddílu přepínače a příklad
switch (i) { case 0: CaseZero(); case 1: CaseZeroOrOne(); default: CaseAny(); }
výsledkem je chyba v době kompilace. Je-li provedení oddílu přepínače následované provedením jiného oddílu přepínače, použije se explicitní
goto case
nebogoto default
prohlášení:switch (i) { case 0: CaseZero(); goto case 1; case 1: CaseZeroOrOne(); goto default; default: CaseAny(); break; }
end example
V switch_section je povoleno více popisků.
Příklad: Příklad
switch (i) { case 0: CaseZero(); break; case 1: CaseOne(); break; case 2: default: CaseTwo(); break; }
je platný. Příklad neporušuje pravidlo "žádný pád", protože popisky
case 2:
adefault:
jsou součástí stejného switch_section.end example
Poznámka: Pravidlo "bez propadnutí" zabraňuje běžné třídě chyb, ke kterým dochází v jazyce C a C++ při
break
náhodném vynechání příkazů. Například oddíly výše uvedenéhoswitch
příkazu se dají vrátit zpět, aniž by to ovlivnilo chování příkazu:switch (i) { default: CaseAny(); break; case 1: CaseZeroOrOne(); goto default; case 0: CaseZero(); goto case 1; }
koncová poznámka
Poznámka: Seznam příkazů oddílu switch obvykle končí na
break
,goto case
nebogoto default
příkaz, ale jakýkoli konstruktor, který vykreslí koncový bod seznamu příkazů nedostupný, je povolen. Napříkladwhile
příkaz řízený logickým výrazemtrue
se nikdy nedosahuje koncového bodu. Podobně příkazthrow
nebo příkazreturn
vždy přenáší řízení jinde a nikdy nedosáhne svého koncového bodu. Následující příklad je tedy platný:switch (i) { case 0: while (true) { F(); } case 1: throw new ArgumentException(); case 2: return; }
koncová poznámka
Příklad: Typ
switch
řízení příkazu může být typstring
. Příklad:void DoCommand(string command) { switch (command.ToLower()) { case "run": DoRun(); break; case "save": DoSave(); break; case "quit": DoQuit(); break; default: InvalidCommand(command); break; } }
end example
Poznámka: Podobně jako operátory rovnosti řetězců (§12.12.8)
switch
se v příkazu rozlišují malá a velká písmena a provede daný oddíl přepínače pouze v případě, že řetězec výrazu switch přesně odpovídá konstantěcase
popisku. end note When the governing type of aswitch
statement isstring
or a nullable value type, the valuenull
is permitted as acase
label constant.
Statement_list switch_block mohou obsahovat prohlášení (§13.6). Obor místní proměnné nebo konstanty deklarované v bloku přepínače je blok přepínače.
Popisek přepínače je dostupný, pokud platí alespoň jedna z následujících možností:
- Výraz switch je konstantní hodnota a buď
- popisek je
case
štítek, jehož vzor by se shodoval (§11.2.1), a stráž popisku buď chybí, nebo není konstantní výraz s hodnotou false; - je
default
to popisek a žádný oddíl přepínače neobsahuje popisek případu, jehož vzor by odpovídal dané hodnotě, a jehož stráž chybí nebo konstantní výraz s hodnotou true.
- popisek je
- Výraz switch není konstantní hodnotou a ani
- popisek je
case
bez stráže nebo s stráží, jejíž hodnota není konstantní nepravda; nebo - je
default
to popisek a- sada vzorů, které se objevují v případech příkazu přepínače, které nemají stráže nebo mají stráže, jejichž hodnota je konstantní pravda, není vyčerpávající (§11.4) pro typ řízení přepínače; nebo
- přepínač řídící typ je typ s možnou hodnotou null a sada vzorů, které se zobrazují mezi případy příkazu switch, které nemají stráže nebo mají stráže, jejichž hodnota je konstanta true neobsahuje vzor, který by odpovídal hodnotě
null
.
- popisek je
- Popisek přepínače je odkazován dosažitelným
goto case
nebogoto default
příkazem.
Seznam příkazů daného oddílu přepínače je dostupný, pokud switch
je příkaz dostupný a oddíl přepínače obsahuje popisek dosažitelného přepínače.
Koncový bod switch
příkazu je dostupný, pokud je příkaz switch dostupný a alespoň jedna z následujících hodnot je pravdivá:
- Příkaz
switch
obsahuje dostupnýbreak
příkaz, který příkaz ukončíswitch
. - Žádný
default
popisek není k dispozici a ani- Výraz switch je nekontinutní hodnota a sada vzorů, které se objevují mezi případy příkazu switch, které nemají stráže nebo mají stráže, jejichž hodnota je konstanta true, není vyčerpávající (§11.4) pro typ řízení přepínače.
- Výraz switch je nekontinutní hodnota typu s možnou hodnotou null a mezi případy příkazu switch, které nemají stráže nebo mají stráže, jejichž hodnota je konstanta true, by odpovídala hodnotě
null
. - Výraz switch je konstantní hodnota a žádný
case
popisek bez stráže nebo jehož stráž je konstanta true by odpovídala této hodnotě.
Příklad: Následující kód ukazuje stručné použití
when
klauzule:static object CreateShape(string shapeDescription) { switch (shapeDescription) { case "circle": return new Circle(2); … case var o when string.IsNullOrWhiteSpace(o): return null; default: return "invalid shape description"; } }
Velikost písmen var odpovídá
null
prázdnému řetězci nebo libovolnému řetězci, který obsahuje pouze prázdné znaky. end example
13.9 Příkazy iterace
13.9.1 Obecné
Příkazy iterace opakovaně spouštějí vložený příkaz.
iteration_statement
: while_statement
| do_statement
| for_statement
| foreach_statement
;
13.9.2 Příkaz while
Příkaz while
podmíněně spustí vložený příkaz nulou nebo vícekrát.
while_statement
: 'while' '(' boolean_expression ')' embedded_statement
;
Příkaz while
se provede následujícím způsobem:
- Vyhodnocuje se boolean_expression (§12.24).
- Pokud logický výraz vrátí hodnotu
true
, ovládací prvek se přenese do vloženého příkazu. Když a pokud ovládací prvek dosáhne koncového bodu vloženého příkazu (pravděpodobně ze spuštěnícontinue
příkazu), ovládací prvek se přenese na začátekwhile
příkazu. - Pokud logický výraz vrátí
false
hodnotu , ovládací prvek se převede na koncový bodwhile
příkazu.
V rámci vloženého prohlášení while
prohlášení break
lze k převodu řízení na koncový bod příkazu použít příkaz (while
) a continue
příkaz (§13.10.3) k převodu řízení na koncový bod vloženého příkazu (tedy k provedení další iterace while
příkazu).
Vložený příkaz while
příkazu je dostupný, pokud while
je příkaz dostupný a logický výraz nemá konstantní hodnotu false
.
Koncový bod while
prohlášení je dosažitelný, pokud platí alespoň jedna z následujících možností:
- Příkaz
while
obsahuje dostupnýbreak
příkaz, který příkaz ukončíwhile
. - Příkaz
while
je dostupný a logický výraz nemá konstantní hodnotutrue
.
13.9.3 Příkaz do
Příkaz do
podmíněně spustí vložený příkaz jednou nebo vícekrát.
do_statement
: 'do' embedded_statement 'while' '(' boolean_expression ')' ';'
;
Příkaz do
se provede následujícím způsobem:
- Ovládací prvek se přenese do vloženého příkazu.
- Pokud a pokud ovládací prvek dosáhne koncového bodu vloženého příkazu (pravděpodobně z provedení
continue
příkazu), vyhodnotí se boolean_expression (§12.24). Pokud logický výraz vrátítrue
hodnotu , ovládací prvek se převede na začátekdo
příkazu. Jinak se ovládací prvek přenese na koncový boddo
příkazu.
V rámci vloženého prohlášení do
prohlášení break
lze k převodu řízení na koncový bod příkazu použít příkaz (do
) a continue
příkaz (§13.10.3) k převodu řízení na koncový bod vloženého příkazu (tedy k provedení další iterace do
příkazu).
Vložený příkaz do
příkazu je dostupný, pokud do
je příkaz dostupný.
Koncový bod do
prohlášení je dosažitelný, pokud platí alespoň jedna z následujících možností:
- Příkaz
do
obsahuje dostupnýbreak
příkaz, který příkaz ukončído
. - Koncový bod vloženého příkazu je dostupný a logický výraz nemá konstantní hodnotu
true
.
13.9.4 Příkaz for
Příkaz for
vyhodnotí sekvenci inicializačních výrazů a poté, když je podmínka pravdivá, opakovaně spustí vložený příkaz a vyhodnotí posloupnost výrazů iterace.
for_statement
: 'for' '(' for_initializer? ';' for_condition? ';' for_iterator? ')'
embedded_statement
;
for_initializer
: local_variable_declaration
| statement_expression_list
;
for_condition
: boolean_expression
;
for_iterator
: statement_expression_list
;
statement_expression_list
: statement_expression (',' statement_expression)*
;
For_initializer, pokud existují, se skládá z local_variable_declaration (§13.6.2) nebo seznamu statement_expressions (§13.7) oddělených čárkami. Obor místní proměnné deklarované for_initializer je for_initializer, for_condition, for_iterator a embedded_statement.
For_condition, pokud existují, je boolean_expression (§12.24).
For_iterator, pokud existují, se skládá ze seznamu statement_expressions (§13.7) oddělených čárkami.
Příkaz for
se provede následujícím způsobem:
- Pokud existuje for_initializer, inicializátory proměnných nebo výrazy příkazů se spustí v pořadí, ve kterém jsou zapsány. Tento krok se provádí pouze jednou.
- Pokud existuje for_condition, vyhodnotí se.
-
Pokud for_condition není k dispozici nebo pokud je vyhodnocení výnosu
true
, ovládací prvek je převeden na vložený příkaz. Když a pokud ovládací prvek dosáhne koncového bodu vloženého příkazu (pravděpodobně z prováděnícontinue
příkazu), výrazy for_iterator, pokud existují, se vyhodnocují v posloupnosti a pak se provede další iterace, počínaje vyhodnocením for_condition v kroku výše. -
Pokud je for_condition přítomna a vyhodnocení se provede
false
, řízení se přenese na koncový bodfor
příkazu.
V rámci vloženého prohlášení lze příkaz (for
) použít k převodu řízení na koncový bod break
příkazu (tedy k ukončení iterace vloženého příkazu) a k převodu řízení na koncový bod vloženého příkazu (tedy k provádění for
a provádění další iterace continue
příkazu, lze použít příkaz (§13.10.3).for
počínaje for_condition).
Vložený příkaz for
příkazu je dostupný, pokud je splněna jedna z následujících možností:
- Příkaz
for
je dostupný a není k dispozici žádná for_condition . - Příkaz
for
je dostupný a for_condition je přítomen a nemá konstantní hodnotufalse
.
Koncový bod for
prohlášení je dosažitelný, pokud platí alespoň jedna z následujících možností:
- Příkaz
for
obsahuje dostupnýbreak
příkaz, který příkaz ukončífor
. - Příkaz
for
je dostupný a for_condition je přítomen a nemá konstantní hodnotutrue
.
13.9.5 Příkaz foreach
Příkaz foreach
vyčíslí prvky kolekce a spustí vložený příkaz pro každý prvek kolekce.
foreach_statement
: 'foreach' '(' ref_kind? local_variable_type identifier 'in'
expression ')' embedded_statement
;
Local_variable_type a identifikátor příkazu foreach deklaruje proměnnou iterace příkazu.
var
Pokud je identifikátor uveden jako local_variable_type a žádný typ pojmenovaný var
není v oboru, iterační proměnná je označena jako implicitně zadaná iterační proměnná a jeho typ je považován za typ foreach
prvku příkazu, jak je uvedeno níže.
Pokud foreach_statement obsahuje obojí nebo ani jednoref
, readonly
iterační proměnná označuje proměnnou, která je považována za jen pro čtení. Jinak pokud foreach_statement obsahuje ref
bez readonly
, iterační proměnná označuje proměnnou, která se má zapisovat.
Iterační proměnná odpovídá místní proměnné s oborem, který se vztahuje na vložený příkaz. Během provádění foreach
příkazu představuje proměnná iterace prvek kolekce, pro který se právě provádí iterace. Pokud iterační proměnná označuje proměnnou určenou jen pro čtení, dojde k chybě v době kompilace, pokud se vložený příkaz pokusí ho upravit (prostřednictvím přiřazení nebo ++
--
operátorů) nebo ho předat jako odkaz nebo výstupní parametr.
V následujícím příkladu pro stručnost, IEnumerable
, IEnumerator
IEnumerable<T>
, a IEnumerator<T>
odkazovat na odpovídající typy v oborech názvů System.Collections
a System.Collections.Generic
.
Zpracování kompilace foreach
příkazu nejprve určuje typ kolekce, typ enumerátoru a typ iterace výrazu. Toto stanovení pokračuje následujícím způsobem:
- Pokud je typ
X
výrazutyp pole, existuje implicitní převod odkazu z X naIEnumerable
rozhraní (protožeSystem.Array
implementuje toto rozhraní). Typ kolekce jeIEnumerable
rozhraní, enumerátor typ jeIEnumerator
rozhraní a typ iterace je typ prvku typuX
pole . - Pokud je typ
X
výrazudynamic
na rozhraní (IEnumerable
). Typ kolekce jeIEnumerable
rozhraní a typ enumerátoruIEnumerator
je rozhraní.var
Pokud je identifikátor uveden jako local_variable_type pak jedynamic
typ iterace , jinak jeobject
. - V opačném případě určete, zda typ
X
má odpovídajícíGetEnumerator
metodu:- Proveďte vyhledávání členů u typu
X
s identifikátoremGetEnumerator
a bez argumentů typu. Pokud vyhledávání členů nevytváří shodu nebo vytvoří nejednoznačnost nebo vytvoří shodu, která není skupinou metod, zkontrolujte výčtové rozhraní, jak je popsáno níže. Doporučuje se vydat upozornění, pokud vyhledávání členů vytvoří cokoli kromě skupiny metod nebo žádné shody. - K vyřešení přetížení použijte výslednou skupinu metod a prázdný seznam argumentů. Pokud výsledkem řešení přetížení nejsou žádné použitelné metody, výsledkem je nejednoznačnost nebo výsledkem je jedna nejlepší metoda, ale tato metoda je buď statická nebo není veřejná, zkontrolujte výčet rozhraní, jak je popsáno níže. Doporučuje se vydat upozornění, pokud rozlišení přetížení vytvoří cokoli kromě jednoznačné metody veřejné instance nebo žádné použitelné metody.
- Pokud návratový
E
typGetEnumerator
metody není třída, struktura nebo typ rozhraní, vytvoří se chyba a neprovedou se žádné další kroky. - Vyhledávání členů se provádí
E
s identifikátoremCurrent
a bez argumentů typu. Pokud vyhledávání člena nevygeneruje žádnou shodu, výsledek je chyba nebo je výsledek cokoli kromě vlastnosti veřejné instance, která umožňuje čtení, vytvoří se chyba a neprovedou se žádné další kroky. - Vyhledávání členů se provádí
E
s identifikátoremMoveNext
a bez argumentů typu. Pokud vyhledávání členů nevygeneruje žádnou shodu, výsledkem je chyba nebo je výsledkem cokoli kromě skupiny metod, vytvoří se chyba a neprovedou se žádné další kroky. - Rozlišení přetížení se provádí ve skupině metod s prázdným seznamem argumentů. Pokud má za následek řešení přetížení žádné použitelné metody, vede k nejednoznačnosti nebo vede k jediné nejlepší metodě, ale tato metoda je buď statická nebo není veřejná, nebo její návratový typ není
bool
, vytvoří se chyba a neprovedou se žádné další kroky. - Typ kolekce je
X
, enumerátor typ jeE
a iterační typ je typCurrent
vlastnosti. VlastnostCurrent
může obsahovatref
modifikátor, v takovém případě vrácený výraz je variable_reference (§9.5), který je volitelně jen pro čtení.
- Proveďte vyhledávání členů u typu
- V opačném případě zkontrolujte výčet rozhraní:
- Pokud mezi všemi typy
Tᵢ
, pro které existuje implicitní převod zX
IEnumerable<Tᵢ>
, existuje jedinečný typT
, kterýT
nenídynamic
a pro všechny ostatníTᵢ
existuje implicitní převod zIEnumerable<T>
IEnumerable<Tᵢ>
, pak typ kolekce je rozhraníIEnumerable<T>
, enumerátor typ je rozhraníIEnumerator<T>
a iterační typ jeT
. - Jinak platí, že pokud existuje více takových typů
T
, dojde k chybě a neprovedou se žádné další kroky. - V opačném případě, pokud existuje implicitní převod z
X
System.Collections.IEnumerable
rozhraní, pak typ kolekce je toto rozhraní, enumerátor typ je rozhraníSystem.Collections.IEnumerator
a iterační typ jeobject
. - V opačném případě se vytvoří chyba a neprovedou se žádné další kroky.
- Pokud mezi všemi typy
Výše uvedené kroky, pokud jsou úspěšné, jednoznačně vytvoří typ C
kolekce , typ E
enumerátoru a typ T
iterace , ref T
nebo ref readonly T
. Příkaz foreach
formuláře
foreach (V v in x) «embedded_statement»
se pak rovná:
{
E e = ((C)(x)).GetEnumerator();
try
{
while (e.MoveNext())
{
V v = (V)(T)e.Current;
«embedded_statement»
}
}
finally
{
... // Dispose e
}
}
Proměnná e
není viditelná ani přístupná pro výraz x
nebo vložený příkaz nebo jakýkoli jiný zdrojový kód programu. Proměnná v
je v vloženém příkazu jen pro čtení. Pokud neexistuje explicitní převod (§10.3) z T
(typ iterace) na V
( local_variable_type v foreach
prohlášení), dojde k chybě a neprovedou se žádné další kroky.
Je-li proměnná iterace referenční proměnnou (§9.7), prohlášení foreach
formuláře
foreach (ref V v in x) «embedded_statement»
se pak rovná:
{
E e = ((C)(x)).GetEnumerator();
try
{
while (e.MoveNext())
{
ref V v = ref e.Current;
«embedded_statement»
}
}
finally
{
... // Dispose e
}
}
Proměnná e
není viditelná ani přístupná pro výraz x
nebo vložený příkaz nebo jakýkoli jiný zdrojový kód programu. Referenční proměnná v
je ve vloženém příkazu pro čtení i zápis, ale v
nesmí být znovu přiřazena odkazem (§12.21.3). Pokud neexistuje převod identity (§10.2.2) z T
(typu iterace) do V
( local_variable_type v foreach
příkazu), dojde k chybě a neprovedou se žádné další kroky.
Příkaz foreach
formuláře foreach (ref readonly V v in x) «embedded_statement»
má podobný ekvivalentní formulář, ale referenční proměnná v
je ref readonly
ve vloženém příkazu, a proto nelze znovu přiřadit ani znovu přiřadit.
Poznámka: Pokud
x
má hodnotunull
,System.NullReferenceException
je vyvolán za běhu. koncová poznámka
Implementace je povolena k implementaci daného foreach_statement odlišně, například z důvodů výkonu, pokud je chování konzistentní s výše uvedeným rozšířením.
Umístění v
uvnitř while
smyčky je důležité pro to, jak je zachycen (§12.19.6.2) všemi anonymními funkcemi, ke kterým dochází v embedded_statement.
Příklad:
int[] values = { 7, 9, 13 }; Action f = null; foreach (var value in values) { if (f == null) { f = () => Console.WriteLine("First value: " + value); } } f();
Pokud
v
by byla v rozšířeném formuláři deklarována mimo smyčkuwhile
, byla by sdílena mezi všemi iteracemi a její hodnota zafor
smyčkou by byla konečná hodnota, což je to,13
co vyvoláníf
by se vytiskne. Místo toho, protože každá iterace má svou vlastní proměnnouv
, ta zachycenáf
v první iteraci bude i nadále obsahovat hodnotu7
, což je to, co se vytiskne. (Všimněte si, že starší verze jazyka C# deklarovanév
mimo smyčkuwhile
.)end example
Tělo finally
bloku je sestaveno podle následujících kroků:
Pokud existuje implicitní převod z
E
System.IDisposable
rozhraní, pakPokud
E
je nenulový typ hodnoty,finally
klauzule se rozbalí na sémantický ekvivalent:finally { ((System.IDisposable)e).Dispose(); }
finally
V opačném případě se klauzule rozšíří na sémantický ekvivalent:finally { System.IDisposable d = e as System.IDisposable; if (d != null) { d.Dispose(); } }
s tím rozdílem, že pokud
E
je typ hodnoty nebo parametr typu, který vytvoří instanci na typ hodnoty, nesmí dojít k převodue
naSystem.IDisposable
box.
Jinak pokud
E
je zapečetěný typ,finally
klauzule se rozbalí na prázdný blok:finally {}
finally
V opačném případě se klauzule rozbalí na:finally { System.IDisposable d = e as System.IDisposable; if (d != null) { d.Dispose(); } }
Místní proměnná d
není viditelná ani přístupná pro žádný uživatelský kód. Konkrétně není v konfliktu s žádnou jinou proměnnou, jejíž obor zahrnuje finally
blok.
Pořadí, ve kterém foreach
prochází prvky pole, je následující: U prvků jednorozměrných polí se prochází vzestupným pořadím indexu, počínaje indexem 0 a končí indexem Length – 1
. U multidimenzionálních polí se prvky procházejí tak, aby se indexy pravé dimenze nejprve zvýšily, pak další levá dimenze atd.
Příklad: Následující příklad vypíše každou hodnotu v dvojrozměrném poli v pořadí prvků:
class Test { static void Main() { double[,] values = { {1.2, 2.3, 3.4, 4.5}, {5.6, 6.7, 7.8, 8.9} }; foreach (double elementValue in values) { Console.Write($"{elementValue} "); } Console.WriteLine(); } }
Výstup vytvořený je následující:
1.2 2.3 3.4 4.5 5.6 6.7 7.8 8.9
end example
Příklad: V následujícím příkladu
int[] numbers = { 1, 3, 5, 7, 9 }; foreach (var n in numbers) { Console.WriteLine(n); }
typ
n
je odvozenint
být , iterační typnumbers
.end example
13.10 Příkazy jump
13.10.1 Obecné
Příkazy skoku bezpodmínečně přenesou řízení.
jump_statement
: break_statement
| continue_statement
| goto_statement
| return_statement
| throw_statement
;
Umístění, do kterého příkaz jump přenáší řízení, se nazývá cíl příkazu jump.
Když se příkaz skoku vyskytuje v bloku a cíl tohoto příkazu skoku je mimo tento blok, příkaz jump se říká, že má opustit blok. I když příkaz jumpu může přenést řízení z bloku, nemůže nikdy převést řízení do bloku.
Provádění příkazů skoků je složité přítomností interveningových try
příkazů. Pokud takové prohlášení chybí try
, příkaz skoku bezpodmínečně přenese kontrolu z příkazu skoku do cíle. V přítomnosti takových intervening try
příkazů je provádění složitější. Pokud příkaz jump ukončí jeden nebo více try
bloků s přidruženými finally
bloky, ovládací prvek se zpočátku přenese do finally
bloku nejvnitřnějšího try
příkazu. Když a pokud ovládací prvek dosáhne koncového finally
bodu bloku, ovládací prvek se přenese do finally
bloku dalšího ohraničujícího try
příkazu. Tento proces se opakuje, dokud finally
se nespustí bloky všech interveningových try
příkazů.
Příklad: V následujícím kódu
class Test { static void Main() { while (true) { try { try { Console.WriteLine("Before break"); break; } finally { Console.WriteLine("Innermost finally block"); } } finally { Console.WriteLine("Outermost finally block"); } } Console.WriteLine("After break"); } }
finally
bloky přidružené ke dvěmatry
příkazům se spustí před přenesením ovládacího prvku do cíle příkazu jump. Výstup vytvořený je následující:Before break Innermost finally block Outermost finally block After break
end example
13.10.2 Příkaz break
Příkaz break
ukončí nejbližší uzavření switch
, while
, do
, for
, nebo foreach
příkaz.
break_statement
: 'break' ';'
;
Cílem break
příkazu je koncový bod nejbližšího uzavření switch
, while
, do
, , for
nebo foreach
příkazu.
break
Pokud příkaz není uzavřen příkazem switch
, , while
do
, for
, nebo foreach
příkaz, dojde k chybě kompilace čas.
Pokud je v sobě vnořeno více switch
, while
, do
, for
nebo foreach
příkazy, break
příkaz se vztahuje pouze na nejvnitřnější příkaz. Pro přenos kontroly mezi více úrovněmi goto
vnoření se použije prohlášení (§13.10.4).
Prohlášení break
nemůže ukončit finally
blok (§13.11).
break
Pokud dojde k příkazu v finally
rámci bloku, cíl break
příkazu musí být ve stejném finally
bloku; jinak dojde k chybě v době kompilace.
Příkaz break
se provede následujícím způsobem:
- Pokud příkaz
break
ukončí jeden nebo vícetry
bloků s přidruženýmifinally
bloky, ovládací prvek se zpočátku přenese dofinally
bloku nejvnitřnějšíhotry
příkazu. Když a pokud ovládací prvek dosáhne koncovéhofinally
bodu bloku, ovládací prvek se přenese dofinally
bloku dalšího ohraničujícíhotry
příkazu. Tento proces se opakuje, dokudfinally
se nespustí bloky všech interveningovýchtry
příkazů. - Ovládací prvek se přenese do cíle
break
příkazu.
Vzhledem k tomu, že break
prohlášení bezpodmínečně přenáší kontrolu jinde, koncový bod break
příkazu není nikdy dostupný.
13.10.3 Příkaz continue
Příkaz continue
spustí novou iteraci nejbližšího uzavření while
, do
, for
, nebo foreach
příkazu.
continue_statement
: 'continue' ';'
;
Cílem continue
příkazu je koncový bod vloženého příkazu nejbližšího uzavřeného while
příkazu , do
, for
nebo foreach
příkazu.
continue
Pokud příkaz není uzavřen příkazem while
, do
, for
nebo foreach
příkaz, dojde k chybě kompilace v době kompilace.
Pokud je v while
sobě vnořeno více do
příkazů , for
, foreach
continue
příkaz se vztahuje pouze na nejvnitřnější příkaz. Pro přenos kontroly mezi více úrovněmi goto
vnoření se použije prohlášení (§13.10.4).
Prohlášení continue
nemůže ukončit finally
blok (§13.11).
continue
Pokud dojde k příkazu v finally
rámci bloku, cíl continue
příkazu musí být ve stejném finally
bloku; jinak dojde k chybě v době kompilace.
Příkaz continue
se provede následujícím způsobem:
- Pokud příkaz
continue
ukončí jeden nebo vícetry
bloků s přidruženýmifinally
bloky, ovládací prvek se zpočátku přenese dofinally
bloku nejvnitřnějšíhotry
příkazu. Když a pokud ovládací prvek dosáhne koncovéhofinally
bodu bloku, ovládací prvek se přenese dofinally
bloku dalšího ohraničujícíhotry
příkazu. Tento proces se opakuje, dokudfinally
se nespustí bloky všech interveningovýchtry
příkazů. - Ovládací prvek se přenese do cíle
continue
příkazu.
Vzhledem k tomu, že continue
prohlášení bezpodmínečně přenáší kontrolu jinde, koncový bod continue
příkazu není nikdy dostupný.
13.10.4 Příkaz goto
Příkaz goto
přenese řízení na příkaz, který je označen popiskem.
goto_statement
: 'goto' identifier ';'
| 'goto' 'case' constant_expression ';'
| 'goto' 'default' ';'
;
Cílem goto
příkazu identifikátoru je označený příkaz s daným popiskem. Pokud popisek s daným názvem v aktuálním členu funkce neexistuje nebo pokud goto
příkaz není v rozsahu popisku, dojde k chybě v době kompilace.
Poznámka: Toto pravidlo umožňuje použití
goto
příkazu k přenosu kontroly z vnořeného oboru, ale ne do vnořeného oboru. V příkladuclass Test { static void Main(string[] args) { string[,] table = { {"Red", "Blue", "Green"}, {"Monday", "Wednesday", "Friday"} }; foreach (string str in args) { int row, colm; for (row = 0; row <= 1; ++row) { for (colm = 0; colm <= 2; ++colm) { if (str == table[row,colm]) { goto done; } } } Console.WriteLine($"{str} not found"); continue; done: Console.WriteLine($"Found {str} at [{row}][{colm}]"); } } }
Příkaz
goto
slouží k přenosu kontroly z vnořeného oboru.koncová poznámka
Cílem goto case
příkazu je seznam příkazů v bezprostředně uzavřeném switch
příkazu (§13.8.3), který obsahuje case
popisek s konstantním vzorem dané konstantní hodnoty a bez ochrany.
goto case
Pokud příkaz není uzavřen příkazemswitch
, pokud nejbližší uzavřený switch
příkaz takový příkaz neobsahuje case
nebo pokud constant_expression není implicitně konvertibilní (§10.2) na typ řízení nejbližšího uzavřeného switch
příkazu, dojde k chybě v době kompilace.
Cílem goto default
příkazu je seznam příkazů v bezprostředně uzavřeném switch
prohlášení (§13.8.3), který obsahuje default
popisek.
goto default
Pokud příkaz není uzavřen příkazem switch
nebo pokud nejbližší uzavřený switch
příkaz neobsahuje default
popisek, dojde k chybě kompilace.
Prohlášení goto
nemůže ukončit finally
blok (§13.11). Pokud dojde k goto
příkazu v finally
rámci bloku, cíl goto
příkazu musí být ve stejném finally
bloku nebo jinak dojde k chybě v době kompilace.
Příkaz goto
se provede následujícím způsobem:
- Pokud příkaz
goto
ukončí jeden nebo vícetry
bloků s přidruženýmifinally
bloky, ovládací prvek se zpočátku přenese dofinally
bloku nejvnitřnějšíhotry
příkazu. Když a pokud ovládací prvek dosáhne koncovéhofinally
bodu bloku, ovládací prvek se přenese dofinally
bloku dalšího ohraničujícíhotry
příkazu. Tento proces se opakuje, dokudfinally
se nespustí bloky všech interveningovýchtry
příkazů. - Ovládací prvek se přenese do cíle
goto
příkazu.
Vzhledem k tomu, že goto
prohlášení bezpodmínečně přenáší kontrolu jinde, koncový bod goto
příkazu není nikdy dostupný.
13.10.5 Příkaz return
Příkaz return
vrátí ovládací prvek aktuálnímu volajícímu člena funkce, ve kterém se zobrazí návratový příkaz, volitelně vrací hodnotu nebo variable_reference (§9.5).
return_statement
: 'return' ';'
| 'return' expression ';'
| 'return' 'ref' variable_reference ';'
;
Return_statement bez výrazu se nazývá return-no-value; jeden obsahující výraz se nazývá return-by-ref; a jeden obsahující ref
pouze výraz se nazývá return-by-value.
Jedná se o chybu v době kompilace, kdy se používá návratová hodnota z metody deklarované jako return-by-value nebo return-by-ref (§15.6.1).
Jedná se o chybu v době kompilace, která používá metodu return-by-ref deklarovanou jako return-no-value nebo return-by-value.
Jedná se o chybu v době kompilace, která používá návrat po hodnotě z metody deklarované jako return-no-value nebo return-by-ref.
Jedná se o chybu v době kompilace, pokud výraz není variable_reference nebo je odkazem na proměnnou, jejíž kontext ref-safe není kontext volajícího (§9.7.2).
Jedná se o chybu v době kompilace, která používá metodu return-by-ref z metody deklarované s method_modifierasync
.
Člen funkce se říká, že vypočítá hodnotu , pokud se jedná o metodu vrácení po hodnotě (§15.6.11), přístupové objekty get vlastnosti nebo indexeru podle hodnoty nebo uživatelem definovaný operátor. Členy funkce, které jsou vráceny bez hodnoty, nevypočítají hodnotu a jsou metodami s efektivním návratovým typem void
, nastavte přístupové objekty vlastností a indexerů, přidejte a odeberte přístupové objekty událostí, konstruktory instancí, statické konstruktory a finalizátory. Členy funkce, které jsou návratem po ref, nevypočítají hodnotu.
Pro návrat po hodnotě musí existovat implicitní převod (§10.2) z typu výrazu na účinný návratový typ (§15.6.11) obsahujícího člena funkce. Pro vrácení podle odkazu musí existovat převod identity (§10.2.2) mezi typem výrazu a účinným návratovým typem obsahujícího člena funkce.
return
výrazy lze použít také v těle anonymních výrazů funkce (§12.19) a účastnit se určení, které převody existují pro tyto funkce (§10.7.1).
Jedná se o chybu v době kompilace, return
která se zobrazí v finally
bloku (§13.11).
Příkaz return
se provede následujícím způsobem:
- Pro návrat po hodnotě se výraz vyhodnotí a jeho hodnota se převede na efektivní návratový typ obsahující funkce implicitním převodem. Výsledkem převodu se stane výsledná hodnota vytvořená funkcí. Pro návrat po ref se výraz vyhodnotí a výsledek se klasifikuje jako proměnná. Pokud argument return-by-ref ohraničující metody obsahuje
readonly
, výsledná proměnná je jen pro čtení. -
return
Pokud je příkaz uzavřený jedním nebo vícetry
catch
bloky s přidruženýmifinally
bloky, ovládací prvek se zpočátku přenese dofinally
bloku nejvnitřnějšíhotry
příkazu. Když a pokud ovládací prvek dosáhne koncovéhofinally
bodu bloku, ovládací prvek se přenese dofinally
bloku dalšího ohraničujícíhotry
příkazu. Tento proces se opakuje, dokudfinally
nebudou provedeny bloky všech uzavřenýchtry
příkazů. - Pokud obsahující funkce není asynchronní funkce, vrátí se ovládací prvek volajícímu obsahující funkce spolu s výslednou hodnotou( pokud existuje).
- Pokud je obsahující funkce asynchronní funkce, vrátí se ovládací prvek aktuálnímu volajícímu a výsledná hodnota, pokud existuje, je zaznamenána v návratovém úkolu, jak je popsáno v (§15.15.3).
Vzhledem k tomu, že return
prohlášení bezpodmínečně přenáší kontrolu jinde, koncový bod return
příkazu není nikdy dostupný.
13.10.6 Příkaz throw
Příkaz throw
vyvolá výjimku.
throw_statement
: 'throw' expression? ';'
;
Příkaz throw
s výrazem vyvolá výjimku vytvořenou vyhodnocením výrazu. Výraz se implicitně převede na System.Exception
a výsledek vyhodnocení výrazu se převede na System.Exception
před vyvolání. Pokud je null
výsledkem převodu , System.NullReferenceException
je vyvolán místo toho.
Příkaz throw
bez výrazu lze použít pouze v catch
bloku, v takovém případě tento příkaz znovu vyvolá výjimku, která je aktuálně zpracována tímto catch
blokem.
Vzhledem k tomu, že throw
prohlášení bezpodmínečně přenáší kontrolu jinde, koncový bod throw
příkazu není nikdy dostupný.
Při vyvolání výjimky se ovládací prvek přenese do první catch
klauzule v uzavřeném try
příkazu, který může zpracovat výjimku. Proces, který probíhá od okamžiku vyvolání výjimky do bodu přenosu ovládacího prvku do vhodné obslužné rutiny výjimky, se označuje jako šíření výjimek. Šíření výjimky se skládá z opakovaného vyhodnocení následujících kroků, dokud catch
se nenajde klauzule, která odpovídá výjimce. V tomto popisu je bod vyvolání počátečním umístěním, ve kterém je vyvolán výjimka. Toto chování je uvedeno v §21.4.
V aktuálním členu funkce se prověří každý
try
příkaz, který uzavře bod vyvolání. Pro každý příkaz počínaje nejvnitřnějšímS
příkazemtry
a končícím vnějšímtry
příkazem se vyhodnocují následující kroky:-
try
Pokud blokS
ohraničuje bod vyvolání a pokudS
má jednu nebo vícecatch
klauzulí,catch
jsou klauzule zkoumány za účelem vyhledání vhodné obslužné rutiny pro výjimku. Prvnícatch
klauzule, která určuje typT
výjimky (nebo parametr typu, který v době běhu označuje typT
výjimky), aby typE
běhu odvozený zT
je považován za shodu. Pokud klauzule obsahuje filtr výjimek, objekt výjimky je přiřazen k proměnné výjimky a filtr výjimky je vyhodnocen.catch
Pokud klauzule obsahuje filtr výjimek, je tato klauzule považována za shodu,catch
pokud je filtr výjimky vyhodnocen jakotrue
. Obecnácatch
klauzule (§13.11) se považuje za shodu pro jakýkoli typ výjimky. Pokud se nachází odpovídajícícatch
klauzule, šíření výjimky se dokončí přenesením ovládacího prvku do bloku tétocatch
klauzule. - Jinak platí, že pokud
try
blok nebocatch
blokS
ohraničuje bod vyvolání a pokudS
obsahujefinally
blok, ovládací prvek se přenese dofinally
bloku.finally
Pokud blok vyvolá jinou výjimku, zpracování aktuální výjimky se ukončí. Jinak, pokud ovládací prvek dosáhne koncovéhofinally
bodu bloku, zpracování aktuální výjimky bude pokračovat.
-
Pokud obslužná rutina výjimky nebyla umístěna v vyvolání aktuální funkce, vyvolání funkce je ukončeno a nastane jedna z následujících situací:
Pokud je aktuální funkce nesynchronní, výše uvedené kroky se opakují pro volajícího funkce s bodem vyvolání bodu throw odpovídajícímu příkazu, ze kterého byl vyvolán člen funkce.
Pokud je aktuální funkce asynchronní a vrací úlohu, zaznamená se v návratovém úkolu výjimka, která je vložena do chybného nebo zrušeného stavu, jak je popsáno v §15.15.3.
Pokud je aktuální funkce asynchronní a
void
vracející se, je kontext synchronizace aktuálního vlákna oznámen, jak je popsáno v §15.15.4.
Pokud zpracování výjimek ukončí všechna vyvolání členů funkce v aktuálním vlákně, což znamená, že vlákno nemá pro výjimku žádnou obslužnou rutinu, vlákno se ukončí. Dopad takového ukončení je definován implementací.
13.11 Příkaz try
Tento try
příkaz poskytuje mechanismus pro zachycení výjimek, ke kterým dochází během provádění bloku. Příkaz navíc try
poskytuje možnost určit blok kódu, který se vždy spustí, když ovládací prvek opustí try
příkaz.
try_statement
: 'try' block catch_clauses
| 'try' block catch_clauses? finally_clause
;
catch_clauses
: specific_catch_clause+
| specific_catch_clause* general_catch_clause
;
specific_catch_clause
: 'catch' exception_specifier exception_filter? block
| 'catch' exception_filter block
;
exception_specifier
: '(' type identifier? ')'
;
exception_filter
: 'when' '(' boolean_expression ')'
;
general_catch_clause
: 'catch' block
;
finally_clause
: 'finally' block
;
Try_statement se skládá z klíčového slovatry
, za kterým následuje blok, pak nula nebo více catch_clauses a pak volitelná finally_clause. Musí existovat alespoň jeden catch_clause nebo finally_clause.
V exception_specifiertyp nebo jeho účinná základní třída, pokud se jedná o type_parameter, musí být System.Exception
nebo typ, který je z něj odvozen.
catch
Když klauzule určuje class_type i identifikátor, deklaruje se proměnná výjimky daného názvu a typu. Proměnná výjimky je zavedena do prostoru deklarace specific_catch_clause (§7.3). Během provádění exception_filter a catch
bloku představuje proměnná výjimky aktuálně zpracovávanou výjimku. Pro účely kontroly určitého přiřazení je proměnná výjimky považována za rozhodně přiřazenou v celém rozsahu.
catch
Pokud klauzule neobsahuje název proměnné výjimky, není možné získat přístup k objektu výjimky ve filtru a catch
bloku.
Klauzule catch
, která specifikuje žádný typ výjimky ani název proměnné výjimky, se nazývá obecná catch
klauzule. Příkaz try
může mít pouze jednu obecnou catch
klauzuli, a pokud existuje, bude to poslední catch
klauzule.
Poznámka: Některé programovací jazyky mohou podporovat výjimky, které nejsou reprezentovatelné jako objekt odvozený z
System.Exception
, ačkoli tyto výjimky nelze nikdy generovat kódem jazyka C#. K zachycení těchto výjimek lze použít obecnoucatch
klauzuli. Obecná klauzule je tedy sémanticky odlišná od té, která určuje typcatch
, v tom,System.Exception
že první klauzule může také zachytit výjimky z jiných jazyků. koncová poznámka
Aby bylo možné vyhledat obslužnou rutinu výjimky, catch
jsou klauzule zkoumány v lexikálním pořadí.
catch
Pokud klauzule určuje typ, ale bez filtru výjimek, jedná se o chybu v době kompilace pro pozdější catch
klauzuli stejného try
příkazu, která určuje typ, který je stejný jako nebo je odvozen z tohoto typu.
Poznámka: Bez tohoto omezení by bylo možné zapisovat nedostupné
catch
klauzule. koncová poznámka
catch
V rámci bloku throw
nelze použít příkaz (§13.10.6) bez výrazu k opětovnému vyvolání výjimky zachycené blokemcatch
. Přiřazení proměnné výjimky nemění výjimku, která se znovu vyvolá.
Příklad: V následujícím kódu
class Test { static void F() { try { G(); } catch (Exception e) { Console.WriteLine("Exception in F: " + e.Message); e = new Exception("F"); throw; // re-throw } } static void G() => throw new Exception("G"); static void Main() { try { F(); } catch (Exception e) { Console.WriteLine("Exception in Main: " + e.Message); } } }
metoda
F
zachytí výjimku, zapíše do konzoly některé diagnostické informace, změní proměnnou výjimky a znovu vyvolá výjimku. Výjimkou, která se znovu vyvolá, je původní výjimka, takže výstup se vytvoří takto:Exception in F: G Exception in Main: G
Pokud by první
catch
blok vyvolale
místo opětovného načítání aktuální výjimky, výstup vytvořený by byl následující:Exception in F: G Exception in Main: F
end example
Jedná se o chybu v době kompilace , break
continue
nebo goto
příkaz pro přenos řízení z finally
bloku. Pokud se v break
bloku vyskytuje continue
příkaz , goto
nebo finally
příkaz, cíl příkazu musí být ve stejném finally
bloku nebo jinak dojde k chybě v době kompilace.
Jedná se o chybu v době kompilace, return
ke které má příkaz dojít v finally
bloku.
Když provádění dosáhne try
příkazu, ovládací prvek se přenese do try
bloku. Pokud ovládací prvek dosáhne koncového try
bodu bloku bez šíření výjimky, ovládací prvek se přenese do finally
bloku, pokud existuje. Pokud žádný blok neexistuje finally
, ovládací prvek se přenese do koncového try
bodu příkazu.
Pokud byla výjimka rozšířena, catch
jsou klauzule ( pokud existují) zkoumány v lexikálním pořadí, kde se hledá první shoda pro výjimku. Hledání odpovídající catch
klauzule pokračuje se všemi uzavřenými bloky, jak je popsáno v §13.10.6. Klauzule catch
je shoda, pokud typ výjimky odpovídá libovolnému exception_specifier a jakýkoli exception_filter je pravdivý. Klauzule catch
bez exception_specifier odpovídá jakémukoli typu výjimky. Typ výjimky odpovídá exception_specifier , pokud exception_specifier určuje typ výjimky nebo základní typ výjimky. Pokud klauzule obsahuje filtr výjimek, objekt výjimky je přiřazen k proměnné výjimky a filtr výjimky je vyhodnocen.
Pokud byla rozšířena výjimka a byla nalezena odpovídající catch
klauzule, ovládací prvek se přenese do prvního odpovídajícího catch
bloku. Pokud ovládací prvek dosáhne koncového catch
bodu bloku bez šíření výjimky, ovládací prvek se přenese do finally
bloku, pokud existuje. Pokud žádný blok neexistuje finally
, ovládací prvek se přenese do koncového try
bodu příkazu. Pokud byla výjimka rozšířena z catch
bloku, řízení se přenese do finally
bloku, pokud existuje. Výjimka se rozšíří do dalšího ohraničujícího try
příkazu.
Pokud byla výjimka rozšířena a nebyla nalezena žádná odpovídající catch
klauzule, řízení se přenese do finally
bloku, pokud existuje. Výjimka se rozšíří do dalšího ohraničujícího try
příkazu.
Příkazy finally
bloku se vždy spustí, když ovládací prvek opustí try
příkaz. To platí, zda se přenos ovládacích prvků vyskytuje v důsledku normálního spuštění, v důsledku provádění break
příkazu , continue
, goto
nebo return
příkazu, nebo v důsledku šíření výjimky z try
příkazu. Pokud ovládací prvek dosáhne koncového finally
bodu bloku bez rozšíření výjimky, ovládací prvek se přenese na koncový bod try
příkazu.
Pokud je při provádění finally
bloku vyvolána výjimka a není zachycena ve stejném finally
bloku, výjimka se rozšíří do dalšího ohraničujícího try
příkazu. Pokud v procesu šíření došlo k jiné výjimce, dojde ke ztrátě této výjimky. Postup šíření výjimky je podrobněji popsán v popisu throw
prohlášení (§13.10.6).
Příklad: V následujícím kódu
public class Test { static void Main() { try { Method(); } catch (Exception ex) when (ExceptionFilter(ex)) { Console.WriteLine("Catch"); } bool ExceptionFilter(Exception ex) { Console.WriteLine("Filter"); return true; } } static void Method() { try { throw new ArgumentException(); } finally { Console.WriteLine("Finally"); } } }
metoda
Method
vyvolá výjimku. První akcí je prozkoumat ohraničujícícatch
klauzule a provádět všechny filtry výjimek. Pak sefinally
klauzule vyřídíMethod
před přenosem řízení do nadřazené odpovídajícícatch
klauzule. Výsledný výstup je:Filter Finally Catch
end example
Blok try
try
příkazu je dostupný, pokud try
je příkaz dostupný.
Blok catch
try
příkazu je dostupný, pokud try
je příkaz dostupný.
Blok finally
try
příkazu je dostupný, pokud try
je příkaz dostupný.
Koncový bod try
příkazu je dostupný, pokud jsou splněny obě následující podmínky:
- Koncový bod
try
bloku je dosažitelný nebo je dostupný koncový bod alespoň jednohocatch
bloku. -
finally
Pokud je blok k dispozici, je koncový bodfinally
bloku dostupný.
13.12 Zaškrtnuté a nezaškrtnuté příkazy
Příkazy checked
slouží k řízení unchecked
kontroly přetečení pro aritmetické operace a převody celočíselného typu.
checked_statement
: 'checked' block
;
unchecked_statement
: 'unchecked' block
;
Příkaz checked
způsobí, že se všechny výrazy v bloku vyhodnotí v kontrolovaném kontextu a unchecked
příkaz způsobí, že se všechny výrazy v bloku vyhodnotí v nezaškrtnutém kontextu.
Tyto checked
příkazy unchecked
jsou přesně ekvivalentní checked
operátorům a unchecked
operátorům (§12.8.20), s tím rozdílem, že pracují s bloky namísto výrazů.
13.13 Příkaz lock
Příkaz lock
získá zámek vzájemného vyloučení pro daný objekt, spustí příkaz a pak uvolní zámek.
lock_statement
: 'lock' '(' expression ')' embedded_statement
;
Výraz lock
příkazu označuje hodnotu typu známého jako odkaz. Pro výraz příkazu se nikdy neprovádí implicitní převod boxingu (lock
), a proto se jedná o chybu v době kompilace, která výrazu označuje hodnotu value_type.
Příkaz lock
formuláře
lock (x)
…
kde x
je výraz reference_type, je přesně ekvivalentní:
bool __lockWasTaken = false;
try
{
System.Threading.Monitor.Enter(x, ref __lockWasTaken);
...
}
finally
{
if (__lockWasTaken)
{
System.Threading.Monitor.Exit(x);
}
}
s tím rozdílem, že x
se vyhodnotí pouze jednou.
I když je uzamčeno vzájemné vyloučení, kód spuštěný ve stejném spouštěcím vlákně může také získat a uvolnit zámek. Kód spuštěný v jiných vláknech je však zablokovaný v získání zámku, dokud se zámek nevolní.
13.14 Příkaz using
Příkaz using
získá jeden nebo více prostředků, spustí příkaz a pak odstraní prostředek.
using_statement
: 'using' '(' resource_acquisition ')' embedded_statement
;
resource_acquisition
: local_variable_declaration
| expression
;
Prostředek je třída nebo struktura, která implementuje System.IDisposable
rozhraní, která zahrnuje jednu metodu bez parametrů s názvem .Dispose
Kód, který používá prostředek, může volat Dispose
, aby značil, že prostředek už není potřeba.
Je-li forma resource_acquisition local_variable_declaration, musí být typ dynamic
buď nebo typ, na který lze implicitně převést System.IDisposable
. Pokud je forma resource_acquisition výrazemSystem.IDisposable
Místní proměnné deklarované v resource_acquisition jsou jen pro čtení a musí obsahovat inicializátor. K chybě v době kompilace dochází v případě, že se vložený příkaz pokusí upravit tyto místní proměnné (prostřednictvím přiřazení nebo ++
operátorů --
), převezměte jejich adresu nebo je předejte jako odkaz nebo výstupní parametry.
Příkaz using
se překládá do tří částí: získání, využití a vyřazení. Použití prostředku je implicitně uzavřeno v try
příkazu, který obsahuje finally
klauzuli. Tato finally
klauzule odstraní prostředek.
null
Pokud se prostředek získá, nebude provedeno žádné volání Dispose
a nevyvolá se žádná výjimka. Pokud je zdroj typu dynamic
, je dynamicky převeden prostřednictvím implicitního dynamického převodu (§10.2.10) na IDisposable
během získávání, aby se zajistilo, že převod bude úspěšný před použitím a odstraněním.
Příkaz using
formuláře
using (ResourceType resource = «expression» ) «statement»
odpovídá jednomu ze tří možných rozšíření. Je-li ResourceType
typ hodnoty nenulový nebo parametr typu s omezením typu hodnoty (§15.2.5), je rozšíření sémanticky ekvivalentní
{
ResourceType resource = «expression»;
try
{
«statement»;
}
finally
{
((IDisposable)resource).Dispose();
}
}
s tím rozdílem, že přetypování resource
System.IDisposable
nesmí způsobit výskyt boxů.
Jinak, pokud ResourceType
je dynamic
, rozšíření je
{
ResourceType resource = «expression»;
IDisposable d = resource;
try
{
«statement»;
}
finally
{
if (d != null)
{
d.Dispose();
}
}
}
V opačném případě je rozšíření
{
ResourceType resource = «expression»;
try
{
«statement»;
}
finally
{
IDisposable d = (IDisposable)resource;
if (d != null)
{
d.Dispose();
}
}
}
V jakémkoli rozšíření resource
je proměnná v vloženém příkazu jen pro čtení a d
proměnná je nepřístupná a neviditelná pro vložený příkaz.
Implementace je povolena k implementaci daného using_statement odlišně, například z důvodů výkonu, pokud je chování konzistentní s výše uvedeným rozšířením.
Příkaz using
formuláře:
using («expression») «statement»
má stejné tři možné rozšíření. V tomto případě ResourceType
je implicitně typ kompilace výrazu, pokud má jeden. V opačném případě se samotné rozhraní IDisposable
použije jako ResourceType
. Proměnná resource
je nepřístupná a neviditelná pro vložený příkaz.
Když resource_acquisition má formu local_variable_declaration, je možné získat více prostředků daného typu. Příkaz using
formuláře
using (ResourceType r1 = e1, r2 = e2, ..., rN = eN) «statement»
je přesně ekvivalentní sekvenci vnořených using
příkazů:
using (ResourceType r1 = e1)
using (ResourceType r2 = e2)
...
using (ResourceType rN = eN)
«statement»
Příklad: Následující příklad vytvoří soubor s názvem log.txt a zapíše do souboru dva řádky textu. Příklad pak otevře stejný soubor pro čtení a zkopíruje obsažené řádky textu do konzoly.
class Test { static void Main() { using (TextWriter w = File.CreateText("log.txt")) { w.WriteLine("This is line one"); w.WriteLine("This is line two"); } using (TextReader r = File.OpenText("log.txt")) { string s; while ((s = r.ReadLine()) != null) { Console.WriteLine(s); } } } }
Vzhledem k tomu, že
TextWriter
aTextReader
třídy implementujíIDisposable
rozhraní, může příklad použítusing
příkazy k zajištění, že podkladový soubor je správně uzavřen po operacích zápisu nebo čtení.end example
13.15 Příkaz výnosu
Příkaz yield
se používá v bloku iterátoru (§13.3) k výnosu hodnoty objektu enumerátoru (§15.14.5) nebo výčtového objektu (§15.14.6) iterátoru nebo k označení konce iterace.
yield_statement
: 'yield' 'return' expression ';'
| 'yield' 'break' ';'
;
yield
je kontextové klíčové slovo (§6.4.4) a má zvláštní význam pouze v případech, kdy se používá bezprostředně před nebo return
klíčovým slovembreak
.
Existuje několik omezení, kdy yield
se příkaz může zobrazit, jak je popsáno v následujícím příkladu.
- Jedná se o chybu v době kompilace, která se zobrazí
yield
mimo method_body, operator_body nebo accessor_body. - Jedná se o chybu v době kompilace pro
yield
příkaz (z obou formulářů), který se zobrazí uvnitř anonymní funkce. - Jedná se o chybu v době kompilace pro
yield
příkaz (z obou formulářů), který se zobrazí vfinally
klauzulitry
příkazu. - Jedná se o chybu v době kompilace,
yield return
kdy se příkaz zobrazí kdekoli vtry
příkazu, který obsahuje všechny catch_clauses.
Příklad: Následující příklad ukazuje některé platné a neplatné použití
yield
příkazů.delegate IEnumerable<int> D(); IEnumerator<int> GetEnumerator() { try { yield return 1; // Ok yield break; // Ok } finally { yield return 2; // Error, yield in finally yield break; // Error, yield in finally } try { yield return 3; // Error, yield return in try/catch yield break; // Ok } catch { yield return 4; // Error, yield return in try/catch yield break; // Ok } D d = delegate { yield return 5; // Error, yield in an anonymous function }; } int MyMethod() { yield return 1; // Error, wrong return type for an iterator block }
end example
Implicitní převod (§10.2) musí existovat z typu výrazu ve yield return
výrazu na typ výnosu (§15.14.4) iterátoru.
Příkaz yield return
se provede následujícím způsobem:
- Výraz zadaný v příkazu je vyhodnocen, implicitně převeden na typ výnosu a přiřazen k
Current
vlastnosti enumerator objektu. - Spuštění bloku iterátoru je pozastaveno.
yield return
Pokud je příkaz v jednom nebo vícetry
blocích, přidruženéfinally
bloky se v tuto chvíli nespustí. - Metoda
MoveNext
objektu enumerátoru se vrátítrue
do svého volajícího, což označuje, že objekt enumerátoru byl úspěšně rozšířen na další položku.
Další volání metody enumerátoru objektu MoveNext
obnoví provádění bloku iterátoru od místa, kde byl naposledy pozastaven.
Příkaz yield break
se provede následujícím způsobem:
-
yield break
Pokud je příkaz uzavřený jedním nebo vícetry
bloky s přidruženýmifinally
bloky, ovládací prvek je zpočátku přenesen dofinally
bloku nejvnitřnějšíhotry
příkazu. Když a pokud ovládací prvek dosáhne koncovéhofinally
bodu bloku, ovládací prvek se přenese dofinally
bloku dalšího ohraničujícíhotry
příkazu. Tento proces se opakuje, dokudfinally
nebudou provedeny bloky všech uzavřenýchtry
příkazů. - Ovládací prvek se vrátí volajícímu bloku iterátoru. Jedná se o metodu
MoveNext
neboDispose
metodu objektu enumerátoru.
Vzhledem k tomu, že yield break
prohlášení bezpodmínečně přenáší kontrolu jinde, koncový bod yield break
příkazu není nikdy dostupný.
ECMA C# draft specification