Condividi tramite


9 Variabili

9.1 Generale

Le variabili rappresentano le posizioni di archiviazione. Ogni variabile ha un tipo che determina quali valori possono essere archiviati nella variabile. C# è un linguaggio indipendente dai tipi e il compilatore C# garantisce che i valori archiviati nelle variabili siano sempre del tipo appropriato. Il valore di una variabile può essere modificato tramite l'assegnazione o tramite l'uso ++ degli operatori e -- .

Una variabile deve essere assegnata in modo definitivo (§9,4) prima che il valore possa essere ottenuto.

Come descritto nelle sottoclause seguenti, le variabili vengono inizialmente assegnate o inizialmente non assegnate. Una variabile inizialmente assegnata ha un valore iniziale ben definito ed è sempre considerata sicuramente assegnata. Una variabile inizialmente non assegnata non ha alcun valore iniziale. Affinché una variabile inizialmente non assegnata venga considerata sicuramente assegnata in una determinata posizione, un'assegnazione alla variabile deve avvenire in ogni possibile percorso di esecuzione che porta a tale posizione.

9.2 Categorie di variabili

9.2.1 Generale

C# definisce otto categorie di variabili: variabili statiche, variabili di istanza, elementi di matrice, parametri di valore, parametri di input, parametri di riferimento, parametri di output e variabili locali. Le sottoclause che seguono descrivono ognuna di queste categorie.

Esempio: nel codice seguente

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 è una variabile statica, y è una variabile di istanza, v[0] è un elemento di matrice, a è un parametro di valore, b è un parametro di riferimento, c è un parametro di output, d è un parametro di input ed i è una variabile locale. esempio finale

9.2.2 Variabili statiche

Un campo dichiarato con il static modificatore è una variabile statica. Una variabile statica viene creata prima dell'esecuzione del static costruttore (§15.12) per il tipo contenitore e cessa di esistere quando il dominio applicazione associato smette di esistere.

Il valore iniziale di una variabile statica è il valore predefinito (§9.3) del tipo della variabile.

Ai fini del controllo dell'assegnazione definita, una variabile statica viene considerata inizialmente assegnata.

9.2.3 Variabili di istanza

9.2.3.1 Generale

Un campo dichiarato senza il static modificatore è una variabile di istanza.

9.2.3.2 Variabili di istanza nelle classi

Una variabile di istanza di una classe viene creata quando viene creata una nuova istanza di tale classe e cessa di esistere quando non sono presenti riferimenti a tale istanza e il finalizzatore dell'istanza (se presente) è stato eseguito.

Il valore iniziale di una variabile di istanza di una classe è il valore predefinito (§9.3) del tipo della variabile.

Ai fini del controllo dell'assegnazione definita, una variabile di istanza di una classe viene considerata inizialmente assegnata.

9.2.3.3 Variabili di istanza negli struct

Una variabile di istanza di uno struct ha esattamente la stessa durata della variabile struct a cui appartiene. In altre parole, quando una variabile di un tipo struct entra in esistenza o cessa di esistere, quindi anche le variabili di istanza dello struct.

Lo stato di assegnazione iniziale di una variabile di istanza di uno struct è uguale a quello della variabile contenitore struct . In altre parole, quando una variabile struct viene considerata inizialmente assegnata, quindi anche le variabili di istanza sono le variabili di istanza e quando una variabile di struct viene considerata inizialmente non assegnata, le relative variabili di istanza vengono allo stesso modo non assegnate.

9.2.4 Elementi della matrice

Gli elementi di una matrice vengono creati quando viene creata un'istanza di matrice e cessano di esistere quando non sono presenti riferimenti a tale istanza di matrice.

Il valore iniziale di ognuno degli elementi di una matrice è il valore predefinito (§9,3) del tipo degli elementi della matrice.

Ai fini del controllo dell'assegnazione definita, un elemento della matrice viene considerato inizialmente assegnato.

9.2.5 Parametri valore

Un parametro value viene esistente al momento della chiamata del membro della funzione (metodo, costruttore di istanza, funzione di accesso o operatore) o funzione anonima a cui appartiene il parametro e viene inizializzato con il valore dell'argomento specificato nella chiamata. Un parametro value in genere smette di esistere quando l'esecuzione del corpo della funzione viene completata. Tuttavia, se il parametro value viene acquisito da una funzione anonima (§12.19.6.2), la durata estende almeno fino a quando il delegato o l'albero delle espressioni creato da tale funzione anonima è idoneo per l'operazione di Garbage Collection.

Ai fini del controllo dell'assegnazione definita, un parametro di valore viene considerato inizialmente assegnato.

I parametri di valore sono descritti più avanti in §15.6.2.2.

9.2.6 Parametri di riferimento

Un parametro di riferimento è una variabile di riferimento (§9.7) che si verifica quando viene chiamata del membro della funzione, delegato, funzione anonima o funzione locale e il relativo referenziale viene inizializzato alla variabile specificata come argomento in tale chiamata. Un parametro di riferimento cessa di esistere quando viene completata l'esecuzione del corpo della funzione. A differenza dei parametri di valore, un parametro di riferimento non deve essere acquisito (§9.7.2.9).

Le regole di assegnazione definite seguenti si applicano ai parametri di riferimento.

Nota: le regole per i parametri di output sono diverse e sono descritte in (§9.2.7). nota finale

  • Una variabile deve essere assegnata definitivamente (§9.4) prima che possa essere passata come parametro di riferimento in un membro della funzione o chiamata di delegato.
  • All'interno di un membro della funzione o di una funzione anonima, un parametro di riferimento viene considerato inizialmente assegnato.

I parametri di riferimento sono descritti più avanti in §15.6.2.3.3.

9.2.7 Parametri di output

Un parametro di output è una variabile di riferimento (§9.7) che entra in vigore dopo la chiamata del membro della funzione, del delegato, della funzione anonima o della funzione locale e il relativo referenziale viene inizializzato alla variabile specificata come argomento in tale chiamata. Un parametro di output smette di esistere al termine dell'esecuzione del corpo della funzione. A differenza dei parametri di valore, un parametro di output non deve essere acquisito (§9.7.2.9).

Le regole di assegnazione definite seguenti si applicano ai parametri di output.

Nota: le regole per i parametri di riferimento sono diverse e sono descritte in (§9.2.6). nota finale

  • Una variabile non deve essere assegnata in modo definitivo prima che possa essere passata come parametro di output in un membro della funzione o una chiamata di delegato.
  • Dopo il normale completamento di una chiamata a un membro di funzione o delegato, ogni variabile passata come parametro di output viene considerata assegnata in tale percorso di esecuzione.
  • All'interno di un membro della funzione o di una funzione anonima, un parametro di output viene considerato inizialmente non assegnato.
  • Ogni parametro di output di un membro di funzione, una funzione anonima o una funzione locale deve essere assegnato in modo definitivo (§9.4) prima che il membro della funzione, la funzione anonima o la funzione locale restituisca normalmente.

I parametri di output sono descritti più avanti in §15.6.2.3.4.

9.2.8 Parametri di input

Un parametro di input è una variabile di riferimento (§9.7) che si verifica quando viene chiamata del membro della funzione, delegato, funzione anonima o funzione locale e il relativo referenziale viene inizializzato nella variable_reference specificata come argomento in tale chiamata. Un parametro di input smette di esistere al termine dell'esecuzione del corpo della funzione. A differenza dei parametri di valore, un parametro di input non deve essere acquisito (§9.7.2.9).

