9 variabelen
9.1 Algemeen
Variabelen vertegenwoordigen opslaglocaties. Elke variabele heeft een type dat bepaalt welke waarden in de variabele kunnen worden opgeslagen. C# is een typeveilige taal en de C#-compiler garandeert dat waarden die zijn opgeslagen in variabelen altijd van het juiste type zijn. De waarde van een variabele kan worden gewijzigd via toewijzing of via het gebruik van de ++
en --
operators.
Een variabele moet definitief worden toegewezen (§9.4) voordat de waarde ervan kan worden verkregen.
Zoals beschreven in de volgende subclauses, worden variabelen in eerste instantie toegewezen of in eerste instantie niet toegewezen. Een aanvankelijk toegewezen variabele heeft een goed gedefinieerde initiële waarde en wordt altijd beschouwd als definitief toegewezen. Een in eerste instantie niet-toegewezen variabele heeft geen initiële waarde. Voor een in eerste instantie niet-toegewezen variabele die definitief op een bepaalde locatie moet worden toegewezen, vindt een toewijzing aan de variabele plaats in elk mogelijk uitvoeringspad dat naar die locatie leidt.
9.2 Variabele categorieën
9.2.1 Algemeen
C# definieert acht categorieën variabelen: statische variabelen, instantievariabelen, matrixelementen, waardeparameters, invoerparameters, referentieparameters, uitvoerparameters en lokale variabelen. De volgende subclauses beschrijven elk van deze categorieën.
Voorbeeld: In de volgende code
class A { public static int x; int y; void F(int[] v, int a, ref int b, out int c, in int d) { int i = 1; c = a + b++ + d; } }
x
is een statische variabele,y
een exemplaarvariabele,v[0]
een matrixelement,a
een waardeparameter,b
een referentieparameter,c
een uitvoerparameter,d
een invoerparameter eni
een lokale variabele. eindvoorbeeld
9.2.2 Statische variabelen
Een veld dat met de static
wijzigingsfunctie is gedeclareerd, is een statische variabele. Er bestaat een statische variabele vóór de uitvoering van de static
constructor (§15.12) voor het bijbehorende type en blijft bestaan wanneer het bijbehorende toepassingsdomein niet meer bestaat.
De initiële waarde van een statische variabele is de standaardwaarde (§9.3) van het type variabele.
Voor het controleren van definitieve toewijzingen wordt een statische variabele beschouwd als in eerste instantie toegewezen.
9.2.3 Instantievariabelen
9.2.3.1 Algemeen
Een veld dat zonder de static
wijzigingsfunctie is gedeclareerd, is een exemplaarvariabele.
9.2.3.2 Instantievariabelen in klassen
Er ontstaat een exemplaarvariabele van een klasse wanneer er een nieuw exemplaar van die klasse wordt gemaakt en niet meer bestaat wanneer er geen verwijzingen naar dat exemplaar zijn en de finalizer van het exemplaar (indien van toepassing) is uitgevoerd.
De oorspronkelijke waarde van een exemplaarvariabele van een klasse is de standaardwaarde (§9.3) van het type van de variabele.
Voor het controleren van definitieve toewijzingen wordt een exemplaarvariabele van een klasse beschouwd als in eerste instantie toegewezen.
9.2.3.3 Instantievariabelen in structs
Een exemplaarvariabele van een struct heeft precies dezelfde levensduur als de structvariabele waartoe deze behoort. Met andere woorden, wanneer een variabele van een structtype tot stand komt of niet meer bestaat, dus ook de instantievariabelen van de struct.
De initiële toewijzingsstatus van een instantievariabele van een struct is hetzelfde als die van de bijbehorende struct
variabele. Met andere woorden, wanneer een structvariabele in eerste instantie wordt beschouwd als toegewezen, dus ook de instantievariabelen en wanneer een structvariabele in eerste instantie wordt beschouwd als niet-toegewezen, worden de instantievariabelen eveneens niet toegewezen.
9.2.4 Matrixelementen
De elementen van een matrix ontstaan wanneer een matrixexemplaren worden gemaakt en blijven bestaan wanneer er geen verwijzingen naar dat matrixexemplaren zijn.
De initiële waarde van elk van de elementen van een matrix is de standaardwaarde (§9.3) van het type matrixelementen.
Voor het controleren van bepaalde toewijzingen wordt een matrixelement in eerste instantie beschouwd als toegewezen.
9.2.5 Waardeparameters
Er bestaat een waardeparameter bij het aanroepen van het functielid (methode, instantieconstructor, accessor of operator) of anonieme functie waartoe de parameter behoort, en wordt geïnitialiseerd met de waarde van het argument dat is opgegeven in de aanroep. Een waardeparameter blijft normaal gesproken bestaan wanneer de uitvoering van de hoofdtekst van de functie is voltooid. Als de waardeparameter echter wordt vastgelegd door een anonieme functie (§12.19.6.2), is de levensduur ten minste verlengd totdat de gemachtigde of expressiestructuur die is gemaakt op basis van die anonieme functie in aanmerking komt voor garbagecollection.
Voor het controleren van definitieve toewijzingen wordt een waardeparameter beschouwd als in eerste instantie toegewezen.
Waardeparameters worden verder besproken in §15.6.2.2.
9.2.6 Referentieparameters
Een referentieparameter is een referentievariabele (§9.7) die voorkomt bij het aanroepen van het functielid, delegeren, anonieme functie of de lokale functie en de bijbehorende referent, wordt geïnitialiseerd naar de variabele die is opgegeven als het argument in die aanroep. Er bestaat geen verwijzingsparameter meer wanneer de uitvoering van de hoofdtekst van de functie is voltooid. In tegenstelling tot waardeparameters wordt een referentieparameter niet vastgelegd (§9.7.2.9).
De volgende regels voor definitieve toewijzing zijn van toepassing op referentieparameters.
Opmerking: de regels voor uitvoerparameters zijn verschillend en worden beschreven in (§9.2.7). eindnotitie
- Een variabele moet zeker worden toegewezen (§9.4) voordat deze kan worden doorgegeven als referentieparameter in een functielid of gemachtigde aanroep.
- Binnen een functielid of anonieme functie wordt een verwijzingsparameter beschouwd als in eerste instantie toegewezen.
Referentieparameters worden verder besproken in §15.6.2.3.3.
9.2.7 Uitvoerparameters
Een uitvoerparameter is een referentievariabele (§9.7) die voorkomt bij het aanroepen van het functielid, delegeren, anonieme functie of de lokale functie en de bijbehorende referent, wordt geïnitialiseerd naar de variabele die is opgegeven als het argument in die aanroep. Er bestaat geen uitvoerparameter meer wanneer de uitvoering van de hoofdtekst van de functie is voltooid. In tegenstelling tot waardeparameters wordt een uitvoerparameter niet vastgelegd (§9.7.2.9).
De volgende regels voor definitieve toewijzing zijn van toepassing op uitvoerparameters.
Opmerking: de regels voor referentieparameters verschillen en worden beschreven in (§9.2.6). eindnotitie
- Een variabele hoeft niet zeker toegewezen te worden voordat deze kan worden doorgegeven als een uitvoerparameter in een functielid of gemachtigde aanroep.
- Na de normale voltooiing van een functielid of gemachtigde aanroep, wordt elke variabele die is doorgegeven als een uitvoerparameter beschouwd als toegewezen in dat uitvoeringspad.
- Binnen een functielid of anonieme functie wordt een uitvoerparameter beschouwd als in eerste instantie niet-toegewezen.
- Elke uitvoerparameter van een functielid, anonieme functie of lokale functie wordt zeker toegewezen (§9.4) voordat het functielid, anonieme functie of lokale functie normaal wordt geretourneerd.
Uitvoerparameters worden verder besproken in §15.6.2.3.4.
9.2.8 Invoerparameters
Een invoerparameter is een referentievariabele (§9.7) die voorkomt bij het aanroepen van het functielid, delegeren, anonieme functie of de lokale functie en de bijbehorende referent, wordt geïnitialiseerd naar het variable_reference gegeven als het argument in die aanroep. Er bestaat geen invoerparameter meer wanneer de uitvoering van de hoofdtekst van de functie is voltooid. In tegenstelling tot waardeparameters wordt een invoerparameter niet vastgelegd (§9.7.2.9).
De volgende definitieve toewijzingsregels zijn van toepassing op invoerparameters.
- Een variabele moet zeker worden toegewezen (§9.4) voordat deze kan worden doorgegeven als invoerparameter in een functielid of gedelegeerde aanroep.
- Binnen een functielid, anonieme functie of lokale functie wordt een invoerparameter beschouwd als in eerste instantie toegewezen.
Invoerparameters worden verder besproken in §15.6.2.3.2.
9.2.9 Lokale variabelen
Een lokale variabele wordt gedeclareerd door een local_variable_declaration, declaration_expression, foreach_statement of specific_catch_clause van een try_statement. Een lokale variabele kan ook worden gedeclareerd door bepaalde soorten patroons(§11). Voor een foreach_statement is de lokale variabele een iteratievariabele (§13.9.5). Voor een specific_catch_clause is de lokale variabele een uitzonderingsvariabele (§13.11). Een lokale variabele die door een foreach_statement of specific_catch_clause is gedeclareerd, wordt in eerste instantie beschouwd als toegewezen.
Een local_variable_declaration kan optreden in een blok, een for_statement, een switch_block of een using_statement. Een declaration_expression kan optreden als een out
argument_value, en als een tuple_element die het doel is van een deconstructerende toewijzing (§12.21.2).
De levensduur van een lokale variabele is het deel van de uitvoering van programma's waarin gegarandeerd opslag voor deze variabele wordt gereserveerd. Deze levensduur loopt uit van de vermelding in het bereik waaraan het is gekoppeld, ten minste totdat de uitvoering van dat bereik op een of andere manier eindigt. (Het invoeren van een ingesloten blok, het aanroepen van een methode of het opleveren van een waarde uit een iteratorblok onderbreekt, maar eindigt niet, uitvoering van het huidige bereik.) Als de lokale variabele wordt vastgelegd met een anonieme functie (§12.19.6.2), is de levensduur ten minste verlengd totdat de gedelegeerde of expressiestructuur die is gemaakt op basis van de anonieme functie, samen met andere objecten die naar de vastgelegde variabele verwijzen, in aanmerking komen voor garbagecollection. Als het bovenliggende bereik recursief of iteratief wordt ingevoerd, wordt elke keer een nieuw exemplaar van de lokale variabele gemaakt en wordt elke keer de initialisatiefunctie geëvalueerd.
Opmerking: elke keer dat het bereik wordt ingevoerd, wordt er een lokale variabele geïnstantieerd. Dit gedrag is zichtbaar voor gebruikerscode die anonieme methoden bevat. eindnotitie
Opmerking: De levensduur van een iteratievariabele (§13.9.5) die door een foreach_statement is gedeclareerd, is één iteratie van die instructie. Elke iteratie maakt een nieuwe variabele. eindnotitie
Opmerking: de werkelijke levensduur van een lokale variabele is afhankelijk van de implementatie. Een compiler kan bijvoorbeeld statisch bepalen dat een lokale variabele in een blok alleen wordt gebruikt voor een klein deel van dat blok. Met behulp van deze analyse kan de compiler code genereren die resulteert in de opslag van de variabele met een kortere levensduur dan het bijbehorende blok.
De opslag waarnaar wordt verwezen door een lokale referentievariabele wordt onafhankelijk van de levensduur van die lokale referentievariabele vrijgemaakt (§7.9).
eindnotitie
Een lokale variabele die wordt geïntroduceerd door een local_variable_declaration of declaration_expression wordt niet automatisch geïnitialiseerd en heeft dus geen standaardwaarde. Een dergelijke lokale variabele wordt in eerste instantie niet toegewezen beschouwd.
Opmerking: een local_variable_declaration die een initialisatiefunctie bevat, wordt in eerste instantie nog steeds niet toegewezen. Uitvoering van de declaratie gedraagt zich precies als een toewijzing aan de variabele (§9.4.4.5). Een variabele gebruiken voordat de initialisatiefunctie is uitgevoerd; Bijvoorbeeld, binnen de initialisatie-expressie zelf of met behulp van een goto_statement die de initialisatiefunctie omzeilt; is een compilatietijdfout:
goto L; int x = 1; // never executed L: x += 1; // error: x not definitely assigned
Binnen het bereik van een lokale variabele is het een compilatiefout die verwijst naar die lokale variabele in een tekstuele positie die voorafgaat aan de declaratie.
eindnotitie
9.2.9.1 Verwijderingen
Een verwijdering is een lokale variabele die geen naam heeft. Een verwijdering wordt geïntroduceerd door een declaratie-expressie (§12.17) met de id _
; en wordt impliciet getypt (_
of var _
) of expliciet getypt (T _
).
Opmerking:
_
is een geldige id in veel vormen van declaraties. eindnotitie
Omdat een verwijdering geen naam heeft, is de enige verwijzing naar de variabele die deze vertegenwoordigt de expressie waarmee deze wordt geïntroduceerd.
Opmerking: een verwijdering kan echter worden doorgegeven als een uitvoerargument, zodat de bijbehorende uitvoerparameter de bijbehorende opslaglocatie kan aanduien. eindnotitie
Een verwijdering wordt niet in eerste instantie toegewezen, dus het is altijd een fout om toegang te krijgen tot de waarde ervan.
Voorbeeld:
_ = "Hello".Length; (int, int, int) M(out int i1, out int i2, out int i3) { ... } (int _, var _, _) = M(out int _, out var _, out _);
In het voorbeeld wordt ervan uitgegaan dat er geen declaratie van de naam
_
in het bereik is.De toewijzing om een eenvoudig patroon weer te
_
geven voor het negeren van het resultaat van een expressie. De aanroep toont de verschillende vormen vanM
verwijderingen die beschikbaar zijn in tuples en als uitvoerparameters.eindvoorbeeld
9.3 Standaardwaarden
De volgende categorieën variabelen worden automatisch geïnitialiseerd naar de standaardwaarden:
- Statische variabelen.
- Exemplaarvariabelen van klasse-exemplaren.
- Matrixelementen.
De standaardwaarde van een variabele is afhankelijk van het type variabele en wordt als volgt bepaald:
- Voor een variabele van een value_type is de standaardwaarde gelijk aan de waarde die wordt berekend door de standaardconstructor van de value_type (§8.3.3).
- Voor een variabele van een reference_type is
null
de standaardwaarde .
Opmerking: initialisatie van standaardwaarden wordt meestal uitgevoerd door het geheugenbeheer of de garbagecollector geheugen te initialiseren naar all-bits-nul voordat deze wordt toegewezen voor gebruik. Daarom is het handig om alle bits-nul te gebruiken om de null-verwijzing weer te geven. eindnotitie
9.4 Definitieve opdracht
9.4.1 Algemeen
Op een bepaalde locatie in de uitvoerbare code van een functielid of een anonieme functie wordt een variabele zeker toegewezen als de compiler kan bewijzen, door een bepaalde statische stroomanalyse (§9.4.4), dat de variabele automatisch is geïnitialiseerd of het doel van ten minste één toewijzing is geweest.
Opmerking: Informeel vermeld, de regels van definitieve toewijzing zijn:
- Een aanvankelijk toegewezen variabele (§9.4.2) wordt altijd beschouwd als definitief toegewezen.
- Een in eerste instantie niet-toegewezen variabele (§9.4.3) wordt beschouwd als definitief toegewezen op een bepaalde locatie, indien alle mogelijke uitvoeringspaden die naar die locatie leiden, ten minste een van de volgende bevatten:
- Een eenvoudige toewijzing (§12.21.2) waarin de variabele de linkeroperand is.
- Een aanroepexpressie (§12.8.9) of expressie voor het maken van objecten (§12.8.16.2) die de variabele doorgeeft als uitvoerparameter.
- Voor een lokale variabele, een lokale variabeledeclaratie voor de variabele (§13.6.2) die een initialisatiefunctie voor variabelen bevat.
De formele specificatie onder de bovenstaande informele regels wordt beschreven in §9.4.2, §9.4.3 en §9.4.4.
eindnotitie
De definitieve toewijzingsstatussen van instantievariabelen van een struct_type variabele worden afzonderlijk en gezamenlijk bijgehouden. Naast de regels die in §9.4.2, §9.4.3 en §9.4.4 worden beschreven, gelden de volgende regels voor struct_type variabelen en hun instantievariabelen:
- Een exemplaarvariabele wordt als definitief toegewezen beschouwd als de variabele die struct_type bevat, wordt beschouwd als definitief toegewezen.
- Een struct_type variabele wordt beschouwd als definitief toegewezen als elk van de instantievariabelen wordt beschouwd als definitief toegewezen.
Definitieve toewijzing is een vereiste in de volgende contexten:
Een variabele wordt zeker toegewezen op elke locatie waar de waarde wordt verkregen.
Opmerking: Dit zorgt ervoor dat niet-gedefinieerde waarden nooit voorkomen. eindnotitie
Het optreden van een variabele in een expressie wordt beschouwd als het verkrijgen van de waarde van de variabele, behalve wanneer
- de variabele de linkeroperand van een eenvoudige toewijzing is,
- de variabele wordt doorgegeven als uitvoerparameter of
- de variabele is een struct_type variabele en treedt op als de linkeroperand van een lidtoegang.
Een variabele wordt zeker toegewezen op elke locatie waar deze wordt doorgegeven als referentieparameter.
Opmerking: Dit zorgt ervoor dat het functielid dat wordt aangeroepen, de verwijzingsparameter kan overwegen die in eerste instantie is toegewezen. eindnotitie
Een variabele wordt zeker toegewezen op elke locatie waar deze wordt doorgegeven als invoerparameter.
Opmerking: Dit zorgt ervoor dat het functielid dat wordt aangeroepen, de invoerparameter kan overwegen die in eerste instantie is toegewezen. eindnotitie
Alle uitvoerparameters van een functielid worden zeker toegewezen op elke locatie waar het functielid retourneert (via een retourinstructie of door uitvoering die het einde van de hoofdtekst van het functielid bereikt).
Opmerking: Dit zorgt ervoor dat functieleden geen niet-gedefinieerde waarden retourneren in uitvoerparameters, waardoor de compiler een functielidaanroep kan overwegen die een variabele als uitvoerparameter gebruikt die gelijk is aan een toewijzing aan de variabele. eindnotitie
De
this
variabele van een struct_type instantieconstructor wordt zeker toegewezen op elke locatie waar die instantieconstructor retourneert.
9.4.2 Aanvankelijk toegewezen variabelen
De volgende categorieën variabelen worden geclassificeerd als in eerste instantie toegewezen:
- Statische variabelen.
- Exemplaarvariabelen van klasse-exemplaren.
- Instantievariabelen van aanvankelijk toegewezen structvariabelen.
- Matrixelementen.
- Waardeparameters.
- Referentieparameters.
- Invoerparameters.
- Variabelen die zijn gedeclareerd in een
catch
component of eenforeach
instructie.
9.4.3 In eerste instantie niet-toegewezen variabelen
De volgende categorieën variabelen worden geclassificeerd als in eerste instantie niet toegewezen:
- Instantievariabelen van in eerste instantie niet-toegewezen structvariabelen.
- Uitvoerparameters, waaronder de
this
variabele van constructors van struct-exemplaren zonder een constructor-initialisatiefunctie. - Lokale variabelen, met uitzondering van variabelen die zijn gedeclareerd in een
catch
component of eenforeach
instructie.
9.4.4 Precieze regels voor het bepalen van de definitieve toewijzing
9.4.4.1 Algemeen
Om te bepalen dat elke gebruikte variabele definitief is toegewezen, gebruikt de compiler een proces dat gelijk is aan het proces dat overeenkomt met het proces dat in deze subclause wordt beschreven.
De compiler verwerkt de hoofdtekst van elk functielid met een of meer in eerste instantie niet-toegewezen variabelen. Voor elke in eerste instantie niet-toegewezen variabele v bepaalt de compiler een definitieve toewijzingsstatus voor v op elk van de volgende punten in het functielid:
- Aan het begin van elke instructie
- Aan het eindpunt (§13.2) van elke instructie
- Op elke boog die het besturingselement overdraagt naar een andere instructie of naar het eindpunt van een instructie
- Aan het begin van elke expressie
- Aan het einde van elke expressie
De definitieve toewijzingsstatus van v kan een van de volgende zijn:
- Zeker toegewezen. Dit geeft aan dat aan alle mogelijke besturingsstromen naar dit punt een waarde is toegewezen.
- Niet zeker toegewezen. Voor de status van een variabele aan het einde van een expressie van het type
bool
valt de status van een variabele die niet zeker is toegewezen (maar niet noodzakelijkerwijs) in een van de volgende substatussen:- Zeker toegewezen na echte expressie. Deze status geeft aan dat v zeker is toegewezen als de Boole-expressie wordt geëvalueerd als waar, maar niet noodzakelijkerwijs wordt toegewezen als de Boole-expressie wordt geëvalueerd als onwaar.
- Zeker toegewezen na valse expressie. Deze status geeft aan dat v zeker is toegewezen als de Boole-expressie wordt geëvalueerd als onwaar, maar niet noodzakelijkerwijs wordt toegewezen als de Boole-expressie is geëvalueerd als waar.
De volgende regels bepalen hoe de status van een variabele v op elke locatie wordt bepaald.
9.4.4.2 Algemene regels voor instructies
- v is niet zeker toegewezen aan het begin van een hoofdtekst van een functielid.
- De definitieve toewijzingsstatus van v aan het begin van een andere instructie wordt bepaald door de status van de definitieve toewijzing van v te controleren op alle controlestroomoverdrachten die gericht zijn op het begin van die instructie. Als (en alleen als) v zeker is toegewezen aan al deze controlestroomoverdrachten, wordt v zeker aan het begin van de instructie toegewezen. De set mogelijke controlestroomoverdrachten wordt op dezelfde manier bepaald als voor het controleren van de bereikbaarheid van de verklaring (§13.2).
- De definitieve toewijzingsstatus van v op het eindpunt van een
block
, ,checked
do
while
unchecked
if
for
foreach
, , ,lock
ofswitch
using
instructie wordt bepaald door de status van de definitieve toewijzing van v te controleren op alle controlestroomoverdrachten die gericht zijn op het eindpunt van die instructie. Als v zeker is toegewezen aan al deze controlestroomoverdrachten, wordt v zeker toegewezen aan het eindpunt van de instructie. Anders wordt v niet zeker toegewezen aan het eindpunt van de instructie. De set mogelijke controlestroomoverdrachten wordt op dezelfde manier bepaald als voor het controleren van de bereikbaarheid van de verklaring (§13.2).
Opmerking: Omdat er geen besturingspaden naar een onbereikbare instructie zijn, wordt v zeker aan het begin van een onbereikbare instructie toegewezen. eindnotitie
9.4.4.3 Blokinstructies, ingeschakelde en niet-gecontroleerde instructies
De definitieve toewijzingsstatus van v op de besturingsoverdracht naar de eerste instructie van de instructielijst in het blok (of naar het eindpunt van het blok, als de instructielijst leeg is) is hetzelfde als de definitieve toewijzingsinstructie van v vóór het blok, checked
of unchecked
de instructie.
9.4.4.4 Expressie-instructies
Voor een expressie-instructie stmt die bestaat uit de expressie expr:
- v heeft dezelfde definitieve toewijzingsstatus aan het begin van expr als aan het begin van stmt.
- Als v aan het einde van expr definitief is toegewezen, wordt deze zeker toegewezen aan het eindpunt van stmt; anders wordt het niet zeker toegewezen aan het eindpunt van stmt.
9.4.4.5 Verklaringsverklaringen
- Als stmt een declaratie-instructie zonder initializers is, heeft v dezelfde status van de definitieve toewijzing aan het eindpunt van stmt als aan het begin van stmt.
- Als stmt een declaratie-instructie is met initialisatieprogramma's, wordt de status van de definitieve toewijzing voor v bepaald alsof stmt een instructielijst is, met één toewijzingsinstructie voor elke declaratie met een initialisatiefunctie (in de volgorde van declaratie).
9.4.4.6 If-instructies
Voor een instructie stmt van het formulier:
if ( «expr» ) «then_stmt» else «else_stmt»
- v heeft dezelfde definitieve toewijzingsstatus aan het begin van expr als aan het begin van stmt.
- Als v zeker aan het einde van expr is toegewezen, wordt deze zeker toegewezen aan de controlestroomoverdracht naar then_stmt en aan else_stmt of aan het eindpunt van stmt als er geen andere component is.
- Als v de status 'zeker toegewezen na echte expressie' aan het einde van expr heeft, wordt deze zeker toegewezen aan de overdracht van de controlestroom naar then_stmt, en niet zeker toegewezen aan de overdracht van de besturingsstroom naar else_stmt of naar het eindpunt van stmt als er geen andere component is.
- Als v aan het einde van expr de status 'definitief toegewezen na onwaar-expressie' heeft, wordt deze zeker toegewezen aan de overdracht van de besturingsstroom naar else_stmt en niet zeker toegewezen aan de controlestroomoverdracht aan then_stmt. Het is zeker toegewezen aan het eindpunt van stmt als en alleen als het zeker is toegewezen aan het eindpunt van then_stmt.
- Anders wordt v beschouwd als niet zeker toegewezen aan de controlestroomoverdracht naar de then_stmt of else_stmt, of naar het eindpunt van stmt als er geen andere component is.
9.4.4.7 Switch-instructies
Voor een switch
instructie stmt met een expressie-expr voor beheer:
De definitieve toewijzingsstatus van v aan het begin van expr is hetzelfde als de status van v aan het begin van stmt.
De status van de definitieve toewijzing van v aan het begin van de guard-component van een zaak is
- Als v een patroonvariabele is die is gedeclareerd in de switch_label: 'zeker toegewezen'.
- Als het switchlabel met die guard-component (§13.8.3) niet bereikbaar is: "zeker toegewezen".
- Anders is de status van v hetzelfde als de status van v na expr.
Voorbeeld: Met de tweede regel hoeft de compiler geen fout uit te geven als een niet-toegewezen variabele wordt geopend in onbereikbare code. De status b is 'zeker toegewezen' in het onbereikbare switchlabel
case 2 when b
.bool b; switch (1) { case 2 when b: // b is definitely assigned here. break; }
eindvoorbeeld
De definitieve toewijzingsstatus van v op de overdracht van de controlestroom naar een bereikbare lijst met blokblokkeringen is
- Als de overdracht van het besturingselement is veroorzaakt door een 'goto case' of 'goto default'-instructie, is de status van v hetzelfde als de status aan het begin van die 'goto'-instructie.
- Als de controleoverdracht het gevolg is van het
default
label van de switch, is de status van v hetzelfde als de status van v na expr. - Als de controleoverdracht te wijten was aan een onbereikbaar switchlabel, is de status van v 'zeker toegewezen'.
- Als de overdracht van het besturingselement te wijten was aan een bereikbaar switchlabel met een guard-component, is de status van v hetzelfde als de status van v na de guard-component.
- Als de overdracht van het besturingselement te wijten was aan een bereikbaar switchlabel zonder een guard-component, is de status van v
- Als v een patroonvariabele is die is gedeclareerd in de switch_label: 'zeker toegewezen'.
- Anders is de status van v hetzelfde als de stat van v na expr.
Een gevolg van deze regels is dat een patroonvariabele die is gedeclareerd in een switch_label 'niet zeker toegewezen' wordt in de instructies van de switchsectie als dit niet het enige bereikbare switchlabel in de sectie is.
Voorbeeld:
public static double ComputeArea(object shape) { switch (shape) { case Square s when s.Side == 0: case Circle c when c.Radius == 0: case Triangle t when t.Base == 0 || t.Height == 0: case Rectangle r when r.Length == 0 || r.Height == 0: // none of s, c, t, or r is definitely assigned return 0; case Square s: // s is definitely assigned return s.Side * s.Side; case Circle c: // c is definitely assigned return c.Radius * c.Radius * Math.PI; … } }
eindvoorbeeld
9.4.4.8 While-instructies
Voor een instructie stmt van het formulier:
while ( «expr» ) «while_body»
- v heeft dezelfde definitieve toewijzingsstatus aan het begin van expr als aan het begin van stmt.
- Als v zeker aan het einde van expr is toegewezen, wordt deze zeker toegewezen aan de controlestroomoverdracht naar while_body en naar het eindpunt van stmt.
- Als v de status 'zeker toegewezen na echte expressie' aan het einde van expr heeft, wordt deze zeker toegewezen aan de overdracht van de besturingsstroom naar while_body, maar niet zeker toegewezen aan het eindpunt van stmt.
- Als v de status 'zeker toegewezen na onwaar-expressie' aan het einde van expr heeft, wordt deze zeker toegewezen aan de controlestroomoverdracht naar het eindpunt van stmt, maar niet zeker toegewezen aan de controlestroomoverdracht naar while_body.
9.4.4.9 Do-instructies
Voor een instructie stmt van het formulier:
do «do_body» while ( «expr» ) ;
- v heeft dezelfde definitieve toewijzingsstatus voor de overdracht van de controlestroom vanaf het begin van stmt naar do_body als aan het begin van stmt.
- v heeft dezelfde definitieve toewijzingsstatus aan het begin van expr als aan het eindpunt van do_body.
- Als v zeker aan het einde van expr is toegewezen, wordt deze zeker toegewezen aan de controlestroomoverdracht naar het eindpunt van stmt.
- Als v de status 'zeker toegewezen na valse expressie' aan het einde van expr heeft, wordt deze zeker toegewezen aan de controlestroomoverdracht naar het eindpunt van stmt, maar niet zeker toegewezen aan de controlestroomoverdracht naar do_body.
9.4.4.10 Voor instructies
Voor een verklaring van het formulier:
for ( «for_initializer» ; «for_condition» ; «for_iterator» )
«embedded_statement»
het controleren van de definitieve opdracht wordt uitgevoerd alsof de instructie is geschreven:
{
«for_initializer» ;
while ( «for_condition» )
{
«embedded_statement» ;
LLoop: «for_iterator» ;
}
}
met continue
instructies die gericht zijn op de for
instructie die wordt vertaald naar goto
instructies die gericht zijn op het label LLoop
. Als de for_condition uit de for
instructie wordt weggelaten, wordt de evaluatie van de definitieve toewijzing voortgezet alsof for_condition zijn vervangen door waar in de bovenstaande uitbreiding.
9.4.4.11 Break, Continue en Goto-instructies
De definitieve toewijzingsstatus van v op de controlestroomoverdracht veroorzaakt door een break
, continue
of goto
instructie is hetzelfde als de status van de definitieve toewijzing van v aan het begin van de instructie.
9.4.4.12 Throw-instructies
Voor een instructie stmt van het formulier:
throw «expr» ;
de status van de definitieve toewijzing van v aan het begin van expr is hetzelfde als de status van de definitieve toewijzing van v aan het begin van stmt.
9.4.4.13 Retourinstructies
Voor een instructie stmt van het formulier:
return «expr» ;
- De status van de definitieve toewijzing van v aan het begin van expr is hetzelfde als de status van de definitieve toewijzing van v aan het begin van stmt.
- Als v een uitvoerparameter is, wordt deze definitief toegewezen:
- na expr
- of aan het einde van het
finally
blok van eentry
finally
-oftry
--catch
finally
die dereturn
instructie omsluit.
Voor een instructie stmt van het formulier:
return ;
- Als v een uitvoerparameter is, wordt deze definitief toegewezen:
- voor stmt
- of aan het einde van het
finally
blok van eentry
finally
-oftry
--catch
finally
die dereturn
instructie omsluit.
9.4.4.14 Try-catch-instructies
Voor een instructie stmt van het formulier:
try «try_block»
catch ( ... ) «catch_block_1»
...
catch ( ... ) «catch_block_n»
- De definitieve toewijzingsstatus van v aan het begin van try_block is hetzelfde als de status van de definitieve toewijzing van v aan het begin van stmt.
- De definitieve toewijzingsstatus van v aan het begin van catch_block_i (voor elke i) is hetzelfde als de status van de definitieve toewijzing van v aan het begin van stmt.
- De definitieve toewijzingsstatus van v op het eindpunt van stmt wordt zeker toegewezen als (en alleen als) v zeker wordt toegewezen aan het eindpunt van try_block en elke catch_block_i (voor elke i van 1 tot n).
9.4.4.15 Try-finally-instructies
Voor een instructie stmt van het formulier:
try «try_block» finally «finally_block»
- De definitieve toewijzingsstatus van v aan het begin van try_block is hetzelfde als de status van de definitieve toewijzing van v aan het begin van stmt.
- De definitieve toewijzingsstatus van v aan het begin van finally_block is hetzelfde als de status van de definitieve toewijzing van v aan het begin van stmt.
- De definitieve toewijzingsstatus van v aan het eindpunt van stmt wordt zeker toegewezen als (en alleen indien) ten minste één van de volgende waar is:
- v is zeker toegewezen aan het eindpunt van try_block
- v is zeker toegewezen aan het eindpunt van finally_block
Als er een controlestroomoverdracht (zoals een goto
instructie) wordt uitgevoerd die binnen try_block begint en buiten try_block eindigt, wordt v ook beschouwd als definitief toegewezen aan die controlestroomoverdracht als v zeker is toegewezen aan het eindpunt van finally_block. (Dit is niet alleen als er zeker een andere reden aan v is toegewezen voor deze overdracht van de controlestroom, wordt deze nog steeds beschouwd als definitief toegewezen.)
9.4.4.16 Try-catch-finally-instructies
Voor een verklaring van het formulier:
try «try_block»
catch ( ... ) «catch_block_1»
...
catch ( ... ) «catch_block_n»
finally «finally_block»
een definitieve toewijzingsanalyse wordt uitgevoerd alsof de instructie een try
-finally
instructie is die een try
-catch
instructie omsluit:
try
{
try «try_block»
catch ( ... ) «catch_block_1»
...
catch ( ... ) «catch_block_n»
}
finally «finally_block»
Voorbeeld: In het volgende voorbeeld ziet u hoe de verschillende blokken van een
try
instructie (§13.11) van invloed zijn op definitieve toewijzing.class A { static void F() { int i, j; try { goto LABEL; // neither i nor j definitely assigned i = 1; // i definitely assigned } catch { // neither i nor j definitely assigned i = 3; // i definitely assigned } finally { // neither i nor j definitely assigned j = 5; // j definitely assigned } // i and j definitely assigned LABEL: ; // j definitely assigned } }
eindvoorbeeld
9.4.4.17 Foreach-instructies
Voor een instructie stmt van het formulier:
foreach ( «type» «identifier» in «expr» ) «embedded_statement»
- De definitieve toewijzingsstatus van v aan het begin van expr is hetzelfde als de status van v aan het begin van stmt.
- De definitieve toewijzingsstatus van v op de controlestroomoverdracht naar embedded_statement of naar het eindpunt van stmt is hetzelfde als de status van v aan het einde van expr.
9.4.4.18 Met behulp van instructies
Voor een instructie stmt van het formulier:
using ( «resource_acquisition» ) «embedded_statement»
- De definitieve toewijzingsstatus van v aan het begin van resource_acquisition is hetzelfde als de status van v aan het begin van stmt.
- De definitieve toewijzingsstatus van v op de controlestroomoverdracht naar embedded_statement is hetzelfde als de status van v aan het einde van resource_acquisition.
9.4.4.19 Lock-instructies
Voor een instructie stmt van het formulier:
lock ( «expr» ) «embedded_statement»
- De definitieve toewijzingsstatus van v aan het begin van expr is hetzelfde als de status van v aan het begin van stmt.
- De definitieve toewijzingsstatus van v op de controlestroomoverdracht naar embedded_statement is hetzelfde als de status van v aan het einde van expr.
9.4.4.20 Rendementsinstructies
Voor een instructie stmt van het formulier:
yield return «expr» ;
- De definitieve toewijzingsstatus van v aan het begin van expr is hetzelfde als de status van v aan het begin van stmt.
- De definitieve toewijzingsstatus van v aan het einde van stmt is hetzelfde als de status van v aan het einde van expr.
Een yield break
instructie heeft geen invloed op de status van de definitieve toewijzing.
9.4.4.21 Algemene regels voor constante expressies
Het volgende is van toepassing op elke constante expressie en heeft voorrang op alle regels uit de volgende secties die van toepassing kunnen zijn:
Voor een constante expressie met waarde true
:
- Als v zeker is toegewezen vóór de expressie, wordt v zeker toegewezen na de expressie.
- Anders wordt ' zeker toegewezen na onwaar-expressie' na de expressie.
Voorbeeld:
int x; if (true) {} else { Console.WriteLine(x); }
eindvoorbeeld
Voor een constante expressie met waarde false
:
- Als v zeker is toegewezen vóór de expressie, wordt v zeker toegewezen na de expressie.
- Anders wordt ' zeker toegewezen na ware expressie' na de expressie.
Voorbeeld:
int x; if (false) { Console.WriteLine(x); }
eindvoorbeeld
Voor alle andere constante expressies is de definitieve toewijzingsstatus van v na de expressie hetzelfde als de status van de definitieve toewijzing van v vóór de expressie.
9.4.4.22 Algemene regels voor eenvoudige expressies
De volgende regel is van toepassing op dit soort expressies: letterlijke waarden (§12.8.2), eenvoudige namen (§12.8.4), toegangsexpressies voor leden (§12.8.7), niet-geïndexeerde basistoegangsexpressies (§12..) 8.14), typeof
expressies (§12.8.17), standaardwaardeexpressies (§12.8.20), nameof
expressies (§12.8.22) en declaratie-expressies (§12.17).
- De definitieve toewijzingsstatus van v aan het einde van een dergelijke expressie is hetzelfde als de status van de definitieve toewijzing van v aan het begin van de expressie.
9.4.4.23 Algemene regels voor expressies met ingesloten expressies
De volgende regels zijn van toepassing op dit soort expressies: expressies tussen haakjes (§12.8.5), tuple-expressies (§12.8.6), expressies voor toegang tot elementen (§12.8.11), basistoegangsexpressies met indexering (§12.8.14), verhogings- en afschaffen expressies (§12.8.15, §12.9.6), cast-expressies (§12.9.7), unaire +
, , -
, expressies~
*
, binair+
, -
, *
/
, , , %
, <<
, >>
, , <
<=
>
, , ==
>=
, !=
, is
, , &
as
, |
^
expressies (§12.10, §12.11, §12.12, §12.13), samengestelde toewijzingsexpressies (§12.21.4) checked
en expressies (§12.8.19), expressies voor het maken van matrices en gemachtigden (§12.8.16) en unchecked
await
expressies (§12.9.8).
Elk van deze expressies heeft een of meer subexpressies die onvoorwaardelijk in een vaste volgorde worden geëvalueerd.
Voorbeeld: De binaire
%
operator evalueert de linkerkant van de operator en vervolgens de rechterzijde. Een indexeringsbewerking evalueert de geïndexeerde expressie en evalueert vervolgens elk van de indexexpressies, in volgorde van links naar rechts. eindvoorbeeld
Voor een expressie expr, die subexpressies expr₁, exprₓ, ..., exprₓ, geëvalueerd in die volgorde:
- De status van de definitieve toewijzing van v aan het begin van expr₁ is hetzelfde als de status van de definitieve toewijzing aan het begin van expr.
- De definitieve toewijzingsstatus van v aan het begin van expri (i groter dan één) is hetzelfde als de status van de definitieve toewijzing aan het einde van expri₋₁.
- De status van de definitieve toewijzing van v aan het einde van expr is hetzelfde als de status van de definitieve toewijzing aan het einde van exprₓ.
9.4.4.24 Aanroepexpressies en expressies voor het maken van objecten
Als de methode die moet worden aangeroepen een gedeeltelijke methode is die geen gedeeltelijke methodedeclaratie heeft of een voorwaardelijke methode is waarvoor de aanroep wordt weggelaten (§22.5.3.2), is de definitieve toewijzingsstatus van v na de aanroep hetzelfde als de status van de definitieve toewijzing van v vóór de aanroep. Anders zijn de volgende regels van toepassing:
Voor een aanroepexpressie expr van het formulier:
«primary_expression» ( «arg₁», «arg₂», … , «argₓ» )
of een expressie voor het maken van objecten van het formulier:
new «type» ( «arg₁», «arg₂», … , «argₓ» )
- Voor een aanroepexpressie is de definitieve toewijzingsstatus van v vóór primary_expression hetzelfde is als de status van v vóór expr.
- Voor een aanroepexpressie is de definitieve toewijzingsstatus van v vóór arg₁ hetzelfde als de status van v na primary_expression.
- Voor een expressie voor het maken van objecten is de definitieve toewijzingsstatus van v vóór arg₁ hetzelfde als de status van v vóór expr.
- Voor elk argument argi wordt de definitieve toewijzingsstatus van v na argi bepaald door de normale expressieregels, waarbij alle
in
,out
ofref
modifiers worden genegeerd. - Voor elk argument argi voor i groter dan één, is de definitieve toewijzingsstatus van v vóór argi hetzelfde als de status van v na argi₋₁.
- Als de variabele v wordt doorgegeven als argument
out
(bijvoorbeeld een argument van het formulier 'out v') in een van de argumenten, wordt de status van v na expr zeker toegewezen. Anders is de status van v na expr hetzelfde als de status van v na argₓ. - Voor matrix initializers (§12.8.16.5), object initializers (§12.8.16.3), verzameling initializers (§12.8.16.4) en anonieme object initializers (§12.8.16.7), wordt de definitieve toewijzingsstatus bepaald door de uitbreiding die deze constructies zijn gedefinieerd in termen van.
9.4.4.25 Eenvoudige toewijzingsexpressies
Laat de set toewijzingsdoelen in een expressie als volgt worden gedefinieerd:
- Als e een tuple-expressie is, zijn de toewijzingsdoelen in e de samenvoeging van de toewijzingsdoelen van de elementen van e.
- Anders zijn de toewijzingsdoelen in e e.
Voor een expressie-expr van het formulier:
«expr_lhs» = «expr_rhs»
- De status van de definitieve toewijzing van v voordat expr_lhs hetzelfde is als de status van de definitieve toewijzing van v vóór expr.
- De status van de definitieve toewijzing van v vóór expr_rhs is hetzelfde als de status van de definitieve toewijzing van v na expr_lhs.
- Als v een toewijzingsdoel van expr_lhs is, wordt de status van v definitief toegewezen nadat expr zeker is toegewezen. Als de toewijzing zich in de instantieconstructor van een struct-type voordoet en v het verborgen backingveld is van een automatisch geïmplementeerde eigenschap P op het exemplaar dat wordt gemaakt, en een eigenschapstoegang tot P een assigmentdoel van expr_lhs is, is de definitieve toewijzingsstatus van v nadat expr zeker is toegewezen. Anders is de status van de definitieve toewijzing van v na expr hetzelfde als de status van de definitieve toewijzing van v na expr_rhs.
Voorbeeld: In de volgende code
class A { static void F(int[] arr) { int x; arr[x = 1] = x; // ok } }
de variabele
x
wordt beschouwd als definitief toegewezen nadatarr[x = 1]
deze is geëvalueerd als de linkerkant van de tweede eenvoudige toewijzing.eindvoorbeeld
9.4.4.26 && expressies
Voor een expressie-expr van het formulier:
«expr_first» && «expr_second»
- De status van de definitieve toewijzing van v voordat expr_first hetzelfde is als de status van de definitieve toewijzing van v vóór expr.
- De definitieve toewijzingsstatus van v vóór expr_second wordt zeker toegewezen als en alleen als de status van v na expr_first definitief is toegewezen of 'definitief toegewezen na echte expressie'. Anders is het niet zeker toegewezen.
- De definitieve toewijzingsstatus van v na expr wordt bepaald door:
- Als de status van v na expr_first zeker is toegewezen, wordt de status van v na expr zeker toegewezen.
- Als de status van v na expr_second zeker is toegewezen en de status van v na expr_first 'zeker toegewezen na valse expressie' is, wordt de status van v na expr zeker toegewezen.
- Als de status van v na expr_second zeker is toegewezen of 'definitief toegewezen na echte expressie', is de status van v na expr 'zeker toegewezen na echte expressie'.
- Als de status van v na expr_first 'zeker toegewezen na onwaar-expressie' is en de status van v na expr_second 'zeker toegewezen na onwaar-expressie', is de status van v na expr 'zeker toegewezen na onwaar-expressie'.
- Anders is de status van v na expr niet zeker toegewezen.
Voorbeeld: In de volgende code
class A { static void F(int x, int y) { int i; if (x >= 0 && (i = y) >= 0) { // i definitely assigned } else { // i not definitely assigned } // i not definitely assigned } }
de variabele
i
wordt beschouwd als definitief toegewezen in een van de ingesloten instructies van eenif
instructie, maar niet in de andere. In deif
instructie in de methodeF
wordt de variabelei
zeker toegewezen in de eerste ingesloten instructie, omdat de uitvoering van de expressie(i = y)
altijd voorafgaat aan de uitvoering van deze ingesloten instructie. Daarentegen is de variabelei
niet zeker toegewezen in de tweede ingesloten instructie, omdatx >= 0
deze mogelijk onwaar heeft getest, waardoor de variabelei
niet wordt toegewezen.eindvoorbeeld
9.4.4.27 || uitdrukkingen
Voor een expressie-expr van het formulier:
«expr_first» || «expr_second»
- De status van de definitieve toewijzing van v voordat expr_first hetzelfde is als de status van de definitieve toewijzing van v vóór expr.
- De definitieve toewijzingsstatus van v vóór expr_second wordt zeker toegewezen als en alleen als de status van v na expr_first definitief is toegewezen of 'definitief toegewezen na echte expressie'. Anders is het niet zeker toegewezen.
- De definitieve toewijzingsinstructie van v na expr wordt bepaald door:
- Als de status van v na expr_first zeker is toegewezen, wordt de status van v na expr zeker toegewezen.
- Als de status van v na expr_second zeker is toegewezen en de status van v na expr_first 'zeker toegewezen na echte expressie' is, wordt de status van v na expr zeker toegewezen.
- Anders, als de status van v na expr_second zeker is toegewezen of 'definitief toegewezen na onwaar-expressie', is de status van v na expr 'zeker toegewezen na valse expressie'.
- Als de status van v na expr_first 'zeker toegewezen na echte expressie' is en de status van v na expr_ seconde 'zeker toegewezen na echte expressie' is, is de status van v na expr 'zeker toegewezen na echte expressie'.
- Anders is de status van v na expr niet zeker toegewezen.
Voorbeeld: In de volgende code
class A { static void G(int x, int y) { int i; if (x >= 0 || (i = y) >= 0) { // i not definitely assigned } else { // i definitely assigned } // i not definitely assigned } }
de variabele
i
wordt beschouwd als definitief toegewezen in een van de ingesloten instructies van eenif
instructie, maar niet in de andere. In deif
instructie in de methodeG
wordt de variabelei
zeker toegewezen in de tweede ingesloten instructie omdat de uitvoering van de expressie(i = y)
altijd voorafgaat aan de uitvoering van deze ingesloten instructie. Daarentegen is de variabelei
niet zeker toegewezen in de eerste ingesloten instructie, omdatx >= 0
deze true kan hebben getest, waardoor de variabelei
niet wordt toegewezen.eindvoorbeeld
9.4.4.28 ! uitdrukkingen
Voor een expressie-expr van het formulier:
! «expr_operand»
- De status van de definitieve toewijzing van v voordat expr_operand hetzelfde is als de status van de definitieve toewijzing van v vóór expr.
- De definitieve toewijzingsstatus van v na expr wordt bepaald door:
- Als de status van
v
na expr_operand zeker is toegewezen, wordt de status vanv
na expr zeker toegewezen. - Als de status na
v
expr_operand 'zeker toegewezen na onwaar-expressie' is, is de status vanv
na expr 'zeker toegewezen na echte expressie'. - Als de status na
v
expr_operand 'zeker toegewezen na echte expressie' is, is de status van v na expr 'zeker toegewezen na valse expressie'. - Anders is de status van
v
na expr niet zeker toegewezen.
- Als de status van
9.4.4.29 ?? uitdrukkingen
Voor een expressie-expr van het formulier:
«expr_first» ?? «expr_second»
- De status van de definitieve toewijzing van v voordat expr_first hetzelfde is als de status van de definitieve toewijzing van v vóór expr.
- De status van de definitieve toewijzing van v vóór expr_second is hetzelfde als de status van de definitieve toewijzing van v na expr_first.
- De definitieve toewijzingsinstructie van v na expr wordt bepaald door:
- Als expr_first een constante expressie (§12.23) met waarde
null
is, is de status van v na expr hetzelfde als de status van v na expr_second. - Anders is de status van v na expr hetzelfde als de status van de definitieve toewijzing van v na expr_first.
- Als expr_first een constante expressie (§12.23) met waarde
9.4.4.30 ?: expressies
Voor een expressie-expr van het formulier:
«expr_cond» ? «expr_true» : «expr_false»
- De definitieve toewijzingsstatus van v voordat expr_cond hetzelfde is als de status van v vóór expr.
- De definitieve toewijzingsstatus van v vóór expr_true wordt zeker toegewezen als de status van v na expr_cond zeker is toegewezen of 'definitief toegewezen na echte expressie'.
- De definitieve toewijzingsstatus van v voordat expr_false zeker wordt toegewezen als de status van v na expr_cond zeker is toegewezen of 'definitief toegewezen na onwaar-expressie'.
- De definitieve toewijzingsstatus van v na expr wordt bepaald door:
- Als expr_cond een constante expressie (§12.23) met waarde
true
is, is de toestand van v na expr hetzelfde als de status van v na expr_true. - Als expr_cond een constante expressie (§12.23) met waarde
false
is, is de toestand van v na expr hetzelfde als de toestand van v na expr_false. - Anders, als de status van v na expr_true zeker is toegewezen en de status van v na expr_false zeker is toegewezen, wordt de status van v na expr zeker toegewezen.
- Anders is de status van v na expr niet zeker toegewezen.
- Als expr_cond een constante expressie (§12.23) met waarde
9.4.4.31 Anonieme functies
Voor een lambda_expression of anonymous_method_expression expr met een hoofdtekst (blok of expressie):
- De definitieve toewijzingsstatus van een parameter is hetzelfde als voor een parameter van een benoemde methode (§9.2.6, §9.2.7, §9.2.8).
- De definitieve toewijzingsstatus van een buitenste variabele v vóór de hoofdtekst is hetzelfde als de status van v vóór expr. Dat wil gezegd, definitieve toewijzingsstatus van buitenste variabelen wordt overgenomen uit de context van de anonieme functie.
- De definitieve toewijzingsstatus van een buitenste variabele v na expr is hetzelfde als de status van v vóór expr.
Voorbeeld: Het voorbeeld
class A { delegate bool Filter(int i); void F() { int max; // Error, max is not definitely assigned Filter f = (int n) => n < max; max = 5; DoWork(f); } void DoWork(Filter f) { ... } }
genereert een compilatiefout omdat max niet zeker is toegewezen waar de anonieme functie wordt gedeclareerd.
eindvoorbeeld
Voorbeeld: Het voorbeeld
class A { delegate void D(); void F() { int n; D d = () => { n = 1; }; d(); // Error, n is not definitely assigned Console.WriteLine(n); } }
genereert ook een compilatiefout omdat de toewijzing in
n
de anonieme functie geen invloed heeft op de status van de definitieve toewijzing vann
buiten de anonieme functie.eindvoorbeeld
9.4.4.32 Expressies voor gooien
Voor een expressie-expr van het formulier:
throw
thrown_expr
- De definitieve toewijzingsstatus van v voordat thrown_expr hetzelfde is als de status van v vóór expr.
- De definitieve toewijzingsstatus van v na expr is 'zeker toegewezen'.
9.4.4.33 Regels voor variabelen in lokale functies
Lokale functies worden geanalyseerd in de context van hun bovenliggende methode. Er zijn twee besturingsstroompaden die van belang zijn voor lokale functies: functie-aanroepen en conversies delegeren.
Duidelijke toewijzing voor de hoofdtekst van elke lokale functie wordt afzonderlijk gedefinieerd voor elke aanroepsite. Bij elke aanroep worden variabelen die door de lokale functie zijn vastgelegd, als definitief toegewezen beschouwd als ze op het moment van aanroep zeker zijn toegewezen. Er bestaat op dit moment ook een besturingsstroompad naar de hoofdtekst van de lokale functie en wordt als bereikbaar beschouwd. Na een aanroep van de lokale functie worden vastgelegde variabelen die zeker op elk besturingspunt zijn toegewezen, waarbij de functie (return
instructies, yield
instructies, await
expressies) wordt beschouwd als definitief toegewezen na de aanroeplocatie.
Conversies van gedelegeerden hebben een besturingsstroompad naar de hoofdtekst van de lokale functie. Vastgelegde variabelen worden zeker toegewezen voor de hoofdtekst als ze zeker zijn toegewezen vóór de conversie. Variabelen die door de lokale functie worden toegewezen, worden niet beschouwd als toegewezen na de conversie.
Opmerking: het bovenstaande impliceert dat lichamen opnieuw worden geanalyseerd voor definitieve toewijzing bij elke aanroep van lokale functies of conversie van gemachtigden. Compilers zijn niet vereist om de hoofdtekst van een lokale functie opnieuw te analyseren bij elke aanroep of delegeringsconversie. De implementatie moet resultaten opleveren die gelijk zijn aan die beschrijving. eindnotitie
Voorbeeld: In het volgende voorbeeld ziet u een duidelijke toewijzing voor vastgelegde variabelen in lokale functies. Als een lokale functie een vastgelegde variabele leest voordat deze wordt geschreven, moet de vastgelegde variabele zeker worden toegewezen voordat de lokale functie wordt aangeroepen. De lokale functie
F1
leests
zonder deze toe te wijzen. Het is een fout alsF1
er eerders
een aangeroepen wordt toegewezen.F2
wordti
toegewezen voordat u het leest. Het kan worden aangeroepen voordati
is zeker toegewezen.F3
Bovendien kan hierna worden aangeroepenF2
omdats2
is zeker toegewezen inF2
.void M() { string s; int i; string s2; // Error: Use of unassigned local variable s: F1(); // OK, F2 assigns i before reading it. F2(); // OK, i is definitely assigned in the body of F2: s = i.ToString(); // OK. s is now definitely assigned. F1(); // OK, F3 reads s2, which is definitely assigned in F2. F3(); void F1() { Console.WriteLine(s); } void F2() { i = 5; // OK. i is definitely assigned. Console.WriteLine(i); s2 = i.ToString(); } void F3() { Console.WriteLine(s2); } }
eindvoorbeeld
9.4.4.34 is-pattern expressies
Voor een expressie-expr van het formulier:
expr_operand is patroon
- De status van de definitieve toewijzing van v voordat expr_operand hetzelfde is als de status van de definitieve toewijzing van v vóór expr.
- Als de variabele 'v' in patroon is gedeclareerd, wordt de status van 'v' definitief toegewezen nadat expr 'zeker is toegewezen als waar'.
- Anders is de definitieve toewijzingsstatus van 'v' na expr hetzelfde als de definitieve toewijzingsstatus van 'v' na expr_operand.
9.5 Variabele verwijzingen
Een variable_reference is een expressie die is geclassificeerd als een variabele. Een variable_reference geeft een opslaglocatie aan die toegankelijk is om zowel de huidige waarde op te halen als om een nieuwe waarde op te slaan.
variable_reference
: expression
;
Opmerking: In C en C++ wordt een variable_reference een lvalue genoemd. eindnotitie
9.6 Atomiciteit van variabele verwijzingen
Lees- en schrijfbewerkingen van de volgende gegevenstypen moeten atomisch zijn: bool
, , char
, short
sbyte
byte
, , ushort
, uint
, , int
, , en float
referentietypen. Daarnaast moeten lees- en schrijfbewerkingen van enumtypen met een onderliggend type in de vorige lijst ook atomisch zijn. Lees- en schrijfbewerkingen van andere typen, waaronder long
, ulong
, double
en , en decimal
, evenals door de gebruiker gedefinieerde typen, hoeven niet atomisch te zijn. Afgezien van de bibliotheekfuncties die voor dat doel zijn ontworpen, is er geen garantie voor atomic read-modify-write, zoals in het geval van incrementele of afkritelling.
9.7 Referentievariabelen en retourneert
9.7.1 Algemeen
Een referentievariabele is een variabele die verwijst naar een andere variabele, de referent genoemd (§9.2.6). Een referentievariabele is een lokale variabele die met de ref
wijzigingsfunctie is gedeclareerd.
Een referentievariabele slaat een variable_reference (§9,5) op naar de referent en niet naar de waarde van de referent. Wanneer een verwijzingsvariabele wordt gebruikt waarbij een waarde vereist is, wordt de waarde van de referent geretourneerd; Op dezelfde manier is een verwijzingsvariabele het doel van een toewijzing, is dit de referent waaraan is toegewezen. De variabele waarnaar een verwijzingsvariabele verwijst, bijvoorbeeld de opgeslagen variable_reference voor de verwijzingsvariabele, kan worden gewijzigd met behulp van een verw-toewijzing (= ref
).
Voorbeeld: In het volgende voorbeeld ziet u een lokale referentievariabele waarvan de referent een element van een matrix is:
public class C { public void M() { int[] arr = new int[10]; // element is a reference variable that refers to arr[5] ref int element = ref arr[5]; element += 5; // arr[5] has been incremented by 5 } }
eindvoorbeeld
Een verwijzingsresource is de variable_reference geretourneerd door een methode returns-by-ref (§15.6.1). Deze variable_reference is de referent van de verwijzings geretourneerde waarde.
Voorbeeld: In het volgende voorbeeld ziet u een verwijzings geretourneerd waarvan de verwijzing een element is van een matrixveld:
public class C { private int[] arr = new int[10]; public ref readonly int M() { // element is a reference variable that refers to arr[5] ref int element = ref arr[5]; return ref element; // return reference to arr[5]; } }
eindvoorbeeld
9.7.2 Ref veilige contexten
9.7.2.1 Algemeen
Alle referentievariabelen voldoen aan de veiligheidsregels die ervoor zorgen dat de ref-safe-context van de referentievariabele niet groter is dan de ref-safe-context van de verwijzingsvariabele.
Opmerking: Het gerelateerde begrip van een veilige context wordt gedefinieerd in (§16.4.12), samen met de bijbehorende beperkingen. eindnotitie
Voor elke variabele is de ref-safe-context van die variabele de context waarbij een variable_reference (§9,5) geldig is voor die variabele. De verwijzingsvariabele heeft een ref-safe-context die ten minste even breed is als de ref-safe-context van de referentievariabele zelf.
Opmerking: De compiler bepaalt de ref-safe-context via een statische analyse van de programmatekst. De ref-safe-context weerspiegelt de levensduur van een variabele tijdens runtime. eindnotitie
Er zijn drie ref-safe-contexten:
declaration-block: de ref-safe-context van een variable_reference naar een lokale variabele (§9.2.9) is dat het bereik van de lokale variabele (§13.6.2), inclusief geneste embedded-statementsin dat bereik.
Een variable_reference naar een lokale variabele is alleen een geldige verwijzingsvariabele voor een referentievariabele als de verwijzingsvariabele wordt gedeclareerd in de ref-safe-context van die variabele.
functielid: Binnen een functie heeft een variable_reference aan een van de volgende functies een ref-safe-context van functielid:
- Waardeparameters (§15.6.2.2) op een functieliddeclaratie, met inbegrip van de impliciete functies
this
van klasseleden; en - De impliciete verwijzingsparameter (
ref
)-parameter (§15.6.2.3.3)this
van een structlidfunctie, samen met de bijbehorende velden.
Een variable_reference met ref-safe-context van functielid is alleen een geldige referent als de referentievariabele wordt gedeclareerd in hetzelfde functielid.
- Waardeparameters (§15.6.2.2) op een functieliddeclaratie, met inbegrip van de impliciete functies
aanroepercontext: binnen een functie heeft een variable_reference aan een van de volgende opties een ref-safe-context van de aanroepercontext:
- Referentieparameters (§9.2.6) behalve de impliciete
this
functie van een structlid; - Lidvelden en -elementen van dergelijke parameters;
- Lidvelden van parameters van klassetype; en
- Elementen van parameters van het matrixtype.
- Referentieparameters (§9.2.6) behalve de impliciete
Een variable_reference met ref-safe-context van de aanroepercontext kan de verwijzing van een verwijzingsresource zijn.
Deze waarden vormen een geneste relatie van smalste (declaratieblok) tot breedste (aanroepercontext). Elk geneste blok vertegenwoordigt een andere context.
Voorbeeld: De volgende code toont voorbeelden van de verschillende ref-safe-contexten. In de declaraties wordt de context voor ref-safe weergegeven, zodat een verwijzing de initialisatie-expressie voor een
ref
variabele is. In de voorbeelden ziet u de ref-safe-context voor een referentie-return:public class C { // ref safe context of arr is "caller-context". // ref safe context of arr[i] is "caller-context". private int[] arr = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; // ref safe context is "caller-context" public ref int M1(ref int r1) { return ref r1; // r1 is safe to ref return } // ref safe context is "function-member" public ref int M2(int v1) { return ref v1; // error: v1 isn't safe to ref return } public ref int M3() { int v2 = 5; return ref arr[v2]; // arr[v2] is safe to ref return } public void M4(int p) { int v3 = 6; // context of r2 is declaration-block, // ref safe context of p is function-member ref int r2 = ref p; // context of r3 is declaration-block, // ref safe context of v3 is declaration-block ref int r3 = ref v3; // context of r4 is declaration-block, // ref safe context of arr[v3] is caller-context ref int r4 = ref arr[v3]; } }
eindvoorbeeld.
Voorbeeld: Voor
struct
typen wordt de implicietethis
parameter doorgegeven als referentieparameter. De ref-safe-context van de velden van eenstruct
type als functielid voorkomt dat deze velden worden geretourneerd door verwijzingsresources. Deze regel voorkomt de volgende code:public struct S { private int n; // Disallowed: returning ref of a field. public ref int GetN() => ref n; } class Test { public ref int M() { S s = new S(); ref int numRef = ref s.GetN(); return ref numRef; // reference to local variable 'numRef' returned } }
eindvoorbeeld.
9.7.2.2 Lokale variabele ref veilige context
Voor een lokale variabele v
:
- Als
v
dit een referentievariabele is, is de verwijzingsveilige context hetzelfde als de ref-safe-context van de initialisatie-expressie. - Anders is de ref-safe-context declaratieblok.
9.7.2.3 Parameter ref safe context
Voor een parameter p
:
- Als
p
dit een verwijzings- of invoerparameter is, is de ref-safe-context de aanroepercontext. Alsp
dit een invoerparameter is, kan deze niet worden geretourneerd als beschrijfbaarref
, maar kan worden geretourneerd alsref readonly
. - Als
p
het een uitvoerparameter is, is de ref-safe-context de aanroepercontext. p
Als dit anders dethis
parameter van een structtype is, is de ref-safe-context het functielid.- Anders is de parameter een waardeparameter en is de ref-safe-context het functielid.
9.7.2.4 Veldverww veilige context
Voor een variabele die een verwijzing naar een veld aanwijst, : e.F
- Als
e
het om een verwijzingstype gaat, is de ref-safe-context de aanroepercontext. e
Als dit niet het waardetype is, is de ref-safe-context hetzelfde als de ref-safe-context vane
.
9.7.2.5 Operators
De voorwaardelijke operator (§12.18) c ? ref e1 : ref e2
en de verwijzingstoewijzingsoperator = ref e
(§12.21.1) hebben verwijzingsvariabelen als operanden en leveren een referentievariabele op. Voor deze operators is de ref-safe-context van het resultaat de kleinste context onder de ref-safe-contexten van alle ref
operanden.
9.7.2.6 Functie aanroepen
Voor een variabele c
die het gevolg is van een aanroep van een functie voor ref-retourneert, is de ref-safe-context het smalst van de volgende contexten:
- De aanroepercontext.
- De ref-safe-context van alle
ref
,out
enin
argumentexpressies (met uitzondering van de ontvanger). - Als er voor elke invoerparameter een overeenkomstige expressie is die een variabele is en er een identiteitsconversie bestaat tussen het type van de variabele en het type van de parameter, wordt de ref-safe-context van de variabele gebruikt, anders de dichtstbijzijnde omsluitcontext.
- De veilige context (§16.4.12) van alle argumentexpressies (inclusief de ontvanger).
Voorbeeld: het laatste opsommingsteken is nodig om code te verwerken, zoals
ref int M2() { int v = 5; // Not valid. // ref safe context of "v" is block. // Therefore, ref safe context of the return value of M() is block. return ref M(ref v); } ref int M(ref int p) { return ref p; }
eindvoorbeeld
Een aanroep van eigenschappen en een aanroep van een indexeerfunctie (een get
of set
) wordt behandeld als een functieaanroep van de onderliggende toegangsfunctie door de bovenstaande regels. Een aanroep van een lokale functie is een functie-aanroep.
9.7.2.7 Waarden
De ref-safe-context van een waarde is de dichtstbijzijnde omsluitcontext.
Opmerking: dit gebeurt in een aanroep, zoals
M(ref d.Length)
waard
het typedynamic
is. Het is ook consistent met argumenten die overeenkomen met invoerparameters. eindnotitie
9.7.2.8 Constructor aanroepen
Een new
expressie die een constructor aanroept, voldoet aan dezelfde regels als een methode-aanroep (§9.7.2.6) die wordt beschouwd om het type te retourneren dat wordt samengesteld.
9.7.2.9 Beperkingen voor referentievariabelen
- Noch een verwijzingsparameter, noch een uitvoerparameter, noch een invoerparameter, noch een lokale, noch een
ref
parameter of lokaal van eenref struct
type wordt vastgelegd door lambda-expressie of lokale functie. - Een verwijzingsparameter, noch een uitvoerparameter, noch een invoerparameter, noch een parameter van een
ref struct
type moet een argument zijn voor een iterator-methode of eenasync
methode. ref
Een lokale, noch een lokale van eenref struct
type moet in context zijn op het punt van eenyield return
instructie of eenawait
expressie.- Voor een hertoewijzing
e1 = ref e2
van ref moet de ref-safe-contexte2
ten minste zo breed zijn als de ref-safe-context vane1
. - Voor een ref-return-instructie
return ref e1
is de ref-safe-context vane1
de aanroepercontext.
ECMA C# draft specification