Dela via


13 instruktioner

13.1 Allmänt

C# innehåller en mängd olika instruktioner.

Obs! De flesta av dessa instruktioner är bekanta för utvecklare som har programmerat i C och C++. slutkommentar

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) och fixed_statement (§23.7) är endast tillgängliga i osäker kod (§23).

Den embedded_statement icke-terminala används för -instruktioner som visas i andra instruktioner. Användningen av embedded_statement i stället för -instruktion utesluter användning av deklarationsuttryck och etiketterade instruktioner i dessa kontexter.

Exempel: Koden

void F(bool b)
{
   if (b)
      int i = 44;
}

resulterar i ett kompileringsfel eftersom en if instruktion kräver en embedded_statement i stället för en instruktion för dess if gren. Om den här koden var tillåten skulle variabeln i deklareras, men den kunde aldrig användas. Observera dock att genom att placera i's-deklarationen i ett block är exemplet giltigt.

slutexempel

13.2 Slutpunkter och nåbarhet

Varje instruktion har en slutpunkt. I intuitiva termer är slutpunkten för en -instruktion den plats som omedelbart följer -instruktionen. Körningsreglerna för sammansatta instruktioner (instruktioner som innehåller inbäddade instruktioner) anger den åtgärd som vidtas när kontrollen når slutpunkten för en inbäddad -instruktion.

Exempel: När kontrollen når slutpunkten för en -instruktion i ett block överförs kontrollen till nästa -instruktion i blocket. slutexempel

Om ett uttalande eventuellt kan nås genom körning, sägs uttalandet vara nåbart. Omvänt, om det inte finns någon möjlighet att ett uttalande kommer att utföras, sägs uttalandet vara oåtkomligt.

Exempel: I följande kod

void F()
{
    Console.WriteLine("reachable");
    goto Label;
    Console.WriteLine("unreachable");
  Label:
    Console.WriteLine("reachable");
}

det andra anropet av Console.WriteLine kan inte nås eftersom det inte finns någon möjlighet att -instruktionen kommer att köras.

slutexempel

En varning rapporteras om en annan instruktion än throw_statement, blockering eller empty_statement inte kan nås. Det är specifikt inte ett fel att en instruktion inte kan nås.

Note: För att avgöra om en viss instruktion eller slutpunkt kan nås utför en kompilator flödesanalys enligt de nåbarhetsregler som definierats för varje instruktion. Flödesanalysen tar hänsyn till värdena för konstanta uttryck (§12.23) som styr beteendet för instruktioner, men de möjliga värdena för icke-konstanta uttryck beaktas inte. Med andra ord anses ett icke-konstant uttryck av en viss typ för kontrollflödesanalys ha ett möjligt värde av den typen.

I exemplet

void F()
{
    const int i = 1;
    if (i == 2)
        Console.WriteLine("unreachable");
}

Det booleska uttrycket för -instruktionen if är ett konstant uttryck eftersom operatorns == båda operander är konstanter. När det konstanta uttrycket utvärderas vid kompileringstillfället anses anropet false inte gå att nå när värdet Console.WriteLinegenereras. Men om i ändras till en lokal variabel

void F()
{
    int i = 1;
    if (i == 2)
        Console.WriteLine("reachable");
}

anropet Console.WriteLine anses vara nåbart, även om det i själva verket aldrig kommer att genomföras.

slutkommentar

Blocket för en funktionsmedlem eller en anonym funktion anses alltid vara nåbar. Genom att successivt utvärdera räckviddsreglerna för varje -instruktion i ett block kan du fastställa nåbarheten för en viss instruktion.

Exempel: I följande kod

void F(int x)
{
    Console.WriteLine("start");
    if (x < 0)
        Console.WriteLine("negative");
}

den andras Console.WriteLine nåbarhet fastställs på följande sätt:

  • Det första Console.WriteLine uttrycket kan nås eftersom blocket för F metoden kan nås (§13.3).
  • Slutpunkten för den första Console.WriteLine uttryckssatsen kan nås eftersom den instruktionen kan nås (§13.7 och §13.3).
  • Instruktionen if kan nås eftersom slutpunkten för den första Console.WriteLine uttryckssatsen kan nås (§13.7 och §13.3).
  • Den andra Console.WriteLine uttryckssatsen kan nås eftersom det booleska uttrycket för -instruktionen if inte har det konstanta värdet false.

slutexempel

Det finns två situationer där det är ett kompileringsfel för att slutpunkten för en -instruktion ska kunna nås:

  • Eftersom instruktionen switch inte tillåter att ett växlingsavsnitt "faller igenom" till nästa växelavsnitt är det ett kompileringsfel för att slutpunkten i instruktionslistan för ett switch-avsnitt ska kunna nås. Om det här felet inträffar är det vanligtvis en indikation på att en break instruktion saknas.

  • Det är ett kompileringsfel för slutpunkten för blocket för en funktionsmedlem eller en anonym funktion som beräknar att ett värde ska kunna nås. Om det här felet uppstår är det vanligtvis en indikation på att en return instruktion saknas (§13.10.5).

13,3 block

13.3.1 Allmänt

Ett block tillåter att flera uttryck skrivs i kontexter där en enda instruktion tillåts.

block
    : '{' statement_list? '}'
    ;

Ett block består av en valfri statement_list (§13.3.2), omgiven av klammerparenteser. Om instruktionslistan utelämnas sägs blocket vara tomt.

Ett block kan innehålla deklarationsuttryck (§13.6). Omfånget för en lokal variabel eller konstant som deklareras i ett block är blocket.

Ett block körs på följande sätt:

  • Om blocket är tomt överförs kontrollen till blockets slutpunkt.
  • Om blocket inte är tomt överförs kontrollen till instruktionslistan. När och om kontrollen når slutpunkten för instruktionslistan överförs kontrollen till blockets slutpunkt.

Instruktionslistan över ett block kan nås om själva blocket kan nås.

Slutpunkten för ett block kan nås om blocket är tomt eller om slutpunkten i instruktionslistan kan nås.

Ett block som innehåller en eller flera yield instruktioner (§13.15) kallas ett iteratorblock. Iteratorblock används för att implementera funktionsmedlemmar som iteratorer (§15.14). Några ytterligare begränsningar gäller för iteratorblock:

  • Det är ett kompileringsfel för en return instruktion som ska visas i ett iteratorblock (men yield return instruktioner tillåts).
  • Det är ett kompileringsfel för ett iteratorblock som innehåller en osäker kontext (§23.2). Ett iteratorblock definierar alltid en säker kontext, även när dess deklaration är kapslad i en osäker kontext.

13.3.2 Instruktionslistor

En instruktionslista består av en eller flera instruktioner som skrivits i följd. Instruktionslistor förekommer i block s (§13.3) och i switch_blocks (§13.8.3).

statement_list
    : statement+
    ;

En instruktionslista körs genom att kontrollen överförs till den första instruktionen. När och om kontrollen når slutpunkten för en -instruktion överförs kontrollen till nästa -instruktion. När och om kontrollen når slutpunkten för den sista instruktionen överförs kontrollen till slutpunkten i instruktionslistan.

En instruktion i en instruktionslista kan nås om minst något av följande är sant:

  • -instruktionen är den första instruktionen och själva instruktionslistan kan nås.
  • Slutpunkten för föregående instruktion kan nås.
  • -instruktionen är en etikett och etiketten refereras till av en nåbar goto instruktion.

Slutpunkten för en instruktionslista kan nås om slutpunkten för den sista instruktionen i listan kan nås.

13.4 Den tomma instruktionen

En empty_statement gör ingenting.

empty_statement
    : ';'
    ;

En tom instruktion används när det inte finns några åtgärder att utföra i en kontext där en instruktion krävs.

Körning av en tom instruktion överför helt enkelt kontrollen till slutpunkten för -instruktionen. Slutpunkten för en tom instruktion kan därför nås om den tomma instruktionen kan nås.

Exempel: En tom instruktion kan användas när du skriver en while instruktion med en null-brödtext:

bool ProcessMessage() {...}
void ProcessMessages()
{
    while (ProcessMessage())
        ;
}

Dessutom kan en tom instruktion användas för att deklarera en etikett precis före stängningen} av ett block:

void F(bool done)
{
    ...
    if (done)
    {
        goto exit;
    }
    ...
  exit:
    ;
}

slutexempel

13.5 Etiketterade instruktioner

En labeled_statement tillåter att en -instruktion prefixeras av en etikett. Etiketterade instruktioner är tillåtna i block, men tillåts inte som inbäddade instruktioner.

labeled_statement
    : identifier ':' statement
    ;

En etikettuttryck deklarerar en etikett med det namn som anges av identifieraren. Omfånget för en etikett är hela blocket där etiketten deklareras, inklusive kapslade block. Det är ett kompileringsfel för två etiketter med samma namn att ha överlappande omfång.

En etikett kan refereras från goto instruktioner (§13.10.4) inom etikettens omfång.

Obs! Det innebär att goto instruktioner kan överföra kontroll inom block och ut ur block, men aldrig till block. slutkommentar

Etiketter har sitt eget deklarationsutrymme och stör inte andra identifierare.

Exempel: Exemplet

int F(int x)
{
    if (x >= 0)
    {
        goto x;
    }
    x = -x;
  x:
    return x;
}

är giltigt och använder namnet x som både en parameter och en etikett.

slutexempel

Körningen av en märkt instruktion motsvarar exakt körningen av -instruktionen efter etiketten.

Förutom den nåbarhet som tillhandahålls av det normala kontrollflödet kan en märkt instruktion nås om etiketten refereras till av en nåbar goto instruktion, såvida inte -instruktionen gototry finns i blocket eller ett catch block av en try_statement som innehåller ett finally block vars slutpunkt inte kan nås och den märkta instruktionen ligger utanför try_statement.

13.6 Förklaringsuttryck