Le regole di assegnazione definite seguenti si applicano ai parametri di input.

  • Una variabile deve essere assegnata definitivamente (§9.4) prima che possa essere passata come parametro di input in un membro della funzione o chiamata di delegato.
  • All'interno di un membro della funzione, una funzione anonima o una funzione locale viene considerato inizialmente assegnato un parametro di input.

I parametri di input sono descritti più avanti in §15.6.2.3.2.

9.2.9 Variabili locali

Una variabile locale viene dichiarata da un local_variable_declaration, declaration_expression, foreach_statement o specific_catch_clause di un try_statement. Una variabile locale può anche essere dichiarata da determinati tipi di criteri(§11). Per un foreach_statement, la variabile locale è una variabile di iterazione (§13.9.5). Per un specific_catch_clause, la variabile locale è una variabile di eccezione (§13.11). Una variabile locale dichiarata da un foreach_statement o specific_catch_clause viene considerata inizialmente assegnata.

Un local_variable_declaration può verificarsi in un blocco, in un for_statement, in un switch_block o in un using_statement. Un declaration_expression può verificarsi come out argument_value e come tuple_element che è la destinazione di un'assegnazione di decostruzione (§12.21.2).

La durata di una variabile locale è la parte dell'esecuzione del programma durante la quale è garantito che l'archiviazione sia riservata. Questa durata si estende dall'ingresso all'ambito a cui è associato, almeno fino a quando l'esecuzione di tale ambito termina in qualche modo. Se si immette un blocco racchiuso, si chiama un metodo o si restituisce un valore da un blocco iteratore viene sospeso, ma non termina l'esecuzione dell'ambito corrente. Se la variabile locale viene acquisita da una funzione anonima (§12.19.6.2), la durata della variabile si estende almeno fino a quando il delegato o l'albero delle espressioni creato dalla funzione anonima, insieme agli altri oggetti che vengono a fare riferimento alla variabile acquisita, sono idonei per l'operazione di Garbage Collection. Se l'ambito padre viene immesso in modo ricorsivo o iterativo, ogni volta viene creata una nuova istanza della variabile locale e il relativo inizializzatore, se presente, viene valutato ogni volta.

Nota: viene creata un'istanza di una variabile locale ogni volta che viene immesso il relativo ambito. Questo comportamento è visibile al codice utente contenente metodi anonimi. nota finale

Nota: la durata di una variabile di iterazione (§13.9.5) dichiarata da un foreach_statement è una singola iterazione di tale istruzione. Ogni iterazione crea una nuova variabile. nota finale

Nota: la durata effettiva di una variabile locale dipende dall'implementazione. Ad esempio, un compilatore potrebbe determinare in modo statico che una variabile locale in un blocco viene usata solo per una piccola parte di tale blocco. Usando questa analisi, il compilatore potrebbe generare codice che comporta una durata più breve dell'archiviazione della variabile rispetto al blocco contenitore.

L'archiviazione a cui fa riferimento una variabile di riferimento locale viene recuperata indipendentemente dalla durata della variabile di riferimento locale (§7.9).

nota finale

Una variabile locale introdotta da un local_variable_declaration o declaration_expression non viene inizializzata automaticamente e pertanto non ha alcun valore predefinito. Tale variabile locale viene considerata inizialmente non assegnata.

Nota: un local_variable_declaration che include un inizializzatore è ancora inizialmente non assegnato. L'esecuzione della dichiarazione si comporta esattamente come un'assegnazione alla variabile (§9.4.4.5). Uso di una variabile prima dell'esecuzione del relativo inizializzatore; Ad esempio, all'interno dell'espressione dell'inizializzatore stesso o usando un goto_statement che ignora l'inizializzatore; è un errore in fase di compilazione:

goto L;

int x = 1; // never executed

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

Nell'ambito di una variabile locale, si tratta di un errore in fase di compilazione per fare riferimento a tale variabile locale in una posizione testuale che precede il relativo dichiaratore.

nota finale

9.2.9.1 Elimina

Una variabile discard è una variabile locale senza nome. Un oggetto discard viene introdotto da un'espressione di dichiarazione (§12.17) con l'identificatore _; ed è tipizzato in modo implicito (_ o var _) o tipizzato in modo esplicito (T _).

Nota: _ è un identificatore valido in molte forme di dichiarazioni. nota finale

Poiché un oggetto discard non ha alcun nome, l'unico riferimento alla variabile rappresentata è l'espressione che lo introduce.

Nota: un oggetto discard può tuttavia essere passato come argomento di output, consentendo al parametro di output corrispondente di indicare il percorso di archiviazione associato. nota finale

Un'eliminazione non viene inizialmente assegnata, pertanto è sempre un errore per accedere al relativo valore.

Esempio:

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

Nell'esempio si presuppone che non sia presente alcuna dichiarazione del nome _ nell'ambito.

L'assegnazione a _ mostra un modello semplice per ignorare il risultato di un'espressione. La chiamata di M mostra le diverse forme di eliminazione disponibili nelle tuple e come parametri di output.

esempio finale

9.3 Valori predefiniti

Le categorie di variabili seguenti vengono inizializzate automaticamente sui valori predefiniti:

  • Variabili statiche.
  • Variabili di istanza delle istanze di classe.
  • Elementi matrice.

Il valore predefinito di una variabile dipende dal tipo della variabile e viene determinato nel modo seguente:

  • Per una variabile di un value_type, il valore predefinito corrisponde al valore calcolato dal costruttore predefinito del value_type (§8.3.3).
  • Per una variabile di un reference_type, il valore predefinito è null.

Nota: l'inizializzazione dei valori predefiniti viene in genere eseguita con gestione della memoria o Garbage Collector inizializzare la memoria a tutti i bit zero prima che venga allocata per l'uso. Per questo motivo, è utile usare tutti i bit-zero per rappresentare il riferimento Null. nota finale

9.4 Assegnazione definita

9.4.1 Generale

In una determinata posizione nel codice eseguibile di un membro di funzione o di una funzione anonima, una variabile viene definitamente assegnata se il compilatore può dimostrare, da una particolare analisi del flusso statico (§9.4.4), che la variabile è stata inizializzata automaticamente o è stata la destinazione di almeno un'assegnazione.

Nota: in modo informale, le regole di assegnazione definita sono:

  • Una variabile inizialmente assegnata (§9.4.2) viene sempre considerata sicuramente assegnata.
  • Una variabile inizialmente non assegnata (§9.4.3) viene considerata definitamente assegnata in una determinata posizione se tutti i possibili percorsi di esecuzione che portano a tale posizione contengono almeno uno dei seguenti:
    • Assegnazione semplice (§12.21.2) in cui la variabile è l'operando sinistro.
    • Espressione di chiamata (§12.8.9) o espressione di creazione di oggetti (§12.8.16.2) che passa la variabile come parametro di output.
    • Per una variabile locale, una dichiarazione di variabile locale per la variabile (§13.6.2) che include un inizializzatore di variabile.

La specifica formale sottostante le regole informali riportate sopra è descritta in §9.4.2, §9.4.3 e §9.4.4.

nota finale

Gli stati di assegnazione definita delle variabili di istanza di una variabile struct_type vengono rilevati singolarmente e collettivamente. In aggiunta alle regole descritte in §9.4.2, §9.4.3 e §9.4.4, le regole seguenti si applicano alle variabili struct_type e alle relative variabili di istanza:

  • Una variabile di istanza viene considerata sicuramente assegnata se la variabile che lo contiene struct_type viene considerata sicuramente assegnata.
  • Una variabile struct_type viene considerata sicuramente assegnata se ognuna delle variabili di istanza viene considerata sicuramente assegnata.

