Delen via


13 Instructies

13.1 Algemeen

C# biedt verschillende instructies.

Opmerking: De meeste van deze instructies zijn bekend bij ontwikkelaars die zijn geprogrammeerd in C en C++. eindnotitie

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) en fixed_statement (§23.7) zijn alleen beschikbaar in onveilige code (§23).

De embedded_statement niet-terminal wordt gebruikt voor instructies die in andere instructies worden weergegeven. Het gebruik van embedded_statement in plaats van instructie sluit het gebruik van declaratie-instructies en gelabelde instructies in deze contexten uit.

Voorbeeld: De code

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

resulteert in een compilatiefout omdat voor een if instructie een embedded_statement is vereist in plaats van een instructie voor de if vertakking. Als deze code is toegestaan, wordt de variabele i gedeclareerd, maar kan deze nooit worden gebruikt. Houd er echter rekening mee dat het voorbeeld geldig is door de aangifte in een blok te plaatsen i.

eindvoorbeeld

13.2 Eindpunten en bereikbaarheid

Elke instructie heeft een eindpunt. In intuïtieve termen is het eindpunt van een instructie de locatie die direct na de instructie volgt. De uitvoeringsregels voor samengestelde instructies (instructies die ingesloten instructies bevatten) geven de actie op die wordt uitgevoerd wanneer het besturingselement het eindpunt van een ingesloten instructie bereikt.

Voorbeeld: Wanneer het besturingselement het eindpunt van een instructie in een blok bereikt, wordt het besturingselement overgebracht naar de volgende instructie in het blok. eindvoorbeeld

Als een instructie mogelijk kan worden bereikt door uitvoering, wordt gezegd dat de instructie bereikbaar is. Als er echter geen mogelijkheid is dat een instructie wordt uitgevoerd, wordt gezegd dat de instructie onbereikbaar is.

Voorbeeld: In de volgende code

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

de tweede aanroep van Console.WriteLine is onbereikbaar omdat de instructie niet kan worden uitgevoerd.

eindvoorbeeld

Er wordt een waarschuwing gerapporteerd als een andere instructie dan throw_statement, blokkeren of empty_statement niet bereikbaar is. Het is specifiek geen fout dat een instructie onbereikbaar is.

Opmerking: om te bepalen of een bepaalde instructie of eindpunt bereikbaar is, voert een compiler stroomanalyse uit op basis van de bereikbaarheidsregels die voor elke instructie zijn gedefinieerd. De stroomanalyse houdt rekening met de waarden van constante expressies (§12.23) die het gedrag van instructies bepalen, maar de mogelijke waarden van niet-constante expressies worden niet overwogen. Met andere woorden, voor controlestroomanalyse wordt een niet-constante expressie van een bepaald type beschouwd als een mogelijke waarde van dat type.

In het voorbeeld

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

de Booleaanse expressie van de if instructie is een constante expressie omdat beide operanden van de == operator constanten zijn. Wanneer de constante expressie tijdens het compileren wordt geëvalueerd, wordt de false aanroep Console.WriteLineals onbereikbaar beschouwd. i Als dit echter wordt gewijzigd in een lokale variabele

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

de Console.WriteLine aanroep wordt beschouwd als bereikbaar, ook al wordt deze in werkelijkheid nooit uitgevoerd.

eindnotitie

Het blok van een functielid of een anonieme functie wordt altijd als bereikbaar beschouwd. Door achtereenvolgens de bereikbaarheidsregels van elke instructie in een blok te evalueren, kan de bereikbaarheid van een bepaalde instructie worden bepaald.

Voorbeeld: In de volgende code

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

de bereikbaarheid van de tweede Console.WriteLine wordt als volgt bepaald:

  • De eerste Console.WriteLine expressie-instructie is bereikbaar omdat het blok van de F methode bereikbaar is (§13.3).
  • Het eindpunt van de eerste Console.WriteLine expressie-instructie is bereikbaar omdat deze instructie bereikbaar is (§13.7 en §13.3).
  • De if instructie is bereikbaar omdat het eindpunt van de eerste Console.WriteLine expressie-instructie bereikbaar is (§13.7 en §13.3).
  • De tweede Console.WriteLine expressie-instructie is bereikbaar omdat de Boole-expressie van de if instructie niet de constante waarde falseheeft.

eindvoorbeeld

Er zijn twee situaties waarin het een compilatiefout is voor het eindpunt van een instructie bereikbaar is:

  • Omdat de switch instructie niet toestaat dat een switchsectie naar de volgende switchsectie 'valt', is het een compilatiefout voor het eindpunt van de instructielijst van een switchsectie bereikbaar. Als deze fout optreedt, is het meestal een indicatie dat een break instructie ontbreekt.

  • Het is een compilatiefout voor het eindpunt van het blok van een functielid of een anonieme functie waarmee een waarde wordt berekend die bereikbaar is. Als deze fout optreedt, is het meestal een indicatie dat een return instructie ontbreekt (§13.10.5).

13.3 Blokken

13.3.1 Algemeen

Met een blok kunnen meerdere instructies worden geschreven in contexten waarin één instructie is toegestaan.

block
    : '{' statement_list? '}'
    ;

Een blok bestaat uit een optionele statement_list (§13.3.2), tussen accolades. Als de lijst met instructies wordt weggelaten, wordt gezegd dat het blok leeg is.

Een blok kan declaratieverklaringen bevatten (§13.6). Het bereik van een lokale variabele of constante die in een blok is gedeclareerd, is het blok.

Er wordt als volgt een blok uitgevoerd:

  • Als het blok leeg is, wordt het besturingselement overgebracht naar het eindpunt van het blok.
  • Als het blok niet leeg is, wordt het besturingselement overgebracht naar de lijst met instructies. Wanneer en als het besturingselement het eindpunt van de instructielijst bereikt, wordt het besturingselement overgebracht naar het eindpunt van het blok.

De instructielijst van een blok is bereikbaar als het blok zelf bereikbaar is.

Het eindpunt van een blok is bereikbaar als het blok leeg is of als het eindpunt van de lijst met instructies bereikbaar is.

Een blok met een of meer yield instructies (§13.15) wordt een iteratorblok genoemd. Iteratorblokken worden gebruikt om functieleden te implementeren als iterators (§15.14). Enkele aanvullende beperkingen zijn van toepassing op iterator-blokken:

  • Het is een compilatiefout voor een return instructie die wordt weergegeven in een iteratorblok (maar yield return instructies zijn toegestaan).
  • Het is een compilatiefout voor een iteratorblok dat een onveilige context bevat (§23.2). Een iteratorblok definieert altijd een veilige context, zelfs wanneer de declaratie is genest in een onveilige context.

13.3.2 Overzichtslijsten

Een instructielijst bestaat uit een of meer instructies die op volgorde zijn geschreven. Overzichtslijsten vinden plaats in blok s (§13.3) en in switch_blocks (§13.8.3).

statement_list
    : statement+
    ;

Er wordt een instructielijst uitgevoerd door het besturingselement over te dragen naar de eerste instructie. Wanneer en als het besturingselement het eindpunt van een instructie bereikt, wordt het besturingselement overgebracht naar de volgende instructie. Wanneer en als het besturingselement het eindpunt van de laatste instructie bereikt, wordt het besturingselement overgebracht naar het eindpunt van de instructielijst.

Een instructie in een instructielijst is bereikbaar als ten minste een van de volgende beweringen waar is:

  • De instructie is de eerste instructie en de instructielijst zelf is bereikbaar.
  • Het eindpunt van de voorgaande instructie is bereikbaar.
  • De instructie is een gelabelde instructie en het label wordt verwezen door een bereikbare goto instructie.

Het eindpunt van een instructielijst is bereikbaar als het eindpunt van de laatste instructie in de lijst bereikbaar is.

13.4 De lege instructie

Een empty_statement doet niets.

empty_statement
    : ';'
    ;

Er wordt een lege instructie gebruikt wanneer er geen bewerkingen zijn die moeten worden uitgevoerd in een context waarin een instructie is vereist.

Het uitvoeren van een lege instructie draagt eenvoudig de besturing over naar het eindpunt van de instructie. Het eindpunt van een lege instructie is dus bereikbaar als de lege instructie bereikbaar is.

Voorbeeld: Een lege instructie kan worden gebruikt bij het schrijven van een while instructie met een null-hoofdtekst:

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

Ook kan een lege instructie worden gebruikt om een label te declareren vlak voor het sluiten van} een blok:

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

eindvoorbeeld

13.5 Gelabelde instructies

Met een labeled_statement kan een instructie worden voorafgegaan door een label. Gelabelde instructies zijn toegestaan in blokken, maar zijn niet toegestaan als ingesloten instructies.

labeled_statement
    : identifier ':' statement
    ;

Een gelabelde instructie declareert een label met de naam die is opgegeven door de id. Het bereik van een label is het hele blok waarin het label wordt gedeclareerd, inclusief geneste blokken. Het is een compilatiefout voor twee labels met dezelfde naam om overlappende bereiken te hebben.

Naar een label kan worden verwezen vanuit goto instructies (§13.10.4) binnen het bereik van het label.

Opmerking: Dit betekent dat goto instructies controle binnen blokken en uit blokken kunnen overdragen, maar nooit in blokken. eindnotitie

Labels hebben hun eigen declaratieruimte en verstoren geen andere id's.

Voorbeeld: Het voorbeeld

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

is geldig en gebruikt de naam x als zowel een parameter als een label.

eindvoorbeeld

De uitvoering van een gelabelde instructie komt exact overeen met de uitvoering van de instructie na het label.

Naast de bereikbaarheid die wordt geboden door een normale controlestroom, is een gelabelde instructie bereikbaar als naar het label wordt verwezen door een bereikbare goto instructie, tenzij de goto instructie binnen het try blok of een catch blok van een try_statement dat een finally blok bevat waarvan het eindpunt onbereikbaar is en de gelabelde instructie zich buiten de try_statement bevindt.

13.6 Verklaringsverklaringen

13.6.1 Algemeen

Een declaration_statement declareert een of meer lokale variabelen, een of meer lokale constanten of een lokale functie. Declaratie-instructies zijn toegestaan in blokken en schakelblokken, maar zijn niet toegestaan als ingesloten instructies.

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

