C#中Foreach和for循环的区别(非性能方面)

5


前段时间我读到了一个关于foreach的文章,说它使用“对象的副本”,因此可以用于信息检索而不是更新。但我并不理解,因为完全可以通过循环遍历类的列表并更改其字段。谢谢!

5个回答

9

你可能读到的是,使用foreach循环迭代集合时不能修改集合,但使用for循环则可以(如果你小心的话)。例如:

using System;
using System.Collections.Generic;

class Test
{
    static void Main()
    {
        var list = new List<int> { 1, 4, 5, 6, 9, 10 };


        /* This version fails with an InvalidOperationException
        foreach (int x in list)
        {
            if (x < 5)
            {
                list.Add(100);
            }
            Console.WriteLine(x);
        }
         */

        // This version is okay
        for (int i = 0; i < list.Count; i++)
        {
            int x = list[i];
            if (x < 5)
            {
                list.Add(100);
            }
            Console.WriteLine(x);            
        }
    }
}

如果这不是你所指的,请提供更多细节 - 不知道确切内容很难解释你所读的。


Jon,谢谢。是的,我已经阅读并理解了,如果循环遍历一个数组,我不能删除其中一个元素。但我认为我可以修改每个元素的内容,对吗? - Radusko
еҸҰдёҖдёӘйңҖиҰҒжіЁж„Ҹзҡ„жҳҜforeachеҸҜиғҪдёҺfor()дҪҝз”Ёзҡ„зҙўеј•еҷЁе®һзҺ°йқһеёёдёҚеҗҢгҖӮеңЁlistviewзҡ„SelectedItemsйӣҶеҗҲзҡ„жғ…еҶөдёӢпјҢиҝҷеҜ№жҖ§иғҪжңүе·ЁеӨ§зҡ„еҪұе“ҚгҖӮ - EricLaw
@Radusko:是的,您可以修改所引用的对象中的数据...尽管如果它是值类型,您仍将处理副本。(希望您不必处理可变值类型。) - Jon Skeet
@Jon Skeet:然而,如果我循环遍历一个字符串数组,我将无法更改它。字符串是不可变的,但引用是可变的,这是原因吗? - Radusko
@Radusko:这取决于你想如何更改它。你不能改变字符串的内容(除了使用反射)。但是,使用for循环遍历字符串数组将允许你更改数组的内容。 - Jon Skeet
显示剩余3条评论

1

foreach 循环中,您无法修改元素:

var list = new List<string>();
list.AddRange(new string[] { "A", "B", "C" });

foreach (var i in list)
{
    // compilation error: Cannot assign 'i' because it is a 'foreach iteration variable'
    i = "X";
}

虽然在使用 for 循环时,您是通过索引访问列表中的元素而不是迭代器,因此您可以修改集合。


实际上,您可以修改引用类型的元素(如果它们可以被修改)。您无法替换、添加或删除它们。您的代码无法工作的原因是字符串是不可变的。一旦创建了一个字符串,它就不能以任何方式被修改。看起来像是修改的东西实际上是用另一个字符串替换了一个字符串。因此,在您上面给出的示例中,您替换了其中一个元素(或删除了一个并添加了一个),这在枚举集合时是不允许的。 - Kirk

1

foreach 用于使用迭代器获取序列的每个元素。该序列可以是任何实现 IEnumerable 接口的对象。IEnumerable 不需要是有限的(例如序列 0 1 2 3 4... 1000... 就是 IEnumerable)。

for 只是 C# 中允许您声明循环的结构(用于执行各种操作,不仅仅是遍历集合)

值得注意的是,在 .NET 中用于集合的迭代器实现不支持在迭代期间修改序列。


1

foreach 使用 IEnumerable 遍历集合。这使得修改集合(删除、添加项目)变得不可能,但如果它们是引用类型,则仍然可以修改其中的对象。

for 是简单循环与直接访问集合中的项的简单组合。在此循环进行时没有任何阻塞。


0

将其与只读字段进行比较:

private readonly List<int> MyList = new List<int>();

现在,在这段代码中,我不能执行MyList = new List<int>(),因为那会改变MyList指向的内容,但我可以用MyList.Add(3)来更改所指向的列表。

同样地,您不能更改foreach迭代所使用的变量,但可以更改它所引用的内容:

foreach(List<int> lst in MyListOfLists)
{
  lst = new List<int>(); // not allowed
  lst.Add(3);  // allowed
}

最后,用于实现foreach的枚举器如果底层集合正在使用时不需要保持有效:

foreach(int x in SomeEnumerable)
{
    if(x != 0)
        SomeEnumerable.Add(0);
}

假设 Add 修改了 SomeEnumerable,那么上面的代码 可能 能够工作,但是它以一种奇怪和难以理解的方式 可能 "工作" 且 可能 抛出异常。这样的代码不保证任何行为,并且在枚举期间修改集合被认为是不正确的原因。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接