L'assegnazione definita è un requisito nei contesti seguenti:

  • Una variabile deve essere assegnata in ogni posizione in cui viene ottenuto il relativo valore.

    Nota: ciò garantisce che i valori non definiti non si verifichino mai. nota finale

    L'occorrenza di una variabile in un'espressione viene considerata per ottenere il valore della variabile, tranne quando

    • la variabile è l'operando sinistro di un'assegnazione semplice,
    • la variabile viene passata come parametro di output o
    • la variabile è una variabile struct_type e si verifica come operando sinistro di un accesso membro.
  • Una variabile deve essere assegnata in ogni posizione in cui viene passata come parametro di riferimento.

    Nota: ciò garantisce che il membro della funzione richiamato possa considerare inizialmente assegnato il parametro di riferimento. nota finale

  • Una variabile deve essere assegnata in ogni posizione in cui viene passata come parametro di input.

    Nota: ciò garantisce che il membro della funzione richiamato possa considerare inizialmente assegnato il parametro di input. nota finale

  • Tutti i parametri di output di un membro della funzione devono essere assegnati in ogni posizione in cui il membro della funzione restituisce (tramite un'istruzione return o tramite l'esecuzione che raggiunge la fine del corpo del membro della funzione).

    Nota: in questo modo, i membri della funzione non restituiscono valori non definiti nei parametri di output, consentendo al compilatore di considerare una chiamata a un membro di funzione che accetta una variabile come parametro di output equivalente a un'assegnazione alla variabile. nota finale

  • La this variabile di un costruttore di istanza di struct_type deve essere assegnata in ogni posizione in cui viene restituito il costruttore dell'istanza.

9.4.2 Variabili inizialmente assegnate

Le categorie di variabili seguenti vengono classificate come assegnate inizialmente:

  • Variabili statiche.
  • Variabili di istanza delle istanze di classe.
  • Variabili di istanza delle variabili di struct assegnate inizialmente.
  • Elementi matrice.
  • Parametri valore.
  • Parametri di riferimento.
  • Parametri di input.
  • Variabili dichiarate in una catch clausola o in un'istruzione foreach .

9.4.3 Variabili inizialmente non firmate

Le categorie di variabili seguenti vengono classificate come inizialmente non firmate:

  • Variabili di istanza delle variabili struct inizialmente non firmate.
  • Parametri di output, inclusa la this variabile dei costruttori di istanza di struct senza un inizializzatore del costruttore.
  • Variabili locali, ad eccezione di quelle dichiarate in una catch clausola o in un'istruzione foreach .

9.4.4 Regole precise per determinare l'assegnazione definita

9.4.4.1 Generale

Per determinare che ogni variabile usata viene assegnata in modo definitivo, il compilatore userà un processo equivalente a quello descritto in questa sottoclausa.

Il compilatore elabora il corpo di ogni membro della funzione con una o più variabili inizialmente non firmate. Per ogni variabile inizialmente non assegnata v, il compilatore determina uno stato di assegnazione definita per v in ognuno dei punti seguenti nel membro della funzione:

  • All'inizio di ogni istruzione
  • Al punto finale (§13.2) di ogni istruzione
  • In ogni arco che trasferisce il controllo a un'altra istruzione o al punto finale di un'istruzione
  • All'inizio di ogni espressione
  • Alla fine di ogni espressione

Lo stato di assegnazione definita di v può essere:

  • Sicuramente assegnato. Ciò indica che in tutti i possibili flussi di controllo a questo punto è stato assegnato un valore.
  • Non sicuramente assegnato. Per lo stato di una variabile alla fine di un'espressione di tipo bool, lo stato di una variabile che non è sicuramente assegnato potrebbe (ma non necessariamente) rientra in uno dei seguenti sotto stati:
    • Assegnato sicuramente dopo l'espressione true. Questo stato indica che v è sicuramente assegnato se l'espressione booleana è valutata come true, ma non è necessariamente assegnata se l'espressione booleana viene valutata come false.
    • Assegnato sicuramente dopo false espressione. Questo stato indica che v viene assegnato sicuramente se l'espressione booleana valutata come false, ma non è necessariamente assegnata se l'espressione booleana viene valutata come true.

Le regole seguenti regolano la modalità di determinazione dello stato di una variabile v in ogni posizione.

9.4.4.2 Regole generali per le istruzioni

  • v non è sicuramente assegnato all'inizio di un corpo membro della funzione.
  • Lo stato di assegnazione definita di v all'inizio di qualsiasi altra istruzione è determinato controllando lo stato di assegnazione definita di v in tutti i trasferimenti di flusso di controllo destinati all'inizio di tale istruzione. Se (e solo se) v viene assegnato in modo definitivo a tutti questi trasferimenti di flusso di controllo, v viene assegnato sicuramente all'inizio dell'istruzione. Il set di possibili trasferimenti di flusso di controllo viene determinato nello stesso modo in cui per verificare la raggiungibilità delle istruzioni (§13.2).
  • Lo stato di assegnazione definita di v al punto finale di un'istruzione block, , ifwhilecheckeduncheckedusinglockforeachfordo, o switch è determinato controllando lo stato di assegnazione definita di v in tutti i trasferimenti di flusso di controllo destinati al punto finale di tale istruzione. Se v è sicuramente assegnato a tutti questi trasferimenti di flusso di controllo, v viene assegnato sicuramente al punto finale dell'istruzione. In caso contrario, v non viene assegnato in modo definitivo al punto finale dell'istruzione. Il set di possibili trasferimenti di flusso di controllo viene determinato nello stesso modo in cui per verificare la raggiungibilità delle istruzioni (§13.2).

Nota: poiché non sono presenti percorsi di controllo per un'istruzione non raggiungibile, v viene assegnato sicuramente all'inizio di qualsiasi istruzione non raggiungibile. nota finale

9.4.4.3 Istruzioni block, checked e unchecked statements

Lo stato di assegnazione definita di v sul trasferimento del controllo alla prima istruzione dell'elenco di istruzioni nel blocco (o al punto finale del blocco, se l'elenco di istruzioni è vuoto) è uguale all'istruzione di assegnazione definita di v prima del blocco, checkedo dell'istruzione unchecked .

9.4.4.4.4 Istruzioni expression

Per un'istruzione expression stmt costituita dall'espressione expr:

  • v ha lo stesso stato di assegnazione definita all'inizio di expr come all'inizio di stmt.
  • Se v se sicuramente assegnato alla fine di expr, viene sicuramente assegnato al punto finale di stmt; in caso contrario, non è sicuramente assegnato al punto finale di stmt.