Een lokale variabele wordt gedeclareerd met behulp van een local_variable_declaration (§13.6.2). Een lokale constante wordt gedeclareerd met behulp van een local_constant_declaration (§13.6.3). Een lokale functie wordt gedeclareerd met behulp van een local_function_declaration (§13.6.4).

De gedeclareerde namen worden ingevoerd in de dichtstbijzijnde declaratieruimte (§7.3).

13.6.2 Declaraties van lokale variabelen

13.6.2.1 Algemeen

Een local_variable_declaration declareert een of meer lokale variabelen.

local_variable_declaration
    : implicitly_typed_local_variable_declaration
    | explicitly_typed_local_variable_declaration
    | explicitly_typed_ref_local_variable_declaration
    ;

Impliciet getypte declaraties bevatten het contextuele trefwoord (§6.4.4) var wat resulteert in een syntactische dubbelzinnigheid tussen de drie categorieën die als volgt worden opgelost:

  • Als er geen type met de naam var in het bereik is en de invoer overeenkomt met implicitly_typed_local_variable_declaration , wordt het gekozen;
  • Als een type met de naam var binnen het bereik valt, wordt implicitly_typed_local_variable_declaration niet beschouwd als een mogelijke overeenkomst.

Binnen een local_variable_declaration elke variabele wordt geïntroduceerd door een declaratie, een van implicitly_typed_local_variable_declarator, explicitly_typed_local_variable_declarator of ref_local_variable_declarator voor impliciet getypte, expliciet getypte en verw lokale variabelen. De declaratie definieert de naam (id) en de initiële waarde, indien van toepassing, van de geïntroduceerde variabele.

Als er meerdere declaraties in een declaratie staan, worden ze verwerkt, inclusief eventuele initialisatie-expressies, in volgorde van links naar rechts (§9.4.4.5).

Opmerking: Voor een local_variable_declaration die niet voorkomt als een for_initializer (§13.9.4) of resource_acquisition (§13.14) is deze links naar rechtse volgorde gelijk aan elke declaratie die zich in een afzonderlijke local_variable_declaration bevindt. Voorbeeld:

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

is gelijk aan:

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

eindnotitie

De waarde van een lokale variabele wordt verkregen in een expressie met behulp van een simple_name (§12.8.4). Op elke locatie waar de waarde wordt verkregen, wordt een lokale variabele definitief toegewezen (§9.4). Elke lokale variabele die wordt geïntroduceerd door een local_variable_declaration wordt in eerste instantie niet toegewezen (§9.4.3). Als een declaratie een initialisatie-expressie heeft, wordt de geïntroduceerde lokale variabele geclassificeerd als toegewezen aan het einde van de declaratie (§9.4.4.5).

Het bereik van een lokale variabele die wordt geïntroduceerd door een local_variable_declaration wordt als volgt gedefinieerd (§7.7):

  • Als de declaratie plaatsvindt als een for_initializer , is het bereik de for_initializer, for_condition, for_iterator en embedded_statement (§13.9.4);
  • Als de verklaring plaatsvindt als een resource_acquisition is het buitenste blok van de semantische equivalente uitbreiding van de using_statement (§13.14);
  • Anders is het bereik het blok waarin de declaratie plaatsvindt.

Het is een fout om te verwijzen naar een lokale variabele op naam in een tekstpositie die voorafgaat aan de declaratie of binnen een initialisatie-expressie binnen de declaratie. Binnen het bereik van een lokale variabele is het een compilatietijdfout om een andere lokale variabele, lokale functie of constante met dezelfde naam te declareren.

De ref-safe-context (§9.7.2) van een lokale ref-variabele is de ref-safe-context van het initialiseren van variable_reference. De ref-safe-context van niet-ref lokale variabelen is declaratieblok.

13.6.2.2 Impliciet getypeerde lokale variabeledeclaraties

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
    ;

Een implicitly_typed_local_variable_declaration introduceert één lokale variabele, id. De expressie of variable_reference moet een type compilatietijd hebben. T Het eerste alternatief declareert een variabele met een initiële waarde van de expressie; het type is T? wanneer T een niet-nullbaar verwijzingstype is, anders is Thet type. Het tweede alternatief declareert een verw-variabele met een initiële waarde van refvariable_reference; het type is ref T? wanneer T het een niet-null-verwijzingstype is, anders is ref Thet type. (ref_kind wordt beschreven in §15.6.1.)

Voorbeeld:

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 impliciet getypte declaraties van lokale variabelen hierboven zijn precies gelijk aan de volgende expliciet getypte declaraties:

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;

Hieronder ziet u een onjuiste, impliciet getypte lokale variabeledeclaraties:

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

eindvoorbeeld

13.6.2.3 Expliciet lokale variabeledeclaraties getypt

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
    ;

Een explicity_typed_local_variable_declaration introduceert een of meer lokale variabelen met het opgegeven type.

Als een local_variable_initializer aanwezig is, is het type passend volgens de regels van eenvoudige toewijzing (§12.21.2) of matrix initialisatie (§17.7) en wordt de waarde toegewezen als de oorspronkelijke waarde van de variabele.

13.6.2.4 Expliciet lokale variabeledeclaraties getypt

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
    ;

Het initialiseren van variable_reference moet een type hebben en voldoen aan dezelfde eisen als voor een verw-toewijzing (§12.21.3).

Als ref_kind is ref readonly, zijn de id(s) die worden gedeclareerd verwijzingen naar variabelen die als alleen-lezen worden behandeld. Anders worden, indien ref_kind is ref, de id(s) die worden gedeclareerd verwijzingen naar variabelen die beschrijfbaar zijn.

Het is een compilatiefout om een lokale verw-variabele of een variabele van een ref struct type te declareren, binnen een methode die is gedeclareerd met de method_modifierasync, of binnen een iterator (§15.14).

13.6.3 Lokale constante declaraties

Een local_constant_declaration declareert een of meer lokale constanten.

local_constant_declaration
    : 'const' type constant_declarators
    ;

constant_declarators
    : constant_declarator (',' constant_declarator)*
    ;

constant_declarator
    : identifier '=' constant_expression
    ;

Het type van een local_constant_declaration geeft het type van de constanten aan die door de declaratie worden ingevoerd. Het type wordt gevolgd door een lijst met constant_declarators, die elk een nieuwe constante introduceert. Een constant_declarator bestaat uit een id die de constante een naam geeft, gevolgd door een '='-token, gevolgd door een constant_expression (§12.23) die de waarde van de constante geeft.

Het type en constant_expression van een lokale constanteverklaring moeten dezelfde regels volgen als die van een constante lidverklaring (§15.4).

De waarde van een lokale constante wordt verkregen in een expressie met behulp van een simple_name (§12.8.4).

Het bereik van een lokale constante is het blok waarin de declaratie plaatsvindt. Het is een fout om te verwijzen naar een lokale constante in een tekstpositie die voorafgaat aan het einde van de constant_declarator.

Een lokale constantedeclaratie die meerdere constanten declareert, is gelijk aan meerdere declaraties van enkelvoudige constanten met hetzelfde type.

13.6.4 Declaraties van lokale functies

Een local_function_declaration declareert een lokale functie.

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

Grammaticanotitie: bij het herkennen van een local_function_body indien zowel de alternatieven voor null_conditional_invocation_expressionals expressie van toepassing zijn, wordt de eerste gekozen. (§15.6.1)

Voorbeeld: Er zijn twee veelvoorkomende use cases voor lokale functies: iterator-methoden en asynchrone methoden. In iterator-methoden worden eventuele uitzonderingen alleen waargenomen bij het aanroepen van code waarmee de geretourneerde reeks wordt geïnventareerd. In asynchrone methoden worden eventuele uitzonderingen alleen waargenomen wanneer de geretourneerde taak wordt gewacht. In het volgende voorbeeld ziet u hoe parametervalidatie wordt gescheiden van de iterator-implementatie met behulp van een lokale functie:

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

eindvoorbeeld

Tenzij hieronder anders is opgegeven, is de semantiek van alle grammatica-elementen hetzelfde als voor method_declaration (§15.6.1), gelezen in de context van een lokale functie in plaats van een methode.

De id van een local_function_declaration moet uniek zijn in het opgegeven blokbereik, met inbegrip van eventuele lokale declaratieruimten voor variabelen. Een gevolg hiervan is dat overbelaste local_function_declarations niet zijn toegestaan.

Een local_function_declaration kan één async modifier (§15.15) en een unsafe (§23.1) modifier bevatten. Als de aangifte de async wijzigingsfunctie bevat, moet void het retourtype of een «TaskType» type zijn (§15.15.11). Als de declaratie de static wijzigingsfunctie bevat, is de functie een statische lokale functie; anders is het een niet-statische lokale functie. Het is een compilatiefout voor type_parameter_list of parameter_list om kenmerken te bevatten. Als de lokale functie wordt gedeclareerd in een onveilige context (§23.2), kan de lokale functie onveilige code bevatten, zelfs als de declaratie van de lokale functie niet de unsafe wijzigingsfunctie bevat.

Een lokale functie wordt gedeclareerd in blokbereik. Een niet-statische lokale functie kan variabelen vastleggen van het bereik insluiten, terwijl een statische lokale functie niet mag worden gebruikt (zodat deze geen toegang heeft tot het insluiten van lokale bevolking, parameters, niet-statische lokale functies of this). Het is een compilatiefout als een vastgelegde variabele wordt gelezen door de hoofdtekst van een niet-statische lokale functie, maar niet zeker is toegewezen voordat elke aanroep naar de functie. Een compiler bepaalt welke variabelen er zeker worden toegewezen aan de return (§9.4.4.33).

Wanneer het type this een structtype is, is het een compilatiefout voor de hoofdtekst van een lokale functie om toegang te krijgen this. Dit is waar of de toegang expliciet (zoals in this.x) of impliciet is (zoals in x waar x zich een exemplaarlid van de struct bevindt). Deze regel verbiedt deze toegang alleen en heeft geen invloed op het feit of het opzoeken van leden resulteert in een lid van de struct.

Het is een compilatiefout voor de hoofdtekst van de lokale functie die een goto instructie, een break instructie of een continue instructie bevat waarvan het doel buiten de hoofdtekst van de lokale functie valt.

