Dichiarazioni variabili che seguono le dichiarazioni

Un problema è emerso su un altro forum e sapevo come risolverlo, ma ha rivelato una caratteristica del compilatore peculiare per me. La persona stava ricevendo l’errore “L’istruzione incorporata non può essere una dichiarazione o un’istruzione etichettata” perché avevano una dichiarazione di una variabile che segue un’istruzione if senza parentesi. Non era quello il loro intento, ma avevano commentato la riga di codice immediatamente successiva all’istruzione if, che rendeva la dichiarazione delle variabili la linea di codice de facto da eseguire. Ad ogni modo, quello è lo sfondo, che mi porta a questo.

Il seguente codice è illegale

if (true) int i = 7; 

Tuttavia, se lo racchiudi tra parentesi, è tutto legale.

 if (true) { int i = 7; } 

Nessun pezzo di codice è utile. Eppure il secondo è OK. Qual è in particolare la spiegazione di questo comportamento?

La specifica del linguaggio C # distingue tra tre tipi di affermazioni (si veda il capitolo 8 per maggiori dettagli). In generale puoi avere queste affermazioni:

  • etichetta-dichiarazione – la mia ipotesi che questo è per la vecchia istruzione goto
  • dichiarazione-dichiarazione – che sarebbe una dichiarazione variabile
  • embedded-statement – che include praticamente tutte le dichiarazioni rimanenti

Nella dichiarazione if il corpo deve essere incorporato , che spiega perché la prima versione del codice non funziona. Ecco la syntax di if dalla specifica (sezione 8.7.1):

if ( embedded-statement ) ( espressione booleana )
if ( espressione booleana ) istruzione -embedded altra istruzione incorporata

Una dichiarazione variabile è dichiarazione-dichiarazione , quindi non può apparire nel corpo. Se si racchiude la dichiarazione tra parentesi, si otterrà un blocco di istruzioni, che è un’istruzione incorporata (e quindi può apparire in quella posizione).

Quando non includi le parentesi, esegue la riga successiva come se fosse circondata da parentesi. Dal momento che non ha molto senso dichiarare una variabile in quella linea (non saresti in grado di usarla mai), il compilatore C # non permetterà che questo ti impedisca di farlo accidentalmente senza sapere (che potrebbe introdurre bug sottili ).

Ecco parte di Eric Lippert che ha da dire sul compilatore C # su questa risposta SO sulla risoluzione dei nomi:

… C # non è un linguaggio “indovina quale utente intendesse” … il compilatore di design si lamenta ad alta voce se la migliore corrispondenza è qualcosa che non funziona

Tutti i compilatori ti permetteranno di compilare un codice che è inutile o di uso estremamente basso. Ci sono semplicemente troppi modi in cui uno sviluppatore può usare il linguaggio per creare costrutti senza alcuno scopo. Avere il compilatore catturato tutti loro è semplicemente uno sforzo eccessivo e in genere non ne vale la pena.

Il secondo caso viene richiamato direttamente nella specifica del linguaggio C # all’inizio della sezione 8.0

L’esempio genera un errore in fase di compilazione poiché un’istruzione if richiede una istruzione incorporata anziché un’istruzione per il relativo ramo if. Se questo codice fosse permesso, allora la variabile sarebbe dichiarata, ma non potrebbe mai essere usata. Si noti, tuttavia, che posizionando la dichiarazione di i in un blocco, l’esempio è valido.

Codice di esempio

 void F(bool b) { if (b) int i = 44; } 

Aggiungere le parentesi graffe di chiusura e di apertura nella parte opposta di if mi ha aiutato come ho fatto di seguito rispetto a quello che stavo facendo prima di aggiungerle;

Prima: questo ha causato l’errore:

 protected void btnAdd_Click(object sender, EventArgs e) { if (btnAdd.Text == "ADD") { CATEGORY cat = new CATEGORY { NAME = tbxCategory.Text.Trim(), TOTALSALEVALUE = tbxSaleValue.Text.Trim(), PROFIT = tbxProfit.Text.Trim() }; dm.AddCategory(cat, tbxCategory.Text.Trim()); } else // missing brackets - this was causing the error var c = getCategory(); c.NAME = tbxCategory.Text.Trim(); c.TOTALSALEVALUE = tbxSaleValue.Text.Trim(); c.PROFIT = tbxProfit.Text.Trim(); dm.UpdateCategory(c); btnSearchCat_Click(btnSearchCat, e); } 

Dopo: aggiunte parentesi nel ramo else

 protected void btnAdd_Click(object sender, EventArgs e) { if (btnAdd.Text == "ADD") { CATEGORY cat = new CATEGORY { NAME = tbxCategory.Text.Trim(), TOTALSALEVALUE = tbxSaleValue.Text.Trim(), PROFIT = tbxProfit.Text.Trim() }; dm.AddCategory(cat, tbxCategory.Text.Trim()); } else { var c = getCategory(); c.NAME = tbxCategory.Text.Trim(); c.TOTALSALEVALUE = tbxSaleValue.Text.Trim(); c.PROFIT = tbxProfit.Text.Trim(); dm.UpdateCategory(c); } btnSearchCat_Click(btnSearchCat, e); }