9.4.4.5 Statements

  • Se stmt è un'istruzione di dichiarazione senza inizializzatori, v ha lo stesso stato di assegnazione definita al punto finale di stmt come all'inizio di stmt.
  • Se stmt è un'istruzione di dichiarazione con inizializzatori, lo stato di assegnazione definita per v viene determinato come se stmt fosse un elenco di istruzioni, con un'istruzione di assegnazione per ogni dichiarazione con un inizializzatore (nell'ordine di dichiarazione).

9.4.4.6 Istruzioni If

Per un'istruzione stmt del formato:

if ( «expr» ) «then_stmt» else «else_stmt»
  • v ha lo stesso stato di assegnazione definita all'inizio di expr come all'inizio di stmt.
  • Se v è sicuramente assegnato alla fine di expr, viene assegnato sicuramente al trasferimento del flusso di controllo a then_stmt e a else_stmt o al punto finale di stmt se non è presente alcuna clausola diversa.
  • Se v ha lo stato "sicuramente assegnato dopo l'espressione true" alla fine di expr, viene assegnato sicuramente al trasferimento del flusso di controllo a then_stmt e non è sicuramente assegnato sul trasferimento del flusso di controllo a else_stmt o al punto finale di stmt se non è presente alcuna clausola diversa.
  • Se v ha lo stato "sicuramente assegnato dopo false espressione" alla fine di expr, viene assegnato sicuramente al trasferimento del flusso di controllo a else_stmt e non è sicuramente assegnato sul trasferimento del flusso di controllo a then_stmt. È sicuramente assegnato al punto finale di stmt se e solo se è sicuramente assegnato al punto finale di then_stmt.
  • In caso contrario, v viene considerato non assegnato in modo definitivo nel trasferimento del flusso di controllo al then_stmt o else_stmt o al punto finale di stmt se non è presente alcuna clausola diversa.

9.4.4.7 Istruzioni Switch

Per un'istruzione switch stmt con un'espressione di controllo:

Lo stato di assegnazione definita di v all'inizio di expr è uguale allo stato di v all'inizio di stmt.

Lo stato di assegnazione definita di v all'inizio della clausola guard di un caso è

  • Se v è una variabile di pattern dichiarata nella switch_label: "sicuramente assegnata".
  • Se l'etichetta switch contenente tale clausola guard (§13.8.3) non è raggiungibile: "sicuramente assegnata".
  • In caso contrario, lo stato di v corrisponde allo stato di v dopo expr.

Esempio: la seconda regola elimina la necessità del compilatore di generare un errore se si accede a una variabile non assegnata nel codice non raggiungibile. Lo stato di b è "sicuramente assegnato" nell'etichetta case 2 when bswitch non raggiungibile .

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

esempio finale

Lo stato di assegnazione definita di v nel trasferimento del flusso di controllo a un elenco di istruzioni di blocco switch raggiungibile è

  • Se il trasferimento del controllo è dovuto a un'istruzione 'goto case' o 'goto default', lo stato di v corrisponde allo stato all'inizio dell'istruzione 'goto'.
  • Se il trasferimento del controllo è dovuto all'etichetta dell'opzionedefault, lo stato di v corrisponde allo stato di v dopo expr.
  • Se il trasferimento del controllo è dovuto a un'etichetta switch non raggiungibile, lo stato di v è "sicuramente assegnato".
  • Se il trasferimento del controllo è dovuto a un'etichetta switch raggiungibile con una clausola guard, lo stato di v corrisponde allo stato di v dopo la clausola guard.
  • Se il trasferimento del controllo è dovuto a un'etichetta switch raggiungibile senza una clausola guard, lo stato di v è
    • Se v è una variabile di pattern dichiarata nella switch_label: "sicuramente assegnata".
    • In caso contrario, lo stato di v corrisponde allo stato di v dopo expr.

Una conseguenza di queste regole è che una variabile di modello dichiarata in un switch_label verrà "non sicuramente assegnata" nelle istruzioni della relativa sezione switch se non è l'unica etichetta switch raggiungibile nella relativa sezione.

Esempio:

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

esempio finale

9.4.4.8 Istruzioni While

Per un'istruzione stmt del formato:

while ( «expr» ) «while_body»
  • v ha lo stesso stato di assegnazione definita all'inizio di expr come all'inizio di stmt.
  • Se v è sicuramente assegnato alla fine di expr, viene assegnato sicuramente al trasferimento del flusso di controllo a while_body e al punto finale di stmt.
  • Se v ha lo stato "sicuramente assegnato dopo l'espressione true" alla fine di expr, viene assegnato sicuramente al trasferimento del flusso di controllo a while_body, ma non sicuramente assegnato al punto finale di stmt.
  • Se v ha lo stato "sicuramente assegnato dopo false espressione" alla fine di expr, viene assegnato sicuramente al trasferimento del flusso di controllo al punto finale di stmt, ma non assegnato sicuramente al trasferimento del flusso di controllo a while_body.

9.4.4.9 Istruzioni Do

Per un'istruzione stmt del formato:

do «do_body» while ( «expr» ) ;
  • v ha lo stesso stato di assegnazione definita sul trasferimento del flusso di controllo dall'inizio di stmt a do_body come all'inizio di stmt.
  • v ha lo stesso stato di assegnazione definita all'inizio di expr come al punto finale di do_body.
  • Se v è sicuramente assegnato alla fine di expr, viene assegnato sicuramente al trasferimento del flusso di controllo al punto finale di stmt.
  • Se v ha lo stato "sicuramente assegnato dopo false espressione" alla fine di expr, viene assegnato sicuramente al trasferimento del flusso di controllo al punto finale di stmt, ma non assegnato sicuramente al trasferimento del flusso di controllo a do_body.

9.4.4.10 Istruzioni For

Per un'istruzione del formato:

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

Il controllo dell'assegnazione definita viene eseguito come se l'istruzione fosse scritta:

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

con continue istruzioni destinate all'istruzione for tradotta in istruzioni destinate all'etichetta goto LLoop. Se il for_condition viene omesso dall'istruzione for , la valutazione dell'assegnazione definita procede come se for_condition fosse stata sostituita con true nell'espansione precedente.

9.4.4.11 Interruzione, continuazione e istruzioni goto

Lo stato di assegnazione definita di v nel trasferimento del flusso di controllo causato da un'istruzione break, continueo goto è uguale allo stato di assegnazione definita di v all'inizio dell'istruzione.

9.4.4.12 Istruzioni Throw

Per un'istruzione stmt del formato:

throw «expr» ;

lo stato di assegnazione definita di v all'inizio di expr è uguale allo stato di assegnazione definita di v all'inizio di stmt.

9.4.4.13 Istruzioni Return

Per un'istruzione stmt del formato:

return «expr» ;
  • Lo stato di assegnazione definita di v all'inizio di expr corrisponde allo stato di assegnazione definita di v all'inizio di stmt.
  • Se v è un parametro di output, sarà sicuramente assegnato:
    • dopo expr
    • o alla fine del finally blocco di unfinally-try oggetto o trycatch--finally che racchiude l'istruzione .return

Per un'istruzione stmt del formato:

return ;
  • Se v è un parametro di output, sarà sicuramente assegnato:
    • prima di stmt
    • o alla fine del finally blocco di unfinally-try oggetto o trycatch--finally che racchiude l'istruzione .return

9.4.4.14 Istruzioni Try-catch

Per un'istruzione stmt del formato:

try «try_block»
catch ( ... ) «catch_block_1»
...
catch ( ... ) «catch_block_n»
  • Lo stato di assegnazione definita di v all'inizio di try_block corrisponde allo stato di assegnazione definita di v all'inizio di stmt.
  • Lo stato di assegnazione definita di v all'inizio di catch_block_i (per qualsiasi i) corrisponde allo stato di assegnazione definita di v all'inizio di stmt.
  • Lo stato di assegnazione definita di v al punto finale di stmt viene assegnato sicuramente se (e solo se) v viene assegnato al punto finale di try_block e ogni catch_block_i (per ogni i da 1 a n).

9.4.4.15 Istruzioni Try-finally

Per un'istruzione stmt del formato:

try «try_block» finally «finally_block»
  • Lo stato di assegnazione definita di v all'inizio di try_block corrisponde allo stato di assegnazione definita di v all'inizio di stmt.
  • Lo stato di assegnazione definita di v all'inizio di finally_block corrisponde allo stato di assegnazione definita di v all'inizio di stmt.
  • Lo stato di assegnazione definita di v al punto finale di stmt viene assegnato sicuramente se (e solo se) almeno uno dei valori seguenti è true:
    • v è sicuramente assegnato al punto finale di try_block
    • v è sicuramente assegnato al punto finale di finally_block

Se viene effettuato un trasferimento del flusso di controllo (ad esempio un'istruzionegoto) che inizia entro try_block e termina all'esterno di try_block, v viene considerato assegnato in modo definitivo a tale trasferimento del flusso di controllo se v viene assegnato definitivamente al punto finale di finally_block. Questo non è un solo se, se v è sicuramente assegnato per un altro motivo per questo trasferimento del flusso di controllo, allora viene comunque considerato sicuramente assegnato.

9.4.4.16 Istruzioni Try-catch-finally

Per un'istruzione del formato:

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

L'analisi dell'assegnazione definita viene eseguita come se l'istruzione fosse un'istruzione che racchiude un'istruzionecatchtry-:try-finally

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

Esempio: nell'esempio seguente viene illustrato come i diversi blocchi di un'istruzione try (§13.11) influiscano sull'assegnazione definita.

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

esempio finale

9.4.4.17 Istruzioni Foreach

Per un'istruzione stmt del formato:

foreach ( «type» «identifier» in «expr» ) «embedded_statement»
  • Lo stato di assegnazione definita di v all'inizio di expr è uguale allo stato di v all'inizio di stmt.
  • Lo stato di assegnazione definita di v nel trasferimento del flusso di controllo a embedded_statement o al punto finale di stmt è uguale allo stato di v alla fine di expr.

9.4.4.18 Istruzioni Using

Per un'istruzione stmt del formato:

using ( «resource_acquisition» ) «embedded_statement»
  • Lo stato di assegnazione definita di v all'inizio di resource_acquisition corrisponde allo stato di v all'inizio di stmt.
  • Lo stato di assegnazione definita di v nel trasferimento del flusso di controllo a embedded_statement corrisponde allo stato di v alla fine di resource_acquisition.

9.4.4.19 Istruzioni di blocco

Per un'istruzione stmt del formato:

lock ( «expr» ) «embedded_statement»
  • Lo stato di assegnazione definita di v all'inizio di expr è uguale allo stato di v all'inizio di stmt.
  • Lo stato di assegnazione definita di v nel trasferimento del flusso di controllo a embedded_statement corrisponde allo stato di v alla fine di expr.

9.4.4.20 Istruzioni Yield

Per un'istruzione stmt del formato:

yield return «expr» ;
  • Lo stato di assegnazione definita di v all'inizio di expr è uguale allo stato di v all'inizio di stmt.
  • Lo stato di assegnazione definita di v alla fine di stmt è uguale allo stato di v alla fine di expr.

Un'istruzione yield break non ha alcun effetto sullo stato di assegnazione definita.

9.4.4.21 Regole generali per le espressioni costanti

Quanto segue si applica a qualsiasi espressione costante e assume la priorità su tutte le regole delle sezioni seguenti che potrebbero essere applicate:

Per un'espressione costante con valore true:

  • Se v viene assegnato sicuramente prima dell'espressione, v viene assegnato definitivamente dopo l'espressione.
  • In caso contrario , v è "sicuramente assegnato dopo false espressione" dopo l'espressione.

Esempio:

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

esempio finale

Per un'espressione costante con valore false:

  • Se v viene assegnato sicuramente prima dell'espressione, v viene assegnato definitivamente dopo l'espressione.
  • In caso contrario , v è "sicuramente assegnato dopo true expression" dopo l'espressione.

Esempio:

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

esempio finale

Per tutte le altre espressioni costanti, lo stato di assegnazione definita di v dopo l'espressione è uguale allo stato di assegnazione definita di v prima dell'espressione.

9.4.4.22 Regole generali per espressioni semplici

La regola seguente si applica a questi tipi di espressioni: valori letterali (§12.8.2), nomi semplici (§12.8.4), espressioni di accesso ai membri (§12.8.7), espressioni di accesso non indicizzate (§12.12. 8.14), typeof espressioni (§12.8.17), espressioni di valore predefinito (§12.8.20), nameof espressioni (§12.8.22) ed espressioni di dichiarazione (§12.17).

  • Lo stato di assegnazione definita di v alla fine di un'espressione di questo tipo corrisponde allo stato di assegnazione definita di v all'inizio dell'espressione.

9.4.4.23 Regole generali per le espressioni con espressioni incorporate

Le regole seguenti si applicano a questi tipi di espressioni: espressioni racchiuse tra parentesi (§12.8.5), espressioni di tupla (§12.8.6), espressioni di accesso agli elementi (§12.8.11), espressioni di accesso di base con indicizzazione (§12.8.14), espressioni di incremento e decremento (§12.8.15, §12.9.6), espressioni cast (§12.9.7), unario +, -, ~, espressioni*, binary +, */-, <<%, , >><<= >, >=, ==!=, is, as, &, |, espressioni ^ (§12.10, §12.11, §12.12, §12.13), espressioni di assegnazione composta (§12.21.4) checked ed unchecked espressioni (§12.8.19), espressioni di creazione di matrici e delegati (§12.8.16) ed await espressioni (§12.9.8).

Ognuna di queste espressioni ha una o più sottoespressioni che vengono valutate in modo incondizionato in un ordine fisso.

Esempio: l'operatore binario % valuta il lato sinistro dell'operatore, quindi il lato destro. Un'operazione di indicizzazione valuta l'espressione indicizzata e quindi valuta ognuna delle espressioni di indice, in ordine da sinistra a destra. esempio finale

Per un'espressione expr, che dispone di sottoespressioni expr₁, exprₓ, valutate in tale ordine:

  • Lo stato di assegnazione definita di v all'inizio di expr₁ corrisponde allo stato di assegnazione definita all'inizio di expr.
  • Lo stato di assegnazione definita di v all'inizio di expri (i maggiore di uno) corrisponde allo stato di assegnazione definita alla fine di expri₋₁.
  • Lo stato di assegnazione definita di v alla fine di expr corrisponde allo stato di assegnazione definita alla fine di exprₓ.

9.4.4.24 Espressioni di chiamata ed espressioni di creazione di oggetti

Se il metodo da richiamare è un metodo parziale che non ha alcuna dichiarazione di metodo parziale o è un metodo condizionale per il quale la chiamata viene omessa (§22.5.3.2), lo stato di assegnazione definita di v dopo la chiamata è uguale allo stato di assegnazione definita di v prima della chiamata. In caso contrario, si applicano le regole seguenti:

Per un'espressione di chiamata espr del form:

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

o un'espressione di creazione di oggetti del form:

new «type» ( «arg₁», «arg₂», … , «argₓ» )
  • Per un'espressione di chiamata, lo stato di assegnazione definito di v prima di primary_expression corrisponde allo stato di v prima di expr.
  • Per un'espressione di chiamata, lo stato di assegnazione definito di v prima di arg₁ corrisponde allo stato di v dopo primary_expression.
  • Per un'espressione di creazione di oggetti, lo stato di assegnazione definito di v prima di arg₁ corrisponde allo stato di v prima di expr.
  • Per ogni argomento argi, lo stato di assegnazione definito di v dopo argi è determinato dalle regole di espressione normali, ignorando eventuali inmodificatori , outo ref .
  • Per ogni argomento argi per qualsiasi i maggiore di uno, lo stato di assegnazione definito di v prima di argi è uguale allo stato di v dopo argi₋₁.
  • Se la variabile v viene passata come out argomento (ad esempio, un argomento del formato "out v") in uno degli argomenti, lo stato di v dopo expr viene assegnato in modo definitivo. In caso contrario, lo stato di v dopo expr corrisponde allo stato di v dopo argₓ.
  • Per gli inizializzatori di matrice (§12.8.16.5), gli inizializzatori di oggetti (§12.8.16.3), gli inizializzatori di raccolta (§12.8.16.4) e gli inizializzatori di oggetti anonimi (§12.8.16.7), lo stato di assegnazione definito è determinato dall'espansione di questi costrutti.

9.4.4.25 Espressioni di assegnazione semplici

Lasciare che il set di destinazioni di assegnazione in un'espressione venga definito come segue:

  • Se e è un'espressione di tupla, le destinazioni di assegnazione in e sono l'unione delle destinazioni di assegnazione degli elementi di e.
  • In caso contrario, le destinazioni di assegnazione in e sono e.

Per un'espressione espr del form:

«expr_lhs» = «expr_rhs»
  • Lo stato di assegnazione definita di v prima di expr_lhs corrisponde allo stato di assegnazione definita di v prima di expr.
  • Lo stato di assegnazione definita di v prima di expr_rhs corrisponde allo stato di assegnazione definita di v dopo expr_lhs.
  • Se v è una destinazione di assegnazione di expr_lhs, lo stato di assegnazione definita di v dopo expr viene assegnato in modo definitivo. In caso contrario, se l'assegnazione si verifica all'interno del costruttore dell'istanza di un tipo struct e v è il campo sottostante nascosto di una proprietà implementata automaticamente P nell'istanza da costruire e una proprietà che designa P è una destinazione di assigmentazione di expr_lhs, lo stato di assegnazione definita di v dopo l'assegnazione definitiva di expr viene assegnato. In caso contrario, lo stato di assegnazione definita di v dopo expr è uguale allo stato di assegnazione definita di v dopo expr_rhs.

Esempio: nel codice seguente

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

la variabile x viene considerata decisamente assegnata dopo arr[x = 1] la valutazione come lato sinistro della seconda assegnazione semplice.

esempio finale

9.4.4.26 & espressioni

Per un'espressione espr del form:

«expr_first» && «expr_second»
  • Lo stato di assegnazione definita di v prima di expr_first corrisponde allo stato di assegnazione definita di v prima di expr.
  • Lo stato di assegnazione definita di v prima di expr_second viene assegnato sicuramente se e solo se lo stato di v dopo expr_first viene assegnato in modo definitivo o "sicuramente assegnato dopo true espressione". In caso contrario, non è sicuramente assegnato.
  • Lo stato di assegnazione definita di v dopo expr è determinato da:
    • Se lo stato di v dopo expr_first è sicuramente assegnato, lo stato di v dopo expr è sicuramente assegnato.
    • In caso contrario, se lo stato di v dopo expr_second viene assegnato definitivamente e lo stato di v dopo expr_first viene "assegnato sicuramente dopo false espressione", lo stato di v dopo expr è sicuramente assegnato.
    • In caso contrario, se lo stato di v dopo expr_second viene assegnato in modo definitivo o "sicuramente assegnato dopo true espressione", lo stato di v dopo expr è "sicuramente assegnato dopo true espressione".
    • In caso contrario, se lo stato di v dopo expr_first viene "assegnato definitivamente dopo false espressione" e lo stato di v dopo expr_second è "assegnato definitivamente dopo false espressione", lo stato di v dopo expr è "definito assegnato dopo false espressione".
    • In caso contrario, lo stato di v dopo expr non è sicuramente assegnato.

Esempio: nel codice seguente

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

la variabile viene considerata sicuramente i assegnata in una delle istruzioni incorporate di un'istruzione if , ma non nell'altra. Nell'istruzione if nel metodo F, la variabile viene assegnata sicuramente i nella prima istruzione incorporata perché l'esecuzione dell'espressione (i = y) precede sempre l'esecuzione di questa istruzione incorporata. Al contrario, la variabile i non viene assegnata in modo definitivo nella seconda istruzione incorporata, poiché x >= 0 potrebbe essere stata testata false, con conseguente mancata assegnazione della variabile i.

esempio finale

9.4.4.27 || Espressioni

Per un'espressione espr del form:

«expr_first» || «expr_second»
  • Lo stato di assegnazione definita di v prima di expr_first corrisponde allo stato di assegnazione definita di v prima di expr.
  • Lo stato di assegnazione definita di v prima di expr_second viene assegnato sicuramente se e solo se lo stato di v dopo expr_first viene assegnato in modo definitivo o "sicuramente assegnato dopo true espressione". In caso contrario, non è sicuramente assegnato.
  • L'istruzione di assegnazione definita di v dopo expr è determinata da:
    • Se lo stato di v dopo expr_first è sicuramente assegnato, lo stato di v dopo expr è sicuramente assegnato.
    • In caso contrario, se lo stato di v dopo expr_second è sicuramente assegnato e lo stato di v dopo expr_first è "sicuramente assegnato dopo true espressione", lo stato di v dopo expr è sicuramente assegnato.
    • In caso contrario, se lo stato di v dopo expr_second viene assegnato in modo definitivo o "sicuramente assegnato dopo false espressione", lo stato di v dopo expr è "sicuramente assegnato dopo false espressione".
    • In caso contrario, se lo stato di v dopo expr_first è "sicuramente assegnato dopo true expression" e lo stato di v dopo expr_ secondo è "sicuramente assegnato dopo true expression", lo stato di v dopo expr è "sicuramente assegnato dopo true expression".
    • In caso contrario, lo stato di v dopo expr non è sicuramente assegnato.

Esempio: nel codice seguente

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

la variabile viene considerata sicuramente i assegnata in una delle istruzioni incorporate di un'istruzione if , ma non nell'altra. Nell'istruzione if nel metodo G, la variabile viene assegnata sicuramente i nella seconda istruzione incorporata perché l'esecuzione dell'espressione (i = y) precede sempre l'esecuzione di questa istruzione incorporata. Al contrario, la variabile i non viene assegnata in modo definitivo nella prima istruzione incorporata, poiché x >= 0 potrebbe essere stata testata true, con conseguente mancata assegnazione della variabile i.

esempio finale

9.4.4.28 ! espressioni

Per un'espressione espr del form:

! «expr_operand»
  • Lo stato di assegnazione definita di v prima di expr_operand corrisponde allo stato di assegnazione definita di v prima di expr.
  • Lo stato di assegnazione definita di v dopo expr è determinato da:
    • Se lo stato di v dopo expr_operand è sicuramente assegnato, lo stato di v dopo expr è sicuramente assegnato.
    • In caso contrario, se lo stato di v dopo expr_operand viene "assegnato definitivamente dopo false espressione", lo stato di v after expr viene "assegnato definitivamente dopo true espressione".
    • In caso contrario, se lo stato di v dopo expr_operand viene "assegnato definitivamente dopo true expression", lo stato di v dopo expr viene "assegnato definitivamente dopo false espressione".
    • In caso contrario, lo stato di v after expr non è sicuramente assegnato.

9.4.4.29 ?? espressioni

Per un'espressione espr del form:

«expr_first» ?? «expr_second»
  • Lo stato di assegnazione definita di v prima di expr_first corrisponde allo stato di assegnazione definita di v prima di expr.
  • Lo stato di assegnazione definita di v prima di expr_second corrisponde allo stato di assegnazione definita di v dopo expr_first.
  • L'istruzione di assegnazione definita di v dopo expr è determinata da:
    • Se expr_first è un'espressione costante (§12.23) con valore null, lo stato di v dopo expr corrisponde allo stato di v dopo expr_second.
    • In caso contrario, lo stato di v dopo expr corrisponde allo stato di assegnazione definita di v dopo expr_first.

9.4.4.30 ?: espressioni

Per un'espressione espr del form:

«expr_cond» ? «expr_true» : «expr_false»
  • Lo stato di assegnazione definita di v prima di expr_cond corrisponde allo stato di v prima di expr.
  • Lo stato di assegnazione definita di v prima di expr_true viene assegnato sicuramente se lo stato di v dopo expr_cond viene assegnato sicuramente o "sicuramente assegnato dopo true expression".
  • Lo stato di assegnazione definita di v prima di expr_false viene assegnato sicuramente se lo stato di v dopo expr_cond viene assegnato definitivamente o "sicuramente assegnato dopo false espressione".
  • Lo stato di assegnazione definita di v dopo expr è determinato da:
    • Se expr_cond è un'espressione costante (§12.23) con valoretrue, lo stato di v dopo expr è uguale allo stato di v dopo expr_true.
    • In caso contrario, se expr_cond è un'espressione costante (§12.23) con valorefalse, lo stato di v dopo expr corrisponde allo stato di v dopo expr_false.
    • In caso contrario, se lo stato di v dopo expr_true è sicuramente assegnato e lo stato di v dopo expr_false viene assegnato, lo stato di v dopo expr è sicuramente assegnato.
    • In caso contrario, lo stato di v dopo expr non è sicuramente assegnato.

9.4.4.31 Funzioni anonime

Per un lambda_expression o anonymous_method_expression expr con un corpo (blocco o espressione):

  • Lo stato di assegnazione definito di un parametro è uguale a quello di un parametro di un metodo denominato (§9.2.6, §9.2.7, §9.2.8).
  • Lo stato di assegnazione definito di una variabile esterna v prima del corpo è uguale allo stato di v prima di expr. Ovvero, lo stato di assegnazione definita delle variabili esterne viene ereditato dal contesto della funzione anonima.
  • Lo stato di assegnazione definito di una variabile esterna v dopo expr è uguale allo stato di v prima di expr.

Esempio: esempio

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

genera un errore in fase di compilazione perché max non viene assegnato in modo definitivo in cui viene dichiarata la funzione anonima.

esempio finale

Esempio: esempio

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

genera anche un errore in fase di compilazione perché l'assegnazione a n nella funzione anonima non ha alcun effetto sullo stato di assegnazione definita di n all'esterno della funzione anonima.

esempio finale

9.4.4.32 Espressioni throw

Per un'espressione espr del form:

throwthrown_expr

  • Lo stato di assegnazione definito di v prima di thrown_expr corrisponde allo stato di v prima di expr.
  • Lo stato di assegnazione definito di v dopo expr è "sicuramente assegnato".

9.4.4.33 Regole per le variabili nelle funzioni locali

Le funzioni locali vengono analizzate nel contesto del metodo padre. Esistono due percorsi di flusso di controllo importanti per le funzioni locali: chiamate di funzione e conversioni di delegati.

L'assegnazione definita per il corpo di ogni funzione locale viene definita separatamente per ogni sito di chiamata. A ogni chiamata, le variabili acquisite dalla funzione locale vengono considerate sicuramente assegnate se sono state assegnate sicuramente al momento della chiamata. A questo punto esiste anche un percorso del flusso di controllo nel corpo della funzione locale ed è considerato raggiungibile. Dopo una chiamata alla funzione locale, le variabili acquisite assegnate in ogni punto di controllo che lasciano la funzione (return istruzioni, yield istruzioni, await espressioni) vengono considerate sicuramente assegnate dopo la posizione della chiamata.

Le conversioni dei delegati hanno un percorso del flusso di controllo al corpo della funzione locale. Le variabili acquisite vengono assegnate sicuramente per il corpo se sono sicuramente assegnate prima della conversione. Le variabili assegnate dalla funzione locale non vengono considerate assegnate dopo la conversione.

Nota: il precedente implica che i corpi vengono analizzati di nuovo per l'assegnazione definita in ogni chiamata di funzione locale o conversione del delegato. I compilatori non devono analizzare nuovamente il corpo di una funzione locale in ogni chiamata o conversione del delegato. L'implementazione deve produrre risultati equivalenti a tale descrizione. nota finale

Esempio: l'esempio seguente illustra l'assegnazione definita per le variabili acquisite nelle funzioni locali. Se una funzione locale legge una variabile acquisita prima di scriverla, la variabile acquisita deve essere assegnata sicuramente prima di chiamare la funzione locale. La funzione F1 locale legge s senza assegnarla. Si tratta di un errore se F1 viene chiamato prima s dell'assegnazione definitiva. F2i assegna prima di leggerlo. Può essere chiamato prima i di essere assegnato sicuramente. Inoltre, F3 può essere chiamato dopo F2 perché s2 è sicuramente assegnato in 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);
    }
}

