foreach()是否通过引用迭代?

时间:2009-10-08 14:47:30

标签: c# reference foreach

考虑一下:

List<MyClass> obj_list = get_the_list();
foreach( MyClass obj in obj_list )
{
    obj.property = 42;
}

'obj'是否是对列表中相应对象的引用,这样当我更改属性时,更改将在构造到某处的对象实例中持续存在?

10 个答案:

答案 0 :(得分:60)

是的,obj是对集合中当前对象的引用(假设MyClass实际上是一个类)。如果您通过引用更改任何属性,那么您正在更改对象,就像您期望的那样。

但请注意,您无法更改变量obj本身,因为它是迭代变量。如果你尝试,你会得到一个编译错误。这意味着您不能将其置零,如果您正在迭代值类型,则无法修改任何成员,因为这会更改值。

C#语言规范声明(8.8.4)

  

“迭代变量对应于   带有一个只读的局部变量   范围扩展到嵌入式   语句“。

答案 1 :(得分:21)

是的,直到您将通用类型从List更改为IEnumerable ..

答案 2 :(得分:19)

你在这里问过两个不同的问题,让我们按顺序排列。

foreach循环是否通过引用迭代?

如果你的意思与C ++ for循环相同,那么没有。 C#没有与C ++相同的局部变量引用,因此不支持这种类型的迭代。

是否会保留更改

假设MyClass是引用类型,答案是肯定的。类是.Net中的引用类型,因此迭代变量是对一个变量的引用,而不是副本。对于值类型,情况并非如此。

答案 3 :(得分:15)

好吧,当我迭代var collection时,我的更改在foreach循环中没有更新:

var players = this.GetAllPlayers();
foreach (Player player in players)
{
    player.Position = 1;
}

当我将var更改为List时,它开始工作。

答案 4 :(得分:6)

你可以在这个例子中(使用List<T>),但如果你要迭代通用IEnumerable<T>,那么它就会依赖于它的实现。

例如,如果它仍然是List<T>T[],则所有内容都会按预期工作。 当您使用使用yield构造的IEnumerable<T>时,会出现大问题。在这种情况下,您不能再在迭代中修改T的属性,并且如果再次迭代相同的IEnumerable<T>,则期望它们存在。

答案 5 :(得分:3)

只要它不是结构,就是这样。

答案 6 :(得分:2)

好吧,如果没有完全理解你的意思是“通过引用迭代”,我不能具体回答是或否,但我可以说,表面下发生的是.net框架正在构建一个“枚举器” “每次客户端代码调用foreach时的类,对于foreach的生命,维护一个引用迭代的集合中的引用指针,每次你的foreach迭代,ir”传递“一个项目并”递增“指针或在枚举器中引用下一个项目......

无论您正在迭代的集合中的项是值类型还是引用类型,都会发生这种情况。

答案 7 :(得分:2)

对于您来说有趣的是,在C#7.3版中,只要枚举器的 Current 属性返回引用类型,就可以通过引用更改值。以下内容将是有效的(MS文档的普通照会副本):

Span<int> storage = stackalloc int[10];
int num = 0;
foreach (ref int item in storage)
{
    item = num++;
}

在此了解有关此新功能的更多信息 C# foreach statement | Microsoft Docs

答案 8 :(得分:0)

obj是对List内部项目的引用,因此如果您更改它的值,它将保持不变。 现在您应该关注的是get_the_list();正在制作列表的深层副本或返回相同的实例。

答案 9 :(得分:0)

是的,这也是为什么你不能在foreach语句的上下文中改变可枚举对象的原因。