13.6.1 Allmänt

En declaration_statement deklarerar en eller flera lokala variabler, en eller flera lokala konstanter eller en lokal funktion. Deklarationssatser tillåts i block och växelblock, men tillåts inte som inbäddade instruktioner.

declaration_statement
    : local_variable_declaration ';'
    | local_constant_declaration ';'
    | local_function_declaration
    ;

En lokal variabel deklareras med hjälp av en local_variable_declaration (§13.6.2). En lokal konstant deklareras med hjälp av en local_constant_declaration (§13.6.3). En lokal funktion deklareras med hjälp av en local_function_declaration (§13.6.4).

De deklarerade namnen introduceras i närmaste omslutande deklarationsutrymme (§7.3).

13.6.2 Lokala variabeldeklarationer

13.6.2.1 Allmänt

En local_variable_declaration deklarerar en eller flera lokala variabler.

local_variable_declaration
    : implicitly_typed_local_variable_declaration
    | explicitly_typed_local_variable_declaration
    | explicitly_typed_ref_local_variable_declaration
    ;

Implicit skrivna deklarationer innehåller det kontextuella nyckelordet (§6.4.4) var som resulterar i en syntaktisk tvetydighet mellan de tre kategorier som löses på följande sätt:

  • Om det inte finns någon typ med namnet var i omfånget och indata matchar implicitly_typed_local_variable_declaration väljs den.
  • Annars betraktas implicitly_typed_local_variable_declaration inte som en möjlig matchning om en typ med namnet var finns i omfånget.

Inom en local_variable_declaration introduceras varje variabel av en deklarator, som är en av implicitly_typed_local_variable_declarator, explicitly_typed_local_variable_declarator eller ref_local_variable_declarator för implicit inskrivna, uttryckligen inskrivna respektive ref lokala variabler. Deklaratorn definierar namnet (identifieraren) och det initiala värdet, om någon, för den introducerade variabeln.

Om det finns flera deklaratorer i en deklaration bearbetas de, inklusive eventuella initierande uttryck, i ordning från vänster till höger (§9.4.4.5).

Obs! För en local_variable_declaration som inte inträffar som en for_initializer (§13.9.4) eller resource_acquisition (§13.14) motsvarar den här vänster-till-höger-ordningen att varje deklarator ligger inom en separat local_variable_declaration. Till exempel:

void F()
{
    int x = 1, y, z = x * 2;
}

motsvarar:

void F()
{
    int x = 1;
    int y;
    int z = x * 2;
}

slutkommentar

Värdet för en lokal variabel erhålls i ett uttryck med hjälp av en simple_name (§12.8.4). En lokal variabel ska definitivt tilldelas (§9.4) på varje plats där dess värde erhålls. Varje lokal variabel som introduceras av en local_variable_declaration tilldelas ursprungligen inte (§9.4.3). Om en deklarator har ett initierande uttryck klassificeras den introducerade lokala variabeln som tilldelad i slutet av deklaratorn (§9.4.4.5).

Omfånget för en lokal variabel som introduceras av en local_variable_declaration definieras enligt följande (§7.7):

  • Om deklarationen sker som en for_initializer är omfånget for_initializer, for_condition, for_iterator och embedded_statement (§13.9.4);
  • Om deklarationen sker som en resource_acquisition är omfånget det yttersta blocket för den semantiskt likvärdiga expansionen av using_statement (§13.14);
  • Annars är omfånget det block där deklarationen inträffar.

Det är ett fel att referera till en lokal variabel efter namn i en textposition som föregår dess deklarator, eller inom något initierande uttryck i dess deklarator. Inom omfånget för en lokal variabel är det ett kompileringsfel att deklarera en annan lokal variabel, lokal funktion eller konstant med samma namn.

Ref-safe-context (§9.7.2) för en lokal referensvariabel är referens-safe-context för dess initierande variable_reference. Ref-safe-context för icke-ref lokala variabler är declaration-block.

13.6.2.2 Implicit skrivna lokala variabeldeklarationer

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
    ;

En implicitly_typed_local_variable_declaration introducerar en enda lokal variabel, identifierare. Uttrycket eller variable_reference ska ha en kompileringstidstyp, T. Det första alternativet deklarerar en variabel med ett inledande uttrycksvärde. Dess typ är T? när T är en referenstyp som inte kan nollas, annars är Tdess typ . Det andra alternativet deklarerar en referensvariabel med ett initialt värde på refvariable_reference. Dess typ är när ref T? är T en referenstyp som inte kan nollas, annars är ref Tdess typ . (ref_kind beskrivs i §15.6.1.)

Exempel:

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;

De implicit inskrivna lokala variabeldeklarationerna ovan motsvarar exakt följande uttryckligen skrivna deklarationer:

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;

Följande är felaktiga implicit inskrivna lokala variabeldeklarationer:

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

slutexempel

13.6.2.3 Uttryckligen skrivna lokala variabeldeklarationer

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
    ;

En explicity_typed_local_variable_declaration introducerar en eller flera lokala variabler med den angivna typen.

Om en local_variable_initializer finns ska dess typ vara lämplig enligt reglerna för enkel tilldelning (§12.21.2) eller matrisinitiering (§17.7) och dess värde tilldelas som variabelns initialvärde.

13.6.2.4 Uttryckligen inskrivna ref lokala variabeldeklarationer

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
    ;

Initieringen av variable_reference ska ha typtyp och uppfylla samma krav som för en referenstilldelning (§12.21.3).

Om ref_kind är ref readonlyär identifierarna som deklareras referenser till variabler som behandlas som skrivskyddade. Annars, om ref_kind är ref, är de identifierare som deklareras referenser till variabler som ska vara skrivbara.

Det är ett kompileringsfel att deklarera en lokal referensvariabel, eller en variabel av en ref struct typ, inom en metod som deklarerats med method_modifierasynceller inom en iterator (§15.14).

13.6.3 Lokala konstanta deklarationer

En local_constant_declaration deklarerar en eller flera lokala konstanter.

local_constant_declaration
    : 'const' type constant_declarators
    ;

constant_declarators
    : constant_declarator (',' constant_declarator)*
    ;

constant_declarator
    : identifier '=' constant_expression
    ;

Typen av local_constant_declaration anger typen av konstanter som introduceras av deklarationen. Typen följs av en lista över constant_declarators, som var och en introducerar en ny konstant. En constant_declarator består av en identifierare som namnger konstanten, följt av en "=" token, följt av en constant_expression (§12.23) som ger konstantens värde.

Typ och constant_expression av en lokal konstant deklaration ska följa samma regler som för en konstant medlemsdeklaration (§15.4).

Värdet för en lokal konstant erhålls i ett uttryck med hjälp av en simple_name (§12.8.4).

Omfånget för en lokal konstant är blocket där deklarationen sker. Det är ett fel att referera till en lokal konstant i en textposition som föregår slutet av dess constant_declarator.

En lokal konstantdeklaration som deklarerar flera konstanter motsvarar flera deklarationer av enskilda konstanter med samma typ.

13.6.4 Lokala funktionsdeklarationer

En local_function_declaration deklarerar en lokal funktion.

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 ';'
    ;

Grammatikanteckning: När du känner igen en local_function_body om alternativ för både null_conditional_invocation_expression och uttryck är tillämpliga ska den förra väljas. (§15.6.1)

Exempel: Det finns två vanliga användningsfall för lokala funktioner: iteratormetoder och asynkrona metoder. I iteratormetoder observeras undantag endast när kod anropas som räknar upp den returnerade sekvensen. I asynkrona metoder observeras undantag endast när den returnerade aktiviteten väntar. I följande exempel visas hur du separerar parameterverifiering från iteratorimplementeringen med hjälp av en lokal funktion:

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;
        }
    }
}

slutexempel

Om inget annat anges nedan är semantiken för alla grammatikelement densamma som för method_declaration (§15.6.1), läs i kontexten för en lokal funktion i stället för en metod.

Identifieraren för en local_function_declaration ska vara unik i dess deklarerade blockomfång, inklusive eventuella omslutande lokala variabeldeklarationsutrymmen. En konsekvens av detta är att överlagrade local_function_declarations inte tillåts.

En local_function_declaration kan innehålla en async (§15.15) modifierare och en unsafe (§23.1) modifierare. Om deklarationen async innehåller modifieraren ska returtypen vara void eller en «TaskType» typ (§15.15.1). Om deklarationen static innehåller modifieraren är funktionen en statisk lokal funktion. Annars är det en icke-statisk lokal funktion. Det är ett kompileringsfel för type_parameter_list eller parameter_list som ska innehålla attribut. Om den lokala funktionen deklareras i ett osäkert sammanhang (§23.2) kan den lokala funktionen innehålla osäker kod, även om den lokala funktionsdeklarationen unsafe inte innehåller modifieraren.

En lokal funktion deklareras i blockomfånget. En icke-statisk lokal funktion kan samla in variabler från omfånget omslutande medan en statisk lokal funktion inte får (så den har ingen åtkomst till omslutning av lokalbefolkningen, parametrar, icke-statiska lokala funktioner eller this). Det är ett kompileringsfel om en insamlad variabel läss av brödtexten i en icke-statisk lokal funktion, men inte definitivt tilldelas före varje anrop till funktionen. En kompilator ska fastställa vilka variabler som definitivt tilldelas vid återgång (§9.4.4.33).

När typen av this är en struct-typ är det ett kompileringsfel för brödtexten för en lokal funktion att komma åt this. Detta gäller om åtkomsten är explicit (som i this.x) eller implicit (som i var x är en instansmedlem i x struct). Den här regeln förbjuder endast sådan åtkomst och påverkar inte om medlemssökning resulterar i en medlem i structen.

Det är ett kompileringsfel som gör att brödtexten i den lokala funktionen innehåller en goto instruktion, en break instruktion eller en continue instruktion vars mål ligger utanför den lokala funktionens brödtext.

