Le variabili non inizializzate C # sono pericolose?

Ho familiarità con la specifica C #, sezione 5.3 che dice che una variabile deve essere assegnata prima dell’uso.

In C e C ++ non gestito questo ha senso in quanto lo stack non viene cancellato e la posizione di memoria utilizzata per un puntatore potrebbe essere ovunque (portando a un bug difficile da rintracciare).

Ma ho l’impressione che non ci siano valori veramente “non assegnati” consentiti dal runtime. In particolare, un tipo di riferimento non inizializzato avrà sempre un valore nullo, mai il valore rimasto da una precedente chiamata del metodo o valore casuale.

È corretto, o ho erroneamente supposto che un assegno per null sia sufficiente in tutti questi anni? Puoi avere trully variabili non calibrate in C #, oppure il CLR si occupa di questo e c’è sempre qualche set di valori.

Ho l’impressione che non ci siano valori veramente “non assegnati” consentiti dal runtime. In particolare, un tipo di riferimento non inizializzato avrà sempre un valore nullo, mai il valore rimasto da una precedente chiamata del metodo o valore casuale. È corretto?

Prendo atto che nessuno ha ancora risposto alla tua domanda.

La risposta alla domanda che hai effettivamente chiesto è “sorta”.

Come altri hanno notato, alcune variabili (elementi di array, campi e così via) sono classificate come automaticamente “inizialmente assegnate” al loro valore predefinito. (Che è null per i tipi di riferimento, zero per i tipi numerici, false per i bool e la ricorsione naturale per le strutture definite dall’utente).

Alcune variabili non sono classificate come inizialmente assegnate; le variabili locali, in particolare, non sono inizialmente assegnate. Devono essere classificati dal compilatore come “definitivamente assegnati” in tutti i punti in cui vengono utilizzati i loro valori .

La tua domanda quindi è in realtà “è una variabile locale classificata come non assegnata definitivamente in realtà inizialmente assegnata allo stesso modo in cui un campo sarebbe?” E la risposta a questa domanda è , in pratica, il runtime inizialmente assegna tutti i locali.

Questo ha molte belle proprietà. Innanzitutto, è ansible osservarli nel debugger per essere nel loro stato predefinito prima del loro primo incarico. In secondo luogo, non vi è alcuna possibilità che il garbage collector venga ingannato nel dereferenziare un puntatore errato solo perché è rimasto dello spazzino nello stack che ora viene trattato come riferimento gestito. E così via.

Il runtime è autorizzato a lasciare lo stato iniziale della gente del posto come se la spazzatura si fosse verificata lì se poteva farlo in modo sicuro. Ma come dettaglio di implementazione, non sceglie mai di farlo. Zeri fuori la memoria per una variabile locale in modo aggressivo.

La ragione per la regola secondo cui i locali devono essere assegnati in modo definitivo prima di essere utilizzati non impedisce di osservare lo stato di non inizializzazione del locale. Ciò è già inosservabile perché il CLR cancella in modo aggressivo i locali ai valori predefiniti, esattamente come per i campi e gli elementi dell’array. Il motivo per cui questo è illegale in C # è perché l’utilizzo di un locale non assegnato ha un’alta probabilità di essere un bug. Semplicemente rendiamo illegale, e quindi il compilatore ti impedisce di avere mai un tale bug.

Per quanto ne so, ogni tipo ha un valore predefinito designato.

In base a questo documento, ai campi di classi viene assegnato il valore predefinito.

http://msdn.microsoft.com/en-us/library/aa645756(v=vs.71).aspx

Questo documento dice che i seguenti hanno sempre i valori predefiniti assegnati automaticamente.

  • Variabili statiche
  • Variabili di istanza delle istanze di class.
  • Variabili di istanza delle variabili struct inizialmente assegnate.
  • Elementi di matrice.
  • Parametri di valore
  • Parametri di riferimento
  • Variabili dichiarate in una clausola catch o in una dichiarazione foreach.

http://msdn.microsoft.com/en-us/library/aa691173(v=vs.71).aspx

Maggiori informazioni sui valori predefiniti qui: http://msdn.microsoft.com/en-us/library/83fhsxwc.aspx

Dipende da dove viene dichiarata la variabile. Le variabili dichiarate all’interno di una class vengono automaticamente inizializzate utilizzando il valore predefinito.

 object o; void Method() { if (o == null) { // this will execute } } 

Le variabili dichiarate all’interno di un metodo non sono inizializzate, ma quando la variabile viene utilizzata per la prima volta il compilatore verifica che sia inizializzato, quindi il codice non verrà compilato.

 void Method() { object o; if (o == null) // compile error on this line { } } 

In particolare, un tipo di riferimento non inizializzato avrà sempre un valore nullo

Penso che tu stia mescolando variabili locali e variabili membro. La sezione 5.3 parla specificamente delle variabili locali. A differenza delle variabili membro che vengono impostate come predefinite, le impostazioni locali non vengono mai impostate come valore nullo o altro: devono semplicemente essere assegnate prima di essere lette per la prima volta. La Sezione 5.3 spiega le regole che il compilatore usa per determinare se una variabile locale è stata assegnata o meno.

Ci sono 3 modi in cui una variabile può essere assegnata a un valore iniziale:

  1. Per impostazione predefinita, ciò accade (ad esempio) se si dichiara una variabile di class senza assegnare un valore iniziale, quindi il valore iniziale diventa default(type) dove type è qualsiasi tipo dichiari la variabile.

  2. Con un inizializzatore – questo succede quando dichiari una variabile con un valore iniziale, come in int i = 12;

  3. Qualsiasi punto prima che il suo valore venga recuperato – ciò accade (ad esempio) se si dispone di una variabile locale senza valore iniziale. Il compilatore garantisce che non ci siano percorsi di codice raggiungibili che leggeranno il valore della variabile prima che sia assegnato.

Il compilatore non ti permetterà mai di leggere il valore di una variabile che non è stata inizializzata, quindi non dovrai mai preoccuparti di cosa succederebbe se ci provassi.

Tutti i tipi di dati primitivi hanno valori predefiniti quindi non è necessario preoccuparsi per loro.

Tutti i tipi di riferimento sono inizializzati su valori nulli quindi se si lasciano i propri tipi di riferimento non inizializzati e quindi si chiama qualche metodo o proprietà su quel tipo di riferimento null si otterrebbe un’eccezione di runtime che dovrebbe essere gestita correttamente.

Ancora una volta, tutti i tipi di Nullable devono essere controllati per il valore nullo o predefinito se non vengono inizializzati come segue:

  int? num = null; if (num.HasValue == true) { System.Console.WriteLine("num = " + num.Value); } else { System.Console.WriteLine("num = Null"); } //y is set to zero int y = num.GetValueOrDefault(); // num.Value throws an InvalidOperationException if num.HasValue is false try { y = num.Value; } catch (System.InvalidOperationException e) { System.Console.WriteLine(e.Message); } 

Ma non otterrai nessun errore complimentoso se lasci tutte le tue variabili non inizializzate come il compilatore non si lamenterà, è solo il tempo di esecuzione di cui devi preoccuparti.