esempio finale

9.4.4.34 espressioni is-pattern

Per un'espressione espr del form:

expr_operand è un modello

  • Lo stato di assegnazione definita di v prima di expr_operand corrisponde allo stato di assegnazione definita di v prima di expr.
  • Se la variabile 'v' viene dichiarata nel modello, lo stato di assegnazione definita di 'v' dopo expr viene "sicuramente assegnato quando true".
  • In caso contrario, lo stato di assegnazione definito di 'v' dopo expr è uguale allo stato di assegnazione definito di 'v' dopo expr_operand.

9.5 Riferimenti a variabili

Un variable_reference è un'espressione classificata come variabile. Un variable_reference indica una posizione di archiviazione accessibile sia per recuperare il valore corrente che per archiviare un nuovo valore.

variable_reference
    : expression
    ;

Nota: in C e C++, un variable_reference è noto come lvalue. nota finale

9.6 Atomicità dei riferimenti a variabili

Le letture e le scritture dei tipi di dati seguenti devono essere atomiche: bool, ushortuintbytefloatcharsbyteshortinte tipi riferimento. Inoltre, le letture e le scritture di tipi enumerazione con un tipo sottostante nell'elenco precedente devono essere atomiche. Le letture e le scritture di altri tipi, tra cui long, ulongdouble, e decimal, nonché tipi definiti dall'utente, non devono essere atomici. Oltre alle funzioni di libreria progettate a tale scopo, non esiste alcuna garanzia di atomic read-modify-write, ad esempio nel caso di incremento o decremento.

