为什么我们不能在foreach循环中更改迭代变量

时间:2012-03-14 11:13:13

标签: c# .net foreach

                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 个答案:

答案 0 :(得分:9)

问题1:为什么我无法更改迭代变量的any属性?

来自Anonymous Types的文档:

  

匿名类型提供了一种方便的方法来封装一组只读属性

您无法更改匿名类型中属性的值,因此

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

同样无效。


问题2.我只能在for循环中为迭代变量分配一个新对象。不在foreach循环中。为什么?

来自IEnumerable的文档:

  

只要集合保持不变,枚举器仍然有效。

如果以任何方式更改支持列表,您将使迭代器无效。考虑如果迭代器根据Age字段以特定顺序返回项目会发生什么,例如。

答案 1 :(得分:7)

  

为什么我无法更改迭代变量的any属性。

您使用的是匿名类型,它们在C#中始终具有只读属性。 (在VB中,它们默认为读/写,但可以使用Key修饰符设置为只读。)

从C#4规范,第7.6.10.6节:

  

匿名对象初始值设定项声明匿名类型并返回该类型的实例。匿名类型是直接从object继承的无名类类型。匿名类型的成员是一系列只读属性,这些属性来自用于创建该类型实例的匿名对象初始值设定项。

对于你的第二个问题......

  

我只能在for循环中为迭代变量分配一个新对象。不在foreach循环中。为什么呢?

语言规范以这种方式定义它。特别是,即使您可以更改变量,也不会更改数组,除非语言规范使其仅用于数组。通常,foreach使用IEnumerable / IEnumerator(或类似的成员),它只提供序列的“阅读”视图。

来自C#4规范的第8.8.4节:

  

迭代变量对应于只读局部变量,其范围扩展到嵌入语句。

(重要的是,虽然它是一个只读变量,但它的值在迭代之间会发生变化。在C#5中,这将被改变,因此它在每次迭代时实际上都是一个“新”变量。差异只有在变量为被lambda表达式捕获。)

答案 2 :(得分:0)

关于问题2):在“工作正常”的情况下,您不会更改迭代变量。在这种情况下,迭代变量是i。你要做的是用一个新元素替换一个数组元素。这总是有效的。

答案 3 :(得分:0)

因为foreach使用枚举器,并且枚举器不能更改基础集合,但是可以更改集合中对象引用的任何对象。这就是Value和Reference-type语义发挥作用的地方。

在引用类型(即类)上,所有正在存储的集合都是对对象的引用。因此,它实际上从未触及任何对象的成员,并且对它们的关注度不高。对象的更改不会触及该集合。

另一方面,值类型将其整个结构存储在集合中。如果不更改集合并使枚举器无效,则无法触及其成员。

此外,枚举器返回集合中值的副本。在ref-type中,这意味着什么。引用的副本将是相同的引用,您可以以任何方式更改引用的对象,并将更改扩展到范围之外。另一方面,在值类型上,意味着您获得的只是对象的副本,因此对所述副本的任何更改都不会传播。