Opmerking: de bovenstaande regels voor this en goto spiegelen de regels voor anonieme functies in §12.19.3. eindnotitie

Een lokale functie kan worden aangeroepen vanaf een lexical punt vóór de declaratie. Het is echter een compilatiefout voor de functie die lexisch moet worden gedeclareerd vóór de declaratie van een variabele die in de lokale functie wordt gebruikt (§7.7).

Het is een compilatiefout voor een lokale functie om een parameter te declareren, parameter of lokale variabele te declareren met dezelfde naam als één die is gedeclareerd in een declaratieruimte voor lokale variabelen.

Lokale functieteksten zijn altijd bereikbaar. Het eindpunt van een declaratie van een lokale functie is bereikbaar als het beginpunt van de declaratie van de lokale functie bereikbaar is.

Voorbeeld: In het volgende voorbeeld is de hoofdtekst L bereikbaar, ook al is het beginpunt niet L bereikbaar. Omdat het beginpunt van L het niet bereikbaar is, is de instructie na het eindpunt L niet bereikbaar:

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

Met andere woorden, de locatie van een declaratie van een lokale functie heeft geen invloed op de bereikbaarheid van instructies in de betreffende functie. eindvoorbeeld

Als het type van het argument voor een lokale functie is dynamic, wordt de aan te roepen functie opgelost tijdens het compileren, niet bij runtime.

Een lokale functie mag niet worden gebruikt in een expressiestructuur.

Een statische lokale functie

  • Kan verwijzen naar statische leden, typeparameters, constante definities en statische lokale functies uit het bereik insluiten.
  • Verwijst niet naar leden van this een impliciete base verwijzing, noch naar this lokale variabelen, parameters of niet-statische lokale functies uit het bereik. Al deze zijn echter toegestaan in een nameof() expressie.

13.7 Expressie-instructies

Een expression_statement evalueert een bepaalde expressie. De waarde die wordt berekend door de expressie, indien van toepassing, wordt verwijderd.

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
    ;

Niet alle expressies zijn toegestaan als instructies.

Opmerking: expressies zoals x + y en x == 1, die alleen een waarde berekenen (die worden verwijderd), zijn niet toegestaan als instructies. eindnotitie

Uitvoering van een expression_statement evalueert de ingesloten expressie en draagt vervolgens het besturingselement over naar het eindpunt van de expression_statement. Het eindpunt van een expression_statement is bereikbaar als die expression_statement bereikbaar is.

13.8 Selectie-instructies

13.8.1 Algemeen

Selectie-instructies selecteren een van een aantal mogelijke instructies voor uitvoering op basis van de waarde van een expressie.

selection_statement
    : if_statement
    | switch_statement
    ;

13.8.2 De if-instructie

De if instructie selecteert een instructie voor uitvoering op basis van de waarde van een Boole-expressie.

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

Een else deel is gekoppeld aan het lexisch dichtstbijzijnde voorafgaande dat if is toegestaan door de syntaxis.

Voorbeeld: Een if instructie van het formulier

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

is gelijk aan

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

eindvoorbeeld

Er wordt als volgt een if instructie uitgevoerd:

  • De boolean_expression (§12.24) wordt geëvalueerd.
  • Als de Boole-expressie oplevert true, wordt het besturingselement overgebracht naar de eerste ingesloten instructie. Wanneer en als het besturingselement het eindpunt van die instructie bereikt, wordt het besturingselement overgebracht naar het eindpunt van de if instructie.
  • Als de Boole-expressie oplevert false en een else onderdeel aanwezig is, wordt het besturingselement overgebracht naar de tweede ingesloten instructie. Wanneer en als het besturingselement het eindpunt van die instructie bereikt, wordt het besturingselement overgebracht naar het eindpunt van de if instructie.
  • Als de Boole-expressie oplevert false en een else onderdeel niet aanwezig is, wordt het besturingselement overgebracht naar het eindpunt van de if instructie.

De eerste ingesloten instructie van een if instructie is bereikbaar als de if instructie bereikbaar is en de Boole-expressie niet de constante waarde falseheeft.

De tweede ingesloten instructie van een if instructie, indien aanwezig, is bereikbaar als de if instructie bereikbaar is en de Boole-expressie heeft niet de constante waarde true.

Het eindpunt van een if instructie is bereikbaar als het eindpunt van ten minste één van de ingesloten instructies bereikbaar is. Daarnaast is het eindpunt van een if instructie zonder else onderdeel bereikbaar als de if instructie bereikbaar is en de Boole-expressie niet de constante waarde trueheeft.

13.8.3 De schakelinstructie

De switch instructie selecteert voor het uitvoeren van een instructielijst met een gekoppeld switchlabel dat overeenkomt met de waarde van de switchexpressie.

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
    ;

Een switch_statement bestaat uit het trefwoord, gevolgd door een haakje expressie (deswitch genoemd), gevolgd door een switch_block. De switch_block bestaat uit nul of meer switch_sections, tussen accolades. Elke switch_section bestaat uit een of meer switch_labelgevolgd door een statement_list (§13.3.2). Elke switch_label met case een bijbehorend patroon (§11) waarmee de waarde van de switchexpressie wordt getest. Als case_guard aanwezig is, moet de expressie impliciet worden omgezet in het type bool en wordt die expressie geëvalueerd als een aanvullende voorwaarde voor het geval dat moet worden beschouwd als voldaan.

Het switch ingesteld door de switchexpressie.

  • Als het type van de schakelexpressie issbyte, , byte, shortushort, int, uintlongulongcharboolstringof een enum_type, of als het het type null-waarde is dat overeenkomt met een van deze typen, is dat het type van de instructie dat van toepassing switch is.
  • Als er anders precies één door de gebruiker gedefinieerde impliciete conversie bestaat van het type switchexpressie naar een van de volgende mogelijke typen van toepassing: sbyte, byte, shortushortint, uintlongulong, charofstring, een type null-waarde dat overeenkomt met een van deze typen, is het geconverteerde type het type van het bestuur van de switch instructie.
  • Anders is het type van de instructie het type switch switchexpressie. Het is een fout als er geen dergelijk type bestaat.

Er kan maximaal één default label in een switch instructie staan.

Dit is een fout als het patroon van een switchlabel niet van toepassing is (§11.2.1) op het type invoerexpressie.

Het is een fout als het patroon van een switchlabel wordt aangevuld met (§11.3) de set patronen van eerdere switchlabels van de switch-instructie die geen case guard hebben of waarvan case guard een constante expressie is met de waarde true.

Voorbeeld:

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

eindvoorbeeld

Er wordt als volgt een switch instructie uitgevoerd:

  • De switchexpressie wordt geëvalueerd en geconverteerd naar het type van het bestuur.
  • Het besturingselement wordt overgedragen op basis van de waarde van de geconverteerde switchexpressie:
    • Het lexically eerste patroon in de set case labels in dezelfde switch instructie die overeenkomt met de waarde van de switchexpressie en waarvoor de guard-expressie afwezig is of waar evalueert, zorgt ervoor dat het besturingselement wordt overgebracht naar de lijst met instructies na het overeenkomende case label.
    • Als er een default label aanwezig is, wordt het besturingselement overgedragen naar de lijst met instructies na het default label.
    • Anders wordt het besturingselement overgedragen naar het eindpunt van de switch instructie.

Opmerking: de volgorde waarin patronen tijdens runtime worden vergeleken, is niet gedefinieerd. Een compiler is toegestaan (maar niet vereist) om patronen uit de juiste volgorde te vinden en de resultaten van reeds overeenkomende patronen opnieuw te gebruiken om het resultaat van vergelijking van andere patronen te berekenen. Niettemin is een compiler vereist om het eerste lexicale patroon te bepalen dat overeenkomt met de expressie en waarvoor de bewakingsclausule afwezig is of evalueert tot true. eindnotitie

Als het eindpunt van de instructielijst van een switchsectie bereikbaar is, treedt er een compilatietijdfout op. Dit wordt de regel 'geen val door' genoemd.

Voorbeeld: Het voorbeeld

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

is geldig omdat er geen switchsectie een bereikbaar eindpunt heeft. In tegenstelling tot C en C++ is het uitvoeren van een switchsectie niet toegestaan om naar de volgende switchsectie te gaan en het voorbeeld

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

resulteert in een compilatietijdfout. Wanneer de uitvoering van een switchsectie moet worden gevolgd door de uitvoering van een andere switchsectie, wordt een expliciete goto case of goto default instructie gebruikt:

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

eindvoorbeeld

Meerdere labels zijn toegestaan in een switch_section.

Voorbeeld: Het voorbeeld

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

is geldig. Het voorbeeld schendt de regel 'geen val door' omdat de labels case 2: en default: deel uitmaken van dezelfde switch_section.

eindvoorbeeld

Opmerking: De regel 'no fall through' voorkomt een veelvoorkomende klasse bugs die optreden in C en C++ wanneer break instructies per ongeluk worden weggelaten. De secties van de switch bovenstaande instructie kunnen bijvoorbeeld worden omgekeerd zonder dat dit van invloed is op het gedrag van de instructie:

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

eindnotitie

Opmerking: De instructielijst van een switchsectie eindigt meestal op een break, goto caseof goto default instructie, maar een constructie die het eindpunt van de instructielijst onbereikbaar weergeeft, is toegestaan. Een instructie die wordt beheerd door de Boole-expressiewhile, is bijvoorbeeld true bekend dat deze nooit het eindpunt bereikt. Op dezelfde manier draagt een throw of return instructie altijd de besturing elders over en bereikt deze nooit het eindpunt. Het volgende voorbeeld is dus geldig:

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

eindnotitie

Voorbeeld: Het type van een instructie kan switch het type stringzijn. Voorbeeld:

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

eindvoorbeeld

Opmerking: Net als bij de operatoren voor gelijkheid van tekenreeksen (§12.12.8) is de switch instructie hoofdlettergevoelig en wordt een bepaalde schakelsectie alleen uitgevoerd als de tekenreeks van de switchexpressie exact overeenkomt met een case labelconstante. eindnotitie Wanneer het type van een instructie van toepassing switch is string of een type null-waarde, is de waarde null toegestaan als labelconstante case .