9.7 Variabili di riferimento e restituisce

9.7.1 Generale

Una variabile di riferimento è una variabile che fa riferimento a un'altra variabile, denominata referenziale (§9.2.6). Una variabile di riferimento è una variabile locale dichiarata con il ref modificatore.

Una variabile di riferimento archivia un variable_reference (§9,5) al relativo referenziale e non al valore del relativo referenziale. Quando viene usata una variabile di riferimento in cui viene richiesto un valore, viene restituito il relativo valore referenziale; analogamente, quando una variabile di riferimento è la destinazione di un'assegnazione, è il referenziale a cui è assegnato. La variabile a cui fa riferimento una variabile di riferimento, ad esempio il variable_reference archiviato per il relativo referenziale, può essere modificata usando un'assegnazione di riferimento (= ref).

Esempio: l'esempio seguente illustra una variabile di riferimento locale il cui referenziale è un elemento di una matrice:

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

esempio finale

Un riferimento restituito è il variable_reference restituito da un metodo return-by-ref (§15.6.1). Questo variable_reference è il referenziale della restituzione del riferimento.

Esempio: nell'esempio seguente viene illustrato un riferimento restituito il cui referenziale è un elemento di un campo di matrice:

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

esempio finale

9.7.2 Contesti sicuri di riferimento

