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 edi
è 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 diM
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'istruzioneforeach
.
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'istruzioneforeach
.
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
, ,if
while
checked
unchecked
using
lock
foreach
for
do
, oswitch
è 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, checked
o 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 b
switch 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'opzione
default
, 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
, continue
o 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 otry
catch
--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 otry
catch
--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'istruzionecatch
try
-: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
in
modificatori ,out
oref
. - 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 dopoarr[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'istruzioneif
, ma non nell'altra. Nell'istruzioneif
nel metodoF
, la variabile viene assegnata sicuramentei
nella prima istruzione incorporata perché l'esecuzione dell'espressione(i = y)
precede sempre l'esecuzione di questa istruzione incorporata. Al contrario, la variabilei
non viene assegnata in modo definitivo nella seconda istruzione incorporata, poichéx >= 0
potrebbe essere stata testata false, con conseguente mancata assegnazione della variabilei
.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'istruzioneif
, ma non nell'altra. Nell'istruzioneif
nel metodoG
, la variabile viene assegnata sicuramentei
nella seconda istruzione incorporata perché l'esecuzione dell'espressione(i = y)
precede sempre l'esecuzione di questa istruzione incorporata. Al contrario, la variabilei
non viene assegnata in modo definitivo nella prima istruzione incorporata, poichéx >= 0
potrebbe essere stata testata true, con conseguente mancata assegnazione della variabilei
.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 div
dopo expr è sicuramente assegnato. - In caso contrario, se lo stato di
v
dopo expr_operand viene "assegnato definitivamente dopo false espressione", lo stato div
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.
- Se lo stato di
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.
- Se expr_first è un'espressione costante (§12.23) con valore
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 valore
true
, 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 valore
false
, 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.
- Se expr_cond è un'espressione costante (§12.23) con valore
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 din
all'esterno della funzione anonima.esempio finale
9.4.4.32 Espressioni throw
Per un'espressione espr del form:
throw
thrown_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 legges
senza assegnarla. Si tratta di un errore seF1
viene chiamato primas
dell'assegnazione definitiva.F2
i
assegna prima di leggerlo. Può essere chiamato primai
di essere assegnato sicuramente. Inoltre,F3
può essere chiamato dopoF2
perchés2
è sicuramente assegnato inF2
.void M() { string s; int i; string s2; // Error: Use of unassigned local variable s: F1(); // OK, F2 assigns i before reading it. F2(); // OK, i is definitely assigned in the body of F2: s = i.ToString(); // OK. s is now definitely assigned. F1(); // OK, F3 reads s2, which is definitely assigned in F2. F3(); void F1() { Console.WriteLine(s); } void F2() { i = 5; // OK. i is definitely assigned. Console.WriteLine(i); s2 = i.ToString(); } void F3() { Console.WriteLine(s2); } }
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
, ushort
uint
byte
float
char
sbyte
short
int
e 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
, ulong
double
, 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.
- Parametri di valore (§15.6.2.2) in una dichiarazione di membro di funzione, incluso l'implicito
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.
- Parametri di riferimento (§9.2.6) diversi dall'implicito
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 implicitothis
viene passato come parametro di riferimento. Il contesto di riferimento dei campi di unstruct
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 v
locale:
- 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. Sep
è un parametro di input, non può essere restituito come scrivibileref
, ma può essere restituito comeref readonly
. - Se
p
è un parametro di output, il contesto di riferimento è il contesto del chiamante. - In caso contrario, se
p
è ilthis
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 die
.
Operatori 9.7.2.5
L'operatore condizionale (§12.18), c ? ref e1 : ref e2
e 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
ref
espressioni di argomento ,out
ein
(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 doved
è di tipodynamic
. È 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 unref 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 unasync
metodo. - Né un
ref
oggetto locale né un locale di unref struct
tipo devono trovarsi nel contesto al punto di un'istruzioneyield return
o di un'espressioneawait
. - Per una riassegnazione
e1 = ref e2
ref, il contesto di riferimento die2
deve essere almeno un contesto ampio come contesto di riferimento die1
. - Per un'istruzione
return ref e1
ref return , il contesto di riferimento di deve essere il contesto chiamantee1
.
ECMA C# draft specification