De statement_listvan een switch_block kunnen verklaringsverklaringen bevatten (§13.6). Het bereik van een lokale variabele of constante die in een schakelblok is gedeclareerd, is het schakelblok.

Een switchlabel is bereikbaar als ten minste één van de volgende waar is:

  • De switchexpressie is een constante waarde en een van beide
    • het label is een case wiens patroon overeenkomt (§11.2.1) die waarde, en de bewaker van het label is afwezig of niet een constante expressie met de waarde onwaar; of
    • het is een default label en geen schakelsectie bevat een hoofdletterlabel waarvan het patroon overeenkomt met die waarde en waarvan de bewaker afwezig is of een constante expressie met de waarde waar.
  • De switchexpressie is geen constante waarde en een van beide
    • het label is een case zonder bewaker of met een bewaker waarvan de waarde niet de constante onwaar is; of
    • het is een default label en
      • de reeks patronen die worden weergegeven in de gevallen van de schakelinstructie die geen bewakers hebben of bewakers waarvan de waarde de constante waar is, niet volledig is (§11.4) voor het schakeltype; of
      • het schakeloptietype is een null-type en de set patronen die worden weergegeven in de gevallen van de switch-instructie die geen bewakers of bewakers hebben waarvan de waarde de constante waar is, bevat geen patroon dat overeenkomt met de waarde null.
  • Er wordt naar het switchlabel verwezen door een bereikbaar goto case of goto default instructie.

De instructielijst van een bepaalde switchsectie is bereikbaar als de switch instructie bereikbaar is en de switchsectie een bereikbaar switchlabel bevat.

Het eindpunt van een switch instructie is bereikbaar als de switch-instructie bereikbaar is en ten minste één van de volgende beweringen waar is:

  • De switch instructie bevat een bereikbare break instructie waarmee de switch instructie wordt afgesloten.
  • Er is geen default label aanwezig en ook
    • De switchexpressie is een niet-constante waarde en de set patronen die worden weergegeven in de gevallen van de switch-instructie die geen bewakers of bewakers hebben waarvan de waarde de constante waar is, is niet volledig (§11.4) voor het type schakeloptie.
    • De switchexpressie is een niet-constante waarde van een null-type en er wordt geen patroon weergegeven in de gevallen van de switchinstructie die geen bewakers hebben of bewakers hebben waarvan de waarde de constante waar is, komt overeen met de waarde null.
    • De switchexpressie is een constante waarde en geen case label zonder een bewaker of waarvan de bewaker de constante waar is, komt overeen met die waarde.

Voorbeeld: De volgende code toont een beknopt gebruik van de when component:

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

De var-case komt overeen null, de lege tekenreeks of een tekenreeks die alleen witruimte bevat. eindvoorbeeld

13.9 Iteratie-instructies

13.9.1 Algemeen

Herhalingsinstructies voeren herhaaldelijk een ingesloten instructie uit.

iteration_statement
    : while_statement
    | do_statement
    | for_statement
    | foreach_statement
    ;

13.9.2 De while-instructie

Met de while instructie wordt een ingesloten instructie nul of meer keer uitgevoerd.

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

Er wordt als volgt een while instructie uitgevoerd:

  • De boolean_expression (§12.24) wordt geëvalueerd.
  • Als de Boole-expressie oplevert true, wordt het besturingselement overgebracht naar de ingesloten instructie. Wanneer en als het besturingselement het eindpunt van de ingesloten instructie bereikt (mogelijk vanaf de uitvoering van een continue instructie), wordt het besturingselement overgebracht naar het begin van de while instructie.
  • Als de Boole-expressie oplevert false, wordt het besturingselement overgebracht naar het eindpunt van de while instructie.

In de ingesloten instructie van een while instructie kan een break instructie (§13.10.2) worden gebruikt om de besturing over te dragen naar het eindpunt van de while instructie (waardoor de iteratie van de ingesloten instructie wordt beëindigd) en kan een continue instructie (§13.10.3) worden gebruikt om het besturingselement over te dragen naar het eindpunt van de ingesloten instructie (waardoor een andere iteratie van de while instructie wordt uitgevoerd).

De ingesloten instructie van een while instructie is bereikbaar als de while instructie bereikbaar is en de Boole-expressie niet de constante waarde falseheeft.

Het eindpunt van een while instructie is bereikbaar als ten minste een van de volgende beweringen waar is:

  • De while instructie bevat een bereikbare break instructie waarmee de while instructie wordt afgesloten.
  • De while instructie is bereikbaar en de Boole-expressie heeft niet de constante waarde true.

13.9.3 De do-instructie

Met de do instructie wordt een ingesloten instructie een of meer keren voorwaardelijk uitgevoerd.

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

Er wordt als volgt een do instructie uitgevoerd:

  • Het besturingselement wordt overgebracht naar de ingesloten instructie.
  • Wanneer en als het besturingselement het eindpunt van de ingesloten instructie bereikt (mogelijk na uitvoering van een continue instructie), wordt de boolean_expression (§12.24) geëvalueerd. Als de Boole-expressie oplevert true, wordt het besturingselement overgebracht naar het begin van de do instructie. Anders wordt het besturingselement overgedragen naar het eindpunt van de do instructie.

In de ingesloten instructie van een do instructie kan een break instructie (§13.10.2) worden gebruikt om de besturing over te dragen naar het eindpunt van de do instructie (waardoor de iteratie van de ingesloten instructie wordt beëindigd) en kan een continue instructie (§13.10.3) worden gebruikt om het besturingselement over te dragen naar het eindpunt van de ingesloten instructie (waardoor een andere iteratie van de do instructie wordt uitgevoerd).

De ingesloten instructie van een do instructie is bereikbaar als de do instructie bereikbaar is.

Het eindpunt van een do instructie is bereikbaar als ten minste een van de volgende beweringen waar is:

  • De do instructie bevat een bereikbare break instructie waarmee de do instructie wordt afgesloten.
  • Het eindpunt van de ingesloten instructie is bereikbaar en de Boole-expressie heeft niet de constante waarde true.

13.9.4 De for-instructie

De for instructie evalueert een reeks initialisatie-expressies en voert vervolgens, terwijl een voorwaarde waar is, herhaaldelijk een ingesloten instructie uit en evalueert een reeks iteratie-expressies.

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

De for_initializer, indien aanwezig, bestaat uit een local_variable_declaration (§13.6.2) of een lijst met statement_expressions (§13.7) gescheiden door komma's. Het bereik van een lokale variabele die door een for_initializer is gedeclareerd, is de for_initializer, for_condition, for_iterator en embedded_statement.

De for_condition is, indien aanwezig, een boolean_expression (§12.24).

De for_iterator, indien aanwezig, bestaat uit een lijst met statement_expressions (§13.7) gescheiden door komma's.

Er wordt als volgt een for instructie uitgevoerd:

  • Als er een for_initializer aanwezig is, worden de variabelen initialisatie- of instructieexpressies uitgevoerd in de volgorde waarin ze worden geschreven. Deze stap wordt slechts eenmaal uitgevoerd.
  • Als er een for_condition aanwezig is, wordt deze geëvalueerd.
  • Als de for_condition niet aanwezig is of als de evaluatie oplevert true, wordt de controle overgedragen naar de ingesloten instructie. Wanneer en als het besturingselement het eindpunt van de ingesloten instructie bereikt (mogelijk van de uitvoering van een continue instructie), worden de expressies van de for_iterator, indien aanwezig, op volgorde geëvalueerd en wordt vervolgens een andere iteratie uitgevoerd, te beginnen met de evaluatie van de for_condition in de bovenstaande stap.
  • Als de for_condition aanwezig is en de evaluatie oplevert false, wordt de controle overgedragen naar het eindpunt van de for instructie.

In de ingesloten instructie van een for instructie kan een break instructie (§13.10.2) worden gebruikt om de besturing over te dragen naar het eindpunt van de for instructie (waardoor de iteratie van de ingesloten instructie wordt beëindigd) en continue een instructie (§13.10.3) kan worden gebruikt om de besturing over te dragen naar het eindpunt van de ingesloten instructie (waardoor de for_iterator wordt uitgevoerd en een andere iteratie van de for instructie uitvoert, te beginnen met de for_condition).

De ingesloten instructie van een for instructie is bereikbaar als een van de volgende beweringen waar is:

  • De for instructie is bereikbaar en er is geen for_condition aanwezig.
  • De for instructie is bereikbaar en een for_condition aanwezig is en heeft niet de constante waarde false.

Het eindpunt van een for instructie is bereikbaar als ten minste een van de volgende beweringen waar is:

  • De for instructie bevat een bereikbare break instructie waarmee de for instructie wordt afgesloten.
  • De for instructie is bereikbaar en een for_condition aanwezig is en heeft niet de constante waarde true.

13.9.5 De foreach-instructie

De foreach instructie bevat de elementen van een verzameling en voert een ingesloten instructie uit voor elk element van de verzameling.

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

De local_variable_type en id van een foreach-instructie declareren de iteratievariabele van de instructie. Als de var id wordt opgegeven als de local_variable_type en er geen type met de naam var binnen het bereik valt, wordt de iteratievariabele als een impliciet getypte iteratievariabele beschouwd en wordt het bijbehorende type beschouwd als het elementtype van de foreach instructie, zoals hieronder is opgegeven.

Als de foreach_statement beide of geen van ref beide bevat en readonly, geeft de iteratievariabele een variabele aan die als alleen-lezen wordt behandeld. Als foreach_statement zonder refbevatreadonly, geeft de iteratievariabele een variabele aan die beschrijfbaar moet zijn.

De iteratievariabele komt overeen met een lokale variabele met een bereik dat zich uitbreidt over de ingesloten instructie. Tijdens het uitvoeren van een foreach instructie vertegenwoordigt de iteratievariabele het verzamelingselement waarvoor momenteel een iteratie wordt uitgevoerd. Als de iteratievariabele een alleen-lezenvariabele aangeeft, treedt er een compileertijdfout op als de ingesloten instructie deze probeert te wijzigen (via toewijzing of de ++ operatoren -- ) of deze als referentie- of uitvoerparameter doorgeeft.

In het volgende, voor beknoptheid, IEnumerableen IEnumeratorIEnumerable<T>IEnumerator<T> verwijzen naar de bijbehorende typen in de naamruimten System.Collections en .System.Collections.Generic

