Циклы С#: итерация через массив
Если у меня есть цикл, такой как ниже:
foreach (string pass in new string[] { "pass1", "pass2", "pass3" })
{
x = pass; //etc
}
Создает ли анонимный массив строк один раз изначально или воссоздан один раз для каждого прохода?
Я считаю, что первый, но коллеги убеждены, что это ошибка, ожидающая, потому что они говорят, что каждая итерация цикла foreach приводит к созданию нового строкового массива.
Код демонтажа VS предполагает, что я прав, но я хочу быть уверенным.
Причина, по которой мы смотрим на это, - это попытаться понять загадочную ошибку, сообщающую о том, что коллекция была изменена во время итерации по ней.
Ответы
Ответ 1
Согласно блог Эрика Липперта и спецификация, foreach loop - синтаксический сахар для:
{
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 оптимизирован для цикла с массивами. Согласно Internals of С# foreach ваш код для С# 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;
}
Инициализация также происходит только один раз.
Ответ 2
Если вы посмотрите в ILSpy, этот код будет переведен в нечто вроде
string[] array = new string[]
{
"pass1",
"pass2",
"pass3"
};
for (int i = 0; i < array.Length; i++)
{
string pass = array[i];
}
так что да, массив создается только один раз.
Однако лучше всего убедить своих коллег, вероятно, в разделе 8.8.4 спецификации С#, которая расскажет вам, в чем заключается ответ LazyBerezovsky.
Ответ 3
Он создается только один раз изначально.
Я попробовал предложение Офера Зелига (из комментариев)
foreach (DateTime pass in new DateTime[] { DateTime.Now, DateTime.Now, DateTime.Now })
{
int x = pass.Second; //etc
}
И поставил точку останова. Он будет давать одинаковые секунды для всех трех итераций, даже если вы ждете между итерациями.
Ответ 4
Вы можете проверить его (много способов сделать это, но это один из вариантов):
string pass4 = "pass4";
foreach (string pass in new string[] { "pass1", "pass2", "pass3", pass4 })
{
pass4="pass5 - oops";
x = pass; //etc
}
Затем посмотрите, что выйдет.
Вы обнаружите, что вы правы - его только один раз выполнил.
Ответ 5
Приведенный ниже пример должен ответить на вопрос, воссоздан ли массив или нет.
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!");
}