13 Instructies
13.1 Algemeen
C# biedt verschillende instructies.
Opmerking: De meeste van deze instructies zijn bekend bij ontwikkelaars die zijn geprogrammeerd in C en C++. eindnotitie
statement
: labeled_statement
| declaration_statement
| embedded_statement
;
embedded_statement
: block
| empty_statement
| expression_statement
| selection_statement
| iteration_statement
| jump_statement
| try_statement
| checked_statement
| unchecked_statement
| lock_statement
| using_statement
| yield_statement
| unsafe_statement // unsafe code support
| fixed_statement // unsafe code support
;
unsafe_statement (§23.2) en fixed_statement (§23.7) zijn alleen beschikbaar in onveilige code (§23).
De embedded_statement niet-terminal wordt gebruikt voor instructies die in andere instructies worden weergegeven. Het gebruik van embedded_statement in plaats van instructie sluit het gebruik van declaratie-instructies en gelabelde instructies in deze contexten uit.
Voorbeeld: De code
void F(bool b) { if (b) int i = 44; }
resulteert in een compilatiefout omdat voor een
if
instructie een embedded_statement is vereist in plaats van een instructie voor deif
vertakking. Als deze code is toegestaan, wordt de variabelei
gedeclareerd, 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.23) die het gedrag van instructies bepalen, maar de mogelijke waarden van niet-constante expressies worden niet overwogen. Met andere woorden, voor controlestroomanalyse wordt een niet-constante expressie van een bepaald type beschouwd als een mogelijke waarde van dat type.
In het voorbeeld
void F() { const int i = 1; if (i == 2) Console.WriteLine("unreachable"); }
de Booleaanse expressie van de
if
instructie is een constante expressie omdat beide operanden van de==
operator constanten zijn. Wanneer de constante expressie tijdens het compileren wordt geëvalueerd, wordt defalse
aanroepConsole.WriteLine
als onbereikbaar beschouwd.i
Als dit echter wordt gewijzigd in een lokale variabelevoid F() { int i = 1; if (i == 2) Console.WriteLine("reachable"); }
de
Console.WriteLine
aanroep wordt beschouwd als bereikbaar, ook al wordt deze in werkelijkheid nooit uitgevoerd.eindnotitie
Het blok van een functielid of een anonieme functie wordt altijd als bereikbaar beschouwd. Door achtereenvolgens de bereikbaarheidsregels van elke instructie in een blok te evalueren, kan de bereikbaarheid van een bepaalde instructie worden bepaald.
Voorbeeld: In de volgende code
void F(int x) { Console.WriteLine("start"); if (x < 0) Console.WriteLine("negative"); }
de bereikbaarheid van de tweede
Console.WriteLine
wordt als volgt bepaald:
- De eerste
Console.WriteLine
expressie-instructie is bereikbaar omdat het blok van deF
methode bereikbaar is (§13.3).- Het eindpunt van de eerste
Console.WriteLine
expressie-instructie is bereikbaar omdat deze instructie bereikbaar is (§13.7 en §13.3).- De
if
instructie is bereikbaar omdat het eindpunt van de eersteConsole.WriteLine
expressie-instructie bereikbaar is (§13.7 en §13.3).- De tweede
Console.WriteLine
expressie-instructie is bereikbaar omdat de Boole-expressie van deif
instructie niet de constante waardefalse
heeft.eindvoorbeeld
Er zijn twee situaties waarin het een compilatiefout is voor het eindpunt van een instructie bereikbaar is:
Omdat de
switch
instructie niet toestaat dat een switchsectie naar de volgende switchsectie 'valt', is het een compilatiefout voor het eindpunt van de instructielijst van een switchsectie bereikbaar. Als deze fout optreedt, is het meestal een indicatie dat eenbreak
instructie ontbreekt.Het is een compilatiefout voor het eindpunt van het blok van een functielid of een anonieme functie waarmee een waarde wordt berekend die bereikbaar is. Als deze fout optreedt, is het meestal een indicatie dat een
return
instructie ontbreekt (§13.10.5).
13.3 Blokken
13.3.1 Algemeen
Met een blok kunnen meerdere instructies worden geschreven in contexten waarin één instructie is toegestaan.
block
: '{' statement_list? '}'
;
Een blok bestaat uit een optionele statement_list (§13.3.2), tussen accolades. Als de lijst met instructies wordt weggelaten, wordt gezegd dat het blok leeg is.
Een blok kan declaratieverklaringen bevatten (§13.6). Het bereik van een lokale variabele of constante die in een blok is gedeclareerd, is het blok.
Er wordt als volgt een blok uitgevoerd:
- Als het blok leeg is, wordt het besturingselement overgebracht naar het eindpunt van het blok.
- Als het blok niet leeg is, wordt het besturingselement overgebracht naar de lijst met instructies. Wanneer en als het besturingselement het eindpunt van de instructielijst bereikt, wordt het besturingselement overgebracht naar het eindpunt van het blok.
De instructielijst van een blok is bereikbaar als het blok zelf bereikbaar is.
Het eindpunt van een blok is bereikbaar als het blok leeg is of als het eindpunt van de lijst met instructies bereikbaar is.
Een blok met een of meer yield
instructies (§13.15) wordt een iteratorblok genoemd. Iteratorblokken worden gebruikt om functieleden te implementeren als iterators (§15.14). Enkele aanvullende beperkingen zijn van toepassing op iterator-blokken:
- Het is een compilatiefout voor een
return
instructie die wordt weergegeven in een iteratorblok (maaryield return
instructies zijn toegestaan). - Het is een compilatiefout voor een iteratorblok dat een onveilige context bevat (§23.2). Een iteratorblok definieert altijd een veilige context, zelfs wanneer de declaratie is genest in een onveilige context.
13.3.2 Overzichtslijsten
Een instructielijst bestaat uit een of meer instructies die op volgorde zijn geschreven. Overzichtslijsten vinden plaats in blok s (§13.3) en in switch_blocks (§13.8.3).
statement_list
: statement+
;
Er wordt een instructielijst uitgevoerd door het besturingselement over te dragen naar de eerste instructie. Wanneer en als het besturingselement het eindpunt van een instructie bereikt, wordt het besturingselement overgebracht naar de volgende instructie. Wanneer en als het besturingselement het eindpunt van de laatste instructie bereikt, wordt het besturingselement overgebracht naar het eindpunt van de instructielijst.
Een instructie in een instructielijst is bereikbaar als ten minste een van de volgende beweringen waar is:
- De instructie is de eerste instructie en de instructielijst zelf is bereikbaar.
- Het eindpunt van de voorgaande instructie is bereikbaar.
- De instructie is een gelabelde instructie en het label wordt verwezen door een bereikbare
goto
instructie.
Het eindpunt van een instructielijst is bereikbaar als het eindpunt van de laatste instructie in de lijst bereikbaar is.
13.4 De lege instructie
Een empty_statement doet niets.
empty_statement
: ';'
;
Er wordt een lege instructie gebruikt wanneer er geen bewerkingen zijn die moeten worden uitgevoerd in een context waarin een instructie is vereist.
Het uitvoeren van een lege instructie draagt eenvoudig de besturing over naar het eindpunt van de instructie. Het eindpunt van een lege instructie is dus bereikbaar als de lege instructie bereikbaar is.
Voorbeeld: Een lege instructie kan worden gebruikt bij het schrijven van een
while
instructie met een null-hoofdtekst:bool ProcessMessage() {...} void ProcessMessages() { while (ProcessMessage()) ; }
Ook kan een lege instructie worden gebruikt om een label te declareren vlak voor het sluiten van
}
een blok:void F(bool done) { ... if (done) { goto exit; } ... exit: ; }
eindvoorbeeld
13.5 Gelabelde instructies
Met een labeled_statement kan een instructie worden voorafgegaan door een label. Gelabelde instructies zijn toegestaan in blokken, maar zijn niet toegestaan als ingesloten instructies.
labeled_statement
: identifier ':' statement
;
Een gelabelde instructie declareert een label met de naam die is opgegeven door de id. Het bereik van een label is het hele blok waarin het label wordt gedeclareerd, inclusief geneste blokken. Het is een compilatiefout voor twee labels met dezelfde naam om overlappende bereiken te hebben.
Naar een label kan worden verwezen vanuit goto
instructies (§13.10.4) binnen het bereik van het label.
Opmerking: Dit betekent dat
goto
instructies controle binnen blokken en uit blokken kunnen overdragen, maar nooit in blokken. eindnotitie
Labels hebben hun eigen declaratieruimte en verstoren geen andere id's.
Voorbeeld: Het voorbeeld
int F(int x) { if (x >= 0) { goto x; } x = -x; x: return x; }
is geldig en gebruikt de naam x als zowel een parameter als een label.
eindvoorbeeld
De uitvoering van een gelabelde instructie komt exact overeen met de uitvoering van de instructie na het label.
Naast de bereikbaarheid die wordt geboden door een normale controlestroom, is een gelabelde instructie bereikbaar als naar het label wordt verwezen door een bereikbare goto
instructie, tenzij de goto
instructie binnen het try
blok of een catch
blok van een try_statement dat een finally
blok bevat waarvan het eindpunt onbereikbaar is en de gelabelde instructie zich buiten de try_statement bevindt.
13.6 Verklaringsverklaringen
13.6.1 Algemeen
Een declaration_statement declareert een of meer lokale variabelen, een of meer lokale constanten of een lokale functie. Declaratie-instructies zijn toegestaan in blokken en schakelblokken, maar zijn niet toegestaan als ingesloten instructies.
declaration_statement
: local_variable_declaration ';'
| local_constant_declaration ';'
| local_function_declaration
;
Een lokale variabele wordt gedeclareerd met behulp van een local_variable_declaration (§13.6.2). Een lokale constante wordt gedeclareerd met behulp van een local_constant_declaration (§13.6.3). Een lokale functie wordt gedeclareerd met behulp van een local_function_declaration (§13.6.4).
De gedeclareerde namen worden ingevoerd in de dichtstbijzijnde declaratieruimte (§7.3).
13.6.2 Declaraties van lokale variabelen
13.6.2.1 Algemeen
Een local_variable_declaration declareert een of meer lokale variabelen.
local_variable_declaration
: implicitly_typed_local_variable_declaration
| explicitly_typed_local_variable_declaration
| explicitly_typed_ref_local_variable_declaration
;
Impliciet getypte declaraties bevatten het contextuele trefwoord (§6.4.4) var
wat resulteert in een syntactische dubbelzinnigheid tussen de drie categorieën die als volgt worden opgelost:
- Als er geen type met de naam
var
in het bereik is en de invoer overeenkomt met implicitly_typed_local_variable_declaration , wordt het gekozen; - Als een type met de naam
var
binnen het bereik valt, wordt implicitly_typed_local_variable_declaration niet beschouwd als een mogelijke overeenkomst.
Binnen een local_variable_declaration elke variabele wordt geïntroduceerd door een declaratie, een van implicitly_typed_local_variable_declarator, explicitly_typed_local_variable_declarator of ref_local_variable_declarator voor impliciet getypte, expliciet getypte en verw lokale variabelen. De declaratie definieert de naam (id) en de initiële waarde, indien van toepassing, van de geïntroduceerde variabele.
Als er meerdere declaraties in een declaratie staan, worden ze verwerkt, inclusief eventuele initialisatie-expressies, in volgorde van links naar rechts (§9.4.4.5).
Opmerking: Voor een local_variable_declaration die niet voorkomt als een for_initializer (§13.9.4) of resource_acquisition (§13.14) is deze links naar rechtse volgorde gelijk aan elke declaratie die zich in een afzonderlijke local_variable_declaration bevindt. Voorbeeld:
void F() { int x = 1, y, z = x * 2; }
is gelijk aan:
void F() { int x = 1; int y; int z = x * 2; }
eindnotitie
De waarde van een lokale variabele wordt verkregen in een expressie met behulp van een simple_name (§12.8.4). Op elke locatie waar de waarde wordt verkregen, wordt een lokale variabele definitief toegewezen (§9.4). Elke lokale variabele die wordt geïntroduceerd door een local_variable_declaration wordt in eerste instantie niet toegewezen (§9.4.3). Als een declaratie een initialisatie-expressie heeft, wordt de geïntroduceerde lokale variabele geclassificeerd als toegewezen aan het einde van de declaratie (§9.4.4.5).
Het bereik van een lokale variabele die wordt geïntroduceerd door een local_variable_declaration wordt als volgt gedefinieerd (§7.7):
- Als de declaratie plaatsvindt als een for_initializer , is het bereik de for_initializer, for_condition, for_iterator en embedded_statement (§13.9.4);
- Als de verklaring plaatsvindt als een resource_acquisition is het buitenste blok van de semantische equivalente uitbreiding van de using_statement (§13.14);
- Anders is het bereik het blok waarin de declaratie plaatsvindt.
Het is een fout om te verwijzen naar een lokale variabele op naam in een tekstpositie die voorafgaat aan de declaratie of binnen een initialisatie-expressie binnen de declaratie. Binnen het bereik van een lokale variabele is het een compilatietijdfout om een andere lokale variabele, lokale functie of constante met dezelfde naam te declareren.
De ref-safe-context (§9.7.2) van een lokale ref-variabele is de ref-safe-context van het initialiseren van variable_reference. De ref-safe-context van niet-ref lokale variabelen is declaratieblok.
13.6.2.2 Impliciet getypeerde lokale variabeledeclaraties
implicitly_typed_local_variable_declaration
: 'var' implicitly_typed_local_variable_declarator
| ref_kind 'var' ref_local_variable_declarator
;
implicitly_typed_local_variable_declarator
: identifier '=' expression
;
Een implicitly_typed_local_variable_declaration introduceert één lokale variabele, id. De expressie of variable_reference moet een type compilatietijd hebben. T
Het eerste alternatief declareert een variabele met een initiële waarde van de expressie; het type is T?
wanneer T
een niet-nullbaar verwijzingstype is, anders is T
het type. Het tweede alternatief declareert een verw-variabele met een initiële waarde van ref
variable_reference; het type is ref T?
wanneer T
het een niet-null-verwijzingstype is, anders is ref T
het type. (ref_kind wordt beschreven in §15.6.1.)
Voorbeeld:
var i = 5; var s = "Hello"; var d = 1.0; var numbers = new int[] {1, 2, 3}; var orders = new Dictionary<int,Order>(); ref var j = ref i; ref readonly var k = ref i;
De impliciet getypte declaraties van lokale variabelen hierboven zijn precies gelijk aan de volgende expliciet getypte declaraties:
int i = 5; string s = "Hello"; double d = 1.0; int[] numbers = new int[] {1, 2, 3}; Dictionary<int,Order> orders = new Dictionary<int,Order>(); ref int j = ref i; ref readonly int k = ref i;
Hieronder ziet u een onjuiste, impliciet getypte lokale variabeledeclaraties:
var x; // Error, no initializer to infer type from var y = {1, 2, 3}; // Error, array initializer not permitted var z = null; // Error, null does not have a type var u = x => x + 1; // Error, anonymous functions do not have a type var v = v++; // Error, initializer cannot refer to v itself
eindvoorbeeld
13.6.2.3 Expliciet lokale variabeledeclaraties getypt
explicitly_typed_local_variable_declaration
: type explicitly_typed_local_variable_declarators
;
explicitly_typed_local_variable_declarators
: explicitly_typed_local_variable_declarator
(',' explicitly_typed_local_variable_declarator)*
;
explicitly_typed_local_variable_declarator
: identifier ('=' local_variable_initializer)?
;
local_variable_initializer
: expression
| array_initializer
;
Een explicity_typed_local_variable_declaration introduceert een of meer lokale variabelen met het opgegeven type.
Als een local_variable_initializer aanwezig is, is het type passend volgens de regels van eenvoudige toewijzing (§12.21.2) of matrix initialisatie (§17.7) en wordt de waarde toegewezen als de oorspronkelijke waarde van de variabele.
13.6.2.4 Expliciet lokale variabeledeclaraties getypt
explicitly_typed_ref_local_variable_declaration
: ref_kind type ref_local_variable_declarators
;
ref_local_variable_declarators
: ref_local_variable_declarator (',' ref_local_variable_declarator)*
;
ref_local_variable_declarator
: identifier '=' 'ref' variable_reference
;
Het initialiseren van variable_reference moet een type hebben en voldoen aan dezelfde eisen als voor een verw-toewijzing (§12.21.3).
Als ref_kind is ref readonly
, zijn de id(s) die worden gedeclareerd verwijzingen naar variabelen die als alleen-lezen worden behandeld. Anders worden, indien ref_kind is ref
, de id(s) die worden gedeclareerd verwijzingen naar variabelen die beschrijfbaar zijn.
Het is een compilatiefout om een lokale verw-variabele of een variabele van een ref struct
type te declareren, binnen een methode die is gedeclareerd met de method_modifierasync
, of binnen een iterator (§15.14).
13.6.3 Lokale constante declaraties
Een local_constant_declaration declareert een of meer lokale constanten.
local_constant_declaration
: 'const' type constant_declarators
;
constant_declarators
: constant_declarator (',' constant_declarator)*
;
constant_declarator
: identifier '=' constant_expression
;
Het type van een local_constant_declaration geeft het type van de constanten aan die door de declaratie worden ingevoerd. Het type wordt gevolgd door een lijst met constant_declarators, die elk een nieuwe constante introduceert. Een constant_declarator bestaat uit een id die de constante een naam geeft, gevolgd door een '=
'-token, gevolgd door een constant_expression (§12.23) die de waarde van de constante geeft.
Het type en constant_expression van een lokale constanteverklaring moeten dezelfde regels volgen als die van een constante lidverklaring (§15.4).
De waarde van een lokale constante wordt verkregen in een expressie met behulp van een simple_name (§12.8.4).
Het bereik van een lokale constante is het blok waarin de declaratie plaatsvindt. Het is een fout om te verwijzen naar een lokale constante in een tekstpositie die voorafgaat aan het einde van de constant_declarator.
Een lokale constantedeclaratie die meerdere constanten declareert, is gelijk aan meerdere declaraties van enkelvoudige constanten met hetzelfde type.
13.6.4 Declaraties van lokale functies
Een local_function_declaration declareert een lokale functie.
local_function_declaration
: local_function_modifier* return_type local_function_header
local_function_body
| ref_local_function_modifier* ref_kind ref_return_type
local_function_header ref_local_function_body
;
local_function_header
: identifier '(' parameter_list? ')'
| identifier type_parameter_list '(' parameter_list? ')'
type_parameter_constraints_clause*
;
local_function_modifier
: ref_local_function_modifier
| 'async'
;
ref_local_function_modifier
: 'static'
| unsafe_modifier // unsafe code support
;
local_function_body
: block
| '=>' null_conditional_invocation_expression ';'
| '=>' expression ';'
;
ref_local_function_body
: block
| '=>' 'ref' variable_reference ';'
;
Grammaticanotitie: bij het herkennen van een local_function_body indien zowel de alternatieven voor null_conditional_invocation_expressionals expressie van toepassing zijn, wordt de eerste gekozen. (§15.6.1)
Voorbeeld: Er zijn twee veelvoorkomende use cases voor lokale functies: iterator-methoden en asynchrone methoden. In iterator-methoden worden eventuele uitzonderingen alleen waargenomen bij het aanroepen van code waarmee de geretourneerde reeks wordt geïnventareerd. In asynchrone methoden worden eventuele uitzonderingen alleen waargenomen wanneer de geretourneerde taak wordt gewacht. In het volgende voorbeeld ziet u hoe parametervalidatie wordt gescheiden van de iterator-implementatie met behulp van een lokale functie:
public static IEnumerable<char> AlphabetSubset(char start, char end) { if (start < 'a' || start > 'z') { throw new ArgumentOutOfRangeException(paramName: nameof(start), message: "start must be a letter"); } if (end < 'a' || end > 'z') { throw new ArgumentOutOfRangeException(paramName: nameof(end), message: "end must be a letter"); } if (end <= start) { throw new ArgumentException( $"{nameof(end)} must be greater than {nameof(start)}"); } return AlphabetSubsetImplementation(); IEnumerable<char> AlphabetSubsetImplementation() { for (var c = start; c < end; c++) { yield return c; } } }
eindvoorbeeld
Tenzij hieronder anders is opgegeven, is de semantiek van alle grammatica-elementen hetzelfde als voor method_declaration (§15.6.1), gelezen in de context van een lokale functie in plaats van een methode.
De id van een local_function_declaration moet uniek zijn in het opgegeven blokbereik, met inbegrip van eventuele lokale declaratieruimten voor variabelen. Een gevolg hiervan is dat overbelaste local_function_declarations niet zijn toegestaan.
Een local_function_declaration kan één async
modifier (§15.15) en een unsafe
(§23.1) modifier bevatten. Als de aangifte de async
wijzigingsfunctie bevat, moet void
het retourtype of een «TaskType»
type zijn (§15.15.11). Als de declaratie de static
wijzigingsfunctie bevat, is de functie een statische lokale functie; anders is het een niet-statische lokale functie. Het is een compilatiefout voor type_parameter_list of parameter_list om kenmerken te bevatten. Als de lokale functie wordt gedeclareerd in een onveilige context (§23.2), kan de lokale functie onveilige code bevatten, zelfs als de declaratie van de lokale functie niet de unsafe
wijzigingsfunctie bevat.
Een lokale functie wordt gedeclareerd in blokbereik. Een niet-statische lokale functie kan variabelen vastleggen van het bereik insluiten, terwijl een statische lokale functie niet mag worden gebruikt (zodat deze geen toegang heeft tot het insluiten van lokale bevolking, parameters, niet-statische lokale functies of this
). Het is een compilatiefout als een vastgelegde variabele wordt gelezen door de hoofdtekst van een niet-statische lokale functie, maar niet zeker is toegewezen voordat elke aanroep naar de functie. Een compiler bepaalt welke variabelen er zeker worden toegewezen aan de return (§9.4.4.33).
Wanneer het type this
een structtype is, is het een compilatiefout voor de hoofdtekst van een lokale functie om toegang te krijgen this
. Dit is waar of de toegang expliciet (zoals in this.x
) of impliciet is (zoals in x
waar x
zich een exemplaarlid van de struct bevindt). Deze regel verbiedt deze toegang alleen en heeft geen invloed op het feit of het opzoeken van leden resulteert in een lid van de struct.
Het is een compilatiefout voor de hoofdtekst van de lokale functie die een goto
instructie, een break
instructie of een continue
instructie bevat waarvan het doel buiten de hoofdtekst van de lokale functie valt.
Opmerking: de bovenstaande regels voor
this
engoto
spiegelen de regels voor anonieme functies in §12.19.3. eindnotitie
Een lokale functie kan worden aangeroepen vanaf een lexical punt vóór de declaratie. Het is echter een compilatiefout voor de functie die lexisch moet worden gedeclareerd vóór de declaratie van een variabele die in de lokale functie wordt gebruikt (§7.7).
Het is een compilatiefout voor een lokale functie om een parameter te declareren, parameter of lokale variabele te declareren met dezelfde naam als één die is gedeclareerd in een declaratieruimte voor lokale variabelen.
Lokale functieteksten zijn altijd bereikbaar. Het eindpunt van een declaratie van een lokale functie is bereikbaar als het beginpunt van de declaratie van de lokale functie bereikbaar is.
Voorbeeld: In het volgende voorbeeld is de hoofdtekst
L
bereikbaar, ook al is het beginpunt nietL
bereikbaar. Omdat het beginpunt vanL
het niet bereikbaar is, is de instructie na het eindpuntL
niet bereikbaar:class C { int M() { L(); return 1; // Beginning of L is not reachable int L() { // The body of L is reachable return 2; } // Not reachable, because beginning point of L is not reachable return 3; } }
Met andere woorden, de locatie van een declaratie van een lokale functie heeft geen invloed op de bereikbaarheid van instructies in de betreffende functie. eindvoorbeeld
Als het type van het argument voor een lokale functie is dynamic
, wordt de aan te roepen functie opgelost tijdens het compileren, niet bij runtime.
Een lokale functie mag niet worden gebruikt in een expressiestructuur.
Een statische lokale functie
- Kan verwijzen naar statische leden, typeparameters, constante definities en statische lokale functies uit het bereik insluiten.
- Verwijst niet naar leden van
this
een implicietebase
verwijzing, noch naarthis
lokale 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 + y
enx == 1
, die alleen een waarde berekenen (die worden verwijderd), zijn niet toegestaan als instructies. eindnotitie
Uitvoering van een expression_statement evalueert de ingesloten expressie en draagt vervolgens het besturingselement over naar het eindpunt van de expression_statement. Het eindpunt van een expression_statement is bereikbaar als die expression_statement bereikbaar is.
13.8 Selectie-instructies
13.8.1 Algemeen
Selectie-instructies selecteren een van een aantal mogelijke instructies voor uitvoering op basis van de waarde van een expressie.
selection_statement
: if_statement
| switch_statement
;
13.8.2 De if-instructie
De if
instructie selecteert een instructie voor uitvoering op basis van de waarde van een Boole-expressie.
if_statement
: 'if' '(' boolean_expression ')' embedded_statement
| 'if' '(' boolean_expression ')' embedded_statement
'else' embedded_statement
;
Een else
deel is gekoppeld aan het lexisch dichtstbijzijnde voorafgaande dat if
is toegestaan door de syntaxis.
Voorbeeld: Een
if
instructie van het 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.24) wordt geëvalueerd.
- Als de Boole-expressie oplevert
true
, wordt het besturingselement overgebracht naar de eerste ingesloten instructie. Wanneer en als het besturingselement het eindpunt van die instructie bereikt, wordt het besturingselement overgebracht naar het eindpunt van deif
instructie. - Als de Boole-expressie oplevert
false
en eenelse
onderdeel aanwezig is, wordt het besturingselement overgebracht naar de tweede ingesloten instructie. Wanneer en als het besturingselement het eindpunt van die instructie bereikt, wordt het besturingselement overgebracht naar het eindpunt van deif
instructie. - Als de Boole-expressie oplevert
false
en eenelse
onderdeel niet aanwezig is, wordt het besturingselement overgebracht naar het eindpunt van deif
instructie.
De eerste ingesloten instructie van een if
instructie is bereikbaar als de if
instructie bereikbaar is en de Boole-expressie niet de constante waarde false
heeft.
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 true
heeft.
13.8.3 De schakelinstructie
De switch
instructie selecteert voor het uitvoeren van een instructielijst met een gekoppeld switchlabel dat overeenkomt met de waarde van de switchexpressie.
switch_statement
: 'switch' '(' expression ')' switch_block
;
switch_block
: '{' switch_section* '}'
;
switch_section
: switch_label+ statement_list
;
switch_label
: 'case' pattern case_guard? ':'
| 'default' ':'
;
case_guard
: 'when' expression
;
Een switch_statement bestaat uit het trefwoord, gevolgd door een haakje expressie (deswitch
genoemd), gevolgd door een switch_block. De switch_block bestaat uit nul of meer switch_sections, tussen accolades. Elke switch_section bestaat uit een of meer switch_labelgevolgd door een statement_list (§13.3.2). Elke switch_label met case
een bijbehorend patroon (§11) waarmee de waarde van de switchexpressie wordt getest. Als case_guard aanwezig is, moet de expressie impliciet worden omgezet in het type bool
en wordt die expressie geëvalueerd als een aanvullende voorwaarde voor het geval dat moet worden beschouwd als voldaan.
Het switch
ingesteld door de switchexpressie.
- Als het type van de schakelexpressie is
sbyte
, ,byte
,short
ushort
,int
,uint
long
ulong
char
bool
string
of 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 toepassingswitch
is. - Als er anders precies één door de gebruiker gedefinieerde impliciete conversie bestaat van het type switchexpressie naar een van de volgende mogelijke typen van toepassing:
sbyte
,byte
,short
ushort
int
,uint
long
ulong
,char
ofstring
, een type null-waarde dat overeenkomt met een van deze typen, is het geconverteerde type het type van het bestuur van deswitch
instructie. - Anders is het type van de instructie het type
switch
switchexpressie. Het is een fout als er geen dergelijk type bestaat.
Er kan maximaal één default
label in een switch
instructie staan.
Dit is een fout als het patroon van een switchlabel niet van toepassing is (§11.2.1) op het type invoerexpressie.
Het is een fout als het patroon van een switchlabel wordt aangevuld met (§11.3) de set patronen van eerdere switchlabels van de switch-instructie die geen case guard hebben of waarvan case guard een constante expressie is met de waarde true.
Voorbeeld:
switch (shape) { case var x: break; case var _: // error: pattern subsumed, as previous case always matches break; default: break; // warning: unreachable, all possible values already handled. }
eindvoorbeeld
Er wordt als volgt een switch
instructie uitgevoerd:
- De switchexpressie wordt geëvalueerd en geconverteerd naar het type van het bestuur.
- Het besturingselement wordt overgedragen op basis van de waarde van de geconverteerde switchexpressie:
- Het lexically eerste patroon in de set
case
labels in dezelfdeswitch
instructie die overeenkomt met de waarde van de switchexpressie en waarvoor de guard-expressie afwezig is of waar evalueert, zorgt ervoor dat het besturingselement wordt overgebracht naar de lijst met instructies na het overeenkomendecase
label. - Als er een
default
label aanwezig is, wordt het besturingselement overgedragen naar de lijst met instructies na hetdefault
label. - Anders wordt het besturingselement overgedragen naar het eindpunt van de
switch
instructie.
- 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 case
ofgoto default
instructie gebruikt:switch (i) { case 0: CaseZero(); goto case 1; case 1: CaseZeroOrOne(); goto default; default: CaseAny(); break; }
eindvoorbeeld
Meerdere labels zijn toegestaan in een switch_section.
Voorbeeld: Het voorbeeld
switch (i) { case 0: CaseZero(); break; case 1: CaseOne(); break; case 2: default: CaseTwo(); break; }
is geldig. Het voorbeeld schendt de regel 'geen val door' omdat de labels
case 2:
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
break
instructies per ongeluk worden weggelaten. De secties van deswitch
bovenstaande instructie kunnen bijvoorbeeld worden omgekeerd zonder dat dit van invloed is op het gedrag van de instructie:switch (i) { default: CaseAny(); break; case 1: CaseZeroOrOne(); goto default; case 0: CaseZero(); goto case 1; }
eindnotitie
Opmerking: De instructielijst van een switchsectie eindigt meestal op een
break
,goto case
ofgoto default
instructie, maar een constructie die het eindpunt van de instructielijst onbereikbaar weergeeft, is toegestaan. Een instructie die wordt beheerd door de Boole-expressiewhile
, is bijvoorbeeldtrue
bekend dat deze nooit het eindpunt bereikt. Op dezelfde manier draagt eenthrow
ofreturn
instructie altijd de besturing elders over en bereikt deze nooit het eindpunt. Het volgende voorbeeld is dus geldig:switch (i) { case 0: while (true) { F(); } case 1: throw new ArgumentException(); case 2: return; }
eindnotitie
Voorbeeld: Het type van een instructie kan
switch
het typestring
zijn. Voorbeeld:void DoCommand(string command) { switch (command.ToLower()) { case "run": DoRun(); break; case "save": DoSave(); break; case "quit": DoQuit(); break; default: InvalidCommand(command); break; } }
eindvoorbeeld
Opmerking: Net als bij de operatoren voor gelijkheid van tekenreeksen (§12.12.8) is de
switch
instructie hoofdlettergevoelig en wordt een bepaalde schakelsectie alleen uitgevoerd als de tekenreeks van de switchexpressie exact overeenkomt met eencase
labelconstante. eindnotitie Wanneer het type van een instructie van toepassingswitch
isstring
of een type null-waarde, is de waardenull
toegestaan als labelconstantecase
.
De statement_listvan een switch_block kunnen verklaringsverklaringen bevatten (§13.6). Het bereik van een lokale variabele of constante die in een schakelblok is gedeclareerd, is het schakelblok.
Een switchlabel is bereikbaar als ten minste één van de volgende waar is:
- De switchexpressie is een constante waarde en een van beide
- het label is een
case
wiens patroon overeenkomt (§11.2.1) die waarde, en de bewaker van het label is afwezig of niet een constante expressie met de waarde onwaar; of - het is een
default
label en geen schakelsectie bevat een hoofdletterlabel waarvan het patroon overeenkomt met die waarde en waarvan de bewaker afwezig is of een constante expressie met de waarde waar.
- het label is een
- De switchexpressie is geen constante waarde en een van beide
- het label is een
case
zonder bewaker of met een bewaker waarvan de waarde niet de constante onwaar is; of - het is een
default
label en- de reeks patronen die worden weergegeven in de gevallen van de schakelinstructie die geen bewakers hebben of bewakers waarvan de waarde de constante waar is, niet volledig is (§11.4) voor het schakeltype; of
- het schakeloptietype is een null-type en de set patronen die worden weergegeven in de gevallen van de switch-instructie die geen bewakers of bewakers hebben waarvan de waarde de constante waar is, bevat geen patroon dat overeenkomt met de waarde
null
.
- het label is een
- Er wordt naar het switchlabel verwezen door een bereikbaar
goto case
ofgoto default
instructie.
De instructielijst van een bepaalde switchsectie is bereikbaar als de switch
instructie bereikbaar is en de switchsectie een bereikbaar switchlabel bevat.
Het eindpunt van een switch
instructie is bereikbaar als de switch-instructie bereikbaar is en ten minste één van de volgende beweringen waar is:
- De
switch
instructie bevat een bereikbarebreak
instructie waarmee deswitch
instructie wordt afgesloten. - Er is geen
default
label aanwezig en ook- De switchexpressie is een niet-constante waarde en de set patronen die worden weergegeven in de gevallen van de switch-instructie die geen bewakers of bewakers hebben waarvan de waarde de constante waar is, is niet volledig (§11.4) voor het type schakeloptie.
- De switchexpressie is een niet-constante waarde van een null-type en er wordt geen patroon weergegeven in de gevallen van de switchinstructie die geen bewakers hebben of bewakers hebben waarvan de waarde de constante waar is, komt overeen met de waarde
null
. - De switchexpressie is een constante waarde en geen
case
label zonder een bewaker of waarvan de bewaker de constante waar is, komt overeen met die waarde.
Voorbeeld: De volgende code toont een beknopt gebruik van de
when
component:static object CreateShape(string shapeDescription) { switch (shapeDescription) { case "circle": return new Circle(2); … case var o when string.IsNullOrWhiteSpace(o): return null; default: return "invalid shape description"; } }
De var-case komt overeen
null
, de lege tekenreeks of een tekenreeks die alleen witruimte bevat. eindvoorbeeld
13.9 Iteratie-instructies
13.9.1 Algemeen
Herhalingsinstructies voeren herhaaldelijk een ingesloten instructie uit.
iteration_statement
: while_statement
| do_statement
| for_statement
| foreach_statement
;
13.9.2 De while-instructie
Met de while
instructie wordt een ingesloten instructie nul of meer keer uitgevoerd.
while_statement
: 'while' '(' boolean_expression ')' embedded_statement
;
Er wordt als volgt een while
instructie uitgevoerd:
- De boolean_expression (§12.24) wordt geëvalueerd.
- Als de Boole-expressie oplevert
true
, wordt het besturingselement overgebracht naar de ingesloten instructie. Wanneer en als het besturingselement het eindpunt van de ingesloten instructie bereikt (mogelijk vanaf de uitvoering van eencontinue
instructie), wordt het besturingselement overgebracht naar het begin van dewhile
instructie. - Als de Boole-expressie oplevert
false
, wordt het besturingselement overgebracht naar het eindpunt van dewhile
instructie.
In de ingesloten instructie van een while
instructie kan een break
instructie (§13.10.2) worden gebruikt om de besturing over te dragen naar het eindpunt van de while
instructie (waardoor de iteratie van de ingesloten instructie wordt beëindigd) en kan een continue
instructie (§13.10.3) worden gebruikt om het besturingselement over te dragen naar het eindpunt van de ingesloten instructie (waardoor een andere iteratie van de while
instructie wordt uitgevoerd).
De ingesloten instructie van een while
instructie is bereikbaar als de while
instructie bereikbaar is en de Boole-expressie niet de constante waarde false
heeft.
Het eindpunt van een while
instructie is bereikbaar als ten minste een van de volgende beweringen waar is:
- De
while
instructie bevat een bereikbarebreak
instructie waarmee dewhile
instructie wordt afgesloten. - De
while
instructie 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
continue
instructie), wordt de boolean_expression (§12.24) geëvalueerd. Als de Boole-expressie opleverttrue
, wordt het besturingselement overgebracht naar het begin van dedo
instructie. Anders wordt het besturingselement overgedragen naar het eindpunt van dedo
instructie.
In de ingesloten instructie van een do
instructie kan een break
instructie (§13.10.2) worden gebruikt om de besturing over te dragen naar het eindpunt van de do
instructie (waardoor de iteratie van de ingesloten instructie wordt beëindigd) en kan een continue
instructie (§13.10.3) worden gebruikt om het besturingselement over te dragen naar het eindpunt van de ingesloten instructie (waardoor een andere iteratie van de do
instructie wordt uitgevoerd).
De ingesloten instructie van een do
instructie is bereikbaar als de do
instructie bereikbaar is.
Het eindpunt van een do
instructie is bereikbaar als ten minste een van de volgende beweringen waar is:
- De
do
instructie bevat een bereikbarebreak
instructie waarmee dedo
instructie wordt afgesloten. - Het eindpunt van de ingesloten instructie is bereikbaar en de Boole-expressie heeft niet de constante waarde
true
.
13.9.4 De for-instructie
De for
instructie evalueert een reeks initialisatie-expressies en voert vervolgens, terwijl een voorwaarde waar is, herhaaldelijk een ingesloten instructie uit en evalueert een reeks iteratie-expressies.
for_statement
: 'for' '(' for_initializer? ';' for_condition? ';' for_iterator? ')'
embedded_statement
;
for_initializer
: local_variable_declaration
| statement_expression_list
;
for_condition
: boolean_expression
;
for_iterator
: statement_expression_list
;
statement_expression_list
: statement_expression (',' statement_expression)*
;
De for_initializer, indien aanwezig, bestaat uit een local_variable_declaration (§13.6.2) of een lijst met statement_expressions (§13.7) gescheiden door komma's. Het bereik van een lokale variabele die door een for_initializer is gedeclareerd, is de for_initializer, for_condition, for_iterator en embedded_statement.
De for_condition is, indien aanwezig, een boolean_expression (§12.24).
De for_iterator, indien aanwezig, bestaat uit een lijst met statement_expressions (§13.7) gescheiden door komma's.
Er wordt als volgt een for
instructie uitgevoerd:
- Als er een for_initializer aanwezig is, worden de variabelen initialisatie- of instructieexpressies uitgevoerd in de volgorde waarin ze worden geschreven. Deze stap wordt slechts eenmaal uitgevoerd.
- Als er een for_condition aanwezig is, wordt deze geëvalueerd.
- Als de for_condition niet aanwezig is of als de evaluatie oplevert
true
, wordt de controle overgedragen naar de ingesloten instructie. Wanneer en als het besturingselement het eindpunt van de ingesloten instructie bereikt (mogelijk van de uitvoering van eencontinue
instructie), worden de expressies van de for_iterator, indien aanwezig, op volgorde geëvalueerd en wordt vervolgens een andere iteratie uitgevoerd, te beginnen met de evaluatie van de for_condition in de bovenstaande stap. - Als de for_condition aanwezig is en de evaluatie oplevert
false
, wordt de controle overgedragen naar het eindpunt van defor
instructie.
In de ingesloten instructie van een for
instructie kan een break
instructie (§13.10.2) worden gebruikt om de besturing over te dragen naar het eindpunt van de for
instructie (waardoor de iteratie van de ingesloten instructie wordt beëindigd) en continue
een instructie (§13.10.3) kan worden gebruikt om de besturing over te dragen naar het eindpunt van de ingesloten instructie (waardoor de for_iterator wordt uitgevoerd en een andere iteratie van de for
instructie uitvoert, te beginnen met de for_condition).
De ingesloten instructie van een for
instructie is bereikbaar als een van de volgende beweringen waar is:
- De
for
instructie is bereikbaar en er is geen for_condition aanwezig. - De
for
instructie is bereikbaar en een for_condition aanwezig is en heeft niet de constante waardefalse
.
Het eindpunt van een for
instructie is bereikbaar als ten minste een van de volgende beweringen waar is:
- De
for
instructie bevat een bereikbarebreak
instructie waarmee defor
instructie wordt afgesloten. - De
for
instructie is bereikbaar en een for_condition aanwezig is en heeft niet de constante waardetrue
.
13.9.5 De foreach-instructie
De foreach
instructie bevat de elementen van een verzameling en voert een ingesloten instructie uit voor elk element van de verzameling.
foreach_statement
: 'foreach' '(' ref_kind? local_variable_type identifier 'in'
expression ')' embedded_statement
;
De local_variable_type en id van een foreach-instructie declareren de iteratievariabele van de instructie. Als de var
id wordt opgegeven als de local_variable_type en er geen type met de naam var
binnen het bereik valt, wordt de iteratievariabele als een impliciet getypte iteratievariabele beschouwd en wordt het bijbehorende type beschouwd als het elementtype van de foreach
instructie, zoals hieronder is opgegeven.
Als de foreach_statement beide of geen van ref
beide bevat en readonly
, geeft de iteratievariabele een variabele aan die als alleen-lezen wordt behandeld.
Als foreach_statement zonder ref
bevatreadonly
, geeft de iteratievariabele een variabele aan die beschrijfbaar moet zijn.
De iteratievariabele komt overeen met een lokale variabele met een bereik dat zich uitbreidt over de ingesloten instructie. Tijdens het uitvoeren van een foreach
instructie vertegenwoordigt de iteratievariabele het verzamelingselement waarvoor momenteel een iteratie wordt uitgevoerd. Als de iteratievariabele een alleen-lezenvariabele aangeeft, treedt er een compileertijdfout op als de ingesloten instructie deze probeert te wijzigen (via toewijzing of de ++
operatoren --
) of deze als referentie- of uitvoerparameter doorgeeft.
In het volgende, voor beknoptheid, IEnumerable
en IEnumerator
IEnumerable<T>
IEnumerator<T>
verwijzen naar de bijbehorende typen in de naamruimten System.Collections
en .System.Collections.Generic
De compileertijdverwerking van een foreach
instructie bepaalt eerst het verzamelingstype, het enumeratortype en het iteratietype van de expressie. Deze bepaling gaat als volgt:
- Als het type
X
expressie een matrixtype is, is er een impliciete verwijzingsconversie van X naar deIEnumerable
interface (omdatSystem.Array
deze interface wordt geïmplementeerd). Het verzamelingstype is deIEnumerable
interface, het enumeratortype is deIEnumerator
interface en het iteratietype is het elementtype van het matrixtypeX
. - Als het type
X
expressie is, is er een impliciete conversie vandynamic
naar de interface (IEnumerable
). Het verzamelingstype is deIEnumerable
interface en het enumeratortype is deIEnumerator
interface. Als devar
id wordt opgegeven als de local_variable_type , isdynamic
het iteratietype , anders isobject
het . - Als dat niet het geval is, moet u bepalen of het type
X
een geschikteGetEnumerator
methode heeft:- Voer lidzoekactie uit op het type
X
met idGetEnumerator
en geen typeargumenten. Als de opzoekactie van het lid geen overeenkomst produceert of een dubbelzinnigheid produceert of een overeenkomst produceert die geen methodegroep is, controleert u op een opsommingsbare interface zoals hieronder wordt beschreven. Het wordt aanbevolen een waarschuwing te afgegeven als lidzoekactie iets produceert behalve een methodegroep of geen overeenkomst. - Voer overbelastingsresolutie uit met behulp van de resulterende methodegroep en een lege lijst met argumenten. Als overbelastingsresolutie resulteert in geen toepasselijke methoden, resulteert in dubbelzinnigheid of resulteert in één beste methode, maar die methode is statisch of niet openbaar, controleert u op een opsommingsbare interface, zoals hieronder wordt beschreven. Het wordt aanbevolen een waarschuwing te afgegeven als overbelastingsresolutie iets produceert behalve een ondubbelzinnige methode voor openbare instanties of geen toepasselijke methoden.
- Als het retourtype
E
van deGetEnumerator
methode geen klasse, struct of interfacetype is, wordt er een fout gegenereerd en worden er geen verdere stappen ondernomen. - Het opzoeken van leden wordt uitgevoerd
E
met de id en geen typeargumentenCurrent
. Als de opzoekactie van het lid geen overeenkomst oplevert, is het resultaat een fout of is het resultaat iets behalve een eigenschap van een openbare instantie die het lezen toestaat, wordt er een fout gegenereerd en worden er geen verdere stappen ondernomen. - Het opzoeken van leden wordt uitgevoerd
E
met de id en geen typeargumentenMoveNext
. Als de opzoekactie van het lid geen overeenkomst oplevert, is het resultaat een fout of is het resultaat iets behalve een methodegroep, wordt er een fout gegenereerd en worden er geen verdere stappen ondernomen. - Overbelastingsresolutie wordt uitgevoerd op de methodegroep met een lege lijst met argumenten. Als overbelastingsresolutie resulteert in geen toepasselijke methoden, resulteert dit in een dubbelzinnigheid of resulteert in één beste methode, maar die methode is statisch of niet openbaar, of het retourtype is niet
bool
, er wordt een fout gegenereerd en er worden geen verdere stappen ondernomen. - Het verzamelingstype is
X
, het enumeratortype isE
en het iteratietype is het type van deCurrent
eigenschap. DeCurrent
eigenschap kan deref
wijzigingsfunctie bevatten. In dat geval is de geretourneerde expressie een variable_reference (§9.5) die optioneel alleen-lezen is.
- Voer lidzoekactie uit op het type
- Controleer anders of er een enumerable interface is:
- Als er een impliciete conversie is van naar alle typen
Tᵢ
, is er een uniek typeX
datIEnumerable<Tᵢ>
nietT
en voor alle andereT
typen een impliciete conversie vandynamic
naarTᵢ
, dan is het verzamelingstype de interfaceIEnumerable<T>
, het type opsomming is de interfaceIEnumerable<Tᵢ>
en het iteratietype .IEnumerable<T>
IEnumerator<T>
T
- Als er meer dan één dergelijk type
T
is, wordt er een fout gegenereerd en worden er geen verdere stappen ondernomen. - Als er anders een impliciete conversie van
X
naar deSystem.Collections.IEnumerable
interface is, is het verzamelingstype deze interface, is het enumeratortype de interfaceSystem.Collections.IEnumerator
en het iteratietypeobject
. - Anders wordt een fout gegenereerd en worden er geen verdere stappen ondernomen.
- Als er een impliciete conversie is van naar alle typen
De bovenstaande stappen, indien geslaagd, produceren ondubbelzinnig een verzamelingstype C
, enumeratortype E
en iteratietype T
, ref T
of ref readonly T
. Een foreach
instructie van het formulier
foreach (V v in x) «embedded_statement»
is dan gelijk aan:
{
E e = ((C)(x)).GetEnumerator();
try
{
while (e.MoveNext())
{
V v = (V)(T)e.Current;
«embedded_statement»
}
}
finally
{
... // Dispose e
}
}
De variabele e
is niet zichtbaar voor of toegankelijk voor de expressie x
of de ingesloten instructie of een andere broncode van het programma. De variabele v
heeft het kenmerk Alleen-lezen in de ingesloten instructie. Als er geen expliciete conversie is (§10.3) van T
(het iteratietype) naar V
(de local_variable_type in de foreach
instructie), wordt er een fout gegenereerd en worden er geen verdere stappen ondernomen.
Wanneer de iteratievariabele een verwijzingsvariabele is (§9.7), een foreach
instructie van het formulier
foreach (ref V v in x) «embedded_statement»
is dan gelijk aan:
{
E e = ((C)(x)).GetEnumerator();
try
{
while (e.MoveNext())
{
ref V v = ref e.Current;
«embedded_statement»
}
}
finally
{
... // Dispose e
}
}
De variabele e
is niet zichtbaar of toegankelijk voor de expressie x
of de ingesloten instructie of een andere broncode van het programma. De referentievariabele v
is lezen/schrijven in de ingesloten instructie, maar v
wordt niet opnieuw toegewezen (§12.21.3). Als er geen identiteitsconversie is (§10.2.2) van T
(het iteratietype) naar V
(de local_variable_type in de foreach
instructie), wordt er een fout gegenereerd en worden er geen verdere stappen ondernomen.
Een foreach
instructie van het formulier foreach (ref readonly V v in x) «embedded_statement»
heeft een vergelijkbare equivalente vorm, maar de verwijzingsvariabele v
bevindt zich ref readonly
in de ingesloten instructie en kan daarom niet opnieuw worden toegewezen of opnieuw worden toegewezen.
Opmerking: Als
x
de waardenull
is, wordt er eenSystem.NullReferenceException
gegenereerd tijdens runtime. eindnotitie
Een implementatie is toegestaan om een bepaalde foreach_statement anders te implementeren, bijvoorbeeld om prestatieredenen, zolang het gedrag consistent is met de bovenstaande uitbreiding.
De plaatsing van v
binnen de while
lus is belangrijk voor hoe deze wordt vastgelegd (§12.19.6.2) door een anonieme functie die in de embedded_statement plaatsvindt.
Voorbeeld:
int[] values = { 7, 9, 13 }; Action f = null; foreach (var value in values) { if (f == null) { f = () => Console.WriteLine("First value: " + value); } } f();
Als
v
in het uitgevouwen formulier buiten dewhile
lus werd gedeclareerd, zou deze worden gedeeld tussen alle iteraties en de bijbehorende waarde na defor
lus de uiteindelijke waarde zou zijn,13
wat de aanroep zouf
afdrukken. Omdat elke iteratie een eigen variabelev
heeft, blijftf
de 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 lusv
zijn gedeclareerdwhile
.)eindvoorbeeld
De hoofdtekst van het finally
blok wordt samengesteld volgens de volgende stappen:
Als er een impliciete conversie van
E
naar deSystem.IDisposable
interface is, danAls
E
dit een niet-null-waardetype is, wordt definally
component uitgebreid naar het semantische equivalent van:finally { ((System.IDisposable)e).Dispose(); }
Anders wordt de
finally
component uitgebreid naar het semantische equivalent van:finally { System.IDisposable d = e as System.IDisposable; if (d != null) { d.Dispose(); } }
behalve dat als
E
het een waardetype is, of een typeparameter die is geïnstantieerd op een waardetype, de conversie vane
naarSystem.IDisposable
mag geen boksen veroorzaken.
E
Als dit een verzegeld type is, wordt definally
component uitgebreid naar een leeg blok:finally {}
Anders wordt de
finally
component uitgebreid naar:finally { System.IDisposable d = e as System.IDisposable; if (d != null) { d.Dispose(); } }
De lokale variabele d
is niet zichtbaar voor of toegankelijk voor gebruikerscode. In het bijzonder is het niet strijdig met een andere variabele waarvan het bereik het finally
blok omvat.
De volgorde waarin foreach
de elementen van een matrix worden doorkruist, is als volgt: Voor elementen met ééndimensionale matrices worden de indexvolgorde verhoogd, beginnend met index 0 en eindigend met index Length – 1
. Voor multidimensionale matrices worden elementen doorkruist, zodat de indexen van de meest rechtse dimensie eerst worden verhoogd, vervolgens de volgende linkerdimensie, enzovoort aan de linkerkant.
Voorbeeld: In het volgende voorbeeld wordt elke waarde afgedrukt in een tweedimensionale matrix, in de elementvolgorde:
class Test { static void Main() { double[,] values = { {1.2, 2.3, 3.4, 4.5}, {5.6, 6.7, 7.8, 8.9} }; foreach (double elementValue in values) { Console.Write($"{elementValue} "); } Console.WriteLine(); } }
De geproduceerde uitvoer is als volgt:
1.2 2.3 3.4 4.5 5.6 6.7 7.8 8.9
eindvoorbeeld
Voorbeeld: In het volgende voorbeeld
int[] numbers = { 1, 3, 5, 7, 9 }; foreach (var n in numbers) { Console.WriteLine(n); }
het type wordt
n
afgeleid datint
het iteratietype isnumbers
.eindvoorbeeld
13.10 Jump-instructies
13.10.1 Algemeen
Jump-instructies dragen voorwaardelijke controle over.
jump_statement
: break_statement
| continue_statement
| goto_statement
| return_statement
| throw_statement
;
De locatie waarnaar een jump-instructie het besturingselement overdraagt, wordt het doel van de jump-instructie genoemd.
Wanneer een jump-instructie binnen een blok plaatsvindt en het doel van die jump-instructie zich buiten dat blok bevindt, wordt de jump-instructie gezegd om het blok af te sluiten . Hoewel een jump-instructie het besturingselement uit een blok kan overdragen, kan het geen besturingselement overdragen naar een blok.
De uitvoering van jump-instructies wordt gecompliceerd door de aanwezigheid van tussenliggende try
instructies. Bij afwezigheid van dergelijke try
verklaringen draagt een jump-instructie voorwaardelijke controle over van de jump-instructie naar het doel ervan. In aanwezigheid van dergelijke tussenliggende try
instructies is de uitvoering complexer. Als de jump-instructie een of meer try
blokken met bijbehorende finally
blokken afsluit, wordt de besturing in eerste instantie overgebracht naar het finally
blok van de binnenste try
instructie. Wanneer en als het besturingselement het eindpunt van een finally
blok bereikt, wordt het besturingselement overgebracht naar het blok van de finally
volgende insluitingsinstructie try
. Dit proces wordt herhaald totdat de finally
blokken van alle tussenliggende try
instructies zijn uitgevoerd.
Voorbeeld: In de volgende code
class Test { static void Main() { while (true) { try { try { Console.WriteLine("Before break"); break; } finally { Console.WriteLine("Innermost finally block"); } } finally { Console.WriteLine("Outermost finally block"); } } Console.WriteLine("After break"); } }
de
finally
blokken die aan tweetry
instructies zijn gekoppeld, worden uitgevoerd voordat het besturingselement wordt overgebracht naar het doel van de jump-instructie. De geproduceerde uitvoer is als volgt:Before break Innermost finally block Outermost finally block After break
eindvoorbeeld
13.10.2 De instructie break
De break
instructie sluit de dichtstbijzijnde omsluitingswitch
, while
, do
of for
foreach
instructie.
break_statement
: 'break' ';'
;
Het doel van een break
instructie is het eindpunt van de dichtstbijzijnde insluitings switch
-, while
, do
, - for
of foreach
instructie. Als een break
instructie niet is ingesloten door een switch
, while
, do
of for
foreach
instructie, treedt er een compilatietijdfout op.
Wanneer meerdere switch
, while
, do
, of for
foreach
instructies zijn genest binnen elkaar, is een break
instructie alleen van toepassing op de binnenste instructie. Om controle over meerdere nestniveaus over te dragen, wordt een goto
instructie (§13.10.4) gebruikt.
Een break
instructie kan geen finally
blok afsluiten (§13.11). Wanneer een break
instructie binnen een finally
blok plaatsvindt, bevindt het doel van de break
instructie zich binnen hetzelfde finally
blok; anders treedt er een compilatiefout op.
Er wordt als volgt een break
instructie uitgevoerd:
- Als de
break
instructie een of meertry
blokken met gekoppeldefinally
blokken afsluit, wordt het besturingselement in eerste instantie overgebracht naar hetfinally
blok van de binnenstetry
instructie. Wanneer en als het besturingselement het eindpunt van eenfinally
blok bereikt, wordt het besturingselement overgebracht naar het blok van definally
volgende insluitingsinstructietry
. Dit proces wordt herhaald totdat definally
blokken van alle tussenliggendetry
instructies zijn uitgevoerd. - Het besturingselement wordt overgebracht naar het doel van de
break
instructie.
Omdat een break
verklaring voorwaardelijke controle elders overdraagt, is het eindpunt van een break
instructie nooit bereikbaar.
13.10.3 De continue instructie
De continue
instructie start een nieuwe iteratie van de dichtstbijzijnde insluitingwhile
, do
of for
foreach
instructie.
continue_statement
: 'continue' ';'
;
Het doel van een continue
instructie is het eindpunt van de ingesloten instructie van de dichtstbijzijnde omsluitende while
, do
of for
foreach
instructie. Als een continue
instructie niet is ingesloten door een while
, do
of for
foreach
instructie, treedt er een compilatietijdfout op.
Wanneer meerdere while
, do
of for
foreach
instructies binnen elkaar zijn genest, is een continue
instructie alleen van toepassing op de binnenste instructie. Om controle over meerdere nestniveaus over te dragen, wordt een goto
instructie (§13.10.4) gebruikt.
Een continue
instructie kan geen finally
blok afsluiten (§13.11). Wanneer een continue
instructie binnen een finally
blok plaatsvindt, bevindt het doel van de continue
instructie zich binnen hetzelfde finally
blok; anders treedt er een compilatiefout op.
Er wordt als volgt een continue
instructie uitgevoerd:
- Als de
continue
instructie een of meertry
blokken met gekoppeldefinally
blokken afsluit, wordt het besturingselement in eerste instantie overgebracht naar hetfinally
blok van de binnenstetry
instructie. Wanneer en als het besturingselement het eindpunt van eenfinally
blok bereikt, wordt het besturingselement overgebracht naar het blok van definally
volgende insluitingsinstructietry
. Dit proces wordt herhaald totdat definally
blokken van alle tussenliggendetry
instructies zijn uitgevoerd. - Het besturingselement wordt overgebracht naar het doel van de
continue
instructie.
Omdat een continue
verklaring voorwaardelijke controle elders overdraagt, is het eindpunt van een continue
instructie nooit bereikbaar.
13.10.4 De goto-instructie
De goto
instructie draagt het besturingselement over naar een instructie die is gemarkeerd door een label.
goto_statement
: 'goto' identifier ';'
| 'goto' 'case' constant_expression ';'
| 'goto' 'default' ';'
;
Het doel van een goto
id-instructie is de gelabelde instructie met het opgegeven label. Als er geen label met de opgegeven naam bestaat in het huidige functielid of als de goto
instructie zich niet binnen het bereik van het label bevindt, treedt er een compilatietijdfout op.
Opmerking: Met deze regel kan het gebruik van een
goto
instructie het beheer buiten een genest bereik overdragen, maar niet in een genest bereik. In het 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
goto
instructie wordt gebruikt om het beheer buiten een genest bereik over te dragen.eindnotitie
Het doel van een goto case
instructie is de instructielijst in de instructie onmiddellijk tussenvoeging switch
(§13.8.3) die een case
label bevat met een constant patroon van de opgegeven constante waarde en geen bewaker. Als de goto case
instructie niet wordt ingesloten door een switch
instructie, als de dichtstbijzijnde insluitingsinstructie switch
geen dergelijke case
, of als de constant_expression niet impliciet converteerbaar is (§10.2) naar het type van de dichtstbijzijnde omsluitingsinstructie switch
, treedt er een compilatietijdfout op.
Het doel van een goto default
instructie is de lijst met instructies in de instructie die onmiddellijk insluit switch
(§13.8.3), dat een default
label bevat. Als de goto default
instructie niet is ingesloten door een switch
instructie of als de dichtstbijzijnde insluitingsinstructie switch
geen label bevat default
, treedt er een compilatietijdfout op.
Een goto
instructie kan geen finally
blok afsluiten (§13.11). Wanneer een goto
instructie binnen een finally
blok plaatsvindt, moet het doel van de goto
instructie zich binnen hetzelfde finally
blok bevinden of op een andere manier een compilatiefout optreedt.
Er wordt als volgt een goto
instructie uitgevoerd:
- Als de
goto
instructie een of meertry
blokken met gekoppeldefinally
blokken afsluit, wordt het besturingselement in eerste instantie overgebracht naar hetfinally
blok van de binnenstetry
instructie. Wanneer en als het besturingselement het eindpunt van eenfinally
blok bereikt, wordt het besturingselement overgebracht naar het blok van definally
volgende insluitingsinstructietry
. Dit proces wordt herhaald totdat definally
blokken van alle tussenliggendetry
instructies zijn uitgevoerd. - Het besturingselement wordt overgebracht naar het doel van de
goto
instructie.
Omdat een goto
verklaring voorwaardelijke controle elders overdraagt, is het eindpunt van een goto
instructie nooit bereikbaar.
13.10.5 De retourinstructie
De return
instructie retourneert het besturingselement naar de huidige aanroeper van het functielid waarin de retourinstructie wordt weergegeven, optioneel een waarde of een variable_reference (§9,5).
return_statement
: 'return' ';'
| 'return' expression ';'
| 'return' 'ref' variable_reference ';'
;
Een return_statement zonder expressie wordt een return-no-value genoemd; een met expressie wordt een ref
genoemd; en één met alleen een expressie wordt een return-by-value genoemd.
Het is een compilatiefout om een return-no-value te gebruiken van een methode die is gedeclareerd als returns-by-value of returns-by-ref (§15.6.1).
Het is een compilatiefout om een return-by-ref van een methode te gebruiken die is gedeclareerd als returns-no-value of returns-by-value.
Het is een compilatiefout om een return-by-value te gebruiken van een methode die is gedeclareerd als returns-no-value of returns-by-ref.
Het is een compilatiefout om een return-by-ref te gebruiken als expressie geen variable_reference is of een verwijzing is naar een variabele waarvan ref-safe-context geen aanroepercontext is (§9.7.2).
Het is een compilatiefout om een retour-by-ref te gebruiken van een methode die is gedeclareerd met de method_modifierasync
.
Een functielid wordt gezegd om een waarde te berekenen als het een methode is met een methode voor retourneren per waarde (§15.6.11), een retour-by-value-get-accessor van een eigenschap of indexeerfunctie of een door de gebruiker gedefinieerde operator. Functieleden die geen waarde retourneren, berekenen geen waarde en zijn methoden met het effectieve retourtype void
, stellen accessors van eigenschappen en indexeerfuncties in, voeg toegangsrechten van gebeurtenissen, instantieconstructors, statische constructors en finalizers toe en verwijder ze. Functieleden die worden geretourneerd per verw berekenen geen waarde.
Voor een retourwaarde bestaat een impliciete conversie (§10.2) van het type expressie tot het effectieve retourtype (§15.6.11) van het functielid. Voor een retouraanwijzing bestaat een identiteitsconversie (§10.2.2) tussen het type expressie en het effectieve retourtype van het functielid.
return
instructies kunnen ook worden gebruikt in de hoofdtekst van anonieme functie-expressies (§12.19) en deelnemen aan het bepalen welke conversies voor deze functies bestaan (§10.7.1).
Het is een compilatiefout voor een return
instructie die in een finally
blok wordt weergegeven (§13.11).
Er wordt als volgt een return
instructie uitgevoerd:
- Voor een return-by-value wordt de expressie geëvalueerd en wordt de waarde ervan geconverteerd naar het effectieve retourtype van de resulterende functie door een impliciete conversie. Het resultaat van de conversie wordt de resultaatwaarde die door de functie wordt geproduceerd. Voor een return-by-ref wordt de expressie geëvalueerd en wordt het resultaat geclassificeerd als een variabele. Als de retour-by-ref van de methode insluiten,
readonly
is de resulterende variabele alleen-lezen. - Als de
return
instructie wordt ingesloten door een of meer oftry
meercatch
blokken met bijbehorendefinally
blokken, wordt het besturingselement in eerste instantie overgebracht naar hetfinally
blok van de binnenstetry
instructie. Wanneer en als het besturingselement het eindpunt van eenfinally
blok bereikt, wordt het besturingselement overgebracht naar het blok van definally
volgende insluitingsinstructietry
. Dit proces wordt herhaald totdat definally
blokken van alle insluitinstructiestry
zijn uitgevoerd. - Als de bevatde functie geen asynchrone functie is, wordt het besturingselement geretourneerd naar de aanroeper van de functie die deze bevat, samen met de resultaatwaarde, indien van toepassing.
- Als de bevatde functie een asynchrone functie is, wordt het besturingselement geretourneerd naar de huidige aanroeper en wordt de resultaatwaarde, indien aanwezig, opgenomen in de retourtaak zoals beschreven in (§15.15.3).
Omdat een return
verklaring voorwaardelijke controle elders overdraagt, is het eindpunt van een return
instructie nooit bereikbaar.
13.10.6 De werpinstructie
De throw
instructie genereert een uitzondering.
throw_statement
: 'throw' expression? ';'
;
Een throw
instructie met een expressie genereert een uitzondering die wordt geproduceerd door de expressie te evalueren. De expressie moet impliciet worden omgezet in System.Exception
, en het resultaat van het evalueren van de expressie wordt geconverteerd naar System.Exception
voordat deze wordt gegenereerd. Als het resultaat van de conversie is null
, wordt er in plaats daarvan een System.NullReferenceException
gegenereerd.
Een throw
instructie zonder expressie kan alleen worden gebruikt in een catch
blok. In dat geval genereert die instructie de uitzondering die momenteel door dat catch
blok wordt verwerkt, opnieuw.
Omdat een throw
verklaring voorwaardelijke controle elders overdraagt, is het eindpunt van een throw
instructie nooit bereikbaar.
Wanneer er een uitzondering wordt gegenereerd, wordt het besturingselement overgebracht naar de eerste catch
component in een insluitinstructie try
die de uitzondering kan verwerken. Het proces dat plaatsvindt vanaf het punt van de uitzondering dat wordt gegenereerd tot het punt van overdracht van het besturingselement naar een geschikte uitzonderingshandler wordt ook wel uitzonderingsdoorgifte genoemd. Het doorgeven van een uitzondering bestaat uit het herhaaldelijk evalueren van de volgende stappen totdat een catch
component die overeenkomt met de uitzondering wordt gevonden. In deze beschrijving is het beginpunt de locatie waar de uitzondering wordt gegenereerd. Dit gedrag wordt opgegeven in (§21.4).
In het huidige functielid wordt elke
try
instructie die het gooipunt omsluit onderzocht. Voor elke instructieS
, beginnend met de binnenste instructie en eindigend met de buitenstetry
try
instructie, worden de volgende stappen geëvalueerd:- Als het
try
blok vanS
het gooipunt en eenS
of meercatch
componenten bevat, worden decatch
componenten onderzocht om een geschikte handler voor de uitzondering te vinden. De eerstecatch
component die een uitzonderingstypeT
aangeeft (of een typeparameter die tijdens runtime een uitzonderingstypeT
aangeeft) zodat het runtimetype vanE
afgeleidenT
wordt beschouwd als een overeenkomst. Als de component een uitzonderingsfilter bevat, wordt het uitzonderingsobject toegewezen aan de uitzonderingsvariabele en wordt het uitzonderingsfilter geëvalueerd. Wanneer eencatch
component een uitzonderingsfilter bevat, wordt diecatch
component beschouwd als een overeenkomst als het uitzonderingsfilter resulteert intrue
. Een algemenecatch
component (§13.11) wordt beschouwd als een overeenkomst voor elk uitzonderingstype. Als een overeenkomendecatch
component zich bevindt, wordt de doorgifte van uitzonderingen voltooid door het besturingselement over te dragen naar het blok van diecatch
component. - Als het
try
blok of eencatch
blok vanS
het gooipunt insluit en alsS
er eenfinally
blok is, wordt het besturingselement overgezet naar hetfinally
blok. Als hetfinally
blok een andere uitzondering genereert, wordt de verwerking van de huidige uitzondering beëindigd. Anders wordt de verwerking van de huidige uitzondering voortgezet wanneer het besturingselement het eindpunt van hetfinally
blok 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.15.3.
Als de huidige functie asynchroon is en
void
-retourneert, krijgt de synchronisatiecontext van de huidige thread een melding zoals beschreven in §15.15.4.
Als de uitzonderingsverwerking alle aanroepen van functieleden in de huidige thread beëindigt, wat aangeeft dat de thread geen handler heeft voor de uitzondering, wordt de thread zelf beëindigd. De impact van deze beëindiging is gedefinieerd door de implementatie.
13.11 De instructie try
De try
instructie biedt een mechanisme voor het ondervangen van uitzonderingen die optreden tijdens het uitvoeren van een blok. Bovendien biedt de try
instructie de mogelijkheid om een codeblok op te geven dat altijd wordt uitgevoerd wanneer het besturingselement de try
instructie verlaat.
try_statement
: 'try' block catch_clauses
| 'try' block catch_clauses? finally_clause
;
catch_clauses
: specific_catch_clause+
| specific_catch_clause* general_catch_clause
;
specific_catch_clause
: 'catch' exception_specifier exception_filter? block
| 'catch' exception_filter block
;
exception_specifier
: '(' type identifier? ')'
;
exception_filter
: 'when' '(' boolean_expression ')'
;
general_catch_clause
: 'catch' block
;
finally_clause
: 'finally' block
;
Een try_statement bestaat uit het trefwoord try
gevolgd door een blok, vervolgens nul of meer catch_clauses en vervolgens een optionele finally_clause. Er is ten minste één catch_clause of een finally_clause.
In een exception_specifier het type, of de effectieve basisklasse, als het een type_parameter is, moet System.Exception
of een type zijn dat ervan is afgeleid.
Wanneer een catch
component zowel een class_type als een id opgeeft, wordt een uitzonderingsvariabele van de opgegeven naam en het type gedeclareerd. De uitzonderingsvariabele wordt in de declaratieruimte van het specific_catch_clause (§7.3) geïntroduceerd. Tijdens de uitvoering van het exception_filter en catch
blokkeren vertegenwoordigt de uitzonderingsvariabele de uitzondering die momenteel wordt verwerkt. Voor het controleren van definitieve toewijzingen wordt de uitzonderingsvariabele beschouwd als definitief toegewezen in het gehele bereik.
Tenzij een component een naam van een catch
uitzonderingsvariabele bevat, is het onmogelijk om toegang te krijgen tot het uitzonderingsobject in het filter en catch
blok.
Een catch
component die een uitzonderingstype of een naam van een uitzonderingsvariabele aangeeft, wordt een algemene catch
component genoemd. Een try
verklaring mag slechts één algemene catch
component hebben en, indien aanwezig, is dit de laatste catch
component.
Opmerking: Sommige programmeertalen ondersteunen mogelijk uitzonderingen die niet kunnen worden weergegeven als een object dat is afgeleid van
System.Exception
, hoewel dergelijke uitzonderingen nooit kunnen worden gegenereerd door C#-code. Een algemenecatch
component kan worden gebruikt om dergelijke uitzonderingen te ondervangen. Een algemenecatch
component verschilt dus semantisch van het type dat het typeSystem.Exception
aangeeft, omdat de voormalige ook uitzonderingen van andere talen kan ondervangen. eindnotitie
Om een handler voor een uitzondering te vinden, catch
worden componenten in lexicale volgorde onderzocht. Als een catch
component een type opgeeft, maar geen uitzonderingsfilter, is het een compilatiefout voor een latere catch
component van dezelfde try
instructie om een type op te geven dat hetzelfde is als of is afgeleid van dat type.
Opmerking: Zonder deze beperking is het mogelijk om onbereikbare
catch
componenten te schrijven. eindnotitie
Binnen een catch
blok kan een throw
instructie (§13.10.6) zonder expressie worden gebruikt om de uitzondering die door het catch
blok is gevangen, opnieuw te genereren. Toewijzingen aan een uitzonderingsvariabele wijzigen niet de uitzondering die opnieuw wordt gegenereerd.
Voorbeeld: In de volgende code
class Test { static void F() { try { G(); } catch (Exception e) { Console.WriteLine("Exception in F: " + e.Message); e = new Exception("F"); throw; // re-throw } } static void G() => throw new Exception("G"); static void Main() { try { F(); } catch (Exception e) { Console.WriteLine("Exception in Main: " + e.Message); } } }
de methode
F
onderschept een uitzondering, schrijft bepaalde diagnostische gegevens naar de console, wijzigt de uitzonderingsvariabele en genereert de uitzondering opnieuw. De uitzondering die opnieuw wordt gegenereerd, is de oorspronkelijke uitzondering, dus de geproduceerde uitvoer is:Exception in F: G Exception in Main: G
Als het eerste
catch
blok had gegenereerde
in plaats van de huidige uitzondering opnieuw te beperken, zou de geproduceerde uitvoer als volgt zijn:Exception in F: G Exception in Main: F
eindvoorbeeld
Het is een compilatiefout voor een break
, continue
of goto
instructie om het besturingselement uit een finally
blok over te dragen. Wanneer een break
, continue
of goto
instructie optreedt in een finally
blok, bevindt het doel van de instructie zich binnen hetzelfde finally
blok, of op een andere manier treedt er een compilatietijdfout op.
Het is een compilatiefout voor een return
instructie die in een finally
blok plaatsvindt.
Wanneer de uitvoering een try
instructie bereikt, wordt het besturingselement overgebracht naar het try
blok. Als het besturingselement het eindpunt van het try
blok bereikt zonder dat er een uitzondering wordt doorgegeven, wordt het besturingselement overgebracht naar het finally
blok als er een bestaat. Als er geen finally
blok bestaat, wordt het besturingselement overgebracht naar het eindpunt van de try
instructie.
Als er een uitzondering is doorgegeven, worden de catch
clausules, indien aanwezig, onderzocht in lexicale volgorde op zoek naar de eerste overeenkomst voor de uitzondering. Het zoeken naar een overeenkomende catch
component gaat verder met alle insluitblokken, zoals beschreven in §13.10.6. Een catch
component is een overeenkomst als het uitzonderingstype overeenkomt met een exception_specifier en eventuele exception_filter waar is. Een catch
component zonder exception_specifier komt overeen met een uitzonderingstype. Het uitzonderingstype komt overeen met de exception_specifier wanneer het exception_specifier het uitzonderingstype of een basistype van het uitzonderingstype opgeeft. Als de component een uitzonderingsfilter bevat, wordt het uitzonderingsobject toegewezen aan de uitzonderingsvariabele en wordt het uitzonderingsfilter geëvalueerd.
Als er een uitzondering is doorgegeven en er een overeenkomende catch
component wordt gevonden, wordt het besturingselement overgebracht naar het eerste overeenkomende catch
blok. Als het besturingselement het eindpunt van het catch
blok bereikt zonder dat er een uitzondering wordt doorgegeven, wordt het besturingselement overgebracht naar het finally
blok als er een bestaat. Als er geen finally
blok bestaat, wordt het besturingselement overgebracht naar het eindpunt van de try
instructie. Als er een uitzondering van het catch
blok is doorgegeven, worden besturingselementen overgedragen naar het finally
blok als er een bestaat. De uitzondering wordt doorgegeven aan de volgende insluitingsinstructie try
.
Als er een uitzondering is doorgegeven en er geen overeenkomende catch
component wordt gevonden, controleert u overdrachten naar het finally
blok als deze bestaat. De uitzondering wordt doorgegeven aan de volgende insluitingsinstructie try
.
De instructies van een finally
blok worden altijd uitgevoerd wanneer het besturingselement een try
instructie verlaat. Dit is waar of de controleoverdracht plaatsvindt als gevolg van normale uitvoering, als gevolg van het uitvoeren van een break
, continue
, goto
of return
instructie, of als gevolg van het doorgeven van een uitzondering buiten de try
instructie. Als het besturingselement het eindpunt van het finally
blok bereikt zonder dat er een uitzondering wordt doorgegeven, wordt het besturingselement overgebracht naar het eindpunt van de try
instructie.
Als er een uitzondering wordt gegenereerd tijdens het uitvoeren van een finally
blok en niet binnen hetzelfde finally
blok wordt gevangen, wordt de uitzondering doorgegeven aan de volgende insluitinstructie try
. Als er een andere uitzondering werd doorgegeven, gaat deze uitzondering verloren. Het doorgifteproces van een uitzondering wordt verder besproken in de beschrijving van de throw
verklaring (§13.10.6).
Voorbeeld: In de volgende code
public class Test { static void Main() { try { Method(); } catch (Exception ex) when (ExceptionFilter(ex)) { Console.WriteLine("Catch"); } bool ExceptionFilter(Exception ex) { Console.WriteLine("Filter"); return true; } } static void Method() { try { throw new ArgumentException(); } finally { Console.WriteLine("Finally"); } } }
de methode
Method
genereert een uitzondering. De eerste actie is het onderzoeken vancatch
de insluitclausules, waarbij eventuele uitzonderingsfilters worden uitgevoerd. Vervolgens wordt definally
component inMethod
uitvoering uitgevoerd voordat het besturingselement wordt overgedragen naar de insluitingscomponentcatch
. De resulterende uitvoer is:Filter Finally Catch
eindvoorbeeld
Het try
blok van een try
instructie is bereikbaar als de try
instructie bereikbaar is.
Een catch
blok van een try
instructie is bereikbaar als de try
instructie bereikbaar is.
Het finally
blok van een try
instructie is bereikbaar als de try
instructie bereikbaar is.
Het eindpunt van een try
instructie is bereikbaar als beide van de volgende waar zijn:
- Het eindpunt van het
try
blok is bereikbaar of het eindpunt van ten minste ééncatch
blok is bereikbaar. - Als er een
finally
blok aanwezig is, is het eindpunt van hetfinally
blok bereikbaar.
13.12 De ingeschakelde en niet-gecontroleerde instructies
De checked
en unchecked
instructies worden gebruikt om de context voor overloopcontrole te beheren voor integrale rekenkundige bewerkingen en conversies.
checked_statement
: 'checked' block
;
unchecked_statement
: 'unchecked' block
;
De checked
instructie zorgt ervoor dat alle expressies in het blok worden geëvalueerd in een gecontroleerde context en de unchecked
instructie zorgt ervoor dat alle expressies in het blok worden geëvalueerd in een niet-gecontroleerde context.
De checked
en unchecked
instructies zijn precies gelijk aan de checked
en unchecked
operatoren (§12.8.20), behalve dat ze op blokken werken in plaats van expressies.
13.13 De vergrendelingsinstructie
De lock
instructie verkrijgt de wederzijdse uitsluitingsvergrendeling voor een bepaald object, voert een instructie uit en geeft vervolgens de vergrendeling vrij.
lock_statement
: 'lock' '(' expression ')' embedded_statement
;
De lock
geeft een waarde aan van een type dat een verwijzing is. Er wordt nooit impliciete boksconversie (§10.2.9) uitgevoerd voor de expressie van een lock
instructie, en het is dus een compilatiefout voor de expressie om een waarde van een value_type aan te geven.
Een lock
instructie van het formulier
lock (x)
…
waarbij x
een expressie van een reference_type precies gelijk is aan:
bool __lockWasTaken = false;
try
{
System.Threading.Monitor.Enter(x, ref __lockWasTaken);
...
}
finally
{
if (__lockWasTaken)
{
System.Threading.Monitor.Exit(x);
}
}
behalve dat dit x
slechts eenmaal wordt geëvalueerd.
Hoewel een wederzijdse uitsluitingsvergrendeling wordt bewaard, kan code die wordt uitgevoerd in dezelfde uitvoeringsthread ook de vergrendeling verkrijgen en vrijgeven. Code die wordt uitgevoerd in andere threads, wordt echter geblokkeerd voor het verkrijgen van de vergrendeling totdat de vergrendeling wordt vrijgegeven.
13.14 De using-instructie
De using
instructie verkrijgt een of meer resources, voert een instructie uit en verwijdert vervolgens de resource.
using_statement
: 'using' '(' resource_acquisition ')' embedded_statement
;
resource_acquisition
: local_variable_declaration
| expression
;
Een resource is een klasse of struct waarmee de interface wordt geïmplementeerd, die één parameterloze methode bevat met de System.IDisposable
naam Dispose
. Code die een resource gebruikt, kan worden aangeroepen Dispose
om aan te geven dat de resource niet meer nodig is.
Als de vorm van resource_acquisition wordt local_variable_declaration, moet het type van de local_variable_declaration een of een type zijn dat impliciet kan worden geconverteerd naar .dynamic
System.IDisposable
Als de vorm van resource_acquisition een expressie is, wordt deze expressie impliciet omgezet in System.IDisposable
.
Lokale variabelen die zijn gedeclareerd in een resource_acquisition zijn alleen-lezen en bevatten een initialisatiefunctie. Er treedt een compilatiefout op als de ingesloten instructie probeert deze lokale variabelen te wijzigen (via toewijzing of de ++
operatoren --
), het adres ervan op te nemen of door te geven als referentie- of uitvoerparameters.
Een using
instructie wordt vertaald in drie delen: verwerving, gebruik en verwijdering. Het gebruik van de resource wordt impliciet ingesloten in een try
instructie die een finally
component bevat. Met deze finally
component wordt de resource verwijderd. Als een null
resource wordt verkregen, wordt er geen aanroep gedaan Dispose
en wordt er geen uitzondering gegenereerd. Als de resource van het type dynamic
is, wordt deze dynamisch geconverteerd via een impliciete dynamische conversie (§10.2.10) naar IDisposable
tijdens het verkrijgen om ervoor te zorgen dat de conversie vóór het gebruik en de verwijdering is geslaagd.
Een using
instructie van het formulier
using (ResourceType resource = «expression» ) «statement»
komt overeen met een van de drie mogelijke uitbreidingen. Wanneer ResourceType
is een niet-null-waardetype of een typeparameter met de waardetypebeperking (§15.2.5), is de uitbreiding semantisch gelijk aan
{
ResourceType resource = «expression»;
try
{
«statement»;
}
finally
{
((IDisposable)resource).Dispose();
}
}
behalve dat de cast van resource
ton System.IDisposable
geen boksen veroorzaakt.
Anders, wanneer ResourceType
is dynamic
, de uitbreiding is
{
ResourceType resource = «expression»;
IDisposable d = resource;
try
{
«statement»;
}
finally
{
if (d != null)
{
d.Dispose();
}
}
}
Anders is de uitbreiding
{
ResourceType resource = «expression»;
try
{
«statement»;
}
finally
{
IDisposable d = (IDisposable)resource;
if (d != null)
{
d.Dispose();
}
}
}
In elke uitbreiding is de resource
variabele alleen-lezen in de ingesloten instructie en is de d
variabele niet toegankelijk en onzichtbaar voor de ingesloten instructie.
Een implementatie is toegestaan om een bepaalde using_statement anders te implementeren, bijvoorbeeld om prestatieredenen, zolang het gedrag consistent is met de bovenstaande uitbreiding.
Een using
verklaring van het formulier:
using («expression») «statement»
heeft dezelfde drie mogelijke uitbreidingen. In dit geval ResourceType
is impliciet het type compileertijd van de expressie, als deze er een heeft. Anders wordt de interface IDisposable
zelf gebruikt als de ResourceType
. De resource
variabele is niet toegankelijk en onzichtbaar voor de ingesloten instructie.
Wanneer een resource_acquisition de vorm van een local_variable_declaration heeft, is het mogelijk om meerdere resources van een bepaald type te verkrijgen. Een using
instructie van het formulier
using (ResourceType r1 = e1, r2 = e2, ..., rN = eN) «statement»
is precies gelijk aan een reeks geneste using
instructies:
using (ResourceType r1 = e1)
using (ResourceType r2 = e2)
...
using (ResourceType rN = eN)
«statement»
Voorbeeld: In het onderstaande voorbeeld wordt een bestand met de naam log.txt gemaakt en worden twee regels tekst naar het bestand geschreven. In het voorbeeld wordt vervolgens hetzelfde bestand geopend voor het lezen en kopiëren van de ingesloten tekstregels naar de console.
class Test { static void Main() { using (TextWriter w = File.CreateText("log.txt")) { w.WriteLine("This is line one"); w.WriteLine("This is line two"); } using (TextReader r = File.OpenText("log.txt")) { string s; while ((s = r.ReadLine()) != null) { Console.WriteLine(s); } } } }
Omdat de
TextWriter
enTextReader
klassen deIDisposable
interface implementeren, kan het voorbeeld instructies gebruikenusing
om ervoor te zorgen dat het onderliggende bestand correct wordt gesloten na de schrijf- of leesbewerkingen.eindvoorbeeld
13.15 De rendementsrekening
De yield
instructie wordt gebruikt in een iteratorblok (§13.3) om een waarde op te geven aan het enumeratorobject (§15.14.5) of een opsommingsobject (§15.14.6) van een iterator of om het einde van de iteratie aan te geven.
yield_statement
: 'yield' 'return' expression ';'
| 'yield' 'break' ';'
;
yield
is een contextueel trefwoord (§6.4.4) en heeft alleen speciale betekenis wanneer deze direct voor een return
of break
trefwoord wordt gebruikt.
Er zijn verschillende beperkingen voor waar een yield
instructie kan worden weergegeven, zoals beschreven in het volgende.
- Het is een compilatiefout voor een
yield
instructie (van beide vormen) die buiten een method_body, operator_body of accessor_body wordt weergegeven. - Het is een compilatiefout voor een
yield
instructie (van beide vormen) die binnen een anonieme functie wordt weergegeven. - Het is een compilatiefout voor een
yield
instructie (van beide vormen) die wordt weergegeven in definally
component van eentry
instructie. - Het is een compilatiefout voor een
yield return
instructie die overal in eentry
instructie wordt weergegeven die catch_clauses bevat.
Voorbeeld: In het volgende voorbeeld ziet u een aantal geldige en ongeldige toepassingen van
yield
instructies.delegate IEnumerable<int> D(); IEnumerator<int> GetEnumerator() { try { yield return 1; // Ok yield break; // Ok } finally { yield return 2; // Error, yield in finally yield break; // Error, yield in finally } try { yield return 3; // Error, yield return in try/catch yield break; // Ok } catch { yield return 4; // Error, yield return in try/catch yield break; // Ok } D d = delegate { yield return 5; // Error, yield in an anonymous function }; } int MyMethod() { yield return 1; // Error, wrong return type for an iterator block }
eindvoorbeeld
Er bestaat een impliciete conversie (§10.2) van het type expressie in de yield return
verklaring tot het rendementstype (§15.14.4) van de iterator.
Er wordt als volgt een yield return
instructie uitgevoerd:
- De expressie die in de instructie wordt gegeven, wordt impliciet geconverteerd naar het rendementstype en toegewezen aan de
Current
eigenschap van het enumerator-object. - Uitvoering van het iteratorblok wordt onderbroken. Als de
yield return
instructie zich binnen een of meertry
blokken bevindt, worden de bijbehorendefinally
blokken op dit moment niet uitgevoerd. - De
MoveNext
methode van het enumerator-object keerttrue
terug naar de aanroeper, waarmee wordt aangegeven dat het enumerator-object naar het volgende item is gevorderd.
De volgende aanroep van de methode van het enumerator-object MoveNext
hervat de uitvoering van het iteratorblok van waaruit het voor het laatst is onderbroken.
Er wordt als volgt een yield break
instructie uitgevoerd:
- Als de
yield break
instructie wordt ingesloten door een of meertry
blokken met bijbehorendefinally
blokken, wordt het besturingselement in eerste instantie overgebracht naar hetfinally
blok van de binnenstetry
instructie. Wanneer en als het besturingselement het eindpunt van eenfinally
blok bereikt, wordt het besturingselement overgebracht naar het blok van definally
volgende insluitingsinstructietry
. Dit proces wordt herhaald totdat definally
blokken van alle insluitinstructiestry
zijn uitgevoerd. - Het besturingselement wordt teruggezet naar de aanroeper van het iteratorblok. Dit is de
MoveNext
methode ofDispose
methode 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