2013-02-27 2 views
14

Если у меня есть цикл, например, как показано ниже:C# петли: перебор массива

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

ли анонимный массив строк получить создается один раз на начальном этапе, или воссоздан один раз для каждого прохода?

Я считаю, что первый, но коллеги убеждены, что это ошибка, ожидающая, потому что они говорят, что каждая итерация цикла foreach приводит к созданию нового массива строк.

Код демонтажа VS предполагает, что я прав, но я хочу быть уверенным.

Причина, по которой мы смотрим на это, - попытаться понять загадочную ошибку, сообщающую о том, что коллекция была изменена во время итерации по ней.

+10

Вы можете легко протестировать его, вместо этого добавить значение «DateTime» вместо строк и проверить элементы на каждой итерации. –

+0

Другой способ показать их, что оценивается только один раз, - это выполнить код в режиме отладки. Код, который создает массив строк, будет подсвечиваться только один раз. –

+0

или 'new string [] {" pass1 "," pass "+ i ++," pass3 "}' – Nolonar

ответ

26

Согласно Eric Lippert blog и specification, цикл Еогеасп является синтаксически:

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

Как вы можете видеть, переписчик создается до цикла.

@Rawling правильно указал, что массив обработан немного другим компилятором. Foreach loop оптимизирован в для цикл с массивами. Согласно The Internals of C# foreach кода для C# 5 будет выглядеть следующим образом:

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

Инициализация также происходит только один раз.

+4

+1 Начиная с C# 5.0 переменная 'pass' будет находиться внутри цикла. – Botz3000

+0

@DominicKexel хорошее уведомление, в переменной C# 5 pass будет объявлено внутри цикла, но перечислитель будет создан до цикла :) –

+4

Я думаю, что это немного отличается для массива ... не превращает ли он 'foreach' loop в цикл 'for'? – Rawling

1

Вы можете проверить это (много способов сделать это, но это один из вариантов):

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

Тогда посмотрим, что выйдет.

Вы найдете, что вы правы - его единственное исполнение было выполнено один раз.

+1

Спасибо - вопрос ответил. – haughtonomous

2

Он создается только один раз изначально.

Я попытался предложение по Офер Зелиг (с комментариями)

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

И поставил точку останова. Он будет давать одинаковые секунды для всех трех итераций, даже если вы ждете между итерациями.

+0

Хороший способ гарантировать :) Я рекомендую использовать 'new [] {DateTime.Now, DateTime.Now, DateTime.Now}', чтобы сделать код более коротким :) – nawfal

5

Если вы смотрите в ILSpy, этот код переводится в нечто вроде

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

да так, массив создается только один раз.

Однако лучше всего убедить своих коллег, вероятно, в разделе 8.8.4 спецификации C#, которая расскажет вам, в сущности, о том, что делает ответ Лазы Березовского.

+0

+1 и спасибо за указание на различные компиляции foreach для массивов! –

1

Приведенный ниже пример должен ответить на вопрос, воссоздан ли массив.

 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!"); 
     } 
Смежные вопросы