9.7.2.1 Generale

Tutte le variabili di riferimento rispettano le regole di sicurezza che assicurano che il contesto di riferimento della variabile di riferimento non sia maggiore del contesto di riferimento del relativo referenziale.

Nota: la nozione correlata di un contesto sicuro è definita in (§16.4.12), insieme ai vincoli associati. nota finale

Per qualsiasi variabile, il contesto di riferimento di tale variabile è il contesto in cui una variable_reference (§9.5) a tale variabile è valida. Il referenziale di una variabile di riferimento deve avere un contesto di riferimento che sia almeno ampio come contesto di riferimento della variabile di riferimento stessa.

Nota: il compilatore determina il contesto sicuro di riferimento tramite un'analisi statica del testo del programma. Il contesto di riferimento riflette la durata di una variabile in fase di esecuzione. nota finale

Esistono tre contesti ref-safe:There are three ref-safe-contexts:

  • declaration-block: il contesto ref-safe di un variable_reference a una variabile locale (§9.2.9) è l'ambito della variabile locale (§13.6.2), incluso qualsiasi istruzioneincorporata annidata in tale ambito.

    Un variable_reference a una variabile locale è un referenziale valido per una variabile di riferimento solo se la variabile di riferimento viene dichiarata all'interno del contesto di riferimento di tale variabile.

  • function-member: all'interno di una funzione un variable_reference a uno dei seguenti include un contesto di riferimento sicuro del membro funzione:

    • Parametri di valore (§15.6.2.2) in una dichiarazione di membro di funzione, incluso l'implicito this delle funzioni membro della classe e
    • Parametro di riferimento implicito ()(ref§15.6.2.3.3) this di una funzione membro dello struct, insieme ai relativi campi.

    Un variable_reference con contesto di riferimento sicuro del membro funzione è un referenziale valido solo se la variabile di riferimento viene dichiarata nello stesso membro della funzione.

  • caller-context: all'interno di una funzione un variable_reference a uno dei seguenti ha un contesto di riferimento sicuro del chiamante:

    • Parametri di riferimento (§9.2.6) diversi dall'implicito this di una funzione membro struct;
    • Campi membri ed elementi di tali parametri;
    • Campi membro dei parametri del tipo di classe; e
    • Elementi dei parametri di tipo matrice.

