La variabile dichiarata in for-loop è una variabile locale?


133

Uso C # da parecchio tempo ma non ho mai realizzato quanto segue:

 public static void Main()
 {
     for (int i = 0; i < 5; i++)
     {

     }

     int i = 4;  //cannot declare as 'i' is declared in child scope                
     int A = i;  //cannot assign as 'i' does not exist in this context
 }

Quindi perché non posso usare il valore di 'i' al di fuori del blocco for se non mi consente di dichiarare una variabile con questo nome?

Ho pensato che la variabile iteratore utilizzata da un for-loop sia valida solo nel suo ambito.


8
Perché l'ambito del blocco esterno include l'ambito del ciclo for
V4Vendetta,

3
Un modo in cui ci penso (e alcune guide di stile del codice lo richiedono, specialmente per i linguaggi di tipo dinamico) è che tutte le variabili dichiarate in un ambito potrebbero essere dichiarate all'inizio di tale ambito, il che significa che la tua funzione potrebbe essere riscrittaint i, A; for(int i = 0; i < 5; i++){ } i=4; A=i
Keith,

2
@ V4Vendetta: è più il contrario. Il blocco interno è una casella nera per il blocco padre.
Sebastian Mach,

4
A parte i motivi tecnici per cui questo non è possibile, perché questo (o una sua variante) sarebbe mai una cosa sensata da fare ?! Ovviamente ha uno scopo diverso, quindi dagli un nome diverso.
Dave,

Non me ne sono reso conto affatto, ma sembra una restrizione completamente stupida.
alan2here

Risposte:


119

Il motivo per cui non è consentito definire una variabile con lo stesso nome sia nel for-loop che all'esterno del for-loop è perché le variabili nell'ambito esterno sono valide nell'ambito interno. Ciò significa che ci sarebbero due variabili 'i' all'interno del for-loop se questo fosse permesso.

Vedi: MSDN Scopes

In particolare:

L'ambito di una variabile locale dichiarata in una dichiarazione di variabile locale (Sezione 8.5.1) è il blocco in cui si verifica la dichiarazione.

e

L'ambito di una variabile locale dichiarata in un inizializzatore for di un'istruzione for (Sezione 8.8.3) è l'inizializzatore for, la condizione for, l'iteratore for e l'istruzione contenuta dell'istruzione for.