De compileertijdverwerking van een foreach instructie bepaalt eerst het verzamelingstype, het enumeratortype en het iteratietype van de expressie. Deze bepaling gaat als volgt:

  • Als het type Xexpressie een matrixtype is, is er een impliciete verwijzingsconversie van X naar de IEnumerable interface (omdat System.Array deze interface wordt geïmplementeerd). Het verzamelingstype is de IEnumerable interface, het enumeratortype is de IEnumerator interface en het iteratietype is het elementtype van het matrixtype X.
  • Als het type X expressie is, is er een impliciete conversie van dynamic naar de interface (IEnumerable). Het verzamelingstype is de IEnumerable interface en het enumeratortype is de IEnumerator interface. Als de var id wordt opgegeven als de local_variable_type , is dynamichet iteratietype , anders is objecthet .
  • Als dat niet het geval is, moet u bepalen of het type X een geschikte GetEnumerator methode heeft:
    • Voer lidzoekactie uit op het type X met id GetEnumerator en geen typeargumenten. Als de opzoekactie van het lid geen overeenkomst produceert of een dubbelzinnigheid produceert of een overeenkomst produceert die geen methodegroep is, controleert u op een opsommingsbare interface zoals hieronder wordt beschreven. Het wordt aanbevolen een waarschuwing te afgegeven als lidzoekactie iets produceert behalve een methodegroep of geen overeenkomst.
    • Voer overbelastingsresolutie uit met behulp van de resulterende methodegroep en een lege lijst met argumenten. Als overbelastingsresolutie resulteert in geen toepasselijke methoden, resulteert in dubbelzinnigheid of resulteert in één beste methode, maar die methode is statisch of niet openbaar, controleert u op een opsommingsbare interface, zoals hieronder wordt beschreven. Het wordt aanbevolen een waarschuwing te afgegeven als overbelastingsresolutie iets produceert behalve een ondubbelzinnige methode voor openbare instanties of geen toepasselijke methoden.
    • Als het retourtype E van de GetEnumerator methode geen klasse, struct of interfacetype is, wordt er een fout gegenereerd en worden er geen verdere stappen ondernomen.
    • Het opzoeken van leden wordt uitgevoerd E met de id en geen typeargumenten Current . Als de opzoekactie van het lid geen overeenkomst oplevert, is het resultaat een fout of is het resultaat iets behalve een eigenschap van een openbare instantie die het lezen toestaat, wordt er een fout gegenereerd en worden er geen verdere stappen ondernomen.
    • Het opzoeken van leden wordt uitgevoerd E met de id en geen typeargumenten MoveNext . Als de opzoekactie van het lid geen overeenkomst oplevert, is het resultaat een fout of is het resultaat iets behalve een methodegroep, wordt er een fout gegenereerd en worden er geen verdere stappen ondernomen.
    • Overbelastingsresolutie wordt uitgevoerd op de methodegroep met een lege lijst met argumenten. Als overbelastingsresolutie resulteert in geen toepasselijke methoden, resulteert dit in een dubbelzinnigheid of resulteert in één beste methode, maar die methode is statisch of niet openbaar, of het retourtype is niet bool, er wordt een fout gegenereerd en er worden geen verdere stappen ondernomen.
    • Het verzamelingstype is X, het enumeratortype is Een het iteratietype is het type van de Current eigenschap. De Current eigenschap kan de ref wijzigingsfunctie bevatten. In dat geval is de geretourneerde expressie een variable_reference (§9.5) die optioneel alleen-lezen is.
  • Controleer anders of er een enumerable interface is:
    • Als er een impliciete conversie is van naar alle typenTᵢ, is er een uniek type X dat IEnumerable<Tᵢ> niet T en voor alle andere T typen een impliciete conversie van dynamic naar Tᵢ, dan is het verzamelingstype de interfaceIEnumerable<T>, het type opsomming is de interface IEnumerable<Tᵢ>en het iteratietype .IEnumerable<T>IEnumerator<T>T
    • Als er meer dan één dergelijk type Tis, wordt er een fout gegenereerd en worden er geen verdere stappen ondernomen.
    • Als er anders een impliciete conversie van X naar de System.Collections.IEnumerable interface is, is het verzamelingstype deze interface, is het enumeratortype de interface System.Collections.IEnumeratoren het iteratietype object.
    • Anders wordt een fout gegenereerd en worden er geen verdere stappen ondernomen.

De bovenstaande stappen, indien geslaagd, produceren ondubbelzinnig een verzamelingstype C, enumeratortype E en iteratietype T, ref Tof ref readonly T. Een foreach instructie van het formulier

foreach (V v in x) «embedded_statement»

is dan gelijk aan:

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

De variabele e is niet zichtbaar voor of toegankelijk voor de expressie x of de ingesloten instructie of een andere broncode van het programma. De variabele v heeft het kenmerk Alleen-lezen in de ingesloten instructie. Als er geen expliciete conversie is (§10.3) van T (het iteratietype) naar V (de local_variable_type in de foreach instructie), wordt er een fout gegenereerd en worden er geen verdere stappen ondernomen.

Wanneer de iteratievariabele een verwijzingsvariabele is (§9.7), een foreach instructie van het formulier

foreach (ref V v in x) «embedded_statement»

is dan gelijk aan:

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

De variabele e is niet zichtbaar of toegankelijk voor de expressie x of de ingesloten instructie of een andere broncode van het programma. De referentievariabele v is lezen/schrijven in de ingesloten instructie, maar v wordt niet opnieuw toegewezen (§12.21.3). Als er geen identiteitsconversie is (§10.2.2) van T (het iteratietype) naar V (de local_variable_type in de foreach instructie), wordt er een fout gegenereerd en worden er geen verdere stappen ondernomen.

Een foreach instructie van het formulier foreach (ref readonly V v in x) «embedded_statement» heeft een vergelijkbare equivalente vorm, maar de verwijzingsvariabele v bevindt zich ref readonly in de ingesloten instructie en kan daarom niet opnieuw worden toegewezen of opnieuw worden toegewezen.

Opmerking: Als x de waarde nullis, wordt er een System.NullReferenceException gegenereerd tijdens runtime. eindnotitie

Een implementatie is toegestaan om een bepaalde foreach_statement anders te implementeren, bijvoorbeeld om prestatieredenen, zolang het gedrag consistent is met de bovenstaande uitbreiding.

De plaatsing van v binnen de while lus is belangrijk voor hoe deze wordt vastgelegd (§12.19.6.2) door een anonieme functie die in de embedded_statement plaatsvindt.

Voorbeeld:

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