Un variable_reference con contesto di riferimento sicuro del contesto del chiamante può essere il riferimento di un riferimento restituito.

Questi valori formano una relazione di annidamento dal più stretto (blocco di dichiarazione) al più ampio (contesto chiamante). Ogni blocco annidato rappresenta un contesto diverso.

Esempio: il codice seguente mostra esempi dei diversi contesti ref-safe.Example: The following code shows examples of the different ref-safe-contexts. Le dichiarazioni mostrano il contesto di riferimento per un referenziale come espressione di inizializzazione per una ref variabile. Gli esempi mostrano il contesto di riferimento per un riferimento restituito:

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

esempio finale.

Esempio: per struct i tipi, il parametro implicito this viene passato come parametro di riferimento. Il contesto di riferimento dei campi di un struct tipo come membro della funzione impedisce la restituzione di tali campi in base al riferimento restituito. Questa regola impedisce il codice seguente:

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

esempio finale.

9.7.2.2 Contesto sicuro variabile locale

Per una variabile vlocale:

  • Se v è una variabile di riferimento, il contesto di riferimento è uguale al contesto di riferimento dell'espressione di inizializzazione.
  • In caso contrario, il contesto ref-safe è declaration-block.

9.7.2.3 Contesto sicuro dei parametri

Per un parametro p:

  • Se p è un parametro di riferimento o di input, il contesto di riferimento è il contesto del chiamante. Se p è un parametro di input, non può essere restituito come scrivibile ref , ma può essere restituito come ref readonly.
  • Se p è un parametro di output, il contesto di riferimento è il contesto del chiamante.
  • In caso contrario, se p è il this parametro di un tipo struct, il relativo contesto di riferimento è il membro della funzione.
  • In caso contrario, il parametro è un parametro value e il relativo contesto di riferimento è il membro della funzione.

9.7.2.4 Contesto sicuro dei campi

Per una variabile che designa un riferimento a un campo, e.F:

  • Se e è di un tipo riferimento, il contesto di riferimento è il contesto del chiamante.
  • In caso contrario, se e è di un tipo valore, il contesto di riferimento è uguale al contesto di riferimento sicuro di e.

Operatori 9.7.2.5

L'operatore condizionale (§12.18), c ? ref e1 : ref e2e l'operatore di assegnazione di riferimento (= ref e§12.21.1) hanno variabili di riferimento come operandi e producono una variabile di riferimento. Per questi operatori, il contesto di riferimento del risultato è il contesto più ristretto tra i contesti ref-safe di tutti gli ref operandi.

9.7.2.6 Chiamata di funzione

Per una variabile risultante c da una chiamata di funzione ref-returning, il contesto ref-safe è il più piccolo dei contesti seguenti:

  • Contesto del chiamante.
  • Contesto sicuro di riferimento di tutte le refespressioni di argomento , oute in (escluso il ricevitore).
  • Per ogni parametro di input, se è presente un'espressione corrispondente che è una variabile ed esiste una conversione identity tra il tipo della variabile e il tipo del parametro, il contesto di riferimento della variabile, altrimenti il contesto di inclusione più vicino.
  • Contesto sicuro (§16.4.12) di tutte le espressioni di argomento (incluso il ricevitore).

Esempio: l'ultimo punto elenco è necessario per gestire il codice, ad esempio

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

esempio finale

Una chiamata di proprietà e una chiamata dell'indicizzatore ( get o set) viene considerata una chiamata di funzione della funzione di accesso sottostante dalle regole precedenti. Una chiamata di funzione locale è una chiamata di funzione.

9.7.2.7 Valori

Il contesto di riferimento di un valore è il contesto di inclusione più vicino.

Nota: ciò si verifica in una chiamata, ad M(ref d.Length) esempio dove d è di tipo dynamic. È anche coerente con gli argomenti corrispondenti ai parametri di input. nota finale

9.7.2.8 Chiamate al costruttore

Un'espressione new che richiama un costruttore rispetta le stesse regole di una chiamata al metodo (§9.7.2.6) considerata la restituzione del tipo costruito.

9.7.2.9 Limitazioni sulle variabili di riferimento

  • Né un parametro di riferimento, né un parametro di output, né un parametro di input, né un ref parametro locale o locale di un ref struct tipo devono essere acquisiti da espressione lambda o funzione locale.
  • Né un parametro di riferimento, né un parametro di output, né un parametro di input, né un parametro di un ref struct tipo devono essere un argomento per un metodo iteratore o un async metodo.
  • Né un ref oggetto locale né un locale di un ref struct tipo devono trovarsi nel contesto al punto di un'istruzione yield return o di un'espressione await .
  • Per una riassegnazione e1 = ref e2ref, il contesto di riferimento di e2 deve essere almeno un contesto ampio come contesto di riferimento di e1.
  • Per un'istruzione return ref e1ref return , il contesto di riferimento di deve essere il contesto chiamante e1 .