Dela via


9 variabler

9.1 Allmänt

Variabler representerar lagringsplatser. Varje variabel har en typ som avgör vilka värden som kan lagras i variabeln. C# är ett typsäkert språk och C#-kompilatorn garanterar att värden som lagras i variabler alltid är av lämplig typ. Värdet för en variabel kan ändras genom tilldelning eller genom användning av operatorerna ++ och -- .

En variabel skall definitivt tilldelas (§9.4) innan dess värde kan erhållas.

Enligt beskrivningen i följande undermappar tilldelas variablerna antingen ursprungligen eller tilldelas inte till en början. En ursprungligen tilldelad variabel har ett väldefinierat initialt värde och anses alltid definitivt tilldelad. En ursprungligen otilldelade variabel har inget initialt värde. För att en ursprungligen otilldelad variabel ska betraktas som definitivt tilldelad på en viss plats ska en tilldelning till variabeln ske i varje möjlig körningsväg som leder till den platsen.

9.2 Variabelkategorier

9.2.1 Allmänt

C# definierar åtta kategorier av variabler: statiska variabler, instansvariabler, matriselement, värdeparametrar, indataparametrar, referensparametrar, utdataparametrar och lokala variabler. De underklisklar som följer beskriver var och en av dessa kategorier.

Exempel: I följande kod

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 är en statisk variabel, y är en instansvariabel, v[0] är ett matriselement, a är en värdeparameter, b är en referensparameter, c är en utdataparameter, d är en indataparameter och i är en lokal variabel. slutexempel

9.2.2 Statiska variabler

Ett fält som deklareras med static modifieraren är en statisk variabel. En statisk variabel finns innan konstruktorn (§15.12) körs static för dess innehållande typ och upphör att existera när den associerade programdomänen upphör att existera.

Det initiala värdet för en statisk variabel är standardvärdet (§9.3) av variabelns typ.

Vid kontroll av bestämd tilldelning betraktas en statisk variabel som ursprungligen tilldelad.

9.2.3 Instansvariabler

9.2.3.1 Allmänt

Ett fält som deklareras utan static modifieraren är en instansvariabel.

9.2.3.2 Instansvariabler i klasser

En instansvariabel för en klass kommer till när en ny instans av den klassen skapas och upphör att finnas när det inte finns några referenser till den instansen och instansens finaliserare (om någon) har körts.

Det initiala värdet för en instansvariabel för en klass är standardvärdet (§9.3) av variabelns typ.

Vid kontroll av bestämd tilldelning betraktas en instansvariabel för en klass som ursprungligen tilldelad.

9.2.3.3 Instansvariabler i structs

En instansvariabel för en struct har exakt samma livslängd som den structvariabel som den tillhör. Med andra ord, när en variabel av en struct-typ kommer till eller upphör att existera, så gör även instansvariablerna i struct.

Det inledande tilldelningstillståndet för en instansvariabel för en struct är samma som för den innehållande struct variabeln. Med andra ord, när en struct-variabel anses vara ursprungligen tilldelad, så är även dess instansvariabler, och när en struct-variabel anses vara ursprungligen otilldelade, tilldelas även dess instansvariabler.

9.2.4 Matriselement

Elementen i en matris kommer till när en matrisinstans skapas och upphör att finnas när det inte finns några referenser till den matrisinstansen.

Det initiala värdet för vart och ett av elementen i en matris är standardvärdet (§9.3) för typen av matriselement.

Vid kontroll av bestämd tilldelning betraktas ett matriselement som ursprungligen tilldelat.

9.2.5 Värdeparametrar

En värdeparameter kommer till vid anrop av funktionsmedlemmen (metod, instanskonstruktor, accessor eller operator) eller anonym funktion som parametern tillhör och initieras med värdet för argumentet som anges i anropet. En värdeparameter upphör normalt att finnas när körningen av funktionstexten slutförs. Men om värdeparametern fångas upp av en anonym funktion (§12.19.6.2) sträcker sig dess livslängd åtminstone tills det ombuds- eller uttrycksträd som skapats från den anonyma funktionen är berättigat till skräpinsamling.

Vid kontroll av bestämd tilldelning betraktas en värdeparameter som ursprungligen tilldelad.

Värdeparametrar beskrivs ytterligare i §15.6.2.2.

Referensparametrar för 9.2.6

En referensparameter är en referensvariabel (§9.7) som kommer till vid anrop av funktionsmedlemmen, delegaten, anonym funktion eller lokal funktion och dess referenser initieras till variabeln som anges som argumentet i det anropet. En referensparameter upphör att finnas när körningen av funktionstexten slutförs. Till skillnad från värdeparametrar ska en referensparameter inte fångas in (§9.7.2.9).

Följande regler för bestämd tilldelning gäller för referensparametrar.

Obs! Reglerna för utdataparametrar skiljer sig åt och beskrivs i (§9.2.7). slutkommentar

  • En variabel ska definitivt tilldelas (§9.4) innan den kan skickas som en referensparameter i en funktionsmedlem eller delegatanrop.
  • I en funktionsmedlem eller anonym funktion betraktas en referensparameter som ursprungligen tilldelad.

Referensparametrar beskrivs ytterligare i §15.6.2.3.3.

9.2.7 Utdataparametrar

En utdataparameter är en referensvariabel (§9.7) som kommer till vid anrop av funktionsmedlemmen, delegaten, anonym funktion eller lokal funktion och dess referenser initieras till variabeln som anges som argumentet i det anropet. En utdataparameter upphör att finnas när körningen av funktionstexten slutförs. Till skillnad från värdeparametrar ska en utdataparameter inte fångas in (§9.7.2.9).

Följande regler för bestämd tilldelning gäller för utdataparametrar.

Obs! Reglerna för referensparametrar skiljer sig åt och beskrivs i (§9.2.6). slutkommentar

  • En variabel behöver inte tilldelas definitivt innan den kan skickas som en utdataparameter i en funktionsmedlem eller ombudsanrop.
  • Efter det normala slutförandet av en funktionsmedlem eller delegatanrop anses varje variabel som skickades som en utdataparameter vara tilldelad i körningssökvägen.
  • I en funktionsmedlem eller anonym funktion betraktas en utdataparameter som ursprungligen inte tilldelad.
  • Varje utdataparameter för en funktionsmedlem, anonym funktion eller lokal funktion ska definitivt tilldelas (§9.4) innan funktionsmedlemmen, anonym funktion eller lokal funktion returnerar normalt.

Utdataparametrar beskrivs ytterligare i §15.6.2.3.4.

9.2.8 Indataparametrar

En indataparameter är en referensvariabel (§9.7) som kommer till vid anrop av funktionsmedlemmen, delegaten, anonym funktion eller lokal funktion och dess referenser initieras till variable_reference som anges som argument i det anropet. En indataparameter upphör att finnas när körningen av funktionstexten slutförs. Till skillnad från värdeparametrar ska en indataparameter inte fångas in (§9.7.2.9).

Följande bestämda tilldelningsregler gäller för indataparametrar.

  • En variabel ska definitivt tilldelas (§9.4) innan den kan skickas som en indataparameter i en funktionsmedlem eller delegatanrop.
  • I en funktionsmedlem, anonym funktion eller lokal funktion betraktas en indataparameter som ursprungligen tilldelad.

Indataparametrar beskrivs ytterligare i §15.6.2.3.2.

9.2.9 Lokala variabler