E anche: dichiarazioni di variabili locali (Sezione 8.5.1 della specifica C #)

In particolare:

L'ambito di una variabile locale dichiarata in una dichiarazione di variabile locale è il blocco in cui si verifica la dichiarazione. È un errore fare riferimento a una variabile locale in una posizione testuale che precede il dichiaratore della variabile locale della variabile locale. Nell'ambito di una variabile locale, è un errore in fase di compilazione dichiarare un'altra variabile locale o costante con lo stesso nome.

(Enfasi mia.)

Ciò significa che l'ambito del ituo for-loop è il for-loop. Considerando che l'ambito iesterno del for-loop è l'intero metodo principale più il for-loop. Significa che avresti due occorrenze iall'interno del loop che non sono valide secondo quanto sopra.

Il motivo per cui non ti è permesso farlo int A = i;è perché int iè solo per uso all'interno del forciclo. Pertanto non è più accessibile al di fuori del forciclo.

Come puoi vedere, entrambi questi problemi sono il risultato dell'ambito; il primo numero ( int i = 4;) comporterebbe due ivariabili nell'ambito del forciclo. Considerando int A = i;che porterebbe ad accesso a una variabile che è fuori dal campo di applicazione.

Quello che potresti fare invece è dichiarare idi essere compreso nell'intero metodo e quindi utilizzarlo sia nel metodo che nell'ambito del ciclo for. Ciò eviterà di infrangere entrambe le regole.

public static void Main()
{
    int i;

    for (i = 0; i < 5; i++)
    {

    }

    // 'i' is only declared in the method scope now, 
    // no longer in the child scope -> valid.
    i = 4;

    // 'i' is declared in the method's scope -> valid. 
    int A = i;
}

MODIFICA :

Il compilatore C # potrebbe ovviamente essere modificato per consentire a questo codice di essere compilato in modo abbastanza valido. Dopo tutto ciò è valido:

for (int i = 0; i < 5; i++)
{
    Console.WriteLine(i);
}

for (int i = 5; i > 0; i--)
{
    Console.WriteLine(i);
}

Ma sarebbe davvero utile per la tua leggibilità e manutenibilità del codice poter scrivere codice come:

public static void Main()
{
    int i = 4;

    for (int i = 0; i < 5; i++)
    {
        Console.WriteLine(i);
    }

    for (int i = 5; i > 0; i--)
    {
        Console.WriteLine(i);
    }

    Console.WriteLine(i);
}

Pensa al potenziale errore qui, l'ultima istampa 0 o 4? Ora questo è un esempio molto piccolo, che è abbastanza facile da seguire e tracciare ma è sicuramente molto meno gestibile e leggibile che aver dichiarato l'esterno icon un nome diverso.

NB:

Nota: le regole di scoping di C # differiscono dalle regole di scoping di C ++ . In C ++ le variabili sono solo nell'ambito da dove vengono dichiarate fino alla fine del blocco. Il che renderebbe il tuo codice un costrutto valido in C ++.


Ha senso, penso che dovrebbe essere comunque un errore sulla ivariabile interna ; questo mi sembra più ovvio.
George Duckett,

Ma l'ambito è per il comando e il suo {}? O significa che il suo genitore {}?
Giovanni V,

2
Bene, se dichiaro 'A' DOPO l'affermazione for, non è valida nel ciclo for come viene dichiarato in seguito. Ecco perché non capisco perché lo stesso nome non possa essere utilizzato.
Giovanni V,

9
Forse questa risposta dovrebbe, per completezza, sottolineare che le regole di ambito C # sono diverse da quelle per C ++ in questo caso. In C ++, le variabili sono solo nell'ambito da dove vengono dichiarate fino alla fine del blocco (consultare msdn.microsoft.com/en-us/library/b7kfh662(v=vs.80).aspx ).
AAT,

2
Si noti che Java adotta un approccio intermedio tra C ++ e C #: poiché l'esempio dell'OP sarebbe valido in Java, ma se la idefinizione esterna fosse spostata prima del ciclo for, la idefinizione interna sarebbe contrassegnata come non valida.
Nicola Musatti,

29

La risposta di J.Kommer è corretta: in breve, è illegale dichiarare una variabile locale in uno spazio di dichiarazione delle variabili locali che si sovrappone ad un altro spazio di dichiarazione delle variabili locali che ha un locale con lo stesso nome.

C'è una regola aggiuntiva di C # che viene violata anche qui. La regola aggiuntiva è che è illegale utilizzare un nome semplice per fare riferimento a due entità diverse all'interno di due spazi di dichiarazione delle variabili locali sovrapposte. Quindi non solo il tuo esempio è illegale, ma anche questo è illegale:

class C
{
    int x;
    void M()
    {
        int y = x;
        if(whatever)
        {
            int x = 123;

Perché ora il nome semplice "x" è stato utilizzato all'interno dello spazio di dichiarazione delle variabili locali di "y" per indicare due cose diverse: "this.x" e il locale "x".

Vedi http://blogs.msdn.com/b/ericlippert/archive/tags/simple+names/ per ulteriori analisi di questi problemi.


2
Inoltre, è interessante notare che il codice di esempio verrà compilato quando si apporta la modificaint y = this.x;
Phil

4
@Phil: Corretto. this.xnon è un nome semplice .
Eric Lippert,

13

Esiste un modo per dichiarare e utilizzare iall'interno del metodo dopo il ciclo:

static void Main()
{
    for (int i = 0; i < 5; i++)
    {

    }

    {
        int i = 4;
        int A = i;
    }
}

Puoi farlo in Java (potrebbe provenire da C non sono sicuro). Naturalmente è un po 'disordinato per il bene di un nome di variabile.


Sì, questo proviene da C / C ++.
Branko Dimitrijevic,

1
Non lo sapevo prima. Funzionalità lingua ordinata!

@MathiasLykkegaardLorenzen yes
Chris S

7

Se avessi dichiarato i prima del tuo forciclo, pensi che dovrebbe essere ancora valido per dichiararlo all'interno del ciclo?

No, perché allora l'ambito dei due si sovrappone.

Per quanto riguarda l'impossibilità di farlo int A=i;, beh, è ​​semplicemente perché iesiste solo nel forciclo, come dovrebbe fare.


7

Oltre alla risposta di J.Kommer (+1 btw). C'è questo nello standard per ambito NET:

bloccare Se si dichiara una variabile all'interno di un costrutto di blocco come un'istruzione If, ​​l'ambito di tale variabile è solo fino alla fine del blocco. La durata è fino al termine della procedura.

Procedura Se si dichiara una variabile all'interno di una procedura, ma al di fuori di qualsiasi istruzione If, ​​l'ambito è fino alla funzione End Sub o End. La durata della variabile è fino al termine delle procedure.

Pertanto, l'int i decimale all'interno dell'intestazione del ciclo for sarà nell'ambito solo durante il blocco del ciclo for, MA la sua durata dura fino al Main()completamento del codice.


5

Il modo più semplice di pensarci è spostare la dichiarazione esterna di I sopra il ciclo. Dovrebbe diventare ovvio allora.

È lo stesso ambito in entrambi i casi, quindi non può essere fatto.


4

Anche le regole di C # sono molte volte non necessarie in termini di programmazione rigorosa, ma sono lì per mantenere il codice pulito e leggibile.

per esempio, avrebbero potuto farlo in modo che se lo definissi dopo il ciclo, allora va bene, tuttavia qualcuno che legge il tuo codice e ha perso la linea di definizione potrebbe pensare che abbia a che fare con la variabile del ciclo.


2

La risposta di Kommer è tecnicamente corretta. Vorrei parafrasarlo con una vivida metafora dello schermo cieco.

C'è uno schermo cieco unidirezionale tra il blocco for e il blocco esterno che racchiude in modo tale che il codice all'interno del blocco for possa vedere il codice esterno ma il codice nel blocco esterno non riesca a vedere il codice all'interno.

Dal momento che il codice esterno non può vedere dentro, non può usare nulla di dichiarato dentro. Ma poiché il codice nel blocco for può vedere sia all'interno che all'esterno, una variabile dichiarata in entrambi i punti non può essere utilizzata in modo univoco per nome.

Quindi o non lo vedi, o C #!


0

Guardalo nello stesso modo in cui potresti dichiararlo intin un usingblocco:

using (int i = 0) {
  // i is in scope here
}
// here, i is out of scope

Tuttavia, poiché intnon implementato IDisposable, questo non può essere fatto. intTuttavia, può aiutare qualcuno a visualizzare come una variabile viene inserita in un ambito privato.

Un altro modo sarebbe quello di dire,

if (true) {
  int i = 0;
  // i is in scope here
}
// here, i is out of scope

Spero che questo aiuti a visualizzare ciò che sta succedendo.

Mi piace molto questa funzione, poiché dichiarare intdall'interno del forciclo mantiene il codice gradevole e preciso.


WTF? Se hai intenzione di taggare un NEG, fai in modo che le palle spieghino il perché.
jp2code

Non il downvoter e penso che il confronto con usingsia abbastanza ok, anche se la semantica è piuttosto diversa (che è forse il motivo per cui è stata sottoposta a downgrade). Si noti che if (true)è ridondante. Rimuovilo e avrai un blocco di scoping.
Abel
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.