考虑以下内容:
List<MyClass> obj_list = get_the_list();
foreach( MyClass obj in obj_list )
{
obj.property = 42;
}
obj
是指向列表中相应对象的引用,因此当我更改属性时,更改将保留在某个地方构造的对象实例中吗?
是的,obj
是集合中当前对象的引用(假设MyClass
确实是一个类)。如果您通过引用更改任何属性,则会更改对象,就像您预期的那样。
但请注意,无法更改变量obj
本身,因为它是迭代变量。如果尝试这样做,将会得到编译错误。这意味着您无法将其设置为空,并且如果您正在迭代值类型,则不能修改任何成员,因为那将更改该值。
C#语言规范说明(8.8.4)
"迭代变量对应于范围延伸到嵌入语句的只读局部变量。"
您在这里提出了两个不同的问题,我们按顺序来回答。
如果您的意思是与C++中的for循环类似的方式,请注意,不是的。C#没有像C++那样的局部变量引用,因此不支持这种类型的迭代。
假设MyClass是引用类型,那么答案是肯定的。在.NET中,类是引用类型,因此迭代变量是一个对一个变量的引用,而不是副本。但对于值类型则不成立。
我的情况是,在使用 foreach 循环遍历 var 集合时,我的更改没有被更新:
var players = this.GetAllPlayers();
foreach (Player player in players)
{
player.Position = 1;
}
当我将 var 改为 List 时它开始工作了。
List<T>
),您可以这样做,但如果您要迭代通用的 IEnumerable<T>
,那么它将取决于其实现。List<T>
或 T[]
,则所有内容都将按预期工作。当您使用 yield 构造一个 IEnumerable<T>
时,最大的问题就来了。在这种情况下,您不能再在迭代中修改 T
的属性,并期望在再次迭代相同的 IEnumerable<T>
时呈现。Span<int> storage = stackalloc int[10];
int num = 0;
foreach (ref int item in storage)
{
item = num++;
}
您可以在C# foreach语句 | Microsoft Docs中了解更多关于这个新功能的内容。
这种情况发生在您迭代的集合中的项目是值类型还是引用类型都一样。
obj是List中的一个项目的引用,因此如果您更改它的值,它将持久存在。现在您应该关注的是get_the_list();是否正在对List进行深度复制或返回相同的实例。
没错,这也是为什么你不能在 foreach 语句的上下文中更改可枚举对象的原因。
foreach(ref var x in XXX)
语法可用,当Enumerator
的Current
属性返回ref T
(例如在Span<T>
上进行迭代)时。有关详细信息,请参见@Wolf的答案。 - R2RT