En lokal variabel deklareras av en local_variable_declaration, declaration_expression, foreach_statement eller specific_catch_clause av en try_statement. En lokal variabel kan också deklareras av vissa typer av mönster(§11). För en foreach_statement är den lokala variabeln en iterationsvariabel (§13.9.5). För en specific_catch_clause är den lokala variabeln en undantagsvariabel (§13.11). En lokal variabel som deklareras av en foreach_statement eller specific_catch_clause anses ursprungligen tilldelad.

En local_variable_declaration kan inträffa i ett block, en for_statement, en switch_block eller en using_statement. En declaration_expression kan inträffa som en out argument_value och som en tuple_element som är målet för en dekonstruktionstilldelning (§12.21.2).

Livslängden för en lokal variabel är den del av programkörningen under vilken lagring garanterat är reserverad för den. Den här livslängden sträcker sig från att ingå i det omfång som det är associerat med, åtminstone tills körningen av det omfånget slutar på något sätt. (Om du anger ett omslutet block, anropar en metod eller ger ett värde från ett iteratorblock pausas, men avslutas inte, körningen av det aktuella omfånget.) Om den lokala variabeln fångas upp av en anonym funktion (§12.19.6.2) sträcker sig dess livslängd åtminstone tills ombuds- eller uttrycksträdet som skapats från den anonyma funktionen, tillsammans med andra objekt som kommer att referera till den insamlade variabeln, är berättigade till skräpinsamling. Om det överordnade omfånget anges rekursivt eller iterativt skapas en ny instans av den lokala variabeln varje gång, och dess initialiserare utvärderas varje gång.

Obs! En lokal variabel instansieras varje gång dess omfång anges. Det här beteendet är synligt för användarkod som innehåller anonyma metoder. slutkommentar

Obs! Livslängden för en iterationsvariabel (§13.9.5) som deklareras av en foreach_statement är en enda iteration av den instruktionen. Varje iteration skapar en ny variabel. slutkommentar

Obs! Den faktiska livslängden för en lokal variabel är implementeringsberoende. En kompilator kan till exempel statiskt fastställa att en lokal variabel i ett block endast används för en liten del av blocket. Med den här analysen kan kompilatorn generera kod som resulterar i att variabelns lagring har en kortare livslängd än dess innehållande block.

Den lagring som en lokal referensvariabel hänvisar till återvinns oberoende av livslängden för den lokala referensvariabeln (§7.9).

slutkommentar

En lokal variabel som introduceras av en local_variable_declaration eller declaration_expression initieras inte automatiskt och har därför inget standardvärde. En sådan lokal variabel anses ursprungligen inte tilldelad.

Obs! En local_variable_declaration som innehåller en initierare är fortfarande ursprungligen inte tilldelad. Körningen av deklarationen beter sig exakt som en tilldelning till variabeln (§9.4.4.5). Använd en variabel innan dess initiering har körts. t.ex. inom själva initieringsuttrycket eller med hjälp av en goto_statement som kringgår initiatorn, är ett kompileringsfel:

goto L;

int x = 1; // never executed

L: x += 1; // error: x not definitely assigned

Inom omfånget för en lokal variabel är det ett kompileringsfel att referera till den lokala variabeln i en textposition som föregår dess deklarator.

slutkommentar

9.2.9.1 Kasseras

Ignorera är en lokal variabel som inte har något namn. Ett ignorerande introduceras av ett deklarationsuttryck (§12.17) med identifieraren _; och skrivs antingen implicit (_ eller var _) eller skrivs uttryckligen (T _).

_ Obs! är en giltig identifierare i många typer av deklarationer. slutkommentar

Eftersom ett ignorerande inte har något namn är den enda referensen till variabeln den representerar uttrycket som introducerar den.

Obs! Ett ignorerande kan dock skickas som ett utdataargument, vilket gör att motsvarande utdataparameter kan ange dess associerade lagringsplats. slutkommentar

Ett ignorerande tilldelas inte från början, så det är alltid ett fel att komma åt dess värde.

Exempel:

_ = "Hello".Length;
(int, int, int) M(out int i1, out int i2, out int i3) { ... }
(int _, var _, _) = M(out int _, out var _, out _);

Exemplet förutsätter att det inte finns någon deklaration av namnet _ i omfånget.

Tilldelningen till _ visar ett enkelt mönster för att ignorera resultatet av ett uttryck. Anropet visar M de olika formerna av borttagningar som är tillgängliga i tupplar och som utdataparametrar.

slutexempel

9.3 Standardvärden

Följande kategorier av variabler initieras automatiskt till deras standardvärden:

  • Statiska variabler.
  • Instansvariabler för klassinstanser.
  • Vektorelement.

Standardvärdet för en variabel beror på typen av variabel och bestäms på följande sätt:

  • För en variabel för en value_type är standardvärdet detsamma som det värde som beräknas av value_type standardkonstruktor (§8.3.3).
  • För en variabel för en reference_type är nullstandardvärdet .

Obs! Initiering av standardvärden görs vanligtvis genom att minneshanteraren eller skräpinsamlaren initierar minnet till all-bits-zero innan det allokeras för användning. Därför är det praktiskt att använda all-bits-zero för att representera null-referensen. slutkommentar

9.4 Bestämd tilldelning

9.4.1 Allmänt

På en viss plats i den körbara koden för en funktionsmedlem eller en anonym funktion sägs en variabel definitivt tilldelas om kompilatorn kan bevisa, genom en viss statisk flödesanalys (§9.4.4), att variabeln har initierats automatiskt eller har varit målet för minst en tilldelning.

Obs! Informellt angivna regler för bestämd tilldelning är:

  • En ursprungligen tilldelad variabel (§9.4.2) anses alltid definitivt tilldelad.
  • En ursprungligen otilldelad variabel (§9.4.3) anses definitivt tilldelad på en viss plats om alla möjliga körningsvägar som leder till den platsen innehåller minst en av följande:
    • En enkel tilldelning (§12.21.2) där variabeln är den vänstra operanden.
    • Ett anropsuttryck (§12.8.9) eller objektskapandeuttryck (§12.8.16.2) som skickar variabeln som en utdataparameter.
    • För en lokal variabel, en lokal variabeldeklaration för variabeln (§13.6.2) som innehåller en variabelinitierare.

Den formella specifikation som ligger till grund för ovanstående informella regler beskrivs i §9.4.2, §9.4.3 och §9.4.4.

slutkommentar

De bestämda tilldelningstillstånden för instansvariabler för en struct_type variabel spåras både individuellt och kollektivt. Utöver de regler som beskrivs i §9.4.2, §9.4.3 och §9.4.4 gäller följande regler för struct_type variabler och deras instansvariabler:

  • En instansvariabel anses definitivt tilldelad om den innehåller struct_type variabel anses definitivt tilldelad.
  • En struct_type variabel anses definitivt tilldelad om var och en av dess instansvariabler anses vara definitivt tilldelad.

