Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
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 (§24.2) en fixed_statement (§24.7) zijn alleen beschikbaar in onveilige code (§24).
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
ifinstructie een embedded_statement is vereist in plaats van een instructie voor deifvertakking. Als deze code is toegestaan, wordt de variabeleigedeclareerd, maar kan deze nooit worden gebruikt. Houd er echter rekening mee dat het voorbeeld geldig is door de aangifte in een blok te plaatseni.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.25) 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
ifinstructie is een constante expressie omdat beide operanden van de==operator constanten zijn. Wanneer de constante expressie tijdens het compileren wordt geëvalueerd, wordt defalseaanroepConsole.WriteLineals onbereikbaar beschouwd.iAls dit echter wordt gewijzigd in een lokale variabelevoid F() { int i = 1; if (i == 2) Console.WriteLine("reachable"); }de
Console.WriteLineaanroep 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.WriteLinewordt als volgt bepaald:
- De eerste
Console.WriteLineexpressie-instructie is bereikbaar omdat het blok van deFmethode bereikbaar is (§13.3).- Het eindpunt van de eerste
Console.WriteLineexpressie-instructie is bereikbaar omdat deze instructie bereikbaar is (§13.7 en §13.3).- De
ifinstructie is bereikbaar omdat het eindpunt van de eersteConsole.WriteLineexpressie-instructie bereikbaar is (§13.7 en §13.3).- De tweede
Console.WriteLineexpressie-instructie is bereikbaar omdat de Boole-expressie van deifinstructie niet de constante waardefalseheeft.eindvoorbeeld
Er zijn twee situaties waarin het een compilatiefout is voor het eindpunt van een instructie bereikbaar is:
Omdat de
switchinstructie 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 eenbreakinstructie 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
returninstructie 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.15). Enkele aanvullende beperkingen zijn van toepassing op iterator-blokken:
- Het is een compilatiefout voor een
returninstructie die wordt weergegeven in een iteratorblok (maaryield returninstructies zijn toegestaan). - Het is een compilatiefout voor een iteratorblok dat een onveilige context bevat (§24.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
gotoinstructie.
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
whileinstructie 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
gotoinstructies 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
varin het bereik is en de invoer overeenkomt met implicitly_typed_local_variable_declaration , wordt het gekozen; - Als een type met de naam
varbinnen 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 linkse 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 itselfeindvoorbeeld
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 explicitly_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.23.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
;
De initialisatie van variable_reference moet een type hebben en voldoen aan dezelfde eisen als voor een reftoewijzing (§12.23.3).
Als ref_kindref readonly is, zijn de identificatoren die worden gedeclareerd verwijzingen naar variabelen die als alleen-lezen worden behandeld. Anders, indien ref_kindref is, zijn de identificatoren die worden gedeclareerd verwijzingen naar variabelen die schrijfbaar moeten zijn.
Het is een compilatiefout om een lokale variabele met doorverwijzing of een variabele van een ref struct type te declareren binnen een methode die gedeclareerd is met de method_modifierasync, of binnen een iterator (§15.15).
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.25) 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.14) en een unsafe (§24.1) modifier bevatten. Als de declaratie de async modifier bevat, moet het retourtype void of een «TaskType» type zijn (§15.14.1). 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 (§24.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
thisengotospiegelen de regels voor anonieme functies in §12.21.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
Lbereikbaar, ook al is het beginpunt nietLbereikbaar. Omdat het beginpunt vanLhet niet bereikbaar is, is de instructie na het eindpuntLniet 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
thiseen implicietebaseverwijzing, noch naarthislokale variabelen, parameters of niet-statische lokale functies uit het bereik. Al deze zijn echter toegestaan in eennameof()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 + yenx == 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
ifinstructie van het formulierif (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.26) 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 deifinstructie. - Als de Boole-expressie oplevert
falseen eenelseonderdeel 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 deifinstructie. - Als de Boole-expressie oplevert
falseen eenelseonderdeel niet aanwezig is, wordt het besturingselement overgebracht naar het eindpunt van deifinstructie.
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 selector_expression van de switch.
switch_statement
: 'switch' selector_expression switch_block
;
selector_expression
: '(' expression ')'
| tuple_expression
;
switch_block
: '{' switch_section* '}'
;
switch_section
: switch_label+ statement_list
;
switch_label
: 'case' pattern case_guard? ':'
| 'default' ':'
;
case_guard
: 'when' null_coalescing_expression
;
Een switch_statement bestaat uit het trefwoord switch, gevolgd door een tuple_expression of haakjes expressie (die elk de selector_expression wordt 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 bevat case een gekoppeld patroon (§11) waarmee de waarde van de selector_expression van de switch 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.
Opmerking: Voor het gemak kunnen de haakjes in switch_statement worden weggelaten wanneer de selector_expression een tuple_expression is. Kan bijvoorbeeld
switch ((a, b)) …worden geschreven alsswitch (a, b) …. eindnotitie
Het bestuurstype van een switch instructie wordt vastgesteld door de selector_expression van de switch.
- Als het type van de selector_expression van de schakeloptie is
sbyte, ,byte,short,longuintulongcharintushort,bool, ,stringof 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 toepassingswitchis. - Als er anders precies één door de gebruiker gedefinieerde impliciete conversie bestaat van het type van de selector_expression van de switch naar een van de volgende mogelijke typen
sbyte: , , ,byteushortintshortuint,ulongcharlongofstring, een null-waardetype dat overeenkomt met een van deze typen, dan is het geconverteerde type het type van het type van de instructie.switch - Anders is het type bestuur van de
switchinstructie het type selector_expression van de switch. 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 selector_expression van de schakeloptie wordt geëvalueerd en geconverteerd naar het type bestuur.
- Het besturingselement wordt overgedragen op basis van de waarde van de selector_expression van de geconverteerde switch:
- Het lexically eerste patroon in de set
caselabels in dezelfdeswitchinstructie die overeenkomt met de waarde van de selector_expression van de switch, en waarvoor de guard-expressie afwezig is of waar evalueert, zorgt ervoor dat het besturingselement wordt overgebracht naar de lijst met instructies na het overeenkomendecaselabel. - Als er een
defaultlabel aanwezig is, wordt het besturingselement overgedragen naar de lijst met instructies na hetdefaultlabel. - Anders wordt het besturingselement overgedragen naar het eindpunt van de
switchinstructie.
- Het lexically eerste patroon in de set
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 caseofgoto defaultinstructie 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:endefault: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
breakinstructies per ongeluk worden weggelaten. De secties van deswitchbovenstaande 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 caseofgoto defaultinstructie, maar een constructie die het eindpunt van de instructielijst onbereikbaar weergeeft, is toegestaan. Een instructie die wordt beheerd door de Boole-expressiewhile, is bijvoorbeeldtruebekend dat deze nooit het eindpunt bereikt. Op dezelfde manier draagt eenthrowofreturninstructie 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
switchhet typestringzijn. 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 de operatoren voor gelijkheid van tekenreeksen (§12.14.8), is de
switchinstructie hoofdlettergevoelig en wordt een bepaalde schakelsectie alleen uitgevoerd als de selector_expression tekenreeks van de switch exact overeenkomt met eencaselabelconstante. eindnotitie
Wanneer het type van een instructie van toepassing switch is string of een null-waardetype, 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 selector_expression van de switch is een constante waarde en een van beide
- het label is een
casewiens 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
defaultlabel 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.
- het label is een
- De selector_expression van de switch is geen constante waarde en een van beide
- het label is een
casezonder bewaker of met een bewaker waarvan de waarde niet de constante onwaar is; of - het is een
defaultlabel 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.
- het label is een
- Er wordt naar het switchlabel verwezen door een bereikbaar
goto caseofgoto defaultinstructie.
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
switchinstructie bevat een bereikbarebreakinstructie waarmee deswitchinstructie wordt afgesloten. - Er is geen
defaultlabel aanwezig en ook- De selector_expression van de switch is een niet-constante waarde en de reeks patronen die worden weergegeven in de gevallen van de switch-instructie die geen bewakers heeft of bewakers waarvan de waarde de constante waar is, is niet volledig (§11.4) voor het type schakeloptie.
- De selector_expression van de switch is een niet-constante waarde van een nullable type en er wordt geen patroon weergegeven in de gevallen van de switch-instructie die geen bewakers heeft of bewakers waarvan de waarde de constante waar is, overeenkomt met de waarde
null. - De selector_expression van de switch is een constante waarde en geen
caselabel zonder bewaker of waarvan de bewaker de constante waar is, komt overeen met die waarde.
Voorbeeld: De volgende code toont een beknopt gebruik van de
whencomponent: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.26) 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 eencontinueinstructie), wordt het besturingselement overgebracht naar het begin van dewhileinstructie. - Als de Boole-expressie oplevert
false, wordt het besturingselement overgebracht naar het eindpunt van dewhileinstructie.
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
whileinstructie bevat een bereikbarebreakinstructie waarmee dewhileinstructie wordt afgesloten. - De
whileinstructie is bereikbaar en de Boole-expressie heeft niet de constante waardetrue.
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
continueinstructie), wordt de boolean_expression (§12.26) geëvalueerd. Als de Boole-expressie opleverttrue, wordt het besturingselement overgebracht naar het begin van dedoinstructie. Anders wordt het besturingselement overgedragen naar het eindpunt van dedoinstructie.
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
doinstructie bevat een bereikbarebreakinstructie waarmee dedoinstructie 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, indien aanwezig, is een boolean_expression (§12.26).
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 eencontinueinstructie), 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 deforinstructie.
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
forinstructie is bereikbaar en er is geen for_condition aanwezig. - De
forinstructie is bereikbaar en een for_condition aanwezig is en heeft niet de constante waardefalse.
Het eindpunt van een for instructie is bereikbaar als ten minste een van de volgende beweringen waar is:
- De
forinstructie bevat een bereikbarebreakinstructie waarmee deforinstructie wordt afgesloten. - De
forinstructie is bereikbaar en een for_condition aanwezig is en heeft niet de constante waardetrue.
13.9.5 De foreach-instructie
13.9.5.1 Algemeen
De foreach instructie bevat de elementen van een verzameling en voert een ingesloten instructie uit voor elk element van de verzameling.
foreach_statement
: 'await'? '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.
Het is een compilatietijdfout als zowel await als ref_kind aanwezig zijn in een foreach statement.
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.
De compileertijdverwerking van een foreach instructie bepaalt eerst het verzamelingstype (C), het enumeratortype (E) en het iteratietype (Tof ref Tref readonly T) van de expressie.
De bepaling is vergelijkbaar voor de synchrone en asynchrone versies. Verschillende interfaces met verschillende methoden en retourtypen onderscheiden de synchrone en asynchrone versies. Het algemene proces verloopt als volgt. Namen binnen '«' en '»' zijn tijdelijke aanduidingen voor de werkelijke namen voor synchrone en asynchrone iterators. De typen die zijn toegestaan voor «GetEnumerator», «MoveNext», «IEnumerable»T, «IEnumerator»<T> en andere onderscheids worden beschreven in < voor een synchrone > instructie en in §13.9.5.3 voor een asynchrone foreach instructie.foreach
- Bepaal of het type
Xexpressie een geschikte methode «GetEnumerator» heeft:- Voer lidzoekactie uit op het type
Xmet de 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 beschreven in stap 2. 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
Evan de methode «GetEnumerator» geen klasse, struct of interfacetype is, produceert u een fout en voert u geen verdere stappen uit. - Voer de opzoekactie
Evoor leden uit met de idCurrenten geen typeargumenten. Als de opzoekactie van het lid geen overeenkomst produceert, is het resultaat een fout of is het resultaat iets behalve een eigenschap van een openbare instantie die het lezen toestaat, een fout produceert en geen verdere stappen uitvoert. - Voer het opzoeken van leden
Euit met de id «MoveNext» en geen typeargumenten. Als de opzoekactie van het lid geen overeenkomst produceert, is het resultaat een fout of het resultaat is iets behalve een methodegroep, produceert u een fout en voert u geen verdere stappen uit. - Voer een overbelastingsresolutie uit voor de methodegroep met een lege lijst met argumenten. Als overbelastingsresolutie resulteert in: geen toepasselijke methoden; dubbelzinnigheid; of één beste methode, maar die methode is statisch, of niet openbaar, of het retourtype is geen toegestaan retourtype; produceert vervolgens een fout en voert u geen verdere stappen uit.
- Het verzamelingstype is
X, het enumeratortype isEen het iteratietype is het type van deCurrenteigenschap.
- Voer lidzoekactie uit op het type
- Controleer anders of er een enumerable interface is:
- Als een van alle typen
Tᵢwaarvoor een impliciete conversie is vanX«IEnumerable»<Ti>, is er een uniek typeTdatTnietdynamicis en voor alle andereTᵢis er een impliciete conversie van «IEnumerable»<T> naar «IEnumerable»<Ti>, dan is het verzamelingstype de interface «IEnumerable»<T, het enumeratortype is de interface «IEnumerator»>T<>, en het iteratietype isT. - Als er meer dan één dergelijk type
Tis, produceert u een fout en voert u geen verdere stappen uit.
- Als een van alle typen
Opmerking: Als de expressie de waarde
nullheeft, wordt er eenSystem.NullReferenceExceptiongegenereerd tijdens runtime. eindnotitie
Een implementatie is toegestaan om een bepaalde foreach_statement anders te implementeren; bijvoorbeeld om prestatieredenen, zolang het gedrag overeenkomt met de uitbreidingen die worden beschreven in §13.9.5.2 en §13.9.5.3.
13.9.5.2 Synchrone foreach
Een synchrone foreach bevat await het trefwoord niet vóór het foreach trefwoord. De bepaling van het type verzameling, opsommingstype en iteratietype, zoals beschreven in §13.9.5.1, waarbij:
- «GetEnumerator» is een
GetEnumeratormethode. - «MoveNext» is een
MoveNextmethode met eenboolretourtype. - «IEnumerable»<T> is de
System.Collections.Generic.IEnumerable<T>interface. - «IEnumerator»<T> is de
System.Collections.Generic.IEnumerator<T>interface.
Daarnaast worden de volgende wijzigingen aangebracht in de stappen in §13.9.5.1:
Vóór het proces dat in §13.9.5.1 wordt beschreven, worden de volgende stappen uitgevoerd:
- Als het type
Xexpressie een matrixtype is, is er een impliciete verwijzingsconversie vanXnaar deIEnumerable<T>interface waarThet elementtype van de matrixXis (§17.2.3). - Als het type
Xexpressie is, is er een impliciete conversie vandynamicnaar de interface (IEnumerable). Het verzamelingstype is deIEnumerableinterface en het enumeratortype is deIEnumeratorinterface. Als devarid wordt opgegeven als de local_variable_type , isdynamichet iteratietype , anders isobjecthet .
Als het proces in §13.9.5.1 is voltooid zonder één verzamelingstype, opsommingstype en herhalingstype te produceren, worden de volgende stappen uitgevoerd:
- Als er een impliciete conversie van
Xnaar deSystem.Collections.IEnumerableinterface is, is het verzamelingstype deze interface, is het enumeratortype de interfaceSystem.Collections.IEnumeratoren het iteratietypeobject. - Anders wordt er een fout gegenereerd en worden er geen verdere stappen ondernomen.
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.23.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.
De plaatsing van v binnen de while lus is belangrijk voor hoe deze wordt vastgelegd (§12.21.6.2) door een anonieme functie die plaatsvindt in de embedded_statement.
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
vin het uitgevouwen formulier buiten dewhilelus werd gedeclareerd, zou deze worden gedeeld tussen alle iteraties en de bijbehorende waarde na deforlus de uiteindelijke waarde zou zijn,13wat de aanroep zoufafdrukken. Omdat elke iteratie een eigen variabelevheeft, blijftfde 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 lusvzijn gedeclareerdwhile.)eindvoorbeeld
De hoofdtekst van het finally blok wordt samengesteld volgens de volgende stappen:
Als er een impliciete conversie van
Enaar deSystem.IDisposableinterface is, danAls
Edit een niet-null-waardetype is, wordt definallycomponent uitgebreid naar het semantische equivalent van:finally { ((System.IDisposable)e).Dispose(); }Anders wordt de
finallycomponent uitgebreid naar het semantische equivalent van:finally { System.IDisposable d = e as System.IDisposable; if (d != null) { d.Dispose(); } }behalve dat als
Ehet een waardetype is, of een typeparameter die is geïnstantieerd op een waardetype, de conversie vanenaarSystem.IDisposablemag geen boksen veroorzaken.
EAls dit een verzegeld type is, wordt definallycomponent uitgebreid naar een leeg blok:finally {}Anders wordt de
finallycomponent 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.9eindvoorbeeld
Voorbeeld: In het volgende voorbeeld
int[] numbers = { 1, 3, 5, 7, 9 }; foreach (var n in numbers) { Console.WriteLine(n); }het type wordt
nafgeleid datinthet iteratietype isnumbers.eindvoorbeeld
13.9.5.3 wachten op foreach
Een asynchrone foreach maakt gebruik van de await foreach syntaxis. De bepaling van het type verzameling, opsommingstype en iteratietype, zoals beschreven in §13.9.5.1, waarbij:
- «GetEnumerator» is een methode met een
GetEnumeratorAsyncverwacht retourtype (§12.9.9.2). - «MoveNext» is een methode met een
MoveNextAsyncverwacht retourtype (§12.9.9.2) waarbij het await_expression is geclassificeerd als eenbool(§12.9.9.3). - «IEnumerable»<T> is de
System.Collections.Generic.IAsyncEnumerable<T>interface. - «IEnumerator»<T> is de
System.Collections.Generic.IAsyncEnumerator<T>interface.
Het is een fout voor het iteratietype van een await foreach instructie als referentievariabele (§9.7).
Een await foreach verklaring van de vorm
await foreach (T item in enumerable) «embedded_statement»
semantisch gelijk is aan:
var enumerator = enumerable.GetAsyncEnumerator();
try
{
while (await enumerator.MoveNextAsync())
{
T item = enumerator.Current;
«embedded_statement»
}
}
finally
{
// dispose of enumerator as described later in this clause.
}
In het geval dat de expressie enumerable een methode-aanroepexpressie vertegenwoordigt en een van de parameters is gemarkeerd met de EnumeratorCancellationAttribute (§23.5.8) wordt de CancellationToken methode doorgegeven GetAsyncEnumerator . Voor andere bibliotheekmethoden is mogelijk vereist dat er een CancellationToken wordt doorgegeven aan GetAsyncEnumerator. Wanneer deze methoden deel uitmaken van de expressieenumerable, worden de tokens gecombineerd tot één token alsof en CreateLinkedTokenSource de Token eigenschap ervan.
De hoofdtekst van het finally blok wordt samengesteld volgens de volgende stappen:
Als
Eer een toegankelijkeDisposeAsync()methode is waarbij het retourtype wachtbaar is (§12.9.9.2), wordt definallycomponent uitgebreid naar het semantische equivalent van:finally { await e.DisposeAsync(); }Als er anders een impliciete conversie van
Enaar deSystem.IAsyncDisposableinterface is enEeen niet-null-waardetype is, wordt definallycomponent uitgebreid naar het semantische equivalent van:finally { await ((System.IAsyncDisposable)e).DisposeAsync(); }behalve dat als
Ehet een waardetype is, of een typeparameter die is geïnstantieerd op een waardetype, de conversie vanenaarSystem.IAsyncDisposablemag geen boksen veroorzaken.EAls dit niet hetref structtype is en een toegankelijkeDispose()methode heeft, wordt definallycomponent uitgebreid naar het semantische equivalent van:finally { e.Dispose(); }EAls dit een verzegeld type is, wordt definallycomponent uitgebreid naar een leeg blok:finally {}Anders wordt de
finallycomponent uitgebreid naar:finally { System.IAsyncDisposable d = e as System.IAsyncDisposable; if (d != null) { await d.DisposeAsync(); } }
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.
Opmerking: een
await foreachis niet vereist om synchroon te verwijdereneals er geen asynchroon verwijderingsmechanisme beschikbaar is. eindnotitie
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
finallyblokken die aan tweetryinstructies 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 breakeindvoorbeeld
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
breakinstructie een of meertryblokken met gekoppeldefinallyblokken afsluit, wordt het besturingselement in eerste instantie overgebracht naar hetfinallyblok van de binnenstetryinstructie. Wanneer en als het besturingselement het eindpunt van eenfinallyblok bereikt, wordt het besturingselement overgebracht naar het blok van definallyvolgende insluitingsinstructietry. Dit proces wordt herhaald totdat definallyblokken van alle tussenliggendetryinstructies zijn uitgevoerd. - Het besturingselement wordt overgebracht naar het doel van de
breakinstructie.
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
continueinstructie een of meertryblokken met gekoppeldefinallyblokken afsluit, wordt het besturingselement in eerste instantie overgebracht naar hetfinallyblok van de binnenstetryinstructie. Wanneer en als het besturingselement het eindpunt van eenfinallyblok bereikt, wordt het besturingselement overgebracht naar het blok van definallyvolgende insluitingsinstructietry. Dit proces wordt herhaald totdat definallyblokken van alle tussenliggendetryinstructies zijn uitgevoerd. - Het besturingselement wordt overgebracht naar het doel van de
continueinstructie.
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
gotoinstructie het beheer buiten een genest bereik overdragen, maar niet in een genest bereik. In het voorbeeldclass 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
gotoinstructie 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
gotoinstructie een of meertryblokken met gekoppeldefinallyblokken afsluit, wordt het besturingselement in eerste instantie overgebracht naar hetfinallyblok van de binnenstetryinstructie. Wanneer en als het besturingselement het eindpunt van eenfinallyblok bereikt, wordt het besturingselement overgebracht naar het blok van definallyvolgende insluitingsinstructietry. Dit proces wordt herhaald totdat definallyblokken van alle tussenliggendetryinstructies zijn uitgevoerd. - Het besturingselement wordt overgebracht naar het doel van de
gotoinstructie.
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.21) 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
returninstructie wordt ingesloten door een of meer oftrymeercatchblokken met bijbehorendefinallyblokken, wordt het besturingselement in eerste instantie overgebracht naar hetfinallyblok van de binnenstetryinstructie. Wanneer en als het besturingselement het eindpunt van eenfinallyblok bereikt, wordt het besturingselement overgebracht naar het blok van definallyvolgende insluitingsinstructietry. Dit proces wordt herhaald totdat definallyblokken van alle insluitinstructiestryzijn 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.14.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 (§22.4).
In het huidige functielid wordt elke
tryinstructie die het gooipunt omsluit onderzocht. Voor elke instructieS, beginnend met de binnenste instructie en eindigend met de buitenstetrytryinstructie, worden de volgende stappen geëvalueerd:- Als het
tryblok vanShet gooipunt en eenSof meercatchcomponenten bevat, worden decatchcomponenten onderzocht om een geschikte handler voor de uitzondering te vinden. De eerstecatchcomponent die een uitzonderingstypeTaangeeft (of een typeparameter die tijdens runtime een uitzonderingstypeTaangeeft) zodat het runtimetype vanEafgeleidenTwordt beschouwd als een overeenkomst. Als de component een uitzonderingsfilter bevat, wordt het uitzonderingsobject toegewezen aan de uitzonderingsvariabele en wordt het uitzonderingsfilter geëvalueerd. Wanneer eencatchcomponent een uitzonderingsfilter bevat, wordt diecatchcomponent beschouwd als een overeenkomst als het uitzonderingsfilter resulteert intrue. Een algemenecatchcomponent (§13.11) wordt beschouwd als een overeenkomst voor elk uitzonderingstype. Als een overeenkomendecatchcomponent zich bevindt, wordt de doorgifte van uitzonderingen voltooid door het besturingselement over te dragen naar het blok van diecatchcomponent. - Als het
tryblok of eencatchblok vanShet gooipunt insluit en alsSer eenfinallyblok is, wordt het besturingselement overgezet naar hetfinallyblok. Als hetfinallyblok 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 hetfinallyblok bereikt.
- Als het
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.14.3.
Als de huidige functie asynchroon is en
void-retourneert, krijgt de synchronisatiecontext van de huidige thread een melding zoals beschreven in §15.14.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 algemenecatchcomponent kan worden gebruikt om dergelijke uitzonderingen te ondervangen. Een algemenecatchcomponent verschilt dus semantisch van het type dat het typeSystem.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
catchcomponenten 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
Fonderschept 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: GAls het eerste
catchblok had gegenereerdein plaats van de huidige uitzondering opnieuw te beperken, zou de geproduceerde uitvoer als volgt zijn:Exception in F: G Exception in Main: Feindvoorbeeld
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 de evaluatie van de boolean_expression voor een exception_filter een uitzondering genereert, wordt die uitzondering gevangen en wordt het uitzonderingsfilter falsegeë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
Methodgenereert een uitzondering. De eerste actie is het onderzoeken vancatchde insluitclausules, waarbij eventuele uitzonderingsfilters worden uitgevoerd. Vervolgens wordt definallycomponent inMethoduitvoering uitgevoerd voordat het besturingselement wordt overgedragen naar de insluitingscomponentcatch. De resulterende uitvoer is:Filter Finally Catcheindvoorbeeld
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
tryblok is bereikbaar of het eindpunt van ten minste ééncatchblok is bereikbaar. - Als er een
finallyblok aanwezig is, is het eindpunt van hetfinallyblok 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 geeft een waarde aan van een type dat een lock 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
13.14.1 Algemeen
De using instructie verkrijgt een of meer resources, voert een instructie uit en verwijdert vervolgens de resource.
using_statement
: 'await'? 'using' '(' resource_acquisition ')' embedded_statement
;
resource_acquisition
: non_ref_local_variable_declaration
| expression
;
non_ref_local_variable_declaration
: implicitly_typed_local_variable_declaration
| explicitly_typed_local_variable_declaration
;
Een resourcetype is een klasse of niet-verw-struct die een of beide of System.IDisposableSystem.IAsyncDisposable beide interfaces implementeert, die één methode zonder parameters bevat met de naam Dispose en/of DisposeAsync; of een verwijzingsstruct die een methode Dispose bevat met dezelfde handtekening als die is gedeclareerd door System.IDisposable. Code die een resource gebruikt, kan aanroepen Dispose of DisposeAsync aangeven dat de resource niet meer nodig is.
Als de vorm van resource_acquisitionis local_variable_declaration , moet het type local_variable_declaration ofwel een resourcetype zijn dynamic . Als de vorm van resource_acquisitioneen expressie is, heeft deze expressie een resourcetype. Indien await aanwezig, wordt het resourcetype geïmplementeerd System.IAsyncDisposable. Een ref struct type kan niet het resourcetype zijn voor een using instructie met de await wijzigingsfunctie.
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 de overname-expressie wordt geëvalueerd null, wordt er geen aanroep naar Dispose (of DisposeAsync) uitgevoerd 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 (of IAsyncDisposable) tijdens het verkrijgen om ervoor te zorgen dat de conversie is geslaagd voordat het gebruik en de verwijdering wordt uitgevoerd.
Een using instructie van het formulier
using (ResourceType resource = «expression» ) «statement»
komt overeen met een van de drie mogelijke formuleringen. Voor klasse- en niet-refstructresources is de ResourceType formulering semantisch gelijk aan de formulering als een niet-null-waardetypetype of een typeparameter met de waardetypebeperking (§15.2.5)
{
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 formulering is
{
ResourceType resource = «expression»;
IDisposable d = resource;
try
{
«statement»;
}
finally
{
if (d != null)
{
d.Dispose();
}
}
}
Anders is de formulering
{
ResourceType resource = «expression»;
try
{
«statement»;
}
finally
{
IDisposable d = (IDisposable)resource;
if (d != null)
{
d.Dispose();
}
}
}
Voor verw-structresources is de enige semantisch equivalente formulering
{
«ResourceType» resource = «expression»;
try
{
«statement»;
}
finally
{
resource.Dispose();
}
}
In elke formulering is de resource variabele alleen-lezen in de ingesloten instructie en is de d variabele niet toegankelijk en onzichtbaar voor de ingesloten instructie.
Een using verklaring van het formulier:
using («expression») «statement»
heeft dezelfde mogelijke formuleringen.
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
TextWriterenTextReaderklassen deIDisposableinterface implementeren, kan het voorbeeld instructies gebruikenusingom ervoor te zorgen dat het onderliggende bestand correct wordt gesloten na de schrijf- of leesbewerkingen.eindvoorbeeld
Wanneer ResourceType is een referentietype dat wordt geïmplementeerd IAsyncDisposable. Andere formuleringen voor await using het uitvoeren van vergelijkbare vervangingen van de synchrone Dispose methode naar de asynchrone DisposeAsync methode. Een await using verklaring van de vorm
await using (ResourceType resource = «expression» ) «statement»
is semantisch equivalent aan de onderstaande formuleringen met IAsyncDisposable in plaats van , IDisposable in plaats van DisposeAsyncDispose, en de Task geretourneerde DisposeAsyncawaitis ed:
await using (ResourceType resource = «expression» ) «statement»
semantisch gelijk is aan
{
ResourceType resource = «expression»;
try
{
«statement»;
}
finally
{
IAsyncDisposable d = (IAsyncDisposable)resource;
if (d != null)
{
await d.DisposeAsync();
}
}
}
Opmerking: eventuele jumpinstructies (§13.10) in de embedded_statement moeten voldoen aan de uitgebreide vorm van de
usinginstructie. eindnotitie
13.14.2 Met declaratie
Een syntactische variant van de using-instructie is een using-declaratie.
using_declaration
: 'await'? 'using' non_ref_local_variable_declaration ';' statement_list?
;
Een using-declaratie heeft dezelfde semantiek als en kan als volgt worden herschreven als de bijbehorende resource-verwervingsvorm van de using-instructie (§13.14.1):
using «local_variable_type» «local_variable_declarators»
// statements
semantisch gelijk is aan
using («local_variable_type» «local_variable_declarators»)
{
// statements
}
en
await using «local_variable_type» «local_variable_declarators»
// statements
semantisch gelijk is aan
await using («local_variable_type» «local_variable_declarators»)
{
// statements
}
De levensduur van de variabelen die zijn gedeclareerd in een non_ref_local_variable_declaration wordt uitgebreid tot het einde van het bereik waarin ze worden gedeclareerd. Deze variabelen worden vervolgens verwijderd in de omgekeerde volgorde waarin ze worden gedeclareerd.
static void M()
{
using FileStream f1 = new FileStream(...);
using FileStream f2 = new FileStream(...), f3 = new FileStream(...);
...
// Dispose f3
// Dispose f2
// Dispose f1
}
Een gebruiksdeclaratie wordt niet rechtstreeks in een switch_label weergegeven, maar kan in plaats daarvan binnen een blok binnen een switch_label zijn.
13.15 De rendementsrekening
De yield instructie wordt gebruikt in een iteratorblok (§13.3) om een waarde op te geven aan het enumeratorobject (§15.15.5) of een opsommingsobject (§15.15.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
yieldinstructie (van beide vormen) die buiten een method_body, operator_body of accessor_body wordt weergegeven. - Het is een compilatiefout voor een
yieldinstructie (van beide vormen) die binnen een anonieme functie wordt weergegeven. - Het is een compilatiefout voor een
yieldinstructie (van beide vormen) die wordt weergegeven in definallycomponent van eentryinstructie. - Het is een compilatiefout voor een
yield returninstructie die overal in eentryinstructie wordt weergegeven die catch_clauses bevat.
Voorbeeld: In het volgende voorbeeld ziet u een aantal geldige en ongeldige toepassingen van
yieldinstructies.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
Een impliciete conversie (§10.2) moet bestaan van het type expressie in de yield return verklaring tot het rendementstype (§15.15.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
Currenteigenschap van het enumerator-object. - Uitvoering van het iteratorblok wordt onderbroken. Als de
yield returninstructie zich binnen een of meertryblokken bevindt, worden de bijbehorendefinallyblokken op dit moment niet uitgevoerd. - De
MoveNextmethode van het enumerator-object keerttrueterug 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 breakinstructie wordt ingesloten door een of meertryblokken met bijbehorendefinallyblokken, wordt het besturingselement in eerste instantie overgebracht naar hetfinallyblok van de binnenstetryinstructie. Wanneer en als het besturingselement het eindpunt van eenfinallyblok bereikt, wordt het besturingselement overgebracht naar het blok van definallyvolgende insluitingsinstructietry. Dit proces wordt herhaald totdat definallyblokken van alle insluitinstructiestryzijn uitgevoerd. - Het besturingselement wordt teruggezet naar de aanroeper van het iteratorblok. Dit is de
MoveNextmethode ofDisposemethode van het enumerator-object.
Omdat een yield break verklaring voorwaardelijke controle elders overdraagt, is het eindpunt van een yield break instructie nooit bereikbaar.
ECMA C# draft specification