Obs! Ovanstående regler för this och goto speglar reglerna för anonyma funktioner i §12.19.3. slutkommentar

En lokal funktion kan anropas från en lexikal punkt före deklarationen. Det är dock ett kompileringsfel för funktionen som ska deklareras lexikalt före deklarationen av en variabel som används i den lokala funktionen (§7.7).

Det är ett kompileringsfel för en lokal funktion att deklarera en parameter, typparameter eller lokal variabel med samma namn som en som deklareras i ett omslutande lokalt variabeldeklarationsutrymme.

Lokala funktionskroppar kan alltid nås. Slutpunkten för en lokal funktionsdeklaration kan nås om startpunkten för den lokala funktionsdeklarationen kan nås.

Exempel: I följande exempel kan brödtexten L i nås även om startpunkten L för inte kan nås. Eftersom startpunkten L för inte kan nås kan instruktionen som följer slutpunkten L för inte nås:

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;
    }
}

Med andra ord påverkar placeringen av en lokal funktionsdeklaration inte nåbarheten för några instruktioner i den innehållande funktionen. slutexempel

Om typen av argumentet till en lokal funktion är dynamic, ska funktionen som anropas matchas vid kompileringstid, inte körning.

En lokal funktion får inte användas i ett uttrycksträd.

En statisk lokal funktion

  • Kan referera till statiska medlemmar, typparametrar, konstanta definitioner och statiska lokala funktioner från omfånget.
  • Ska inte referera till this eller base instansmedlemmar från en implicit this referens, eller lokala variabler, parametrar eller icke-statiska lokala funktioner från omfånget. Alla dessa är dock tillåtna i ett nameof() uttryck.

Uttryckssatser för 13.7

En expression_statement utvärderar ett visst uttryck. Det värde som beräknas av uttrycket, om det finns något, ignoreras.

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
    ;

Alla uttryck tillåts inte som uttryck.

Obs! I synnerhet tillåts inte uttryck som x + y och x == 1, som bara beräknar ett värde (som tas bort) som uttryck. slutkommentar

Körning av en expression_statement utvärderar det inneslutna uttrycket och överför sedan kontrollen till slutpunkten för expression_statement. Slutpunkten för en expression_statement kan nås om den expression_statement kan nås.

13.8 Valinstruktioner

13.8.1 Allmänt

Urvalsuttryck väljer ett av ett antal möjliga instruktioner för körning baserat på värdet för vissa uttryck.

selection_statement
    : if_statement
    | switch_statement
    ;

13.8.2 If-instruktionen

Instruktionen if väljer en instruktion för körning baserat på värdet för ett booleskt uttryck.

if_statement
    : 'if' '(' boolean_expression ')' embedded_statement
    | 'if' '(' boolean_expression ')' embedded_statement
      'else' embedded_statement
    ;

En else del är associerad med den lexikalt närmast föregående if som tillåts av syntaxen.

Exempel: En if instruktion i formuläret

if (x) if (y) F(); else G();

motsvarar

if (x)
{
    if (y)
    {
        F();
    }
    else
    {
        G();
    }
}

slutexempel

En if instruktion körs på följande sätt:

  • Boolean_expression (§12.24) utvärderas.
  • Om det booleska uttrycket ger trueöverförs kontrollen till den första inbäddade instruktionen. När och om kontrollen når slutpunkten för instruktionen överförs kontrollen till slutpunkten för -instruktionen if .
  • Om det booleska uttrycket ger false och om en else del finns överförs kontrollen till den andra inbäddade instruktionen. När och om kontrollen når slutpunkten för instruktionen överförs kontrollen till slutpunkten för -instruktionen if .
  • Om det booleska uttrycket ger false och om en else del inte finns överförs kontrollen till slutpunkten för -instruktionen if .

Den första inbäddade instruktionen för en if -instruktion kan nås om -instruktionen if kan nås och det booleska uttrycket inte har det konstanta värdet false.

Den andra inbäddade instruktionen för en if -instruktion, om den finns, kan nås om -instruktionen if kan nås och det booleska uttrycket inte har konstantvärdet true.

Slutpunkten för en if -instruktion kan nås om slutpunkten för minst en av dess inbäddade instruktioner kan nås. Dessutom kan slutpunkten för en if -instruktion utan else del nås om -instruktionen if kan nås och det booleska uttrycket inte har konstantvärdet true.

13.8.3 Switch-instruktionen

Instruktionen switch väljer för körning av en instruktionslista med en associerad växeletikett som motsvarar värdet för switch-uttrycket.

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
    ;

En switch_statement består av nyckelordet switch, följt av ett parentesiserat uttryck (kallas switch-uttrycket), följt av en switch_block. Switch_block består av noll eller fler switch_sections, omgiven av klammerparenteser. Varje switch_section består av en eller flera switch_labelföljt av en statement_list (§13.3.2). Varje switch_label som innehåller case har ett associerat mönster (§11) mot vilket värdet för switchuttrycket testas. Om case_guard finns ska uttrycket implicit konverteras till typen bool och uttrycket utvärderas som ytterligare ett villkor för att ärendet ska anses uppfyllt.

Den styrande typen av en switch -instruktion upprättas av switch-uttrycket.

  • Om typen av switch-uttryck är sbyte, byte, short, ushort, int, uint, long, ulong, char, , bool, stringeller en enum_type, eller om det är den nullbara värdetyp som motsvarar någon av dessa typer, så är det den styrande typen av -instruktionen switch .
  • Om det annars finns exakt en användardefinierad implicit konvertering från typen av switch-uttrycket till någon av följande möjliga styrande typer: sbyte, byte, short, ushortint, uint, , long, ulong, , charstringeller, en nullbar värdetyp som motsvarar en av dessa typer, är den konverterade typen den styrande typen av -instruktionenswitch.
  • Annars är instruktionens switch styrande typ typen av växeluttryck. Det är ett fel om det inte finns någon sådan typ.

Det kan finnas högst en default etikett i en switch -instruktion.

Det är ett fel om mönstret för en växeletikett inte är tillämpligt (§11.2.1) på typen av indatauttryck.

Det är ett fel om mönstret för en växeletikett underordnas (§11.3) uppsättningen mönster för tidigare växeletiketter för switch-instruktionen som inte har en skiftskydd eller vars skiftskydd är ett konstant uttryck med värdet true.

Exempel:

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.
}

slutexempel

En switch instruktion körs på följande sätt:

  • Switch-uttrycket utvärderas och konverteras till den styrande typen.
  • Kontrollen överförs enligt värdet för det konverterade växeluttrycket:
    • Det lexikalt första mönstret i uppsättningen case med etiketter i samma switch -instruktion som matchar värdet för switch-uttrycket, och för vilket skyddsuttrycket antingen saknas eller utvärderas till sant, gör att kontrollen överförs till instruktionslistan case efter den matchade etiketten.
    • Om en default etikett finns överförs annars kontrollen till instruktionslistan efter default etiketten.
    • Annars överförs kontrollen till slutpunkten för -instruktionen switch .

Obs! Den ordning i vilken mönster matchas vid körning definieras inte. En kompilator tillåts (men krävs inte) att matcha mönster i fel ordning och återanvända resultaten från redan matchade mönster för att beräkna resultatet av matchning av andra mönster. En kompilator krävs dock för att fastställa det lexikalt första mönstret som matchar uttrycket och för vilket skyddssatsen antingen saknas eller utvärderas till true. slutkommentar

Om slutpunkten för instruktionslistan för ett switch-avsnitt kan nås uppstår ett kompileringsfel. Detta kallas regeln "inget fall igenom".

Exempel: Exemplet

switch (i)
{
    case 0:
        CaseZero();
        break;
    case 1:
        CaseOne();
        break;
    default:
        CaseOthers();
        break;
}

är giltigt eftersom inget växelavsnitt har en nåbar slutpunkt. Till skillnad från C och C++ tillåts inte körning av ett växelavsnitt att "falla igenom" till nästa växelavsnitt, och exemplet

switch (i)
{
    case 0:
        CaseZero();
    case 1:
        CaseZeroOrOne();
    default:
        CaseAny();
}

resulterar i ett kompileringsfel. När körningen av ett växelavsnitt ska följas av ett annat växelavsnitt ska en explicit goto case eller goto default instruktion användas:

switch (i)
{
    case 0:
        CaseZero();
        goto case 1;
    case 1:
        CaseZeroOrOne();
        goto default;
    default:
        CaseAny();
        break;
}

slutexempel

Flera etiketter tillåts i en switch_section.

Exempel: Exemplet

switch (i)
{
    case 0:
        CaseZero();
        break;
    case 1:
        CaseOne();
        break;
    case 2:
    default:
        CaseTwo();
        break;
}

är giltig. Exemplet bryter inte mot regeln "no fall through" eftersom etiketterna case 2: och default: är en del av samma switch_section.

slutexempel

Obs! Regeln "no fall through" förhindrar en vanlig klass av buggar som inträffar i C och C++ när break instruktioner utelämnas av misstag. Avsnitten i instruktionen switch ovan kan till exempel ångras utan att påverka instruktionens beteende:

switch (i)
{
    default:
        CaseAny();
        break;
    case 1:
        CaseZeroOrOne();
        goto default;
    case 0:
        CaseZero();
        goto case 1;
}

slutkommentar

Obs! Instruktionslistan för ett switch-avsnitt slutar vanligtvis i en break, goto case, eller goto default -instruktion, men alla konstruktioner som återger slutpunkten för instruktionslistan kan inte nås. Till exempel är en while instruktion som styrs av det booleska uttrycket true känd för att aldrig nå sin slutpunkt. På samma sätt överför en throw eller return -instruktion alltid kontrollen någon annanstans och når aldrig sin slutpunkt. Därför är följande exempel giltigt:

