2013-02-27 13 views
14

Se ho un ciclo, come di seguito:C# loop: l'iterazione attraverso una serie

foreach (string pass in new string[] { "pass1", "pass2", "pass3" }) 
{ 
x = pass; //etc 
} 

valuta la matrice di stringhe anonima vengono creati una volta inizialmente, o ricreato una volta per ogni passaggio?

Credo che il primo, ma i colleghi sono convinti che questo è un errore che si aspetta che accada perché dicono che ogni iterazione del ciclo foreach determina la creazione di una nuova serie di stringhe.

Il codice di disassemblaggio VS suggerisce che ho ragione, ma voglio essere sicuro.

Il motivo per cui stiamo osservando questo è cercare di capire un bug misterioso che segnala che una raccolta è stata cambiata mentre si scorre su di essa.

+10

È possibile testarlo facilmente, inserire un valore 'DateTime' ad esempio invece di stringhe e ispezionare i membri in ciascuna iterazione. –

+0

Un altro modo per mostrare che viene valutato solo una volta è quello di scorrere il codice in modalità di debug. Il codice che crea l'array di stringhe verrà evidenziato solo una volta. –

+0

o 'nuova stringa [] {" pass1 "," pass "+ i ++," pass3 "}' – Nolonar

risposta

26

Secondo Eric Lippert blog e specification, ciclo foreach è uno zucchero sintattico per:

{ 
    IEnumerator<string> e = ((IEnumerable<string>)new string[] { "pass1", "pass2", "pass3" }).GetEnumerator(); 
    try 
    { 
    string pass; // OUTSIDE THE ACTUAL LOOP 
     while(e.MoveNext()) 
     { 
     pass = (string)e.Current; 
     x = pass; 
     } 
    } 
    finally 
    { 
     if (e != null) ((IDisposable)e).Dispose(); 
    } 
} 

Come si può vedere, si crea enumeratore prima di loop.

@Rawling correttamente indicato, quell'array ha un aspetto leggermente diverso dal compilatore. Il ciclo Foreach è ottimizzato per per il ciclo con gli array. Secondo The Internals of C# foreach il codice per C# 5 sarà simile:

string[] tempArray; 
string[] array = new string[] { "pass1", "pass2", "pass3" }; 
tempArray = array; 

for (string counter = 0; counter < tempArray.Length; counter++) 
{ 
    string pass = tempArray[counter]; 
    x = pass; 
} 

inizializzazione avviene anche solo una volta.

+4

+1 A partire da C# 5.0, la variabile 'pass' sarebbe all'interno del ciclo. – Botz3000

+0

@DominicKexel buona notizia, in C# 5 la variabile pass sarà dichiarata all'interno del ciclo, ma l'enumeratore sarà ancora creato prima del ciclo :) –

+4

Penso che questo sia leggermente diverso per un array ... non gira un ciclo 'foreach' in un ciclo 'for'? – Rawling

1

Si potrebbe testarlo (un sacco di modi per farlo, ma questo è un'opzione):

string pass4 = "pass4"; 
foreach (string pass in new string[] { "pass1", "pass2", "pass3", pass4 }) 
{ 
    pass4="pass5 - oops"; 
    x = pass; //etc 
} 

poi vedere che cosa viene fuori.

Troverete che avete ragione - è stato eseguito solo una volta.

+1

Grazie - domanda risposta. – haughtonomous

2

Viene creato solo una volta inizialmente.

Ho provato il suggerimento di Ofer Zelig (dai commenti)

foreach (DateTime pass in new DateTime[] { DateTime.Now, DateTime.Now, DateTime.Now }) 
{ 
    int x = pass.Second; //etc 
} 

e collocato un punto di interruzione. Darà gli stessi secondi per tutte e 3 le iterazioni anche se attendi tra le iterazioni.

+0

Bel modo per assicurarti :) Raccomando di usare 'new [] {DateTime.Now, DateTime.Now, DateTime.Now}' per far apparire il codice più breve :) – nawfal

5

Se si guarda in ILSpy, questo codice si traduce in qualcosa di simile

string[] array = new string[] 
{ 
    "pass1", 
    "pass2", 
    "pass3" 
}; 
for (int i = 0; i < array.Length; i++) 
{ 
    string pass = array[i]; 
} 

quindi sì, la matrice viene creata solo una volta.

Tuttavia, il miglior riferimento per convincere i tuoi colleghi è probabilmente la sezione 8.8.4 della specifica C#, che ti dirà essenzialmente quale sia la risposta di LazyBerezovsky.

+0

+1 e grazie per indicare la compilazione foreach differente per gli array! –

1

L'esempio seguente dovrebbe rispondere alla domanda se l'array viene ricreato o meno.