Почему операторы присваивания (=) недействительны в цикле foreach?

Почему операторы присваивания (=) недействительны в цикле foreach? Я использую С#, но я бы предположил, что аргумент одинаков для других языков, поддерживающих foreach (например, PHP). Например, если я делаю что-то вроде этого:

string[] sArray = new string[5];

foreach (string item in sArray)
{
   item = "Some assignment.\r\n";
}

Я получаю сообщение об ошибке: "Невозможно назначить" элемент ", потому что это" переменная итерации foreach ".

Ответ 1

Вот ваш код:

foreach (string item in sArray)
{
   item = "Some assignment.\r\n";
}

Здесь приблизительное приближение того, что делает компилятор с этим:

using (var enumerator = sArray.GetEnumerator())
{
    string item;
    while (enumerator.MoveNext())
    {
        item = enumerator.Current;

        // Your code gets put here
    }
}

Свойство IEnumerator<T>.Current доступно только для чтения, но это на самом деле не актуально здесь, поскольку вы пытаетесь назначить локальную переменную item для нового значения. Проверка времени компиляции, препятствующая этому, заключается в основном в том, чтобы защитить вас от выполнения чего-то, что не будет работать так, как вы ожидаете (т.е. Изменения локальной переменной и не влияющей на базовую коллекцию/последовательность).

Если вы хотите изменить внутренности индексированной коллекции, такой как string[] при перечислении, традиционным способом является использование цикла for вместо foreach:

for (int i = 0; i < sArray.Length; ++i)
{
    sArray[i] = "Some assignment.\r\n";
}

Ответ 2

Цикл foreach предназначен для итерации по объектам в коллекции, а не для назначения вещей - это просто дизайн языка.

Кроме того, из MSDN:

"Эта ошибка возникает, когда присваивание переменной происходит в режиме чтения- только контекст. Контексты только для чтения включают переменные итерации foreach, используя переменные и фиксированные переменные. Чтобы устранить эту ошибку, присвоения переменной оператора при использовании блоков, foreach операторов и фиксированных операторов.

Ключевое слово foreach просто перечисляет IEnumerable экземпляры (получение Экземпляры IEnumerator, вызвав Метод GetEnumerator()). IEnumerator доступен только для чтения, поэтому значения не могут изменить с помощью IEnumerator = не может быть изменен с использованием контекста foreach.

Ответ 3

Потому что спецификация языка так говорит.

Но серьезно, не все последовательности - это массивы или вещи, которые могут быть логически изменены или записаны. Например:

foreach (var i in Enumerable.Range(1, 100)) {
   // modification of `i` will not make much sense here.
}

Хотя было бы технически возможно изменить i = something; локальную переменную, это может ввести в заблуждение (вы можете подумать, что это действительно что-то изменит под капотом, и это не так).

Чтобы поддерживать такие последовательности, IEnumerable<T> не требует аксессуар set для своего свойства Current, делая его доступным только для чтения. Таким образом, foreach не может изменить базовую коллекцию (если таковая существует) с использованием свойства Current.

Ответ 4

Потому что вы не можете использовать цикл foreach для изменения массива, который вы зацикливаете. Цикл выполняет итерацию через массив, поэтому, если вы попытаетесь изменить то, что он итерирует, произойдет непредвиденное поведение. Кроме того, как указывали Дарин и DMan, вы повторяете через IEnumerable, который сам по себе доступен только для чтения.

PHP создает копию массива в цикле foreach и выполняет итерацию через эту копию, если вы не используете ссылки, и в этом случае вы сами измените массив.

Ответ 5

Поскольку IEnumerable доступен только для чтения.

Ответ 6

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

string[] sArray = Enumerable.Repeat("Some assignment.\r\n", 5).ToArray();

Конструкции более высокого уровня почти всегда используются вместо этого типа цикла в С#. (И С++, но это целая другая тема)

Ответ 7

Вы не можете изменить массив, который вы используете. Вместо этого используйте следующий код:

string[] sArray = new string[5]; 

for (int i=0;i<sArray.Length;i++)
{
    item[i] = "Some Assignment.\r\n";
}

Ответ 8

Предпросмотр предназначен для взаимодействия через массив один раз, без повторения или пропусков (хотя вы можете пропустить какое-либо действие внутри конструкции foreach, используя ключевое слово continue). Если вы хотите изменить текущий элемент, используйте вместо этого цикл for.

Ответ 9

Вы не можете изменить список, который зацикливается через "ForEach".

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

Ответ 10

Это было бы возможно, если бы оно было изменено. Однако что это значит? Он будет читаться так же, как базовое перечисление было изменено, а это не так (можно было бы это допускать, но у него свои собственные недостатки).

Итак, у вас будет код, который люди, естественно, читали бы как указание чего-то другого, кроме того, что на самом деле произошло. Учитывая, что цель компьютерного языка в первую очередь должна быть понята людьми (компиляторы имеют дело с балансом, установленным против них, если вы не используете сборку, машинный код или соответствующим образом названный Brainf ** k), это указывает на недостаток в язык.