switch (i)
{
     case 0:
         while (true)
         {
             F();
         }
     case 1:
         throw new ArgumentException();
     case 2:
         return;
}

slutkommentar

Exempel: Den styrande typen av en switch -instruktion kan vara typen string. Till exempel:

void DoCommand(string command)
{
    switch (command.ToLower())
    {
        case "run":
            DoRun();
            break;
        case "save":
            DoSave();
            break;
        case "quit":
            DoQuit();
            break;
        default:
            InvalidCommand(command);
            break;
    }
}

slutexempel

Obs! Precis som strängjämlikhetsoperatorerna (§12.12.8) är instruktionen switch skiftlägeskänslig och kör endast ett givet växelavsnitt om switchuttryckssträngen exakt matchar en case etikettkonstant. slutkommentar När den styrande typen av en switch instruktion är string eller en nullbar värdetyp tillåts värdet null som en case etikettkonstant.

Statement_list av en switch_block får innehålla deklarationsutdrag (§13.6). Omfånget för en lokal variabel eller konstant som deklarerats i ett växelblock är växelblocket.

En växeletikett kan nås om minst något av följande är sant:

  • Switch-uttrycket är ett konstant värde och antingen
    • etiketten är en case vars mönster skulle matcha (§11.2.1) det värdet, och etikettens skydd är antingen frånvarande eller inte ett konstant uttryck med värdet false, eller
    • det är en default etikett och inget växelavsnitt innehåller en skiftlägesetikett vars mönster skulle matcha det värdet, och vars skydd antingen är frånvarande eller ett konstant uttryck med värdet true.
  • Switch-uttrycket är inte ett konstant värde och antingen
    • etiketten är en case utan skydd eller med en vakt vars värde inte är konstant falskt, eller
    • det är en default etikett och
      • Den uppsättning mönster som förekommer bland fall av växelsatsen som inte har vakter eller har vakter vars värde är konstant sant, är inte fullständig (§11.4) för växelns styrtyp, eller
      • växelns styrningstyp är en nullbar typ och den uppsättning mönster som visas bland fallen med switch-instruktionen som inte har vakter eller har vakter vars värde är konstant sant innehåller inte ett mönster som skulle matcha värdet null.
  • Växeletiketten refereras till av en åtkomlig goto case eller goto default instruktion.

Instruktionslistan för ett angivet switchavsnitt kan nås om instruktionen switch kan nås och switchavsnittet innehåller en etikett för att nå en växlingsbar växel.

Slutpunkten för en switch -instruktion kan nås om switch-instruktionen kan nås och minst något av följande är sant:

  • -instruktionen switch innehåller en instruktion som kan break nås och som avslutar -instruktionen switch .
  • Det finns ingen default etikett och ingen av dem
    • Växlingsuttrycket är ett icke-konstant värde, och den uppsättning mönster som förekommer bland de fall av switch-instruktionen som inte har vakter eller har vakter vars värde är konstant sant, är inte fullständig (§11.4) för växelns styrtyp.
    • Switch-uttrycket är ett icke-konstant värde av en nullbar typ, och inget mönster visas bland fallen med switch-instruktionen som inte har vakter eller har vakter vars värde är konstant sant skulle matcha värdet null.
    • Switch-uttrycket är ett konstant värde och ingen case etikett utan skydd eller vars vakt är konstanten true skulle matcha det värdet.

Exempel: Följande kod visar en kortfattad användning av when -satsen:

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";
    }
}

Var-skiftläget matchar null, den tomma strängen eller en sträng som endast innehåller tomt utrymme. slutexempel

13.9 Iterationsinstruktioner

13.9.1 Allmänt

Iterationsinstruktioner kör upprepade gånger en inbäddad instruktion.

iteration_statement
    : while_statement
    | do_statement
    | for_statement
    | foreach_statement
    ;

13.9.2 While-instruktionen

Instruktionen while kör villkorligt en inbäddad instruktion noll eller fler gånger.

while_statement
    : 'while' '(' boolean_expression ')' embedded_statement
    ;

En while instruktion körs på följande sätt:

  • Boolean_expression (§12.24) utvärderas.
  • Om det booleska uttrycket ger trueöverförs kontrollen till den inbäddade instruktionen. När och om kontrollen når slutpunkten för den inbäddade -instruktionen (eventuellt från körning av en continue -instruktion) överförs kontrollen till början av -instruktionen while .
  • Om det booleska uttrycket ger falseöverförs kontrollen till slutpunkten för -instruktionen while .

I den inbäddade instruktionen i en while instruktion kan en break instruktion (§13.10.2) användas för att överföra kontrollen till slutpunkten för -instruktionen while (vilket avslutar iterationen av den inbäddade instruktionen) och ett continue uttalande (§13.10.3) kan användas för att överföra kontrollen till slutpunkten för den inbäddade instruktionen (vilket gör en annan iteration av -instruktionen while ).

Den inbäddade instruktionen för en while -instruktion kan nås om -instruktionen while kan nås och det booleska uttrycket inte har det konstanta värdet false.

Slutpunkten för en while -instruktion kan nås om minst något av följande är sant:

  • -instruktionen while innehåller en instruktion som kan break nås och som avslutar -instruktionen while .
  • -instruktionen while kan nås och det booleska uttrycket har inte konstantvärdet true.

13.9.3 Do-instruktionen

Instruktionen do kör villkorligt en inbäddad instruktion en eller flera gånger.

do_statement
    : 'do' embedded_statement 'while' '(' boolean_expression ')' ';'
    ;

En do instruktion körs på följande sätt:

  • Kontrollen överförs till den inbäddade instruktionen.
  • När och om kontrollen når slutpunkten för den inbäddade instruktionen (eventuellt från körning av en continue instruktion), utvärderas boolean_expression (§12.24). Om det booleska uttrycket ger trueöverförs kontrollen till början av -instruktionen do . Annars överförs kontrollen till slutpunkten för -instruktionen do .

I den inbäddade instruktionen i en do instruktion kan en break instruktion (§13.10.2) användas för att överföra kontrollen till slutpunkten för -instruktionen do (vilket avslutar iterationen av den inbäddade instruktionen) och ett continue uttalande (§13.10.3) kan användas för att överföra kontrollen till slutpunkten för den inbäddade instruktionen (vilket gör en annan iteration av -instruktionen do ).

Den inbäddade instruktionen för en do -instruktion kan nås om -instruktionen do kan nås.

Slutpunkten för en do -instruktion kan nås om minst något av följande är sant:

  • -instruktionen do innehåller en instruktion som kan break nås och som avslutar -instruktionen do .
  • Slutpunkten för den inbäddade -instruktionen kan nås och det booleska uttrycket har inte konstantvärdet true.

13.9.4 För -instruktionen

-instruktionen for utvärderar en sekvens med initieringsuttryck och kör sedan upprepade gånger en inbäddad instruktion och utvärderar en sekvens med iterationsuttryck.

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)*
    ;

Den for_initializer, om den finns, består av antingen en local_variable_declaration (§13.6.2) eller en lista över statement_expressions (§13.7) avgränsade med kommatecken. Omfånget för en lokal variabel som deklareras av en for_initializer är for_initializer, for_condition, for_iterator och embedded_statement.

I förekommande fall skall for_condition vara en boolean_expression (§12.24).

I förekommande fall består for_iterator av en lista över statement_expressions (§13.7) avgränsade med kommatecken.

En for instruktion körs på följande sätt:

  • Om det finns en for_initializer körs variabelinitierare eller instruktionsuttryck i den ordning de skrivs. Det här steget utförs bara en gång.
  • Om det finns en for_condition utvärderas den.
  • Om for_condition inte finns eller om utvärderingen ger trueöverförs kontrollen till den inbäddade instruktionen. När och om kontrollen når slutpunkten för den inbäddade -instruktionen (eventuellt från körning av en continue -instruktion), utvärderas uttrycken för for_iterator, om sådana finns, i sekvens, och sedan utförs en annan iteration, med början i utvärderingen av for_condition i steget ovan.
  • Om for_condition finns och utvärderingen ger falseöverförs kontrollen till slutpunkten för -instruktionenfor.

I den inbäddade instruktionen i en for instruktion kan en break instruktion (§13.10.2) användas för att överföra kontrollen till slutpunkten för -instruktionen for (vilket avslutar iterationen av den inbäddade instruktionen) och ett continue uttalande (§13.10.3) kan användas för att överföra kontrollen till slutpunkten för den inbäddade instruktionen (vilket utför for_iterator och utför en annan iteration av -instruktionenfor, börjar med for_condition).

Den inbäddade instruktionen för en for -instruktion kan nås om något av följande är sant:

  • Instruktionen for kan nås och inga for_condition finns.
  • Instruktionen for kan nås och en for_condition finns och har inte konstantvärdet false.

Slutpunkten för en for -instruktion kan nås om minst något av följande är sant:

  • -instruktionen for innehåller en instruktion som kan break nås och som avslutar -instruktionen for .
  • Instruktionen for kan nås och en for_condition finns och har inte konstantvärdet true.

13.9.5 Foreach-instruktionen

-instruktionen foreach räknar upp elementen i en samling och kör en inbäddad instruktion för varje element i samlingen.

foreach_statement
    : 'foreach' '(' ref_kind? local_variable_type identifier 'in' 
      expression ')' embedded_statement
    ;

Local_variable_type och identifieraren för en foreach-instruktion deklarerar iterationsvariabeln för -instruktionen. Om identifieraren var anges som local_variable_type och ingen typ med namnet var finns i omfånget, sägs iterationsvariabeln vara en implicit typ av iterationsvariabel och dess typ anses vara elementtypen för -instruktionen enligt beskrivningen foreach nedan.

Om foreach_statement innehåller både eller inget ref av dem och readonlyanger iterationsvariabeln en variabel som behandlas som skrivskyddad. Annars, om foreach_statement innehåller ref utan readonly, anger iterationsvariabeln en variabel som ska vara skrivbar.

