为什么赋值运算符 (=) 在 foreach 循环中无效?

问题描述 投票:0回答:11

为什么赋值运算符 (=) 在

foreach
循环中无效?我使用的是 C#,但我假设该参数对于支持
foreach
的其他语言(例如 PHP)是相同的。例如,如果我做这样的事情:

string[] sArray = new string[5];

foreach (string item in sArray)
{
   item = "Some assignment.\r\n";
}

我收到错误,“无法分配给‘item’,因为它是‘foreach 迭代变量’。”

c# foreach variable-assignment
11个回答
70
投票

这是您的代码:

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";
}

6
投票

因为语言规范是这么说的。

但说真的,并非所有序列都是数组或可以逻辑修改或写入的东西。例如:

foreach (var i in Enumerable.Range(1, 100)) {
   // modification of `i` will not make much sense here.
}

虽然在技术上可以让

i = something;
修改局部变量,但它可能会产生误导(您可能认为它确实在幕后改变了某些东西,但事实并非如此)。

为了支持此类序列,

IEnumerable<T>
set
属性不需要
Current
访问器,从而使其只读。因此,
foreach
无法使用
Current
属性修改底层集合(如果存在)。


5
投票

foreach
循环旨在迭代集合中的对象,而不是分配事物 - 这只是语言的设计。

另外,来自 MSDN:

“当对变量的赋值发生在读操作中时,就会出现此错误 只有上下文。只读上下文包括 foreach 迭代变量, 使用变量和固定变量。要解决此错误,请避免 在 using 块、foreach 中对语句变量进行赋值 陈述和固定陈述。”

foreach关键字只是枚举 IEnumerable 实例(获取 IEnumerator 实例通过调用 GetEnumerator() 方法)。 IE枚举器 是只读的,因此值不能 使用 IEnumerator 进行更改 = 不能 使用 foreach 上下文进行更改。


4
投票

因为您无法使用

foreach
循环来修改正在循环的数组。循环“迭代”数组,因此如果您尝试修改其迭代的内容,则可能会发生意外行为。此外,正如 Darin 和 DMan 所指出的,您正在迭代一个 IEnumerable,它本身是只读的。

PHP 在其

foreach

循环中创建数组的副本,并迭代该副本,除非您使用引用,在这种情况下,您将修改数组本身。

    


2
投票
IEnumerable

是只读的。

    


2
投票

string[] sArray = Enumerable.Repeat("Some assignment.\r\n", 5).ToArray();

在 C# 中,几乎总是可以使用更高级别的构造来代替这种循环。 (还有 C++,但那是另一个话题了)


1
投票

string[] sArray = new string[5]; for (int i=0;i<sArray.Length;i++) { item[i] = "Some Assignment.\r\n"; }



0
投票


0
投票

最好的选择是简单地创建一个临时列表来存储您想要使用的项目。


0
投票

因此,您的代码会被人们自然地解读为表明实际发生的情况以外的情况。考虑到计算机语言的目的主要是为了被人们理解(编译器处理与它们之间的平衡,除非您使用汇编、机器代码或适当命名的 Brainf**k),这将表明计算机语言中存在缺陷语言。


0
投票

解决方案是使用 for 循环。

string[] names = { "Alex", "Eddie", "David", "Michael" }; for (int i = 0; i < names.Length; i++) if (names[i] == "David") names[i] = "Sammy"; foreach (var name in names) Console.

© www.soinside.com 2019 - 2024. All rights reserved.