2012-03-14 2 views
0
   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. Зачем?Почему мы не можем изменить переменную итерации в цикле foreach

ответ

9

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

Из документации на Anonymous Types:

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

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

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

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


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

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

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

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

+0

Примечание: Это ответ на вопрос 1. – Matthias

+0

Я был все в несогласии с вами, пока вы не упомянули проблему сортировки. Теперь я понимаю. Большое спасибо! –

7

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

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

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

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

Для вашего второго вопроса ...

я только смог назначить новый объект переменной итерации в цикле для. Не в петле foreach. Зачем?

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

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

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

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

+0

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

+1

@vaibhav: Нет, он содержит копию * ссылки * к исходному объекту. В массиве также содержатся ссылки. Если вы хотите изменить значение в массиве, вам нужно * изменить содержимое массива *. Просто изменение переменной, значение которой является * копией * ссылки в массиве, ничего не сделает. –

+0

names [i] = новый объект. В этом утверждении я меняю исходный объект, а не его копию. –

0

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

0

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

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

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

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

Смежные вопросы