Iterationsvariabeln motsvarar en lokal variabel med ett omfång som sträcker sig över den inbäddade instruktionen. Under körningen av en foreach -instruktion representerar iterationsvariabeln samlingselementet som en iteration för närvarande utförs för. Om iterationsvariabeln anger en skrivskyddad variabel uppstår ett kompileringsfel om den inbäddade instruktionen försöker ändra den (via tilldelning eller ++ operatorerna och -- ) eller skicka den som en referens- eller utdataparameter.

I följande, för korthet, , , och referera till motsvarande typer i namnrymderna IEnumerable och IEnumerator.IEnumerable<T>IEnumerator<T>System.CollectionsSystem.Collections.Generic

Kompileringstidsbearbetningen av en foreach -instruktion avgör först samlingstypen, uppräkningstypen och iterationstypen för uttrycket. Den här bedömningen fortsätter på följande sätt:

  • Om typen X av uttryck är en matristyp finns det en implicit referenskonvertering från X till IEnumerable gränssnittet (eftersom System.Array implementerar det här gränssnittet). Samlingstypen är IEnumerable gränssnittet, uppräkningstypen är IEnumerator gränssnittet och iterationstypen är elementtypen för matristypen X.
  • Om typen av uttryck finns finns det en implicit konvertering från X till gränssnittet (dynamic).IEnumerable Samlingstypen är IEnumerable gränssnittet och uppräkningstypen är IEnumerator gränssnittet. Om identifieraren var anges som local_variable_type är dynamiciterationstypen , annars är objectden .
  • I annat fall avgör du om typen X har en lämplig GetEnumerator metod:
    • Utför medlemssökning på typen X med identifierare GetEnumerator och inga typargument. Om medlemssökningen inte ger någon matchning, eller om den skapar en tvetydighet eller skapar en matchning som inte är en metodgrupp, kontrollerar du om det finns ett uppräkningsbart gränssnitt enligt beskrivningen nedan. Vi rekommenderar att en varning utfärdas om medlemssökningen genererar något annat än en metodgrupp eller ingen matchning.
    • Utför överbelastningsmatchning med hjälp av den resulterande metodgruppen och en tom argumentlista. Om överbelastningsmatchning inte resulterar i några tillämpliga metoder, resulterar i en tvetydighet eller resulterar i en enda bästa metod, men den metoden antingen är statisk eller inte offentlig, kontrollerar du om det finns ett uppräkningsbart gränssnitt enligt beskrivningen nedan. Vi rekommenderar att en varning utfärdas om överbelastningsmatchning ger något annat än en entydig offentlig instansmetod eller inga tillämpliga metoder.
    • Om returtypen E för GetEnumerator metoden inte är en klass, struct eller gränssnittstyp genereras ett fel och inga ytterligare åtgärder vidtas.
    • Medlemssökning utförs på E med identifieraren Current och inga typargument. Om medlemssökningen inte ger någon matchning är resultatet ett fel eller resultatet är något annat än en offentlig instansegenskap som tillåter läsning, ett fel skapas och inga ytterligare åtgärder vidtas.
    • Medlemssökning utförs på E med identifieraren MoveNext och inga typargument. Om medlemssökningen inte ger någon matchning är resultatet ett fel, eller resultatet är något annat än en metodgrupp, ett fel genereras och inga ytterligare åtgärder vidtas.
    • Överbelastningsmatchning utförs på metodgruppen med en tom argumentlista. Om överbelastningsmatchning inte resulterar i några tillämpliga metoder resulterar det i en tvetydighet eller resulterar i en enda bästa metod, men den metoden är antingen statisk eller inte offentlig, eller dess returtyp är inte bool, ett fel genereras och inga ytterligare åtgärder vidtas.
    • Samlingstypen är X, uppräkningstypen är Eoch iterationstypen är egenskapens Current typ. Egenskapen Current kan innehålla ref modifieraren, i vilket fall uttrycket som returneras är en variable_reference (§9.5) som är skrivskyddad.
  • Annars kontrollerar du om det finns ett uppräkningsbart gränssnitt:
    • Om det bland alla typer Tᵢ för vilka det finns en implicit konvertering från X till IEnumerable<Tᵢ>finns det en unik typ T som T inte dynamic är det och för alla andra Tᵢ finns det en implicit konvertering från IEnumerable<T> till IEnumerable<Tᵢ>, är samlingstypen gränssnittet IEnumerable<T>, uppräkningstypen är gränssnittet IEnumerator<T>och iterationstypen är T.
    • Om det finns fler än en sådan typ Tgenereras annars ett fel och inga ytterligare åtgärder vidtas.
    • Annars, om det finns en implicit konvertering från X till System.Collections.IEnumerable gränssnittet, är samlingstypen det här gränssnittet, uppräkningstypen är gränssnittet System.Collections.IEnumeratoroch iterationstypen är object.
    • Annars genereras ett fel och inga ytterligare åtgärder vidtas.

Ovanstående steg, om det lyckas, skapar entydigt en samlingstyp C, uppräkningstyp E och iterationstyp T, ref Teller ref readonly T. En foreach instruktion i formuläret

foreach (V v in x) «embedded_statement»

motsvarar sedan:

{
    E e = ((C)(x)).GetEnumerator();
    try
    {
        while (e.MoveNext())
        {
            V v = (V)(T)e.Current;
            «embedded_statement»
        }
    }
    finally
    {
        ... // Dispose e
    }
}

Variabeln e är inte synlig för eller tillgänglig för uttrycket x eller den inbäddade instruktionen eller någon annan källkod för programmet. Variabeln v är skrivskyddad i den inbäddade instruktionen. Om det inte finns en explicit konvertering (§10.3) från T (iterationstypen) till V ( local_variable_type i -instruktionen foreach ), genereras ett fel och inga ytterligare steg vidtas.

När iterationsvariabeln är en referensvariabel (§9.7), en foreach instruktion i formuläret

foreach (ref V v in x) «embedded_statement»

motsvarar sedan:

{
    E e = ((C)(x)).GetEnumerator();
    try
    {
        while (e.MoveNext())
        {
            ref V v = ref e.Current;
            «embedded_statement»
        }
    }
    finally
    {
        ... // Dispose e
    }
}

Variabeln e är inte synlig eller tillgänglig för uttrycket x eller den inbäddade instruktionen eller någon annan källkod för programmet. Referensvariabeln v är skrivskyddad i den inbäddade instruktionen, men v får inte omtilldelas (§12.21.3). Om det inte finns en identitetskonvertering (§10.2.2) från T (iterationstypen) till V (local_variable_typei -instruktionenforeach), genereras ett fel och inga ytterligare steg vidtas.

En foreach instruktion i formuläret foreach (ref readonly V v in x) «embedded_statement» har ett liknande motsvarande formulär, men referensvariabeln v finns ref readonly i den inbäddade -instruktionen och kan därför inte återtilldelas eller omtilldelas.

Obs! Om x har värdet nullgenereras en System.NullReferenceException vid körning. slutkommentar

En implementering är tillåten att implementera en viss foreach_statement på olika sätt, t.ex. av prestandaskäl, så länge beteendet överensstämmer med ovanstående expansion.

Placeringen av v inuti slingan while är viktig för hur den fångas in (§12.19.6.2) av någon anonym funktion som inträffar i embedded_statement.

Exempel:

int[] values = { 7, 9, 13 };
Action f = null;
foreach (var value in values)
{
    if (f == null)
    {
        f = () => Console.WriteLine("First value: " + value);
    }
}
f();

Om v det expanderade formuläret deklarerades utanför loopen while skulle det delas mellan alla iterationer och dess värde efter loopen for skulle vara det slutliga värdet, 13, vilket är vad anropet av f skulle skrivas ut. Eftersom varje iteration i stället har en egen variabel vfortsätter den som samlas in av f i den första iterationen att innehålla värdet 7, vilket är vad som ska skrivas ut. (Observera att tidigare versioner av C# som deklarerats v utanför loopen while .)

slutexempel

Blockets finally brödtext är konstruerad enligt följande steg:

  • Om det finns en implicit konvertering från E till System.IDisposable gränssnittet,

    • Om E är en icke-nullbar värdetyp finally expanderas satsen till den semantiska motsvarigheten till:

      finally
      {
          ((System.IDisposable)e).Dispose();
      }
      
    • Annars utökas finally satsen till den semantiska motsvarigheten till:

      finally
      {
          System.IDisposable d = e as System.IDisposable;
          if (d != null)
          {
              d.Dispose();
          }
      }
      

      förutom att om E är en värdetyp, eller en typparameter som instansieras till en värdetyp, ska konverteringen av e till System.IDisposable inte orsaka boxning.

  • Annars, om E är en förseglad typ, finally expanderas satsen till ett tomt block:

    finally {}
    
  • Annars utökas finally -satsen till:

    finally
    {
        System.IDisposable d = e as System.IDisposable;
        if (d != null)
        {
            d.Dispose();
        }
    }
    

Den lokala variabeln d är inte synlig för eller tillgänglig för någon användarkod. I synnerhet står den inte i konflikt med någon annan variabel vars omfång omfattar finally blocket.

Ordningen som foreach passerar elementen i en matris är följande: För endimensionella matriser bläddras elementen i ökande indexordning, börjar med index 0 och slutar med index Length – 1. För flerdimensionella matriser passerar elementen så att indexen för den högra dimensionen ökas först, sedan nästa vänstra dimension och så vidare till vänster.

Exempel: I följande exempel skrivs varje värde ut i en tvådimensionell matris i elementordning:

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();
    }
}

De utdata som genereras är följande:

1.2 2.3 3.4 4.5 5.6 6.7 7.8 8.9

slutexempel

Exempel: I följande exempel