En bestämd tilldelning är ett krav i följande kontexter:

  • En variabel ska definitivt tilldelas på varje plats där dess värde erhålls.

    Obs! Detta säkerställer att odefinierade värden aldrig inträffar. slutkommentar

    Förekomsten av en variabel i ett uttryck anses hämta variabelns värde, förutom när

    • variabeln är den vänstra operanden i en enkel tilldelning,
    • variabeln skickas som en utdataparameter, eller
    • variabeln är en struct_type variabel och inträffar som den vänstra operanden för en medlemsåtkomst.
  • En variabel ska definitivt tilldelas på varje plats där den skickas som en referensparameter.

    Obs! Detta säkerställer att funktionsmedlemmen som anropas kan överväga den referensparameter som ursprungligen tilldelades. slutkommentar

  • En variabel ska definitivt tilldelas på varje plats där den skickas som en indataparameter.

    Obs! Detta säkerställer att funktionsmedlemmen som anropas kan överväga den indataparameter som ursprungligen tilldelades. slutkommentar

  • Alla utdataparametrar för en funktionsmedlem ska definitivt tilldelas på varje plats där funktionsmedlemmen returnerar (genom en retursats eller genom körning som når slutet av funktionsmedlemsorganet).

    Obs! Detta säkerställer att funktionsmedlemmar inte returnerar odefinierade värden i utdataparametrar, vilket gör det möjligt för kompilatorn att överväga ett funktionsmedlemsanrop som tar en variabel som en utdataparameter som motsvarar en tilldelning till variabeln. slutkommentar

  • Variabeln this för en struct_type instanskonstruktor ska definitivt tilldelas på varje plats där instanskonstruktorn returnerar.

9.4.2 Ursprungligen tilldelade variabler

Följande kategorier av variabler klassificeras som ursprungligen tilldelade:

  • Statiska variabler.
  • Instansvariabler för klassinstanser.
  • Instansvariabler för ursprungligen tilldelade structvariabler.
  • Vektorelement.
  • Värdeparametrar.
  • Referensparametrar.
  • Indataparametrar.
  • Variabler som deklareras i en catch sats eller en foreach -instruktion.

9.4.3 Ursprungligen otilldelade variabler

Följande kategorier av variabler klassificeras som ursprungligen otilldelade:

  • Instansvariabler för ursprungligen otilldelade structvariabler.
  • Utdataparametrar, inklusive variabeln this för struct-instanskonstruktorer utan konstruktorinitierare.
  • Lokala variabler, förutom de som deklareras i en catch -sats eller en foreach -instruktion.

9.4.4 Exakta regler för att fastställa en bestämd tilldelning

9.4.4.1 Allmänt

För att fastställa att varje använd variabel definitivt tilldelas ska kompilatorn använda en process som motsvarar den som beskrivs i den här underklienten.

Kompilatorn bearbetar brödtexten för varje funktionsmedlem som har en eller flera initialt otilldelade variabler. För varje ursprungligt otilldelad variabel v bestämmer kompilatorn ett definitivt tilldelningstillstånd för v vid var och en av följande punkter i funktionsmedlemmen:

  • I början av varje instruktion
  • Vid slutpunkten (§13.2) för varje instruktion
  • På varje båge som överför kontrollen till en annan instruktion eller till slutpunkten för en -instruktion
  • I början av varje uttryck
  • I slutet av varje uttryck

Det slutgiltiga tilldelningstillståndet för v kan vara antingen:

  • Definitivt tilldelad. Detta indikerar att v har tilldelats ett värde för alla möjliga kontrollflöden till den här punkten.
  • Inte definitivt tilldelad. För tillståndet för en variabel i slutet av ett uttryck av typen boolkan tillståndet för en variabel som inte är definitivt tilldelad (men inte nödvändigtvis) hamna i något av följande undertillstånd:
    • Definitivt tilldelad efter sant uttryck. Det här tillståndet anger att v definitivt tilldelas om det booleska uttrycket utvärderas som sant, men inte nödvändigtvis tilldelas om det booleska uttrycket utvärderas som falskt.
    • Definitivt tilldelad efter falskt uttryck. Det här tillståndet anger att v definitivt tilldelas om det booleska uttrycket utvärderas som falskt, men inte nödvändigtvis tilldelas om det booleska uttrycket utvärderas som sant.

Följande regler styr hur tillståndet för en variabel v bestäms på varje plats.

9.4.4.2 Allmänna regler för instruktioner

  • v tilldelas inte definitivt i början av ett funktionsmedlemsorgan.
  • Det slutgiltiga tilldelningstillståndet för v i början av en annan instruktion bestäms genom att kontrollera tillståndet för bestämd tilldelning av v på alla kontrollflödesöverföringar som är inriktade på början av den instruktionen. Om (och endast om) v definitivt tilldelas för alla sådana kontrollflödesöverföringar tilldelas v definitivt i början av -instruktionen. Uppsättningen möjliga kontrollflödesöverföringar bestäms på samma sätt som för kontroll av instruktionens nåbarhet (§13.2).
  • Det slutgiltiga tilldelningstillståndet för v vid slutpunkten för en block, checked, unchecked, if, while, do, foreachfor, , usinglockeller switch -instruktion bestäms genom att kontrollera tillståndet för bestämd tilldelning av v för alla kontrollflödesöverföringar som riktar sig mot slutpunkten för den instruktionen. Om v definitivt tilldelas för alla sådana kontrollflödesöverföringar tilldelas v definitivt vid slutpunkten för -instruktionen. Annars tilldelas v inte definitivt vid slutpunkten för -instruktionen. Uppsättningen möjliga kontrollflödesöverföringar bestäms på samma sätt som för kontroll av instruktionens nåbarhet (§13.2).

Obs! Eftersom det inte finns några kontrollsökvägar till en icke åtkomlig instruktion tilldelas v definitivt i början av en icke åtkomlig instruktion. slutkommentar

9.4.4.3 Blockinstruktioner, markerade och omarkerade instruktioner

Det slutgiltiga tilldelningstillståndet för v vid kontrollöverföringen till den första instruktionen i instruktionslistan i blocket (eller till blockets slutpunkt, om instruktionslistan är tom) är samma som instruktionen för bestämd tilldelning av v före blocket, checkedeller unchecked -instruktionen.

Uttrycksuttryck för 9.4.4.4

För en uttrycksuttrycksstmt som består av uttrycket expr:

  • v har samma definitivt tilldelningstillstånd i början av uttr som i början av stmt.
  • Om v definitivt tilldelas i slutet av expr, tilldelas det definitivt vid stmt-slutpunkten. Annars tilldelas det inte definitivt vid stmt-slutpunkten.

9.4.4.5 Förklaringsutdrag

  • Om stmt är en deklarationssats utan initierare har v samma tillstånd för bestämd tilldelning vid stmt-slutpunkten som i början av stmt.
  • Om stmt är en deklarationsinstruktuering med initialiserare bestäms tillståndet för bestämd tilldelning för v som om stmt var en instruktionslista, med en tilldelningsuttryck för varje deklaration med en initialiserare (i deklarationsordning).

9.4.4.6 If-instruktioner

För en instruktionsstmt i formuläret:

if ( «expr» ) «then_stmt» else «else_stmt»
  • v har samma definitivt tilldelningstillstånd i början av uttr som i början av stmt.
  • Om v definitivt tilldelas i slutet av expr tilldelas det definitivt på kontrollflödesöverföringen till then_stmt och till antingen else_stmt eller till slutpunkten för stmt om det inte finns någon annan sats.
  • Om v har tillståndet "definitivt tilldelat efter sant uttryck" i slutet av expr, tilldelas det definitivt på kontrollflödesöverföringen till then_stmt och tilldelas inte definitivt på kontrollflödesöverföringen till antingen else_stmt eller till stmt-slutpunkten om det inte finns någon annan sats.
  • Om v har tillståndet "definitivt tilldelat efter falskt uttryck" i slutet av expr, tilldelas det definitivt på kontrollflödesöverföringen till else_stmt och tilldelas inte definitivt på kontrollflödesöverföringen till then_stmt. Det tilldelas definitivt i slutet av stmt om och endast om det definitivt tilldelas vid slutpunkten för then_stmt.
  • Annars anses v inte definitivt tilldelas för kontrollflödesöverföringen till antingen then_stmt eller else_stmt, eller till slutpunkten för stmt om det inte finns någon annan sats.