Als v in het uitgevouwen formulier buiten de while lus werd gedeclareerd, zou deze worden gedeeld tussen alle iteraties en de bijbehorende waarde na de for lus de uiteindelijke waarde zou zijn, 13wat de aanroep zou f afdrukken. Omdat elke iteratie een eigen variabele vheeft, blijft fde waarde die in de eerste iteratie is vastgelegd7, behouden. Dit is wat wordt afgedrukt. (Houd er rekening mee dat eerdere versies van C# buiten de lus v zijn gedeclareerdwhile.)

eindvoorbeeld

De hoofdtekst van het finally blok wordt samengesteld volgens de volgende stappen:

  • Als er een impliciete conversie van E naar de System.IDisposable interface is, dan

    • Als E dit een niet-null-waardetype is, wordt de finally component uitgebreid naar het semantische equivalent van:

      finally
      {
          ((System.IDisposable)e).Dispose();
      }
      
    • Anders wordt de finally component uitgebreid naar het semantische equivalent van:

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

      behalve dat als E het een waardetype is, of een typeparameter die is geïnstantieerd op een waardetype, de conversie van e naar System.IDisposable mag geen boksen veroorzaken.

  • E Als dit een verzegeld type is, wordt de finally component uitgebreid naar een leeg blok:

    finally {}
    
  • Anders wordt de finally component uitgebreid naar:

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

De lokale variabele d is niet zichtbaar voor of toegankelijk voor gebruikerscode. In het bijzonder is het niet strijdig met een andere variabele waarvan het bereik het finally blok omvat.

De volgorde waarin foreach de elementen van een matrix worden doorkruist, is als volgt: Voor elementen met ééndimensionale matrices worden de indexvolgorde verhoogd, beginnend met index 0 en eindigend met index Length – 1. Voor multidimensionale matrices worden elementen doorkruist, zodat de indexen van de meest rechtse dimensie eerst worden verhoogd, vervolgens de volgende linkerdimensie, enzovoort aan de linkerkant.

Voorbeeld: In het volgende voorbeeld wordt elke waarde afgedrukt in een tweedimensionale matrix, in de elementvolgorde:

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 geproduceerde uitvoer is als volgt:

1.2 2.3 3.4 4.5 5.6 6.7 7.8 8.9

eindvoorbeeld

Voorbeeld: In het volgende voorbeeld

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

het type wordt n afgeleid dat inthet iteratietype is numbers.

eindvoorbeeld

13.10 Jump-instructies

13.10.1 Algemeen

Jump-instructies dragen voorwaardelijke controle over.

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

De locatie waarnaar een jump-instructie het besturingselement overdraagt, wordt het doel van de jump-instructie genoemd.

Wanneer een jump-instructie binnen een blok plaatsvindt en het doel van die jump-instructie zich buiten dat blok bevindt, wordt de jump-instructie gezegd om het blok af te sluiten . Hoewel een jump-instructie het besturingselement uit een blok kan overdragen, kan het geen besturingselement overdragen naar een blok.

De uitvoering van jump-instructies wordt gecompliceerd door de aanwezigheid van tussenliggende try instructies. Bij afwezigheid van dergelijke try verklaringen draagt een jump-instructie voorwaardelijke controle over van de jump-instructie naar het doel ervan. In aanwezigheid van dergelijke tussenliggende try instructies is de uitvoering complexer. Als de jump-instructie een of meer try blokken met bijbehorende finally blokken afsluit, wordt de besturing in eerste instantie overgebracht naar het finally blok van de binnenste try instructie. Wanneer en als het besturingselement het eindpunt van een finally blok bereikt, wordt het besturingselement overgebracht naar het blok van de finally volgende insluitingsinstructie try . Dit proces wordt herhaald totdat de finally blokken van alle tussenliggende try instructies zijn uitgevoerd.

Voorbeeld: In de volgende code

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

de finally blokken die aan twee try instructies zijn gekoppeld, worden uitgevoerd voordat het besturingselement wordt overgebracht naar het doel van de jump-instructie. De geproduceerde uitvoer is als volgt:

Before break
Innermost finally block
Outermost finally block
After break

eindvoorbeeld

13.10.2 De instructie break

De break instructie sluit de dichtstbijzijnde omsluitingswitch, while, doof forforeach instructie.

break_statement
    : 'break' ';'
    ;

Het doel van een break instructie is het eindpunt van de dichtstbijzijnde insluitings switch-, while, do, - forof foreach instructie. Als een break instructie niet is ingesloten door een switch, while, doof forforeach instructie, treedt er een compilatietijdfout op.

Wanneer meerdere switch, while, do, of forforeach instructies zijn genest binnen elkaar, is een break instructie alleen van toepassing op de binnenste instructie. Om controle over meerdere nestniveaus over te dragen, wordt een goto instructie (§13.10.4) gebruikt.

Een break instructie kan geen finally blok afsluiten (§13.11). Wanneer een break instructie binnen een finally blok plaatsvindt, bevindt het doel van de break instructie zich binnen hetzelfde finally blok; anders treedt er een compilatiefout op.

Er wordt als volgt een break instructie uitgevoerd:

  • Als de break instructie een of meer try blokken met gekoppelde finally blokken afsluit, wordt het besturingselement in eerste instantie overgebracht naar het finally blok van de binnenste try instructie. Wanneer en als het besturingselement het eindpunt van een finally blok bereikt, wordt het besturingselement overgebracht naar het blok van de finally volgende insluitingsinstructie try . Dit proces wordt herhaald totdat de finally blokken van alle tussenliggende try instructies zijn uitgevoerd.
  • Het besturingselement wordt overgebracht naar het doel van de break instructie.

Omdat een break verklaring voorwaardelijke controle elders overdraagt, is het eindpunt van een break instructie nooit bereikbaar.

13.10.3 De continue instructie

De continue instructie start een nieuwe iteratie van de dichtstbijzijnde insluitingwhile, doof forforeach instructie.

continue_statement
    : 'continue' ';'
    ;

Het doel van een continue instructie is het eindpunt van de ingesloten instructie van de dichtstbijzijnde omsluitende while, doof forforeach instructie. Als een continue instructie niet is ingesloten door een while, doof forforeach instructie, treedt er een compilatietijdfout op.

Wanneer meerdere while, doof forforeach instructies binnen elkaar zijn genest, is een continue instructie alleen van toepassing op de binnenste instructie. Om controle over meerdere nestniveaus over te dragen, wordt een goto instructie (§13.10.4) gebruikt.

Een continue instructie kan geen finally blok afsluiten (§13.11). Wanneer een continue instructie binnen een finally blok plaatsvindt, bevindt het doel van de continue instructie zich binnen hetzelfde finally blok; anders treedt er een compilatiefout op.

Er wordt als volgt een continue instructie uitgevoerd:

  • Als de continue instructie een of meer try blokken met gekoppelde finally blokken afsluit, wordt het besturingselement in eerste instantie overgebracht naar het finally blok van de binnenste try instructie. Wanneer en als het besturingselement het eindpunt van een finally blok bereikt, wordt het besturingselement overgebracht naar het blok van de finally volgende insluitingsinstructie try . Dit proces wordt herhaald totdat de finally blokken van alle tussenliggende try instructies zijn uitgevoerd.
  • Het besturingselement wordt overgebracht naar het doel van de continue instructie.

Omdat een continue verklaring voorwaardelijke controle elders overdraagt, is het eindpunt van een continue instructie nooit bereikbaar.

13.10.4 De goto-instructie

De goto instructie draagt het besturingselement over naar een instructie die is gemarkeerd door een label.

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

Het doel van een gotoid-instructie is de gelabelde instructie met het opgegeven label. Als er geen label met de opgegeven naam bestaat in het huidige functielid of als de goto instructie zich niet binnen het bereik van het label bevindt, treedt er een compilatietijdfout op.

Opmerking: Met deze regel kan het gebruik van een goto instructie het beheer buiten een genest bereik overdragen, maar niet in een genest bereik. In het voorbeeld

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

een goto instructie wordt gebruikt om het beheer buiten een genest bereik over te dragen.

eindnotitie

Het doel van een goto case instructie is de instructielijst in de instructie onmiddellijk tussenvoeging switch (§13.8.3) die een case label bevat met een constant patroon van de opgegeven constante waarde en geen bewaker. Als de goto case instructie niet wordt ingesloten door een switch instructie, als de dichtstbijzijnde insluitingsinstructie switch geen dergelijke case, of als de constant_expression niet impliciet converteerbaar is (§10.2) naar het type van de dichtstbijzijnde omsluitingsinstructie switch , treedt er een compilatietijdfout op.

Het doel van een goto default instructie is de lijst met instructies in de instructie die onmiddellijk insluit switch (§13.8.3), dat een default label bevat. Als de goto default instructie niet is ingesloten door een switch instructie of als de dichtstbijzijnde insluitingsinstructie switch geen label bevat default , treedt er een compilatietijdfout op.

Een goto instructie kan geen finally blok afsluiten (§13.11). Wanneer een goto instructie binnen een finally blok plaatsvindt, moet het doel van de goto instructie zich binnen hetzelfde finally blok bevinden of op een andere manier een compilatiefout optreedt.

Er wordt als volgt een goto instructie uitgevoerd:

  • Als de goto instructie een of meer try blokken met gekoppelde finally blokken afsluit, wordt het besturingselement in eerste instantie overgebracht naar het finally blok van de binnenste try instructie. Wanneer en als het besturingselement het eindpunt van een finally blok bereikt, wordt het besturingselement overgebracht naar het blok van de finally volgende insluitingsinstructie try . Dit proces wordt herhaald totdat de finally blokken van alle tussenliggende try instructies zijn uitgevoerd.
  • Het besturingselement wordt overgebracht naar het doel van de goto instructie.

Omdat een goto verklaring voorwaardelijke controle elders overdraagt, is het eindpunt van een goto instructie nooit bereikbaar.

13.10.5 De retourinstructie

De return instructie retourneert het besturingselement naar de huidige aanroeper van het functielid waarin de retourinstructie wordt weergegeven, optioneel een waarde of een variable_reference (§9,5).

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

Een return_statement zonder expressie wordt een return-no-value genoemd; een met expressie wordt een ref genoemd; en één met alleen een expressie wordt een return-by-value genoemd.

Het is een compilatiefout om een return-no-value te gebruiken van een methode die is gedeclareerd als returns-by-value of returns-by-ref (§15.6.1).

Het is een compilatiefout om een return-by-ref van een methode te gebruiken die is gedeclareerd als returns-no-value of returns-by-value.

Het is een compilatiefout om een return-by-value te gebruiken van een methode die is gedeclareerd als returns-no-value of returns-by-ref.

Het is een compilatiefout om een return-by-ref te gebruiken als expressie geen variable_reference is of een verwijzing is naar een variabele waarvan ref-safe-context geen aanroepercontext is (§9.7.2).

Het is een compilatiefout om een retour-by-ref te gebruiken van een methode die is gedeclareerd met de method_modifierasync.

Een functielid wordt gezegd om een waarde te berekenen als het een methode is met een methode voor retourneren per waarde (§15.6.11), een retour-by-value-get-accessor van een eigenschap of indexeerfunctie of een door de gebruiker gedefinieerde operator. Functieleden die geen waarde retourneren, berekenen geen waarde en zijn methoden met het effectieve retourtype void, stellen accessors van eigenschappen en indexeerfuncties in, voeg toegangsrechten van gebeurtenissen, instantieconstructors, statische constructors en finalizers toe en verwijder ze. Functieleden die worden geretourneerd per verw berekenen geen waarde.

Voor een retourwaarde bestaat een impliciete conversie (§10.2) van het type expressie tot het effectieve retourtype (§15.6.11) van het functielid. Voor een retouraanwijzing bestaat een identiteitsconversie (§10.2.2) tussen het type expressie en het effectieve retourtype van het functielid.

return instructies kunnen ook worden gebruikt in de hoofdtekst van anonieme functie-expressies (§12.19) en deelnemen aan het bepalen welke conversies voor deze functies bestaan (§10.7.1).

Het is een compilatiefout voor een return instructie die in een finally blok wordt weergegeven (§13.11).

Er wordt als volgt een return instructie uitgevoerd:

  • Voor een return-by-value wordt de expressie geëvalueerd en wordt de waarde ervan geconverteerd naar het effectieve retourtype van de resulterende functie door een impliciete conversie. Het resultaat van de conversie wordt de resultaatwaarde die door de functie wordt geproduceerd. Voor een return-by-ref wordt de expressie geëvalueerd en wordt het resultaat geclassificeerd als een variabele. Als de retour-by-ref van de methode insluiten, readonlyis de resulterende variabele alleen-lezen.
  • Als de return instructie wordt ingesloten door een of meer of try meer catch blokken met bijbehorende finally blokken, wordt het besturingselement in eerste instantie overgebracht naar het finally blok van de binnenste try instructie. Wanneer en als het besturingselement het eindpunt van een finally blok bereikt, wordt het besturingselement overgebracht naar het blok van de finally volgende insluitingsinstructie try . Dit proces wordt herhaald totdat de finally blokken van alle insluitinstructies try zijn uitgevoerd.
  • Als de bevatde functie geen asynchrone functie is, wordt het besturingselement geretourneerd naar de aanroeper van de functie die deze bevat, samen met de resultaatwaarde, indien van toepassing.
  • Als de bevatde functie een asynchrone functie is, wordt het besturingselement geretourneerd naar de huidige aanroeper en wordt de resultaatwaarde, indien aanwezig, opgenomen in de retourtaak zoals beschreven in (§15.15.3).

Omdat een return verklaring voorwaardelijke controle elders overdraagt, is het eindpunt van een return instructie nooit bereikbaar.

13.10.6 De werpinstructie

De throw instructie genereert een uitzondering.

throw_statement
    : 'throw' expression? ';'
    ;

Een throw instructie met een expressie genereert een uitzondering die wordt geproduceerd door de expressie te evalueren. De expressie moet impliciet worden omgezet in System.Exception, en het resultaat van het evalueren van de expressie wordt geconverteerd naar System.Exception voordat deze wordt gegenereerd. Als het resultaat van de conversie is null, wordt er in plaats daarvan een System.NullReferenceException gegenereerd.

Een throw instructie zonder expressie kan alleen worden gebruikt in een catch blok. In dat geval genereert die instructie de uitzondering die momenteel door dat catch blok wordt verwerkt, opnieuw.

Omdat een throw verklaring voorwaardelijke controle elders overdraagt, is het eindpunt van een throw instructie nooit bereikbaar.

Wanneer er een uitzondering wordt gegenereerd, wordt het besturingselement overgebracht naar de eerste catch component in een insluitinstructie try die de uitzondering kan verwerken. Het proces dat plaatsvindt vanaf het punt van de uitzondering dat wordt gegenereerd tot het punt van overdracht van het besturingselement naar een geschikte uitzonderingshandler wordt ook wel uitzonderingsdoorgifte genoemd. Het doorgeven van een uitzondering bestaat uit het herhaaldelijk evalueren van de volgende stappen totdat een catch component die overeenkomt met de uitzondering wordt gevonden. In deze beschrijving is het beginpunt de locatie waar de uitzondering wordt gegenereerd. Dit gedrag wordt opgegeven in (§21.4).

  • In het huidige functielid wordt elke try instructie die het gooipunt omsluit onderzocht. Voor elke instructie S, beginnend met de binnenste instructie en eindigend met de buitenste trytry instructie, worden de volgende stappen geëvalueerd:

    • Als het try blok van S het gooipunt en een S of meer catch componenten bevat, worden de catch componenten onderzocht om een geschikte handler voor de uitzondering te vinden. De eerste catch component die een uitzonderingstype T aangeeft (of een typeparameter die tijdens runtime een uitzonderingstype Taangeeft) zodat het runtimetype van E afgeleiden T wordt beschouwd als een overeenkomst. Als de component een uitzonderingsfilter bevat, wordt het uitzonderingsobject toegewezen aan de uitzonderingsvariabele en wordt het uitzonderingsfilter geëvalueerd. Wanneer een catch component een uitzonderingsfilter bevat, wordt die catch component beschouwd als een overeenkomst als het uitzonderingsfilter resulteert in true. Een algemene catch component (§13.11) wordt beschouwd als een overeenkomst voor elk uitzonderingstype. Als een overeenkomende catch component zich bevindt, wordt de doorgifte van uitzonderingen voltooid door het besturingselement over te dragen naar het blok van die catch component.
    • Als het try blok of een catch blok van S het gooipunt insluit en als S er een finally blok is, wordt het besturingselement overgezet naar het finally blok. Als het finally blok een andere uitzondering genereert, wordt de verwerking van de huidige uitzondering beëindigd. Anders wordt de verwerking van de huidige uitzondering voortgezet wanneer het besturingselement het eindpunt van het finally blok bereikt.
  • Als een uitzonderingshandler zich niet in de huidige functieaanroep bevindt, wordt de aanroep van de functie beëindigd en treedt een van de volgende handelingen op:

    • Als de huidige functie niet-asynchroon is, worden de bovenstaande stappen herhaald voor de aanroeper van de functie met een gooipunt dat overeenkomt met de instructie waaruit het functielid is aangeroepen.

    • Als de huidige functie asynchroon is en taken worden geretourneerd, wordt de uitzondering vastgelegd in de retourtaak, die een foutieve of geannuleerde status heeft, zoals beschreven in §15.15.3.

    • Als de huidige functie asynchroon is en void-retourneert, krijgt de synchronisatiecontext van de huidige thread een melding zoals beschreven in §15.15.4.

  • Als de uitzonderingsverwerking alle aanroepen van functieleden in de huidige thread beëindigt, wat aangeeft dat de thread geen handler heeft voor de uitzondering, wordt de thread zelf beëindigd. De impact van deze beëindiging is gedefinieerd door de implementatie.

13.11 De instructie try

De try instructie biedt een mechanisme voor het ondervangen van uitzonderingen die optreden tijdens het uitvoeren van een blok. Bovendien biedt de try instructie de mogelijkheid om een codeblok op te geven dat altijd wordt uitgevoerd wanneer het besturingselement de try instructie verlaat.

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
    ;

Een try_statement bestaat uit het trefwoord try gevolgd door een blok, vervolgens nul of meer catch_clauses en vervolgens een optionele finally_clause. Er is ten minste één catch_clause of een finally_clause.

In een exception_specifier het type, of de effectieve basisklasse, als het een type_parameter is, moet System.Exception of een type zijn dat ervan is afgeleid.

Wanneer een catch component zowel een class_type als een id opgeeft, wordt een uitzonderingsvariabele van de opgegeven naam en het type gedeclareerd. De uitzonderingsvariabele wordt in de declaratieruimte van het specific_catch_clause (§7.3) geïntroduceerd. Tijdens de uitvoering van het exception_filter en catch blokkeren vertegenwoordigt de uitzonderingsvariabele de uitzondering die momenteel wordt verwerkt. Voor het controleren van definitieve toewijzingen wordt de uitzonderingsvariabele beschouwd als definitief toegewezen in het gehele bereik.

Tenzij een component een naam van een catch uitzonderingsvariabele bevat, is het onmogelijk om toegang te krijgen tot het uitzonderingsobject in het filter en catch blok.

Een catch component die een uitzonderingstype of een naam van een uitzonderingsvariabele aangeeft, wordt een algemene catch component genoemd. Een try verklaring mag slechts één algemene catch component hebben en, indien aanwezig, is dit de laatste catch component.

Opmerking: Sommige programmeertalen ondersteunen mogelijk uitzonderingen die niet kunnen worden weergegeven als een object dat is afgeleid van System.Exception, hoewel dergelijke uitzonderingen nooit kunnen worden gegenereerd door C#-code. Een algemene catch component kan worden gebruikt om dergelijke uitzonderingen te ondervangen. Een algemene catch component verschilt dus semantisch van het type dat het type System.Exceptionaangeeft, omdat de voormalige ook uitzonderingen van andere talen kan ondervangen. eindnotitie

Om een handler voor een uitzondering te vinden, catch worden componenten in lexicale volgorde onderzocht. Als een catch component een type opgeeft, maar geen uitzonderingsfilter, is het een compilatiefout voor een latere catch component van dezelfde try instructie om een type op te geven dat hetzelfde is als of is afgeleid van dat type.

Opmerking: Zonder deze beperking is het mogelijk om onbereikbare catch componenten te schrijven. eindnotitie

Binnen een catch blok kan een throw instructie (§13.10.6) zonder expressie worden gebruikt om de uitzondering die door het catch blok is gevangen, opnieuw te genereren. Toewijzingen aan een uitzonderingsvariabele wijzigen niet de uitzondering die opnieuw wordt gegenereerd.

Voorbeeld: In de volgende code

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

de methode F onderschept een uitzondering, schrijft bepaalde diagnostische gegevens naar de console, wijzigt de uitzonderingsvariabele en genereert de uitzondering opnieuw. De uitzondering die opnieuw wordt gegenereerd, is de oorspronkelijke uitzondering, dus de geproduceerde uitvoer is:

Exception in F: G
Exception in Main: G

Als het eerste catch blok had gegenereerd e in plaats van de huidige uitzondering opnieuw te beperken, zou de geproduceerde uitvoer als volgt zijn:

Exception in F: G
Exception in Main: F

eindvoorbeeld

Het is een compilatiefout voor een break, continueof goto instructie om het besturingselement uit een finally blok over te dragen. Wanneer een break, continueof goto instructie optreedt in een finally blok, bevindt het doel van de instructie zich binnen hetzelfde finally blok, of op een andere manier treedt er een compilatietijdfout op.

Het is een compilatiefout voor een return instructie die in een finally blok plaatsvindt.

Wanneer de uitvoering een try instructie bereikt, wordt het besturingselement overgebracht naar het try blok. Als het besturingselement het eindpunt van het try blok bereikt zonder dat er een uitzondering wordt doorgegeven, wordt het besturingselement overgebracht naar het finally blok als er een bestaat. Als er geen finally blok bestaat, wordt het besturingselement overgebracht naar het eindpunt van de try instructie.

Als er een uitzondering is doorgegeven, worden de catch clausules, indien aanwezig, onderzocht in lexicale volgorde op zoek naar de eerste overeenkomst voor de uitzondering. Het zoeken naar een overeenkomende catch component gaat verder met alle insluitblokken, zoals beschreven in §13.10.6. Een catch component is een overeenkomst als het uitzonderingstype overeenkomt met een exception_specifier en eventuele exception_filter waar is. Een catch component zonder exception_specifier komt overeen met een uitzonderingstype. Het uitzonderingstype komt overeen met de exception_specifier wanneer het exception_specifier het uitzonderingstype of een basistype van het uitzonderingstype opgeeft. Als de component een uitzonderingsfilter bevat, wordt het uitzonderingsobject toegewezen aan de uitzonderingsvariabele en wordt het uitzonderingsfilter geëvalueerd.

Als er een uitzondering is doorgegeven en er een overeenkomende catch component wordt gevonden, wordt het besturingselement overgebracht naar het eerste overeenkomende catch blok. Als het besturingselement het eindpunt van het catch blok bereikt zonder dat er een uitzondering wordt doorgegeven, wordt het besturingselement overgebracht naar het finally blok als er een bestaat. Als er geen finally blok bestaat, wordt het besturingselement overgebracht naar het eindpunt van de try instructie. Als er een uitzondering van het catch blok is doorgegeven, worden besturingselementen overgedragen naar het finally blok als er een bestaat. De uitzondering wordt doorgegeven aan de volgende insluitingsinstructie try .

Als er een uitzondering is doorgegeven en er geen overeenkomende catch component wordt gevonden, controleert u overdrachten naar het finally blok als deze bestaat. De uitzondering wordt doorgegeven aan de volgende insluitingsinstructie try .

De instructies van een finally blok worden altijd uitgevoerd wanneer het besturingselement een try instructie verlaat. Dit is waar of de controleoverdracht plaatsvindt als gevolg van normale uitvoering, als gevolg van het uitvoeren van een break, continue, gotoof return instructie, of als gevolg van het doorgeven van een uitzondering buiten de try instructie. Als het besturingselement het eindpunt van het finally blok bereikt zonder dat er een uitzondering wordt doorgegeven, wordt het besturingselement overgebracht naar het eindpunt van de try instructie.

Als er een uitzondering wordt gegenereerd tijdens het uitvoeren van een finally blok en niet binnen hetzelfde finally blok wordt gevangen, wordt de uitzondering doorgegeven aan de volgende insluitinstructie try . Als er een andere uitzondering werd doorgegeven, gaat deze uitzondering verloren. Het doorgifteproces van een uitzondering wordt verder besproken in de beschrijving van de throw verklaring (§13.10.6).

Voorbeeld: In de volgende code

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

de methode Method genereert een uitzondering. De eerste actie is het onderzoeken van catch de insluitclausules, waarbij eventuele uitzonderingsfilters worden uitgevoerd. Vervolgens wordt de finally component in Method uitvoering uitgevoerd voordat het besturingselement wordt overgedragen naar de insluitingscomponent catch . De resulterende uitvoer is:

Filter
Finally
Catch

eindvoorbeeld

Het try blok van een try instructie is bereikbaar als de try instructie bereikbaar is.

Een catch blok van een try instructie is bereikbaar als de try instructie bereikbaar is.

Het finally blok van een try instructie is bereikbaar als de try instructie bereikbaar is.

Het eindpunt van een try instructie is bereikbaar als beide van de volgende waar zijn:

  • Het eindpunt van het try blok is bereikbaar of het eindpunt van ten minste één catch blok is bereikbaar.
  • Als er een finally blok aanwezig is, is het eindpunt van het finally blok bereikbaar.

13.12 De ingeschakelde en niet-gecontroleerde instructies

De checked en unchecked instructies worden gebruikt om de context voor overloopcontrole te beheren voor integrale rekenkundige bewerkingen en conversies.

checked_statement
    : 'checked' block
    ;

unchecked_statement
    : 'unchecked' block
    ;

De checked instructie zorgt ervoor dat alle expressies in het blok worden geëvalueerd in een gecontroleerde context en de unchecked instructie zorgt ervoor dat alle expressies in het blok worden geëvalueerd in een niet-gecontroleerde context.

De checked en unchecked instructies zijn precies gelijk aan de checked en unchecked operatoren (§12.8.20), behalve dat ze op blokken werken in plaats van expressies.

13.13 De vergrendelingsinstructie

De lock instructie verkrijgt de wederzijdse uitsluitingsvergrendeling voor een bepaald object, voert een instructie uit en geeft vervolgens de vergrendeling vrij.

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

De lock geeft een waarde aan van een type dat een verwijzing is. Er wordt nooit impliciete boksconversie (§10.2.9) uitgevoerd voor de expressie van een lock instructie, en het is dus een compilatiefout voor de expressie om een waarde van een value_type aan te geven.

Een lock instructie van het formulier

lock (x)

waarbij x een expressie van een reference_type precies gelijk is aan:

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

behalve dat dit x slechts eenmaal wordt geëvalueerd.

Hoewel een wederzijdse uitsluitingsvergrendeling wordt bewaard, kan code die wordt uitgevoerd in dezelfde uitvoeringsthread ook de vergrendeling verkrijgen en vrijgeven. Code die wordt uitgevoerd in andere threads, wordt echter geblokkeerd voor het verkrijgen van de vergrendeling totdat de vergrendeling wordt vrijgegeven.

13.14 De using-instructie

De using instructie verkrijgt een of meer resources, voert een instructie uit en verwijdert vervolgens de resource.

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

resource_acquisition
    : local_variable_declaration
    | expression
    ;

Een resource is een klasse of struct waarmee de interface wordt geïmplementeerd, die één parameterloze methode bevat met de System.IDisposable naam Dispose. Code die een resource gebruikt, kan worden aangeroepen Dispose om aan te geven dat de resource niet meer nodig is.

Als de vorm van resource_acquisition wordt local_variable_declaration, moet het type van de local_variable_declaration een of een type zijn dat impliciet kan worden geconverteerd naar .dynamicSystem.IDisposable Als de vorm van resource_acquisition een expressie is, wordt deze expressie impliciet omgezet in System.IDisposable.

Lokale variabelen die zijn gedeclareerd in een resource_acquisition zijn alleen-lezen en bevatten een initialisatiefunctie. Er treedt een compilatiefout op als de ingesloten instructie probeert deze lokale variabelen te wijzigen (via toewijzing of de ++ operatoren -- ), het adres ervan op te nemen of door te geven als referentie- of uitvoerparameters.

Een using instructie wordt vertaald in drie delen: verwerving, gebruik en verwijdering. Het gebruik van de resource wordt impliciet ingesloten in een try instructie die een finally component bevat. Met deze finally component wordt de resource verwijderd. Als een null resource wordt verkregen, wordt er geen aanroep gedaan Dispose en wordt er geen uitzondering gegenereerd. Als de resource van het type dynamic is, wordt deze dynamisch geconverteerd via een impliciete dynamische conversie (§10.2.10) naar IDisposable tijdens het verkrijgen om ervoor te zorgen dat de conversie vóór het gebruik en de verwijdering is geslaagd.

Een using instructie van het formulier

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

komt overeen met een van de drie mogelijke uitbreidingen. Wanneer ResourceType is een niet-null-waardetype of een typeparameter met de waardetypebeperking (§15.2.5), is de uitbreiding semantisch gelijk aan

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

behalve dat de cast van resource ton System.IDisposable geen boksen veroorzaakt.

Anders, wanneer ResourceType is dynamic, de uitbreiding is

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

Anders is de uitbreiding

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

In elke uitbreiding is de resource variabele alleen-lezen in de ingesloten instructie en is de d variabele niet toegankelijk en onzichtbaar voor de ingesloten instructie.

Een implementatie is toegestaan om een bepaalde using_statement anders te implementeren, bijvoorbeeld om prestatieredenen, zolang het gedrag consistent is met de bovenstaande uitbreiding.

Een using verklaring van het formulier:

using («expression») «statement»

heeft dezelfde drie mogelijke uitbreidingen. In dit geval ResourceType is impliciet het type compileertijd van de expressie, als deze er een heeft. Anders wordt de interface IDisposable zelf gebruikt als de ResourceType. De resource variabele is niet toegankelijk en onzichtbaar voor de ingesloten instructie.

Wanneer een resource_acquisition de vorm van een local_variable_declaration heeft, is het mogelijk om meerdere resources van een bepaald type te verkrijgen. Een using instructie van het formulier

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

is precies gelijk aan een reeks geneste using instructies:

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

Voorbeeld: In het onderstaande voorbeeld wordt een bestand met de naam log.txt gemaakt en worden twee regels tekst naar het bestand geschreven. In het voorbeeld wordt vervolgens hetzelfde bestand geopend voor het lezen en kopiëren van de ingesloten tekstregels naar de console.

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

Omdat de TextWriter en TextReader klassen de IDisposable interface implementeren, kan het voorbeeld instructies gebruiken using om ervoor te zorgen dat het onderliggende bestand correct wordt gesloten na de schrijf- of leesbewerkingen.

eindvoorbeeld

13.15 De rendementsrekening

De yield instructie wordt gebruikt in een iteratorblok (§13.3) om een waarde op te geven aan het enumeratorobject (§15.14.5) of een opsommingsobject (§15.14.6) van een iterator of om het einde van de iteratie aan te geven.

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

yield is een contextueel trefwoord (§6.4.4) en heeft alleen speciale betekenis wanneer deze direct voor een return of break trefwoord wordt gebruikt.

Er zijn verschillende beperkingen voor waar een yield instructie kan worden weergegeven, zoals beschreven in het volgende.

  • Het is een compilatiefout voor een yield instructie (van beide vormen) die buiten een method_body, operator_body of accessor_body wordt weergegeven.
  • Het is een compilatiefout voor een yield instructie (van beide vormen) die binnen een anonieme functie wordt weergegeven.
  • Het is een compilatiefout voor een yield instructie (van beide vormen) die wordt weergegeven in de finally component van een try instructie.
  • Het is een compilatiefout voor een yield return instructie die overal in een try instructie wordt weergegeven die catch_clauses bevat.

Voorbeeld: In het volgende voorbeeld ziet u een aantal geldige en ongeldige toepassingen van yield instructies.

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
}