int[] numbers = { 1, 3, 5, 7, 9 };
foreach (var n in numbers)
{
    Console.WriteLine(n);
}

typen av n härleds till int, iterationstypen för numbers.

slutexempel

13.10 Jump-instruktioner

13.10.1 Allmänt

Jump-instruktioner överför villkorslöst kontrollen.

jump_statement
    : break_statement
    | continue_statement
    | goto_statement
    | return_statement
    | throw_statement
    ;

Platsen där en jump-instruktion överför kontrollen kallas målet för jump-instruktionen.

När en jump-instruktion inträffar inom ett block och målet för den jump-instruktionen ligger utanför det blocket, sägs jump-instruktionen avsluta blocket. Även om en jump-instruktion kan överföra kontrollen från ett block, kan den aldrig överföra kontrollen till ett block.

Körning av jump-instruktioner kompliceras av förekomsten av mellanliggande try instruktioner. I avsaknad av sådana try instruktioner överför en jump-instruktion villkorslöst kontrollen från jump-instruktionen till målet. I närvaro av sådana mellanliggande try instruktioner är körningen mer komplex. Om jump-instruktionen avslutar ett eller flera try block med associerade finally block överförs finally kontrollen till blocket i den innersta try instruktionen. När och om kontrollen når slutpunkten för ett finally block överförs kontrollen till finally blocket för nästa omslutande-instruktion try . Den här processen upprepas tills blocken för finally alla mellanliggande try instruktioner har körts.

Exempel: I följande kod

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");
    }
}

blocken finally som är associerade med två try instruktioner körs innan kontrollen överförs till målet för jump-instruktionen. De utdata som genereras är följande:

Before break
Innermost finally block
Outermost finally block
After break

slutexempel

13.10.2 Brytsatsen

-instruktionen break avslutar den närmaste omslutande switch, while, do, foreller foreach -instruktionen.

break_statement
    : 'break' ';'
    ;

Målet för en break -instruktion är slutpunkten för den närmaste omslutande switch, , whiledo, foreller foreach -instruktionen. Om en break instruktion inte omges av en switchinstruktion , while, do, foreller foreach uppstår ett kompileringsfel.

När flera switch, while, do, foreller foreach -instruktioner kapslas inom varandra, gäller en break instruktion endast för den innersta instruktionen. För att överföra kontrollen över flera kapslingsnivåer ska en goto instruktion (§13.10.4) användas.

En break instruktion kan inte avsluta ett finally block (§13.11). När en break instruktion inträffar inom ett finally block ska målet för instruktionen break ligga inom samma finally block. Annars uppstår ett kompileringsfel.

En break instruktion körs på följande sätt:

  • Om instruktionen break avslutar ett eller flera try block med associerade finally block överförs finally kontrollen till blocket i den innersta try instruktionen. När och om kontrollen når slutpunkten för ett finally block överförs kontrollen till finally blocket för nästa omslutande-instruktion try . Den här processen upprepas tills blocken för finally alla mellanliggande try instruktioner har körts.
  • Kontrollen överförs till målet för -instruktionen break .

Eftersom en break instruktion villkorslöst överför kontrollen någon annanstans kan slutpunkten för en break -instruktion aldrig nås.

13.10.3 Instruktionen Fortsätt

-instruktionen continue startar en ny iteration av närmaste omslutande while, do, foreller foreach -instruktion.

continue_statement
    : 'continue' ';'
    ;

Målet för en continue -instruktion är slutpunkten för den inbäddade -instruktionen för närmaste omslutande while, do, foreller foreach -instruktion. Om en continue instruktion inte omges av en while- - doforeller foreach -instruktion uppstår ett kompileringsfel.

När flera while, do, foreller foreach -instruktioner kapslas inom varandra, gäller en continue -instruktion endast för den innersta instruktionen. För att överföra kontrollen över flera kapslingsnivåer ska en goto instruktion (§13.10.4) användas.

En continue instruktion kan inte avsluta ett finally block (§13.11). När en continue instruktion inträffar inom ett finally block ska målet för instruktionen continue ligga inom samma finally block. Annars uppstår ett kompileringsfel.

En continue instruktion körs på följande sätt:

  • Om instruktionen continue avslutar ett eller flera try block med associerade finally block överförs finally kontrollen till blocket i den innersta try instruktionen. När och om kontrollen når slutpunkten för ett finally block överförs kontrollen till finally blocket för nästa omslutande-instruktion try . Den här processen upprepas tills blocken för finally alla mellanliggande try instruktioner har körts.
  • Kontrollen överförs till målet för -instruktionen continue .

Eftersom en continue instruktion villkorslöst överför kontrollen någon annanstans kan slutpunkten för en continue -instruktion aldrig nås.

13.10.4 Goto-instruktionen

-instruktionen goto överför kontrollen till en -instruktion som är markerad med en etikett.

goto_statement
    : 'goto' identifier ';'
    | 'goto' 'case' constant_expression ';'
    | 'goto' 'default' ';'
    ;

Målet för en gotoidentifieraruttryck är den märkta instruktionen med den angivna etiketten. Om en etikett med det angivna namnet inte finns i den aktuella funktionsmedlemmen, eller om -instruktionen goto inte ligger inom etikettens omfång, uppstår ett kompileringsfel.

Obs! Den här regeln tillåter användning av en goto -instruktion för att överföra kontrollen från ett kapslat omfång, men inte till ett kapslat omfång. I exemplet

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}]");
        }
    }
}

en goto instruktion används för att överföra kontrollen från ett kapslat omfång.

slutkommentar

Målet för en goto case instruktion är instruktionslistan i den omedelbart omslutande switch instruktionen (§13.8.3) som innehåller en case etikett med ett konstant mönster av det angivna konstantvärdet och inget skydd. Om instruktionen goto case inte omges av en switch instruktion, om den närmaste omslutande switch instruktionen inte innehåller en casesådan , eller om constant_expression inte implicit är konvertibel (§10.2) till den styrande typen av närmaste omslutande instruktion, uppstår ett kompileringsfel switch .

Målet med en goto default instruktion är instruktionslistan i den omedelbart omslutande switch instruktionen (§13.8.3), som innehåller en default etikett. Om -instruktionen goto default inte omges av en switch -instruktion, eller om den närmaste omslutande switch instruktionen inte innehåller en default etikett, uppstår ett kompileringsfel.

En goto instruktion kan inte avsluta ett finally block (§13.11). När en goto instruktion inträffar inom ett finally block ska målet för -instruktionen goto ligga inom samma finally block, eller på annat sätt uppstår ett kompileringsfel.

En goto instruktion körs på följande sätt:

  • Om instruktionen goto avslutar ett eller flera try block med associerade finally block överförs finally kontrollen till blocket i den innersta try instruktionen. När och om kontrollen når slutpunkten för ett finally block överförs kontrollen till finally blocket för nästa omslutande-instruktion try . Den här processen upprepas tills blocken för finally alla mellanliggande try instruktioner har körts.
  • Kontrollen överförs till målet för -instruktionen goto .

Eftersom en goto instruktion villkorslöst överför kontrollen någon annanstans kan slutpunkten för en goto -instruktion aldrig nås.

13.10.5 Retursatsen

-instruktionen return returnerar kontrollen till den aktuella anroparen för funktionsmedlemmen där retursatsen visas, och returnerar eventuellt ett värde eller en variable_reference (§9.5).

return_statement
    : 'return' ';'
    | 'return' expression ';'
    | 'return' 'ref' variable_reference ';'
    ;

Ett return_statement utan uttryck kallas för return-no-value. Ett som innehåller uttryck kallas return-by-ref refoch ett som endast innehåller uttryck kallas för return-by-value.

Det är ett kompileringsfel att använda ett return-no-value från en metod som deklareras som return-by-value eller returns-by-ref (§15.6.1).

Det är ett kompileringsfel att använda en return-by-ref från en metod som deklareras som returns-no-value eller returns-by-value.

Det är ett kompileringsfel att använda ett return-by-value från en metod som deklareras som returns-no-value eller returns-by-ref.

Det är ett kompileringsfel att använda en return-by-ref om uttrycket inte är en variable_reference eller är en referens till en variabel vars referens-safe-context inte är caller-context (§9.7.2).

Det är ett kompileringsfel att använda en return-by-ref från en metod som deklarerats med method_modifierasync.

En funktionsmedlem sägs beräkna ett värde om det är en metod med metoden returns-by-value (§15.6.11), en return-by-value get-accessor för en egenskap eller indexerare eller en användardefinierad operator. Funktionsmedlemmar som är return-no-value beräknar inte ett värde och är metoder med den effektiva returtypen void, anger åtkomst till egenskaper och indexerare, lägger till och tar bort åtkomst till händelser, instanskonstruktorer, statiska konstruktorer och finalizers. Funktionsmedlemmar som returneras per referens beräknar inte ett värde.

För en return-by-value ska en implicit konvertering (§10.2) finnas från uttryckstypen till den effektiva returtypen (§15.6.11) av den innehållande funktionsmedlemmen. För en return-by-ref ska en identitetskonvertering (§10.2.2) finnas mellan uttryckstypen och den faktiska returtypen för den innehållande funktionsmedlemmen.

return instruktioner kan också användas i brödtexten av anonyma funktionsuttryck (§12.19) och delta i att avgöra vilka konverteringar som finns för dessa funktioner (§10.7.1).

Det är ett kompileringsfel för en return instruktion som ska visas i ett finally block (§13.11).