9.4.4.7 Switch-instruktioner

För en switch instruktionsstmt med ett kontrollerande uttryck uttr:

Det definitiva tilldelningstillståndet för v i början av uttr är detsamma som tillståndet för v i början av stmt.

Det definitiva tilldelningstillståndet för v i början av ett ärendes skyddsklausul är

  • Om v är en mönstervariabel som deklareras i switch_label: "definitivt tilldelad".
  • Om växeletiketten som innehåller skyddssatsen (§13.8.3) inte kan nås: "definitivt tilldelad".
  • I annat fall är tillståndet för v samma som tillståndet för v efter uttr.

Exempel: Den andra regeln eliminerar behovet av att kompilatorn utfärdar ett fel om en otilldelad variabel nås i oåtkomlig kod. Tillståndet för b är "definitivt tilldelad" i den oåtkomliga växeletiketten case 2 when b.

bool b;
switch (1) 
{
    case 2 when b: // b is definitely assigned here.
    break;
}

slutexempel

Det slutgiltiga tilldelningstillståndet för v på kontrollflödesöverföringen till en lista över reglageblockering som kan nås är

  • Om kontrollöverföringen berodde på ett "goto-fall" eller "goto default"-instruktion är tillståndet för v detsamma som tillståndet i början av den "goto"-instruktionen.
  • Om kontrollöverföringen berodde på default växelns etikett är tillståndet v samma som tillståndet för v efter uttr.
  • Om kontrollöverföringen berodde på en oåtkomlig växeletikett tilldelas tillståndet v "definitivt".
  • Om kontrollöverföringen berodde på en kopplingsetikett som kan nås med en skyddssats är tillståndet för v detsamma som tillståndet för v efter skyddssatsen.
  • Om kontrollöverföringen berodde på en kopplingsetikett som kan nås utan en skyddssats, är tillståndet för v
    • Om v är en mönstervariabel som deklareras i switch_label: "definitivt tilldelad".
    • I annat fall är tillståndet för v detsamma som statistiken för v efter uttr.

En följd av dessa regler är att en mönstervariabel som deklareras i en switch_label "inte definitivt tilldelas" i satserna i switchavsnittet om den inte är den enda nåbara växeletiketten i avsnittet.

Exempel:

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

slutexempel

9.4.4.8 While-instruktioner

För en instruktionsstmt i formuläret:

while ( «expr» ) «while_body»
  • v har samma definitivt tilldelningstillstånd i början av uttr som i början av stmt.
  • Om v definitivt tilldelas i slutet av expr tilldelas det definitivt på kontrollflödesöverföringen till while_body och till slutpunkten för stmt.
  • Om v har tillståndet "definitivt tilldelat efter sant uttryck" i slutet av expr, tilldelas det definitivt på kontrollflödesöverföringen till while_body, men tilldelas inte definitivt vid stmt-slutpunkten.
  • Om v har tillståndet "definitivt tilldelad efter falskt uttryck" i slutet av uttr, tilldelas det definitivt på kontrollflödesöverföringen till slutpunkten för stmt, men tilldelas inte definitivt på kontrollflödesöverföringen till while_body.

9.4.4.9 Do-instruktioner

För en instruktionsstmt i formuläret:

do «do_body» while ( «expr» ) ;
  • v har samma definitivt tilldelningstillstånd för kontrollflödesöverföringen från början av stmt till do_body som i början av stmt.
  • v har samma definitivt tilldelningstillstånd i början av uttr som vid slutpunkten för do_body.
  • Om v definitivt tilldelas i slutet av expr tilldelas det definitivt på kontrollflödesöverföringen till stmt-slutpunkten.
  • Om v har tillståndet "definitivt tilldelat efter falskt uttryck" i slutet av expr, tilldelas det definitivt på kontrollflödesöverföringen till slutpunkten för stmt, men tilldelas inte definitivt på kontrollflödesöverföringen till do_body.

9.4.4.10 För -instruktioner

För en instruktion i formuläret:

for ( «for_initializer» ; «for_condition» ; «for_iterator» )
    «embedded_statement»

kontroll av slutgiltig tilldelning görs som om instruktionen skrevs:

{
    «for_initializer» ;
    while ( «for_condition» )
    {
        «embedded_statement» ;
        LLoop: «for_iterator» ;
    }
}

med continue instruktioner som riktar sig till -instruktionen for som översätts till goto -instruktioner som riktar sig mot etiketten LLoop. Om for_condition utelämnas från -instruktionen for fortsätter utvärderingen av slutgiltig tilldelning som om for_condition ersattes med sant i ovanstående expansion.

9.4.4.11 Bryt, fortsätt och goto-instruktioner

Det slutgiltiga tilldelningstillståndet för v på kontrollflödesöverföringen som orsakas av en break, continue, eller goto -instruktion är samma som det definitiva tilldelningstillståndet för v i början av -instruktionen.

9.4.4.12 Kasta instruktioner

För en instruktionsstmt i formuläret:

throw «expr» ;

det bestämda tilldelningstillståndet för v i början av uttr är detsamma som tillståndet för bestämd tilldelning av v i början av stmt.

9.4.4.13 Returinstruktioner

För en instruktionsstmt i formuläret:

return «expr» ;
  • Det definitiva tilldelningstillståndet för v i början av uttr är detsamma som tillståndet för bestämd tilldelning av v i början av stmt.
  • Om v är en utdataparameter ska den definitivt tilldelas antingen:
    • efter uttr
    • eller i slutet av finally blocket för en tryfinally-eller trycatch--finally som omger -instruktionen.return

För en instruktionsstmt i formuläret:

return ;
  • Om v är en utdataparameter ska den definitivt tilldelas antingen:
    • före stmt
    • eller i slutet av finally blocket för en tryfinally-eller trycatch--finally som omger -instruktionen.return

9.4.4.14 Try-catch-instruktioner

För en instruktionsstmt i formuläret:

try «try_block»
catch ( ... ) «catch_block_1»
...
catch ( ... ) «catch_block_n»
  • Det definitiva tilldelningstillståndet för v i början av try_block är detsamma som tillståndet för bestämd tilldelning av v i början av stmt.
  • Det definitiva tilldelningstillståndet för v i början av catch_block_i (för alla i) är detsamma som det bestämda tilldelningstillståndet för v i början av stmt.
  • Det definitiva tilldelningstillståndet för v vid slutpunkten av stmt tilldelas definitivt om (och endast om) v definitivt tilldelas i slutet av try_block och varje catch_block_i (för varje i från 1 till n).

9.4.4.15 Try-finally-instruktioner

För en instruktionsstmt i formuläret:

try «try_block» finally «finally_block»
  • Det definitiva tilldelningstillståndet för v i början av try_block är detsamma som tillståndet för bestämd tilldelning av v i början av stmt.
  • Det slutgiltiga tilldelningstillståndet för v i början av finally_block är detsamma som det bestämda tilldelningstillståndet för v i början av stmt.
  • Det slutgiltiga tilldelningstillståndet för v vid stmt-slutpunkten tilldelas definitivt om (och endast om) minst något av följande är sant:
    • v tilldelas definitivt i slutet av try_block
    • v tilldelas definitivt i slutet av finally_block

