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循环中。为什么呢?
答案 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中,这意味着什么。引用的副本将是相同的引用,您可以以任何方式更改引用的对象,并将更改扩展到范围之外。另一方面,在值类型上,意味着您获得的只是对象的副本,因此对所述副本的任何更改都不会传播。