eindvoorbeeld

Er bestaat een impliciete conversie (§10.2) van het type expressie in de yield return verklaring tot het rendementstype (§15.14.4) van de iterator.

Er wordt als volgt een yield return instructie uitgevoerd:

  • De expressie die in de instructie wordt gegeven, wordt impliciet geconverteerd naar het rendementstype en toegewezen aan de Current eigenschap van het enumerator-object.
  • Uitvoering van het iteratorblok wordt onderbroken. Als de yield return instructie zich binnen een of meer try blokken bevindt, worden de bijbehorende finally blokken op dit moment niet uitgevoerd.
  • De MoveNext methode van het enumerator-object keert true terug naar de aanroeper, waarmee wordt aangegeven dat het enumerator-object naar het volgende item is gevorderd.

De volgende aanroep van de methode van het enumerator-object MoveNext hervat de uitvoering van het iteratorblok van waaruit het voor het laatst is onderbroken.

Er wordt als volgt een yield break instructie uitgevoerd:

  • Als de yield break instructie wordt ingesloten door een of meer try blokken met bijbehorende finally blokken, wordt het besturingselement in eerste instantie overgebracht naar het finally blok van de binnenste try instructie. Wanneer en als het besturingselement het eindpunt van een finally blok bereikt, wordt het besturingselement overgebracht naar het blok van de finally volgende insluitingsinstructie try . Dit proces wordt herhaald totdat de finally blokken van alle insluitinstructies try zijn uitgevoerd.
  • Het besturingselement wordt teruggezet naar de aanroeper van het iteratorblok. Dit is de MoveNext methode of Dispose methode van het enumerator-object.

Omdat een yield break verklaring voorwaardelijke controle elders overdraagt, is het eindpunt van een yield break instructie nooit bereikbaar.