Om en kontrollflödesöverföring (till exempel en goto -instruktion) görs som börjar inom try_block och slutar utanför try_block, anses v också definitivt tilldelas den kontrollflödesöverföringen om v definitivt tilldelas vid slutpunkten för finally_block. (Detta är inte bara om – om v definitivt tilldelas av en annan anledning för denna kontrollflödesöverföring anses det fortfarande vara definitivt tilldelat.)

9.4.4.16 Try-catch-finally-instruktioner

För en instruktion i formuläret:

try «try_block»
catch ( ... ) «catch_block_1»
...
catch ( ... ) «catch_block_n»
finally «finally_block»

slutgiltig tilldelningsanalys görs som om -instruktionen var en try-finally instruktion som omsluter en try-catch -instruktion:

try
{
    try «try_block»
    catch ( ... ) «catch_block_1»
    ...
    catch ( ... ) «catch_block_n»
}
finally «finally_block»

Exempel: I följande exempel visas hur de olika blocken i en try instruktion (§13.11) påverkar en bestämd tilldelning.

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

slutexempel

9.4.4.17 Foreach-instruktioner

För en instruktionsstmt i formuläret:

foreach ( «type» «identifier» in «expr» ) «embedded_statement»
  • Det definitiva tilldelningstillståndet för v i början av uttr är detsamma som tillståndet för v i början av stmt.
  • Det definitiva tilldelningstillståndet för v vid kontrollflödesöverföringen till embedded_statement eller till slutpunkten för stmt är samma som tillståndet för v i slutet av uttr.

9.4.4.18 Använda instruktioner

För en instruktionsstmt i formuläret:

using ( «resource_acquisition» ) «embedded_statement»
  • Det definitiva tilldelningstillståndet för v i början av resource_acquisition är detsamma som tillståndet för v i början av stmt.
  • Det slutgiltiga tilldelningstillståndet för v vid kontrollflödesöverföringen till embedded_statement är detsamma som tillståndet för v i slutet av resource_acquisition.

9.4.4.19 Låssatser

För en instruktionsstmt i formuläret:

lock ( «expr» ) «embedded_statement»
  • Det definitiva tilldelningstillståndet för v i början av uttr är detsamma som tillståndet för v i början av stmt.
  • Det slutgiltiga tilldelningstillståndet för v vid kontrollflödesöverföringen till embedded_statement är detsamma som tillståndet för v i slutet av uttr.

9.4.4.20 Avkastningsrapporter

För en instruktionsstmt i formuläret:

yield return «expr» ;
  • Det definitiva tilldelningstillståndet för v i början av uttr är detsamma som tillståndet för v i början av stmt.
  • Det slutgiltiga tilldelningstillståndet för v i slutet av stmt är detsamma som tillståndet för v i slutet av uttr.

En yield break instruktion har ingen effekt på tillståndet för bestämd tilldelning.

9.4.4.21 Allmänna regler för konstanta uttryck

Följande gäller för alla konstanta uttryck och prioriteras framför alla regler från följande avsnitt som kan gälla:

För ett konstant uttryck med värdet true:

  • Om v definitivt tilldelas före uttrycket tilldelas v definitivt efter uttrycket.
  • Annars tilldelas v "definitivt efter falskt uttryck" efter uttrycket.

Exempel:

int x;
if (true) {}
else
{
    Console.WriteLine(x);
}

slutexempel

För ett konstant uttryck med värdet false:

  • Om v definitivt tilldelas före uttrycket tilldelas v definitivt efter uttrycket.
  • Annars tilldelas v "definitivt efter true expression" efter uttrycket.

Exempel:

int x;
if (false)
{
    Console.WriteLine(x);
}

slutexempel

För alla andra konstanta uttryck är tillståndet för bestämd tilldelning av v efter uttrycket detsamma som tillståndet för bestämd tilldelning av v före uttrycket.

9.4.4.22 Allmänna regler för enkla uttryck

Följande regel gäller för dessa typer av uttryck: literaler (§12.8.2), enkla namn (§12.8.4), medlemsåtkomstuttryck (§12.8.7), icke-indexerade basåtkomstuttryck (§12 .8.14), typeof uttryck (§12.8.17), standardvärdeuttryck (§12.8.20), nameof uttryck (§12.8.22) och deklarationsuttryck (§12.17).

  • Det slutgiltiga tilldelningstillståndet för v i slutet av ett sådant uttryck är detsamma som tillståndet för bestämd tilldelning av v i början av uttrycket.

9.4.4.23 Allmänna regler för uttryck med inbäddade uttryck

Följande regler gäller för dessa typer av uttryck: parentesiserade uttryck (§12.8.5), tupppeluttryck (§12.8.6), elementåtkomstuttryck (§12.8.11), grundläggande åtkomstuttryck med indexering (§12.8.14), inkrements- och dementuttryck (§12.8.15, §12.9.6), gjutna uttryck (§12.9.7), unary +, -, ~, * uttryck, binär +, -, *, /, %, <<, >>, , <, <= >, >=, ==, !=, is, as, &, , |uttryck ^ (§12.10, §12.11, §12.12, §12.13), sammansatta tilldelningsuttryck (§12.21.4) checked och unchecked uttryck (§12.8.19), matris- och delegatskapandeuttryck (§12.8.16) och await uttryck (§12.9.8).

Vart och ett av dessa uttryck har en eller flera underuttryck som villkorslöst utvärderas i en fast ordning.

Exempel: Binäroperatorn % utvärderar operatorns vänstra sida och sedan den högra sidan. En indexeringsåtgärd utvärderar det indexerade uttrycket och utvärderar sedan vart och ett av indexuttrycken i ordning från vänster till höger. slutexempel

För ett uttryck expr, som har underuttryck expr₁, expr₂, ..., exprₓ, utvärderas i den ordningen:

  • Det definitiva tilldelningstillståndet för v i början av expr₁ är detsamma som det bestämda tilldelningstillståndet i början av expr.
  • Det definitiva tilldelningstillståndet för v i början av expri (i större än ett) är detsamma som det bestämda tilldelningstillståndet i slutet av expri₋₁.
  • Det definitiva tilldelningstillståndet för v i slutet av uttr är detsamma som tillståndet för bestämd tilldelning i slutet av exprₓ.

9.4.4.24 Anropsuttryck och uttryck för objektskapande

Om den metod som ska anropas är en partiell metod som inte har någon partiell metoddeklaration, eller är en villkorsstyrd metod för vilken anropet utelämnas (§22.5.3.2), är det slutgiltiga tilldelningstillståndet för v efter anropet detsamma som det slutgiltiga tilldelningstillståndet för v före anropet. I annat fall gäller följande regler:

För ett anropsuttryck i formuläret:

«primary_expression» ( «arg₁», «arg₂», … , «argₓ» )

eller ett uttryck för att skapa objekt i formuläret:

new «type» ( «arg₁», «arg₂», … , «argₓ» )
  • För ett anropsuttryck är det bestämda tilldelningstillståndet för v före primary_expression samma som tillståndet för v före uttr.
  • För ett anropsuttryck är det bestämda tilldelningstillståndet v före arg₁ detsamma som tillståndet för v efter primary_expression.
  • För ett objektskapandeuttryck är det bestämda tilldelningstillståndet v före arg₁ samma som tillståndet för v före uttr.
  • För varje argument argi bestäms det definitiva tilldelningstillståndet för v efter argi av reglerna för normala uttryck och ignorerar eventuella in, outeller ref modifierare.
  • För varje argument argi för någon i större än en, är det bestämda tilldelningstillståndet v före argi detsamma som tillståndet för v efter argi₋₁.
  • Om variabeln v skickas som ett out argument (dvs. ett argument i formuläret "ut v") i något av argumenten tilldelas definitivt tillståndet v efter expr . Annars är tillståndet för v efter expr detsamma som tillståndet för v efter argₓ.
  • För matrisinitierare (§12.8.16.5), objektinitierare (§12.8.16.3), insamlingsinitierare (§12.8.16.4) och anonyma objektinitierare (§12.8.16.7), bestäms det bestämda tilldelningstillståndet av den expansion som dessa konstruktioner definieras i termer av.

9.4.4.25 Enkla tilldelningsuttryck

Låt uppsättningen tilldelningsmål i ett uttryck definieras på följande sätt:

  • Om e är ett tupppeluttryck är tilldelningsmålen i e en union av tilldelningsmålen för elementen i e.
  • Annars är tilldelningsmålen e.

För ett uttryck i formulärets uttr :

«expr_lhs» = «expr_rhs»
  • Det slutgiltiga tilldelningstillståndet för v före expr_lhs är detsamma som tillståndet för bestämd tilldelning av v före uttr.
  • Det slutgiltiga tilldelningstillståndet för v före expr_rhs är detsamma som tillståndet för bestämd tilldelning av v efter expr_lhs.
  • Om v är ett tilldelningsmål för expr_lhs tilldelas definitivt tillståndet för den slutgiltiga tilldelningen av v efter expr . Annars, om tilldelningen sker inom instanskonstruktorn av en struct-typ, och v är det dolda bakgrundsfältet för en automatiskt implementerad egenskap P på instansen som skapas, och en egenskapsåtkomst som anger P är ett mål för expr_lhs, tilldelas definitivt tillståndet för definitivt tilldelning av v efter expr. I annat fall är tillståndet för bestämd tilldelning av v efter uttr samma som tillståndet för bestämd tilldelning av v efter expr_rhs.

Exempel: I följande kod

class A
{
    static void F(int[] arr)
    {
        int x;
        arr[x = 1] = x; // ok
    }
}

variabeln x anses definitivt tilldelad efter arr[x = 1] att ha utvärderats som vänster sida av den andra enkla tilldelningen.

slutexempel

9.4.4.26 &-uttryck

För ett uttryck i formulärets uttr :

«expr_first» && «expr_second»
  • Det slutgiltiga tilldelningstillståndet för v före expr_first är detsamma som tillståndet för bestämd tilldelning av v före uttr.
  • Det slutgiltiga tilldelningstillståndet för v före expr_second tilldelas definitivt om och endast om tillståndet för v efter expr_first antingen definitivt tilldelas eller "definitivt tilldelas efter sant uttryck". Annars tilldelas den inte definitivt.
  • Det slutgiltiga tilldelningstillståndet för v efter uttr bestäms av:
    • Om tillståndet för v efter expr_first definitivt tilldelas tilldelas tillståndet v efter expr definitivt.
    • Annars, om tillståndet för v efter expr_second definitivt tilldelas, och tillståndet för v efter expr_first "definitivt tilldelas efter falskt uttryck", tilldelas tillståndet v efter uttr definitivt.
    • Annars, om tillståndet för v efter expr_second definitivt tilldelas eller "definitivt tilldelas efter sant uttryck", är tillståndet för v efter expr "definitivt tilldelad efter sant uttryck".
    • Annars, om tillståndet för v efter expr_first "definitivt tilldelas efter falskt uttryck", och tillståndet för v efter expr_second är "definitivt tilldelad efter falskt uttryck", tilldelas tillståndet v efter expr "definitivt efter falskt uttryck".
    • Annars tilldelas inte tillståndet för v efter expr definitivt.

Exempel: I följande kod

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

variabeln i anses definitivt tilldelad i en av de inbäddade uttrycken i en if -instruktion men inte i den andra. I -instruktionen if i -metoden Ftilldelas variabeln i definitivt i den första inbäddade -instruktionen eftersom körningen av uttrycket (i = y) alltid föregår körningen av den här inbäddade instruktionen. Variabeln i tilldelas däremot inte definitivt i den andra inbäddade -instruktionen, eftersom x >= 0 den kan ha testat false, vilket resulterar i att variabeln iär otilldelad.

slutexempel

9.4.4.27 || Uttryck

För ett uttryck i formulärets uttr :

«expr_first» || «expr_second»
  • Det slutgiltiga tilldelningstillståndet för v före expr_first är detsamma som tillståndet för bestämd tilldelning av v före uttr.
  • Det slutgiltiga tilldelningstillståndet för v före expr_second tilldelas definitivt om och endast om tillståndet för v efter expr_first antingen definitivt tilldelas eller "definitivt tilldelas efter sant uttryck". Annars tilldelas den inte definitivt.
  • Instruktionen för bestämd tilldelning av v efter uttr bestäms av:
    • Om tillståndet för v efter expr_first definitivt tilldelas tilldelas tillståndet v efter expr definitivt.
    • Annars, om tillståndet för v efter expr_second definitivt tilldelas, och tillståndet för v efter expr_first "definitivt tilldelas efter sant uttryck", tilldelas tillståndet för v efter expr definitivt.
    • Annars, om tillståndet för v efter expr_second definitivt tilldelas eller "definitivt tilldelas efter falskt uttryck", så är tillståndet v efter expr "definitivt tilldelad efter falskt uttryck".
    • Annars, om tillståndet för v efter expr_first "definitivt tilldelas efter sant uttryck", och tillståndet för v efter expr_ andra är "definitivt tilldelad efter sant uttryck", är tillståndet för v efter expr "definitivt tilldelad efter sant uttryck".
    • Annars tilldelas inte tillståndet för v efter expr definitivt.

Exempel: I följande kod

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

variabeln i anses definitivt tilldelad i en av de inbäddade uttrycken i en if -instruktion men inte i den andra. I -instruktionen if i -metoden Gtilldelas variabeln i definitivt i den andra inbäddade -instruktionen eftersom körningen av uttrycket (i = y) alltid föregår körningen av den här inbäddade instruktionen. Variabeln i tilldelas däremot inte definitivt i den första inbäddade -instruktionen, eftersom x >= 0 den kan ha testat sant, vilket resulterar i att variabeln iinte tilldelas.

slutexempel

9.4.4.28 ! Uttryck

För ett uttryck i formulärets uttr :

! «expr_operand»
  • Det slutgiltiga tilldelningstillståndet för v före expr_operand är detsamma som tillståndet för bestämd tilldelning av v före uttr.
  • Det slutgiltiga tilldelningstillståndet för v efter uttr bestäms av:
    • Om tillståndet v för efter expr_operand definitivt tilldelas, tilldelas tillståndet v efter expr definitivt.
    • Annars, om tillståndet v för efter expr_operand är "definitivt tilldelad efter falskt uttryck", är tillståndet v efter expr "definitivt tilldelat efter sant uttryck".
    • Annars, om tillståndet v för efter expr_operand "definitivt tilldelas efter sant uttryck", så är tillståndet för v efter expr "definitivt tilldelad efter falskt uttryck".
    • I annat fall tilldelas inte tillståndet v för efterpr definitivt.

