2013-02-27 7 views
14

Jeśli mam pętlę takie jak poniżej:C# pętle: iteracja tablicy

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

sposób anonimowy array ciąg dostać utworzone, gdy początkowo lub odtworzone raz na każdym przejściu?

Wierzę, że ci pierwsi, ale koledzy są przekonani, że to błąd, który czeka, aby się wydarzyć, ponieważ mówią, że każda iteracja pętli foreach powoduje utworzenie nowej tablicy napisów.

Kod demontażu VS sugeruje, że mam rację, ale chcę mieć pewność.

Powodem, dla którego na to patrzymy jest próba zrozumienia tajemniczego błędu, który informuje, że kolekcja została zmieniona podczas iteracji.

+10

Możesz go łatwo przetestować, wstaw wartość 'DateTime' zamiast ciągów i sprawdź elementy w każdej iteracji. –

+0

Innym sposobem pokazania im, że jest oceniany tylko raz, jest przejście przez kod w trybie debugowania. Kod, który tworzy tablicę znaków, zostanie podświetlony tylko raz. –

+0

lub 'nowy ciąg [] {" pass1 "," pass "+ i ++," pass3 "}' – Nolonar

Odpowiedz

26

Według Eric Lippert blog i specification, pętla foreach to cukier syntaktyczny dla:

{ 
    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(); 
    } 
} 

Jak widać, wyliczający jest tworzony przed pętlą.

@Rawling poprawnie wskazuje, że tablica jest traktowana trochę inaczej przez kompilator. Foreach pętla jest zoptymalizowana pod kątem dla pętli z tablicami. Według The Internals of C# foreach kodu dla C# 5 będzie wyglądać następująco:

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; 
} 

Inicjowanie również dzieje się tylko raz.

+4

+1 Począwszy od C# 5.0, zmienna 'pass' będzie w pętli. – Botz3000

+0

@ DominicKexel to dobry anons, w C# 5 zmienna pass zostanie zadeklarowana wewnątrz pętli, ale moduł wyliczający nadal będzie utworzony przed pętlą :) –

+4

Myślę, że jest to subtelnie odmienne dla tablicy ... czy nie zmienia ona pętli foreach? do pętli 'for'? – Rawling

1

Można go przetestować (mnóstwo sposobów, aby to zrobić, ale jest to jedna opcja):

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

potem zobaczymy co wyjdzie.

Przekonasz się, że masz rację - wykonano ją tylko raz.

+1

Dzięki - pytanie odpowiedziało. – haughtonomous

2

Jest tworzony tylko raz na początku.

Próbowałem sugestię Ofer Zelig (z komentarzami)

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

i umieszcza punkt przerwania. Daje te same sekundy dla wszystkich 3 iteracji, nawet jeśli czekasz pomiędzy iteracjami.

+0

Dobry sposób na zapewnienie :) Polecam używanie 'new [] {DateTime.Now, DateTime.Now, DateTime.Now}' aby kod wyglądał krótko :) – nawfal

5

Jeśli spojrzeć w ILSpy, kod ten jest tłumaczony na coś takiego

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

więc tak, tablica jest tworzony tylko raz.

Jednak najlepszym punktem odniesienia, aby przekonać swoich kolegów jest prawdopodobnie sekcja 8.8.4 specyfikacji C#, która powie ci w istocie, co robi odpowiedź LazyBerezovsky.

+0

+1 i dziękuję za wskazanie różnych kompilacji foreach dla tablic! –

1

Poniższy przykład powinien odpowiedzieć na pytanie, czy tablica zostanie odtworzona, czy nie.

 int i = 0; 
     int last = 0; 

     foreach (int pass in new int[] { i++, i++, i++, i++, i++, i++, i++ }) 
     { 
      if (pass != last) 
      { 
       throw new Exception("Array is reintialized!"); 
      } 
      last++; 
     } 

     if (i > 7) 
     { 
      throw new Exception("Array is reintialized!"); 
     }