En return instruktion körs på följande sätt:

  • För ett return-by-value utvärderas uttrycket och dess värde konverteras till den effektiva returtypen för den innehållande funktionen genom en implicit konvertering. Resultatet av konverteringen blir det resultatvärde som genereras av funktionen. För en return-by-ref utvärderas uttrycket och resultatet ska klassificeras som en variabel. Om den omslutande metodens return-by-ref innehåller readonlyär den resulterande variabeln skrivskyddad.
  • Om -instruktionen return omges av en eller flera try eller catch block med associerade finally block överförs finally kontrollen till blocket i den innersta try instruktionen. När och om kontrollen når slutpunkten för ett finally block överförs kontrollen till finally blocket för nästa omslutande-instruktion try . Den här processen upprepas tills blocken finally för alla omslutande try instruktioner har körts.
  • Om den innehållande funktionen inte är en asynkron funktion, returneras kontrollen till anroparen för den innehållande funktionen tillsammans med resultatvärdet, om någon.
  • Om den innehållande funktionen är en asynkron funktion returneras kontrollen till den aktuella anroparen och resultatvärdet, om det finns något, registreras i returaktiviteten enligt beskrivningen i (§15.15.3).

Eftersom en return instruktion villkorslöst överför kontrollen någon annanstans kan slutpunkten för en return -instruktion aldrig nås.

13.10.6 Instruktionen kasta

-instruktionen throw utlöser ett undantag.

throw_statement
    : 'throw' expression? ';'
    ;

En throw -instruktion med ett uttryck utlöser ett undantag som skapas genom att uttrycket utvärderas. Uttrycket ska implicit konverteras till System.Exception, och resultatet av utvärderingen av uttrycket konverteras till System.Exception innan det utlöses. Om resultatet av konverteringen är nullgenereras en System.NullReferenceException i stället.

En throw -instruktion utan uttryck kan endast användas i ett catch block, i vilket fall instruktionen åter genererar undantaget som för närvarande hanteras av det catch blocket.

Eftersom en throw instruktion villkorslöst överför kontrollen någon annanstans kan slutpunkten för en throw -instruktion aldrig nås.

När ett undantag utlöses överförs kontrollen till den första catch satsen i en omslutande try instruktion som kan hantera undantaget. Den process som sker från den tidpunkt då undantaget utlöses till den tidpunkt då kontrollen överfördes till en lämplig undantagshanterare kallas för undantagsspridning. Spridning av ett undantag består av upprepade utvärderingar av följande steg tills en catch sats som matchar undantaget hittas. I den här beskrivningen är startpunkten den plats där undantaget genereras. Det här beteendet anges i (§21.4).

  • I den aktuella funktionsmedlemmen undersöks varje try instruktion som omger kastpunkten. För varje instruktion S, som börjar med den innersta try instruktionen och slutar med den yttersta try instruktionen, utvärderas följande steg:

    • try Om blocket S omsluter kastpunkten och om S det finns en eller flera catch satser, granskas satserna catch i utseendeordning för att hitta en lämplig hanterare för undantaget. Den första catch satsen som anger en undantagstyp T (eller en typparameter som vid körning anger en undantagstyp T) så att körningstypen för E härleds från T betraktas som en matchning. Om satsen innehåller ett undantagsfilter tilldelas undantagsobjektet till undantagsvariabeln och undantagsfiltret utvärderas. När en catch sats innehåller ett undantagsfilter betraktas den satsen som catch en matchning om undantagsfiltret utvärderas till true. En allmän catch klausul (§13.11) anses vara en matchning för alla undantagstyper. Om en matchande catch sats finns slutförs undantagsspridningen genom att kontrollen överförs till blocket i den catch satsen.
    • Annars, om try blocket eller ett catch block av S omsluter kastpunkten och om S har ett finally block, överförs kontrollen till finally blocket. Om blocket finally utlöser ett annat undantag avslutas bearbetningen av det aktuella undantaget. I annat fall fortsätter bearbetningen finally av det aktuella undantaget när kontrollen når blockets slutpunkt.
  • Om en undantagshanterare inte finns i den aktuella funktionsanropet avslutas funktionsanropet och något av följande inträffar:

    • Om den aktuella funktionen inte är asynkron upprepas stegen ovan för anroparen av funktionen med en utkastspunkt som motsvarar instruktionen som funktionsmedlemmen anropades från.

    • Om den aktuella funktionen är asynkron och uppgiftsretur registreras undantaget i returaktiviteten, som försätts i ett felaktigt eller avbrutet tillstånd enligt beskrivningen i §15.15.3.

    • Om den aktuella funktionen är asynkron och voidreturneras meddelas synkroniseringskontexten för den aktuella tråden enligt beskrivningen i §15.15.4.

  • Om undantagsbearbetningen avslutar alla funktionsmedlemsanrop i den aktuella tråden, vilket indikerar att tråden inte har någon hanterare för undantaget, avslutas tråden själv. Effekten av en sådan uppsägning är implementeringsdefinierad.

13.11 Try-instruktionen

Instruktionen try innehåller en mekanism för att fånga undantag som inträffar under körningen av ett block. Dessutom ger -instruktionen try möjlighet att ange ett kodblock som alltid körs när kontrollen lämnar -instruktionen try .

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
    ;

Ett try_statement består av nyckelordet try följt av ett block, sedan noll eller fler catch_clauses och sedan en valfri finally_clause. Det ska finnas minst en catch_clause eller en finally_clause.

I ett exception_specifierska typen, eller dess effektiva basklass om den är en type_parameter, vara System.Exception eller en typ som härleds från den.

När en catch sats anger både en class_type och en identifierare deklareras en undantagsvariabel för det angivna namnet och typen. Undantagsvariabeln introduceras i deklarationsutrymmet för specific_catch_clause (§7.3). Under körningen av exception_filter och catch blockera representerar undantagsvariabeln det undantag som för närvarande hanteras. Vid slutgiltig tilldelningskontroll anses undantagsvariabeln definitivt tilldelad i hela omfånget.

Om inte en catch sats innehåller ett undantagsvariabelnamn är det omöjligt att komma åt undantagsobjektet i filtret och catch blocket.

En catch sats som varken anger en undantagstyp eller ett undantagsvariabelnamn kallas för en allmän catch sats. En try instruktion kan bara ha en allmän catch klausul, och om en finns ska den vara den sista catch satsen.

Obs! Vissa programmeringsspråk kan ha stöd för undantag som inte kan representeras som ett objekt som härleds från System.Exception, även om sådana undantag aldrig kan genereras av C#-kod. En allmän catch sats kan användas för att fånga sådana undantag. Därför skiljer sig en allmän catch sats semantiskt från en som anger typen System.Exception, eftersom den förstnämnda också kan fånga undantag från andra språk. slutkommentar

För att hitta en hanterare för ett undantag catch granskas satser i lexikal ordning. Om en catch sats anger en typ men inget undantagsfilter är det ett kompileringsfel för en senare catch sats av samma try instruktion för att ange en typ som är samma som eller härleds från den typen.

Obs! Utan den här begränsningen skulle det vara möjligt att skriva oåtkomliga catch satser. slutkommentar

Inom ett catch block kan en throw instruktion (§13.10.6) utan uttryck användas för att återskapa undantaget som fångades av catch blocket. Tilldelningar till en undantagsvariabel ändrar inte undantaget som genereras igen.

Exempel: I följande kod

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);
        }
    }
}

metoden F fångar upp ett undantag, skriver viss diagnostikinformation till konsolen, ändrar undantagsvariabeln och genererar undantaget igen. Undantaget som genereras på nytt är det ursprungliga undantaget, så de utdata som genereras är:

Exception in F: G
Exception in Main: G

Om det första catch blocket hade genererats e i stället för att återvätna det aktuella undantaget skulle de utdata som skapas vara följande:

Exception in F: G
Exception in Main: F

slutexempel

Det är ett kompileringsfel för en - eller breakcontinue -gotoinstruktion för att överföra kontrollen från ett finally block. När en break, continue, eller goto -instruktion inträffar i ett finally block, ska målet för -instruktionen ligga inom samma finally block, eller på annat sätt uppstår ett kompileringsfel.

Det är ett kompileringsfel för en return instruktion som ska inträffa i ett finally block.

När körningen når en try -instruktion överförs kontrollen till try blocket. Om kontrollen når blockets slutpunkt try utan att ett undantag sprids överförs kontrollen till finally blocket om det finns någon sådan. Om det inte finns något finally block överförs kontrollen till slutpunkten för -instruktionen try .

Om ett undantag har spridits catch granskas eventuella klausuler i lexikal ordning för att söka den första matchningen för undantaget. Sökningen efter en matchningsklausul catch fortsätter med alla omslutande block enligt beskrivningen i §13.10.6. En catch sats är en matchning om undantagstypen matchar alla exception_specifier och eventuella exception_filter är sanna. En catch sats utan exception_specifier matchar alla undantagstyper. Undantagstypen matchar exception_specifier när exception_specifier anger undantagstypen eller en bastyp för undantagstypen. Om satsen innehåller ett undantagsfilter tilldelas undantagsobjektet till undantagsvariabeln och undantagsfiltret utvärderas.

Om ett undantag har spridits och en matchande catch sats hittas överförs kontrollen till det första matchande catch blocket. Om kontrollen når blockets slutpunkt catch utan att ett undantag sprids överförs kontrollen till finally blocket om det finns någon sådan. Om det inte finns något finally block överförs kontrollen till slutpunkten för -instruktionen try . Om ett undantag har spridits från catch blocket kontrollerar du överföringar till finally blocket om det finns något. Undantaget sprids till nästa omslutande try instruktion.

Om ett undantag har spridits och ingen matchningssats catch hittas kontrollerar du överföringar till finally blocket om det finns. Undantaget sprids till nästa omslutande try instruktion.

Instruktioner för ett finally block körs alltid när kontrollen lämnar en try -instruktion. Detta gäller om kontrollöverföringen sker som ett resultat av normal körning, som ett resultat av att en break- , continuegoto, eller return -instruktion har körts eller som ett resultat av att ett undantag sprids ut ur -instruktionentry. Om kontrollen når slutpunkten för finally blocket utan att ett undantag sprids överförs kontrollen till slutpunkten för -instruktionen try .