9.4.4.29 ?? Uttryck

För ett uttryck i formulärets uttr :

«expr_first» ?? «expr_second»
  • Det slutgiltiga tilldelningstillståndet för v före expr_first är detsamma som tillståndet för bestämd tilldelning av v före uttr.
  • Det slutgiltiga tilldelningstillståndet för v före expr_second är detsamma som tillståndet för bestämd tilldelning av v efter expr_first.
  • Instruktionen för bestämd tilldelning av v efter uttr bestäms av:
    • Om expr_first är ett konstant uttryck (§12.23) med värdet null, är tillståndet för v efter expr samma som tillståndet för v efter expr_second.
    • I annat fall är tillståndet för v efter uttr samma som tillståndet för bestämd tilldelning av v efter expr_first.

9.4.4.30 ?: uttryck

För ett uttryck i formulärets uttr :

«expr_cond» ? «expr_true» : «expr_false»
  • Det slutgiltiga tilldelningstillståndet för v före expr_cond är detsamma som tillståndet för v före uttr.
  • Det slutgiltiga tilldelningstillståndet för v före expr_true tilldelas definitivt om tillståndet för v efter expr_cond definitivt tilldelas eller "definitivt tilldelas efter sant uttryck".
  • Det slutgiltiga tilldelningstillståndet för v före expr_false tilldelas definitivt om tillståndet v efter expr_cond definitivt tilldelas eller "definitivt tilldelas efter falskt uttryck".
  • Det slutgiltiga tilldelningstillståndet för v efter uttr bestäms av:
    • Om expr_cond är ett konstant uttryck (§12.23) med värde true är tillståndet v efter expr detsamma som tillståndet för v efter expr_true.
    • Annars, om expr_cond är ett konstant uttryck (§12.23) med värde false är tillståndet för v efter expr detsamma som tillståndet för v efter expr_false.
    • Annars, om tillståndet för v efter expr_true definitivt tilldelas och tillståndet för v efter expr_false definitivt tilldelas, tilldelas tillståndet v efter expr definitivt.
    • Annars tilldelas inte tillståndet för v efter expr definitivt.

9.4.4.31 Anonyma funktioner

För en lambda_expression eller anonymous_method_expression expr med en brödtext (antingen block eller uttryck) brödtext:

  • Det bestämda tilldelningstillståndet för en parameter är detsamma som för en parameter för en namngiven metod (§9.2.6, §9.2.7, §9.2.8).
  • Det definitiva tilldelningstillståndet för en yttre variabel v före brödtexten är samma som tillståndet för v före uttr. Det vill säga att det definitiva tilldelningstillståndet för yttre variabler ärvs från kontexten för den anonyma funktionen.
  • Det definitiva tilldelningstillståndet för en yttre variabel v efter uttr är samma som tillståndet för v före uttr.

Exempel: Exemplet

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

genererar ett kompileringsfel eftersom max inte definitivt tilldelas där den anonyma funktionen deklareras.

slutexempel

Exempel: Exemplet

class A
{
    delegate void D();
    void F()
    {
        int n;
        D d = () => { n = 1; };
        d();
        // Error, n is not definitely assigned
        Console.WriteLine(n);
    }
}

genererar också ett kompileringsfel eftersom tilldelningen till n i den anonyma funktionen inte påverkar tillståndet n för bestämd tilldelning utanför den anonyma funktionen.

slutexempel

9.4.4.32 Kasta uttryck

För ett uttryck i formulärets uttr :

throwthrown_expr

  • Det definitiva tilldelningstillståndet för v före thrown_expr är samma som tillståndet för v före uttr.
  • Det definitiva tilldelningstillståndet för v efter uttr är "definitivt tilldelat".

9.4.4.33 Regler för variabler i lokala funktioner

Lokala funktioner analyseras i kontexten för den överordnade metoden. Det finns två sökvägar för kontrollflöden som är viktiga för lokala funktioner: funktionsanrop och delegatkonverteringar.

En bestämd tilldelning för brödtexten för varje lokal funktion definieras separat för varje anropsplats. Vid varje anrop anses variabler som fångas upp av den lokala funktionen definitivt tilldelas om de definitivt tilldelades vid anropspunkten. Det finns också en kontrollflödessökväg till den lokala funktionstexten vid den här tidpunkten och anses vara nåbar. Efter ett anrop till den lokala funktionen anses insamlade variabler som definitivt tilldelades vid varje kontrollpunkt som lämnar funktionen (return instruktioner, yield uttryck, await uttryck) definitivt tilldelas efter anropsplatsen.

Ombudskonverteringar har en kontrollflödessökväg till den lokala funktionstexten. Insamlade variabler tilldelas definitivt för brödtexten om de definitivt tilldelas före konverteringen. Variabler som tilldelats av den lokala funktionen anses inte vara tilldelade efter konverteringen.

Obs! Ovanstående innebär att organ analyseras på nytt för bestämd tilldelning vid varje lokal funktionsanrop eller delegatkonvertering. Kompilatorer behöver inte analysera om brödtexten för en lokal funktion vid varje anrop eller delegatkonvertering. Implementeringen måste ge resultat som motsvarar den beskrivningen. slutkommentar

Exempel: I följande exempel visas en bestämd tilldelning för insamlade variabler i lokala funktioner. Om en lokal funktion läser en insamlad variabel innan den skrivs måste den insamlade variabeln definitivt tilldelas innan den lokala funktionen anropas. Den lokala funktionen F1 läser s utan att tilldela den. Det är ett fel om F1 anropas innan s tilldelas definitivt. F2 tilldelas i innan du läser den. Det kan anropas innan i tilldelas definitivt. Dessutom F3 kan anropas efter F2 eftersom s2 är definitivt tilldelad i F2.

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

slutexempel

9.4.4.34 is-pattern-uttryck

För ett uttryck i formulärets uttr :

expr_operand är mönster

  • Det slutgiltiga tilldelningstillståndet för v före expr_operand är detsamma som tillståndet för bestämd tilldelning av v före uttr.
  • Om variabeln "v" deklareras i mönster tilldelas tillståndet "v" efter uttr "definitivt när det är sant".
  • Annars är det definitiva tilldelningstillståndet för "v" efter uttr samma som det definitiva tilldelningstillståndet "v" efter expr_operand.

9.5 Variabelreferenser

En variable_reference är ett uttryck som klassificeras som en variabel. En variable_reference anger en lagringsplats som kan nås både för att hämta det aktuella värdet och för att lagra ett nytt värde.

variable_reference
    : expression
    ;

Obs! I C och C++kallas en variable_reference för en lvalue. slutkommentar

9.6 Atomitet av variabelreferenser

Läsningar och skrivningar av följande datatyper ska vara atomiska: bool, char, byte, sbyte, short, ushort, uint, int, floatoch referenstyper. Dessutom ska läsningar och skrivningar av uppräkningstyper med en underliggande typ i föregående lista också vara atomiska. Läsningar och skrivningar av andra typer, inklusive long, ulong, doubleoch decimal, samt användardefinierade typer, behöver inte vara atomiska. Förutom biblioteksfunktionerna som utformats för detta ändamål finns det ingen garanti för atomisk läs-ändra-skrivning, till exempel vid inkrement eller minskning.

9.7 Referensvariabler och returer

9.7.1 Allmänt

