为什么不在Array.ForEach中的foreach迭代变量上赋值错误?

时间:2019-01-23 11:38:00

标签: c#

给出int个数字数组,例如:

int[] arr = new int[] { 0, 1, 2, 3, 4, 5 };

如果我们想将每个数字加1,最好的选择是:

for(int i = 0; i < arr.Length; i++)
{
    arr[i]++;
}

如果我们尝试使用foreach

foreach(int n in arr)
{
    n++;
}

按预期,我们遇到了错误:

  

由于它是“ foreach迭代变量”,因此无法分配给“ n”


为什么要使用这种方法:

Array.ForEach(arr, (n) => {
    n++;
});

与上面的foreach相等,Visual Studio和编译器不会告诉我们任何信息,代码将进行编译,并且在运行时不会产生任何结果,也不会引发异常? >

5 个答案:

答案 0 :(得分:2)

foreach(int n in arr)
{
    n++;
}

这是一种语言构造,编译器确切地知道应该执行foreach循环以及执行n。因此,它可以防止您更改迭代变量n

Array.ForEach(arr, (n) => {
    n++;
});

这是一个传入lambda的常规函数​​调用。修改函数(或lambda)中的局部变量是完全有效的,因此可以更改n。尽管编译器可以提醒您,增量没有作用,因为以后将永远不会使用它,但这是有效的代码,并且仅仅是因为该函数被称为ForEach并且实际上执行了与foreach类似的操作-loop不会改变这是一个常规函数和常规lambda的事实。

答案 1 :(得分:0)

两者都是两回事。

首先,我们需要弄清楚我们需要什么。如果要求更改现有值,则可以使用for循环,因为在枚举集合时不应修改值,这就是为什么您在第一个foreach循环中会遇到错误。

所以一种方法可能是意图突变:

for(int i=0; i< arr.Length; i++)
{
    arr[i] = arr[i] +1;
}

第二,如果要获取具有更新后值的新集合,请考虑使用linq Select方法,该方法将返回int的新集合。

var incrementedArray = arr.Select( x=> (x+1));

编辑:

关键区别在于,在第一个示例中,我们在枚举collecoll的值的同时对其进行了修改,而在lambda语法foreach中,使用了一个委托作为输入,以作为局部变量。

答案 2 :(得分:0)

foreach语句为实现System.Collections.IEnumerableSystem.Collections.Generic.IEnumerable<T>接口的类型的实例中的每个元素执行一个语句或语句块。您无法修改迭代值,因为您正在使用支持延迟执行的System.Collections.IEnumberableSystem.COllections.Generic.IEnumberable<T>接口。

如果您想修改值,也可以使用

foreach(ref int n in arr)
{
    n++;
}

已更新

Array.Foreach是一种对指定数组的每个元素执行指定操作的方法。此函数支持立即执行行为,并且只能应用于内存中保存的数据。 Array.Foreach方法采用数组,并使用For循环遍历集合。

foreachArray.Foreach看上去相同,但工作方式不同。

答案 3 :(得分:0)

正如@tkausl所指出的,nForEach是局部变量。因此:

static void Main()
{
    int[] arr = new int[] { 0, 1, 2, 3, 4, 5 };
    Console.WriteLine(string.Join(" ",arr));

    Array.ForEach(arr, (n) => {
        n++;
    });
    Console.WriteLine(string.Join(" ",arr));
}

将输出:

0 1 2 3 4 5
0 1 2 3 4 5

表示您不要更改 arr的值。

答案 4 :(得分:0)

Array.ForEachforeach循环相同。这是一种扩展方法,它将对集合进行迭代并对其每个元素执行操作。

Array.ForEach(arr, (n) => {
    n++;
});

但是不会修改actuzal集合,它只会将一个新值重新分配给n,该新值与数组中的基础值无关,因为它的值类型是**复制*到匿名方法。因此,对匿名方法中的param所做的任何操作都不会反映到ForEach方法中,因此在数组中没有任何作用。这就是为什么您可以这样做。

但是,即使您有一个可以使用的引用类型数组,因为您只需将新实例重新分配给所提供的参数,这对基础数组也不起作用。

看看这个简化的例子:

MyClass
{
    void ForEach(Action<Item> a)
    {
        foreach(var e in myList)
            Action(e);
    }
}

在您的情况下,操作如下所示:

x => x++

只是为x分配一个新值。然而,由于x是通过传递的,因此这对调用方法以及myList都没有任何影响。