Sdílet prostřednictvím


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 jeho if 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ím ideklarace 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í hodnoty false, Console.WriteLine vyvolání je považováno za nedostupné. Pokud i se ale změní na místní proměnnou

void 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 blok F 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ího Console.WriteLine výrazu je dosažitelný (§13.7 a §13.3).
  • Druhý Console.WriteLine příkaz výrazu je dostupný, protože logický výraz if příkazu nemá konstantní hodnotu false.

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čí, že break 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 (ale yield 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):

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 Tjeho typ . Druhá alternativa deklaruje proměnnou ref s počáteční hodnotou refvariable_reference; její typ je ref T? v případě, že T se jedná o nenulový odkazový typ, jinak je ref Tjeho 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 refdeklarová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 xx 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 a goto 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í bod L není dosažitelný. Vzhledem k tomu, že počáteční bod L není dostupný, příkaz následující za koncovým bodem L 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 dynamictyp 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 ani base na členy instancí z implicitního this 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ýrazu nameof() 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 a x == 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áře

if (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í truehodnotu , 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ý bod if příkazu.
  • Pokud logický výraz získá false hodnotu a pokud else 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ý bod if příkazu.
  • Pokud logický výraz vrátí false hodnotu a pokud else část není k dispozici, ovládací prvek se převede na koncový bod if 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 sbytetyp výrazu switch , , , byteshort, ushortintuintlong, , ulong, char, boolstringnebo enum_type, nebo pokud se jedná o typ hodnoty nullable odpovídající jednomu z těchto typů, pak se jedná o řídicí typ switch 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, , byteshort, ushort, , int, , uint, longulongchar, , stringnebo, nullable typ hodnoty odpovídající jednomu z těchto typů, pak převedený typ je řídí typ switch 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ém switch 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ím case popiskem.
    • Pokud je popisek k dispozici, default ovládací prvek se přenese do seznamu příkazů za default popiskem.
    • Jinak se ovládací prvek přenese na koncový bod switch příkazu.

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 nebo goto 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: a default: 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ého switch 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 casenebo goto default příkaz, ale jakýkoli konstruktor, který vykreslí koncový bod seznamu příkazů nedostupný, je povolen. Například while příkaz řízený logickým výrazem true se nikdy nedosahuje koncového bodu. Podobně příkaz throw nebo příkaz return 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 typ string. 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 a switch statement is string or a nullable value type, the value null is permitted as a case 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.
  • 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 přepínače je odkazován dosažitelným goto case nebo goto 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á nullprá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čátek while příkazu.
  • Pokud logický výraz vrátí falsehodnotu , ovládací prvek se převede na koncový bod while 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í hodnotu true.

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í truehodnotu , ovládací prvek se převede na začátek do příkazu. Jinak se ovládací prvek přenese na koncový bod do 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ý bod for 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í hodnotu false.

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í hodnotu true.

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, readonlyiterač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, IEnumeratorIEnumerable<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 na IEnumerable rozhraní (protože System.Array implementuje toto rozhraní). Typ kolekce je IEnumerable rozhraní, enumerátor typ je IEnumerator rozhraní a typ iterace je typ prvku typu Xpole .
  • Pokud je typ X výrazudynamic na rozhraní (IEnumerable). Typ kolekce je IEnumerable rozhraní a typ enumerátoru IEnumerator je rozhraní. var Pokud je identifikátor uveden jako local_variable_type pak je dynamictyp iterace , jinak je object.
  • 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átorem GetEnumerator 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 typ GetEnumerator 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átorem Current 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átorem MoveNext 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 je Ea iterační typ je typ Current vlastnosti. Vlastnost Current může obsahovat ref modifikátor, v takovém případě vrácený výraz je variable_reference (§9.5), který je volitelně jen pro čtení.
  • V opačném případě zkontrolujte výčet rozhraní:
    • Pokud mezi všemi typy Tᵢ , pro které existuje implicitní převod z XIEnumerable<Tᵢ>, existuje jedinečný typ T , který T není dynamic a pro všechny ostatní Tᵢ existuje implicitní převod z IEnumerable<T>IEnumerable<Tᵢ>, pak typ kolekce je rozhraní IEnumerable<T>, enumerátor typ je rozhraní IEnumerator<T>a iterační typ je T.
    • 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 XSystem.Collections.IEnumerable rozhraní, pak typ kolekce je toto rozhraní, enumerátor typ je rozhraní System.Collections.IEnumeratora iterační typ je object.
    • V opačném případě se vytvoří chyba a neprovedou se žádné další kroky.

Výše uvedené kroky, pokud jsou úspěšné, jednoznačně vytvoří typ Ckolekce , typ E enumerátoru a typ Titerace , ref Tnebo 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á hodnotu null, 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čku while , byla by sdílena mezi všemi iteracemi a její hodnota za for smyčkou by byla konečná hodnota, což je to, 13co vyvolání f by se vytiskne. Místo toho, protože každá iterace má svou vlastní proměnnou v, ta zachycená f v první iteraci bude i nadále obsahovat hodnotu 7, což je to, co se vytiskne. (Všimněte si, že starší verze jazyka C# deklarované v mimo smyčku while .)

end example

Tělo finally bloku je sestaveno podle následujících kroků:

  • Pokud existuje implicitní převod z ESystem.IDisposable rozhraní, pak

    • Pokud 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řevodu e na System.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 odvozen intbýt , iterační typ numbers.

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ěma try 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, , fornebo foreach příkazu. break Pokud příkaz není uzavřen příkazem switch, , whiledo, for, nebo foreach příkaz, dojde k chybě kompilace čas.

Pokud je v sobě vnořeno více switch, while, do, fornebo 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í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ů.
  • 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 whilepříkazu , do, fornebo foreach příkazu. continue Pokud příkaz není uzavřen příkazem while, do, fornebo foreach příkaz, dojde k chybě kompilace v době kompilace.

Pokud je v while sobě vnořeno více dopříkazů , for, foreachcontinue 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í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ů.
  • 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 gotopří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říkladu

class 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 casenebo 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í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ů.
  • 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í refpouze 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íce trycatch bloky 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 nebudou provedeny bloky všech uzavřených try 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.Exceptiona výsledek vyhodnocení výrazu se převede na System.Exception před vyvolání. Pokud je nullvý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ším S příkazem trya končícím vnějším try příkazem se vyhodnocují následující kroky:

    • try Pokud blok S ohraničuje bod vyvolání a pokud S má jednu nebo více catch klauzulí, catch jsou klauzule zkoumány za účelem vyhledání vhodné obslužné rutiny pro výjimku. První catch klauzule, která určuje typ T výjimky (nebo parametr typu, který v době běhu označuje typ Tvýjimky), aby typ E běhu odvozený z T 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 jako true. 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éto catch klauzule.
    • Jinak platí, že pokud try blok nebo catch blok S ohraničuje bod vyvolání a pokud S obsahuje finally blok, ovládací prvek se přenese do finally bloku. finally Pokud blok vyvolá jinou výjimku, zpracování aktuální výjimky se ukončí. Jinak, pokud ovládací prvek dosáhne koncového finally 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 voidvracejí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 obecnou catch klauzuli. Obecná klauzule je tedy sémanticky odlišná od té, která určuje typ catch, 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 vyvolal e 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 , breakcontinuenebo goto příkaz pro přenos řízení z finally bloku. Pokud se v breakbloku vyskytuje continue příkaz , gotonebo 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í breakpříkazu , continue, gotonebo 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 se finally 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 trytry příkazu je dostupný, pokud try je příkaz dostupný.

Blok catchtry příkazu je dostupný, pokud try je příkaz dostupný.

Blok finallytry 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ň jednoho catch bloku.
  • finally Pokud je blok k dispozici, je koncový bod finally 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í resourceSystem.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 a TextReader třídy implementují IDisposable rozhraní, může příklad použít using 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' ';'
    ;

yieldje 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í v finally klauzuli try příkazu.
  • Jedná se o chybu v době kompilace, yield return kdy se příkaz zobrazí kdekoli v try 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íce try 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íce try bloky s přidruženými finally bloky, ovládací prvek je zpočátku přenesen 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 nebudou provedeny bloky všech uzavřených try příkazů.
  • Ovládací prvek se vrátí volajícímu bloku iterátoru. Jedná se o metodu MoveNext nebo Dispose 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ý.