Почему мы не можем изменить итерационную переменную в цикле foreach - PullRequest
0 голосов
/ 14 марта 2012
                var names = new[] { 
                            new { Name = "John", Age = 44 },
                            new { Name = "Diana", Age = 45 },
                            new { Name = "James", Age = 17 },
                            new { Name = "Francesca", Age = 15} 
                            };

            for (int i = 0; i < names.Length; i++)
            {
                names[i].Age = 23; //-------->Error
                names[i] = new { Name = "XYX", Age = 26 }; //----->Works fine
            }

            foreach(var name in names)
            {
                name.Age = 1;  //-------->Error
                name = new { Name = "ABC", Age = 25 };  //-------->Error
            }

У меня здесь два вопроса. 1. Почему я не смог изменить любой атрибут итерационной переменной.
2. Мне удалось только назначить новый объект переменной итерации в цикле for. Не в цикле foreach. Почему?

Ответы [ 4 ]

9 голосов
/ 14 марта 2012

Вопрос 1: Почему я не смог изменить какой-либо атрибут переменной итерации?

Из документации по Анонимные типы :

Анонимные типы предоставляют удобный способ инкапсулировать набор только для чтения свойств

Вы не можете изменить значения свойств в вашем анонимном типе, поэтому

name.Age = 1;
// and
names[i].Age = 1; 

одинаково недействительны.

<ч />

Вопрос 2. Мне удалось только назначить новый объект переменной итерации в цикле for. Не в цикле foreach. Почему?

Из документации по IEnumerable :

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

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

7 голосов
/ 14 марта 2012

Почему я не смог изменить любой атрибут переменной итерации.

Вы используете анонимные типы, которые всегда имеют свойства только для чтения в C #. (В VB они по умолчанию доступны для чтения / записи, но могут быть сделаны только для чтения с модификатором Key.)

Из спецификации C # 4, раздел 7.6.10.6:

Инициализатор анонимного объекта объявляет анонимный тип и возвращает экземпляр этого типа. Анонимный тип - это безымянный тип класса, который наследуется непосредственно от object. Члены анонимного типа - это последовательность свойств только для чтения, полученных из инициализатора анонимного объекта, используемого для создания экземпляра типа.

На второй вопрос ...

Мне удалось только назначить новый объект переменной итерации в цикле for. Не в цикле foreach. Почему?

Спецификация языка определяет его таким образом. В частности, даже если вы могли бы изменить переменную, это не изменило бы массив, если бы спецификация языка не заставила его работать только для массивов . Как правило, foreach использует IEnumerable / IEnumerator (или элементы, выглядящие так), что обеспечивает только «чтение» представления последовательности.

Из раздела 8.8.4 спецификации C # 4:

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

(Важно то, что хотя эта переменная доступна только для чтения, ее значение меняется между итерациями. В C # 5 это будет изменено, так что она фактически будет «новой» переменной на каждой итерации. Разница важна только тогда, когда переменная захваченный чем-то вроде лямбда-выражения.)

0 голосов
/ 14 марта 2012

Поскольку foreach использует перечислитель, и перечислители не могут изменять базовую коллекцию, но могут, однако, изменять любые объекты, на которые ссылается объект в коллекции. Именно здесь вступают в игру семантика типа Value и Reference.

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

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

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

0 голосов
/ 14 марта 2012

Что касается вопроса 2): в случае «отлично работает» вы не меняете переменную итерацииВ этом случае переменная итерации равна i.Что вы делаете, это замените один элемент массива новым элементом.Это всегда работает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...