Om ett undantag utlöses under körningen av ett finally block och inte fångas inom samma finally block, sprids undantaget till nästa omslutande try -instruktion. Om ett annat undantag höll på att spridas går undantaget förlorat. Processen att sprida ett undantag diskuteras ytterligare i beskrivningen av instruktionen throw (§13.10.6).

Exempel: I följande kod

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");
        }
    }
}

metoden Method utlöser ett undantag. Den första åtgärden är att undersöka omslutande catch satser och köra eventuella undantagsfilter. finally Sedan körs -satsen i Method innan kontrollen överförs till den omslutande matchningssatsencatch. De resulterande utdata är:

Filter
Finally
Catch

slutexempel

Blocket try för en try -instruktion kan nås om -instruktionen try kan nås.

Ett catch block med en try instruktion kan nås om -instruktionen try kan nås.

Blocket finally för en try -instruktion kan nås om -instruktionen try kan nås.

Slutpunkten för en try -instruktion kan nås om båda följande är sanna:

  • Blockets try slutpunkt kan nås eller så kan slutpunkten för minst ett catch block nås.
  • Om det finns ett finally block kan blockets finally slutpunkt nås.

13.12 De markerade och omarkerade instruktionerna

-checkedinstruktionerna och unchecked används för att styra överflödeskontrollkontexten för aritmetiska åtgärder av typen integraltyp och konverteringar.

checked_statement
    : 'checked' block
    ;

unchecked_statement
    : 'unchecked' block
    ;

-instruktionen checkedgör att alla uttryck i blocket utvärderas i en markerad kontext, och -instruktionen unchecked gör att alla uttryck i blocket utvärderas i en omarkerad kontext.

Och-instruktionerna checkedunchecked är exakt likvärdiga med operatorerna checked och unchecked (§12.8.20), förutom att de fungerar på block i stället för uttryck.

13.13 Låssatsen

-instruktionen lock hämtar låset för ömsesidig uteslutning för ett visst objekt, kör en -instruktion och släpper sedan låset.

lock_statement
    : 'lock' '(' expression ')' embedded_statement
    ;

Uttrycket för en lock förklaring ska ange ett värde av en typ som är känd för att vara en referens. Ingen implicit boxningskonvertering (§10.2.9) utförs någonsin för uttrycket av en lock instruktion, och därför är det ett kompileringsfel för uttrycket att ange ett värde för en value_type.

En lock instruktion i formuläret

lock (x)

där x är ett uttryck för en reference_type, är exakt detsamma som:

bool __lockWasTaken = false;
try
{
    System.Threading.Monitor.Enter(x, ref __lockWasTaken);
    ...
}
finally
{
    if (__lockWasTaken)
    {
        System.Threading.Monitor.Exit(x);
    }
}

förutom att x endast utvärderas en gång.

Medan ett lås för ömsesidig uteslutning hålls kan kod som körs i samma körningstråd också hämta och frigöra låset. Men kodkörning i andra trådar blockeras från att hämta låset tills låset släpps.

13.14 Instruktionen using

-instruktionen using hämtar en eller flera resurser, kör en instruktion och gör sig sedan av med resursen.

using_statement
    : 'using' '(' resource_acquisition ')' embedded_statement
    ;

resource_acquisition
    : local_variable_declaration
    | expression
    ;

En resurs är en klass eller struct som implementerar System.IDisposable gränssnittet, som innehåller en enda parameterlös metod med namnet Dispose. Kod som använder en resurs kan anropa Dispose för att indikera att resursen inte längre behövs.

Om resource_acquisition är local_variable_declaration ska typen av local_variable_declaration vara antingen dynamic eller en typ som implicit kan konverteras till .System.IDisposable Om formen av resource_acquisition är uttryck ska det här uttrycket implicit konverteras till System.IDisposable.

Lokala variabler som deklareras i en resource_acquisition är skrivskyddade och ska innehålla en initierare. Ett kompileringstidsfel uppstår om den inbäddade instruktionen försöker ändra dessa lokala variabler (via tilldelning eller ++ operatorerna och -- ), ta adressen till dem eller skicka dem som referens- eller utdataparametrar.

En using instruktion översätts till tre delar: förvärv, användning och avyttring. Användningen av resursen omges implicit av en try instruktion som innehåller en finally -sats. Den här finally satsen gör sig av med resursen. Om en null resurs hämtas görs inget anrop till Dispose och inget undantag genereras. Om resursen är av typen dynamic konverteras den dynamiskt genom en implicit dynamisk konvertering (§10.2.10) till IDisposable under förvärvet för att säkerställa att konverteringen lyckas före användning och avyttring.

En using instruktion i formuläret

using (ResourceType resource = «expression» ) «statement»

motsvarar en av tre möjliga expansioner. När ResourceType är en icke-nullbar värdetyp eller en typparameter med begränsningen för värdetyp (§15.2.5) är expansionen semantiskt likvärdig med

{
    ResourceType resource = «expression»;
    try
    {
        «statement»;
    }
    finally
    {
        ((IDisposable)resource).Dispose();
    }
}

utom att kastet av resource till System.IDisposable inte får orsaka boxning.

I annat fall, när ResourceType är dynamic, är expansionen

{
    ResourceType resource = «expression»;
    IDisposable d = resource;
    try
    {
        «statement»;
    }
    finally
    {
        if (d != null)
        {
            d.Dispose();
        }
    }
}

I annat fall är expansionen

{
    ResourceType resource = «expression»;
    try
    {
        «statement»;
    }
    finally
    {
        IDisposable d = (IDisposable)resource;
        if (d != null)
        {
            d.Dispose();
        }
    }
}

I alla expansioner är variabeln resource skrivskyddad i den inbäddade instruktionen och variabeln d är otillgänglig i och osynlig för den inbäddade instruktionen.

En implementering är tillåten att implementera en viss using_statement på olika sätt, t.ex. av prestandaskäl, så länge beteendet överensstämmer med ovanstående expansion.

En using instruktion i formuläret:

using («expression») «statement»

har samma tre möjliga expansioner. I det här fallet ResourceType är uttryckets kompileringstidstyp implicit, om det har en. Annars används själva gränssnittet IDisposable som ResourceType. Variabeln resource är otillgänglig i och osynlig för den inbäddade instruktionen.

När en resource_acquisition har formen av en local_variable_declaration går det att hämta flera resurser av en viss typ. En using instruktion i formuläret

using (ResourceType r1 = e1, r2 = e2, ..., rN = eN) «statement»

motsvarar exakt en sekvens med kapslade using instruktioner:

using (ResourceType r1 = e1)
using (ResourceType r2 = e2)
...
using (ResourceType rN = eN)
«statement»

Exempel: I exemplet nedan skapas en fil med namnet log.txt och två textrader skrivs till filen. Exemplet öppnar sedan samma fil för att läsa och kopierar de inneslutna textraderna till konsolen.

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);
            }
        }
    }
}

TextWriter Eftersom klasserna och TextReader implementerar IDisposable gränssnittet kan exemplet använda using instruktioner för att säkerställa att den underliggande filen stängs korrekt efter skriv- eller läsåtgärderna.

slutexempel

13.15 Avkastningsutdraget

Instruktionen yield används i ett iteratorblock (§13.3) för att ge ett värde till uppräkningsobjektet (§15.14.5) eller uppräkningsbart objekt (§15.14.6) av en iterator eller för att signalera slutet på iterationen.

yield_statement
    : 'yield' 'return' expression ';'
    | 'yield' 'break' ';'
    ;

yield är ett kontextuellt nyckelord (§6.4.4) och har särskild betydelse endast när det används omedelbart före ett return eller break ett nyckelord.

Det finns flera begränsningar för var en yield instruktion kan visas, enligt beskrivningen i följande.

  • Det är ett kompileringsfel för en yield instruktion (i något av formulären) som ska visas utanför en method_body, operator_body eller accessor_body.
  • Det är ett kompileringsfel för en yield instruktion (i något av formulären) som ska visas i en anonym funktion.
  • Det är ett kompileringsfel för en yield instruktion (i något av formulären) som ska visas i instruktionens finally sats try .
  • Det är ett kompileringsfel för en yield return instruktion som ska visas var som helst i en try -instruktion som innehåller alla catch_clauses.

Exempel: I följande exempel visas några giltiga och ogiltiga användningsområden för yield instruktioner.

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
}

slutexempel

En implicit konvertering (§10.2) ska finnas från typen av uttrycket i instruktionen yield return till iteratorns avkastningstyp (§15.14.4).

En yield return instruktion körs på följande sätt:

  • Uttrycket som anges i -instruktionen utvärderas, konverteras implicit till avkastningstypen och tilldelas egenskapen för Current uppräkningsobjektet.
  • Körningen av iteratorblocket pausas. Om -instruktionen yield return finns inom ett eller flera try block körs inte de associerade finally blocken just nu.
  • Metoden MoveNext för uppräkningsobjektet återgår true till anroparen, vilket anger att uppräkningsobjektet har avancerat till nästa objekt.

Nästa anrop till uppräkningsobjektets MoveNext metod återupptar körningen av iteratorblocket där det senast pausades.

En yield break instruktion körs på följande sätt:

  • Om -instruktionen yield break omges av ett eller flera try block med associerade finally block överförs kontrollen initialt till finally blocket i den innersta try instruktionen. När och om kontrollen når slutpunkten för ett finally block överförs kontrollen till finally blocket för nästa omslutande-instruktion try . Den här processen upprepas tills blocken finally för alla omslutande try instruktioner har körts.
  • Kontrollen returneras till anroparen för iteratorblocket. Det här är antingen MoveNext metoden eller Dispose metoden för uppräkningsobjektet.

Eftersom en yield break instruktion villkorslöst överför kontrollen någon annanstans kan slutpunkten för en yield break -instruktion aldrig nås.