En referensvariabel är en variabel som refererar till en annan variabel, kallad referenten (§9.2.6). En referensvariabel är en lokal variabel som deklarerats ref med modifieraren.

En referensvariabel lagrar en variable_reference (§9.5) till referensen och inte värdet för dess referenser. När en referensvariabel används där ett värde krävs returneras dess referensvärde. På samma sätt när en referensvariabel är målet för en tilldelning är det referensen som tilldelas till. Variabeln som en referensvariabel refererar till, d.v.s. den lagrade variable_reference för referensen, kan ändras med hjälp av en referenstilldelning (= ref).

Exempel: I följande exempel visas en lokal referensvariabel vars referens är ett element i en matris:

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

slutexempel

En referensretur är den variable_reference som returneras från en return-by-ref-metod (§15.6.1). Den här variable_reference är referensreturens referens.

Exempel: I följande exempel visas en referensretur vars referens är ett element i ett matrisfält:

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

slutexempel

9.7.2 Referenssäkra kontexter

9.7.2.1 Allmänt

Alla referensvariabler följer säkerhetsregler som säkerställer att referensvariabelns referenssäkerhetskontext inte är större än referenssäkerhetskontexten för referensreferensen.

Obs! Begreppet säker kontext definieras i (§16.4.12), tillsammans med tillhörande begränsningar. slutkommentar

För alla variabler är ref-safe-context för variabeln kontexten där en variable_reference (§9.5) till variabeln är giltig. Referensvariabelns referensvariabel ska ha en referenssäker kontext som är minst lika bred som referensvariabelns referenssäkerhetskontext.

Obs! Kompilatorn avgör referenssäker kontexten genom en statisk analys av programtexten. Referenssäker kontexten återspeglar livslängden för en variabel vid körning. slutkommentar

Det finns tre referenssäkra kontexter:

  • declaration-block: Ref-safe-context för en variable_reference till en lokal variabel (§9.2.9) är den lokala variabelns omfång (§13.6.2), inklusive kapslade inbäddade uttrycki det omfånget.

    En variable_reference till en lokal variabel är en giltig referens för en referensvariabel endast om referensvariabeln deklareras inom referens-safe-kontexten för variabeln.

  • function-member: I en funktion har en variable_reference till något av följande en referenssäker kontext av funktionsmedlem:

    • Värdeparametrar (§15.6.2.2) för en funktionsmedlemsdeklaration, inklusive implicita this klassmedlemsfunktioner, och
    • Den implicita referensparametern (ref) (§15.6.2.3.3) this för en struct-medlemsfunktion, tillsammans med dess fält.

    En variable_reference med referenssäker kontext för funktionsmedlem är endast en giltig referens om referensvariabeln deklareras i samma funktionsmedlem.

  • caller-context: I en funktion har en variable_reference till något av följande en referens-safe-context för anroparkontext:

    • Referensparametrar (§9.2.6) förutom implicita this för en struct-medlemsfunktion;
    • Medlemsfält och element i sådana parametrar.
    • Medlemsfält för parametrar av klasstyp; och
    • Element av parametrar av matristyp.

En variable_reference med referenssäker kontext för anroparkontext kan vara referensen för en referensretur.

Dessa värden utgör en kapslingsrelation från smalaste (deklarationsblock) till bredast (anroparkontext). Varje kapslat block representerar en annan kontext.

Exempel: Följande kod visar exempel på de olika referenssäkra kontexterna. Deklarationerna visar ref-safe-context för att en referens ska vara initieringsuttrycket för en ref variabel. Exemplen visar referens-safe-context för en referensretur:

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

slutexempel.

Exempel: För struct typer skickas den implicita this parametern som en referensparameter. Referens-safe-kontexten för fält av en struct typ som funktionsmedlem förhindrar att dessa fält returneras med referensretur. Den här regeln förhindrar följande kod:

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

slutexempel.

9.7.2.2 Lokal variabel referenssäker kontext

För en lokal variabel v:

  • Om v är en referensvariabel är dess ref-safe-context samma som ref-safe-context för dess initieringsuttryck.
  • Annars är dess referens-safe-context declaration-block.

9.7.2.3 Parameter ref safe context

För en parameter p:

  • Om p är en referens- eller indataparameter är dess referens-safe-context anroparkontexten. Om p är en indataparameter kan den inte returneras som skrivbar ref men kan returneras som ref readonly.
  • Om p är en utdataparameter är dess referens-safe-context anroparkontexten.
  • Annars, om p är parametern this av en struct-typ, är dess ref-safe-context function-member.
  • Annars är parametern en värdeparameter och dess referens-safe-context är funktionsmedlemmen.

9.7.2.4 Fält ref säker kontext

För en variabel som anger en referens till ett fält, e.F:

  • Om e är av en referenstyp är dess referens-safe-context anroparen-kontexten.
  • Annars, om e är av en värdetyp, är dess ref-safe-context samma som ref-safe-context för e.

9.7.2.5 Operatorer

Den villkorsstyrda operatorn (§12.18), c ? ref e1 : ref e2, och referenstilldelningsoperatorn = ref e (§12.21.1) har referensvariabler som operander och ger en referensvariabel. För dessa operatorer är ref-safe-context för resultatet den smalaste kontexten bland ref-safe-contexts för alla ref operander.

9.7.2.6 Funktionsanrop

För en variabel c som härrör från en referensreturfunktionsanrop är dess referenssäkra kontext den smalaste av följande kontexter:

  • Anroparkontexten.
  • Ref-safe-context för alla ref, outoch in argumentuttryck (exklusive mottagaren).
  • För varje indataparameter, om det finns ett motsvarande uttryck som är en variabel och det finns en identitetskonvertering mellan variabeltypen och typen av parameter, variabelns ref-safe-context, annars den närmaste omslutande kontexten.
  • Safe-context (§16.4.12) av alla argumentuttryck (inklusive mottagaren).

Exempel: den sista punkten är nödvändig för att hantera kod, till exempel

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

slutexempel

Ett egenskapsanrop och ett indexeringsanrop (antingen get eller set) behandlas som ett funktionsanrop för den underliggande accessorn av ovanstående regler. Ett lokalt funktionsanrop är ett funktionsanrop.

9.7.2.7 Värden

Ett värdes referens-safe-context är den närmaste omslutande kontexten.

Obs! Detta inträffar i ett anrop, till exempel M(ref d.Length) var d är av typen dynamic. Den är också konsekvent med argument som motsvarar indataparametrar. slutkommentar

9.7.2.8 Konstruktoranrop

Ett new uttryck som anropar en konstruktor följer samma regler som ett metodanrop (§9.7.2.6) som anses returnera den typ som skapas.

9.7.2.9 Begränsningar för referensvariabler

  • Varken en referensparameter eller en utdataparameter, en indataparameter eller en ref lokal parameter eller en lokal av en ref struct typ ska fångas upp av lambda-uttryck eller lokal funktion.
  • Varken en referensparameter, en utdataparameter eller en indataparameter eller en parameter av en ref struct typ ska vara ett argument för en iteratormetod eller en async metod.
  • Varken en ref lokal eller en lokal av en ref struct typ ska vara i sitt sammanhang vid tidpunkten för en yield return instruktion eller ett await uttryck.
  • För omtilldelning e1 = ref e2av e2 referens ska referenssäker kontexten för vara minst lika omfattande som referens-safe-context för e1.
  • För ett referensreturmeddelande return ref e1ska referenssäker kontexten e1 för vara anroparkontexten.