为什么在foreach
循环中赋值运算符(=)无效?我正在使用C#,但我认为对于其他支持foreach
的语言来说,这个问题也是相同的(例如PHP)。例如,如果我像下面这样做:
string[] sArray = new string[5];
foreach (string item in sArray)
{
item = "Some assignment.\r\n";
}
我遇到了一个错误:"无法向 'item' 赋值,因为它是一个 'foreach 迭代变量'。"
为什么在foreach
循环中赋值运算符(=)无效?我正在使用C#,但我认为对于其他支持foreach
的语言来说,这个问题也是相同的(例如PHP)。例如,如果我像下面这样做:
string[] sArray = new string[5];
foreach (string item in sArray)
{
item = "Some assignment.\r\n";
}
我遇到了一个错误:"无法向 'item' 赋值,因为它是一个 'foreach 迭代变量'。"
以下是您的代码:
foreach (string item in sArray)
{
item = "Some assignment.\r\n";
}
using (var enumerator = sArray.GetEnumerator())
{
string item;
while (enumerator.MoveNext())
{
item = enumerator.Current;
// Your code gets put here
}
}
IEnumerator<T>.Current
属性是只读的,但实际上在这里并不相关,因为您正在尝试将局部变量item
赋值为一个新值。编译时检查防止您这样做基本上是为了保护您免受执行不像您期望的操作(例如更改局部变量而对基础集合/序列没有影响)的影响。string[]
这样的索引集合的内部,请使用传统的for
循环而不是foreach
:for (int i = 0; i < sArray.Length; ++i)
{
sArray[i] = "Some assignment.\r\n";
}
foreach
只是语法糖没错,但是你对编译器实际转换的理解有点不准确。在 Eric Lippert 的博客 中有一个更准确的解释。这篇文章讨论了闭包,但也解释了编译器的工作原理。其中一个非常重要的区别是临时变量在循环外部声明。 - Romanusing
而不是try
/finally
块,因为毕竟编译器会将using
转换为后者(我的主要关注点是可读性)。 - Dan Taousing
语句所做的。 - Dan Taoforeach (var i in Enumerable.Range(1, 100)) {
// modification of `i` will not make much sense here.
}
i = something;
修改一个本地变量,但这可能会产生误导(你可能会认为它真的在幕后更改了某些东西,但实际情况并非如此)。IEnumerable<T>
不需要 set
访问器来修改其 Current
属性,使其成为只读属性。 因此,foreach
无法使用 Current
属性修改底层集合(如果存在)。foreach
循环是设计用于遍历集合中的对象,而不是为了分配事物-这只是语言的设计。foreach
关键字只枚举IEnumerable实例(通过调用GetEnumerator()方法获取IEnumerator实例)。 IEnumerator是只读的,因此值不能在foreach上下文中更改。foreach
循环来修改你正在遍历的数组。循环通过数组进行迭代,因此如果尝试修改正在迭代的内容,则可能会发生意外的行为。此外,正如 Darin 和 DMan 指出的那样,您正在迭代一个只读的 IEnumerable
。foreach
循环中复制了数组并遍历该副本,除非你使用引用,否则你将修改数组自身。通常情况下,如果您正在尝试这样做,您需要仔细考虑您的设计,因为您可能没有使用最佳构造。在这种情况下,最好的答案可能是
string[] sArray = Enumerable.Repeat("Some assignment.\r\n", 5).ToArray();
在C#中,几乎总是可以使用更高级别的结构代替这种循环。(而在C++中则是另一个话题)
ref
参数(在这种情况下,它实际上是相同的引用)。也许你应该说,“因为item
具有局部作用域…”? - Dan Tao因为 IEnumerable
是只读的。
IEnumerator
修改值。 IEnumerator.Current
属性是只读的,它没有setter。 - Darin DimitrovCurrent
属性。请参考Mehrdad或我的回答来理解我的意思。 - Dan Tao你不能在 foreach 循环中修改正在遍历的数组。请使用以下代码:
string[] sArray = new string[5];
for (int i=0;i<sArray.Length;i++)
{
item[i] = "Some Assignment.\r\n";
}
Enumerable.Repeat
将整个事情简化为一个语句。 - Billy ONeal在“ForEach”循环遍历列表时,您无法修改该列表。
最好的选择是简单地创建一个临时列表来存储您希望使用的项目。
让它可以被修改完全是可能的。然而,这意味着什么?它会读起来像底层枚举已被修改,但实际上并没有被修改(虽然也有可能允许这样做,但这有其自身的缺点)。
因此,你会得到一个人们自然而然会读错所发生的事情的代码。考虑到计算机语言的主要目的是为了让人们理解(编译器处理平衡设置对它们不利,除非你使用汇编语言、机器码或适当命名的 Brainf**k),这将表明该语言存在缺陷。