在 foreach 循环中修改列表

Posted

技术标签:

【中文标题】在 foreach 循环中修改列表【英文标题】:Modifying list inside foreach loop 【发布时间】:2011-09-11 19:20:21 【问题描述】:

我有一个类似的结构(但很多更复杂):

var list = new List<string>();

// .. populate list ..

foreach(var item in list)

    DoFunction(list);


public void DoFunction(List<string> list)

    if(someCondition == true)
    
        // .. modify list in here ..
    

现在,我知道无法编辑您正在遍历的集合,但是如果您必须编辑列表(没有try catch 语句),如何优雅地跳出循环?有什么方法可以判断列表是否已被编辑?你能在它注意到之前编辑列表并快速break;吗?

【问题讨论】:

IIRC 会抛出并发修改异常。 如果出现某些情况,退出循环并在此之后编辑列表有什么问题? How to modify or delete items from an enumerable collection while iterating through it in C# 的可能重复项 我不会尝试修改然后破坏。它可能适用于某些枚举器,但会中断其他枚举器(例如,那些底层处理是异步的)。 您必须在更改列表后中断循环,以便不调用 IEnumerator 接口的 MoveNext 方法将迭代器定位到下一个元素 【参考方案1】:

如果不知道正在进行哪些编辑,就很难提供有用的建议。不过,我发现的模式具有最通用的价值,仅用于构建一个新列表。

例如,如果您需要查看每个项目并决定是删除它、保持原样还是在其后插入项目,您可以使用这样的模式:

IEnumerable<string> butcherTheList(IEnumerable<string> input)

    foreach (string current in input)
    
        if(case1(current))  
        
            yield return current;
        
        else if(case2(current))
        
            yield return current;
            yield return someFunc(current);
        
        // default behavior is to yield nothing, effectively removing the item
    


List<string> newList = butcherTheList(input).ToList();

【讨论】:

我很欣赏这种实用的方法。【参考方案2】:

而不是使用foreach 构造,for 循环将允许您更改列表。

for (var x = 0; x < list.Count; x++) 


【讨论】:

这可能会变得非常棘手,具体取决于对原始列表进行了哪些修改。如果删除遍历列表时发现的项目,则必须适当修改 x 值以避免错误。 @StriplingWarrior 这并不难。只需x--,删除后就可以了 我要看他的DoFunction有多复杂。如果他要从列表中删除 other 项(除了x 处的项),那么他必须确定这些项是在当前位置之前还是之后。我并不是说它不能完成——它只是变得足够复杂以至于在许多情况下不值得。 DoFunction 实际上是非常复杂的......但它最多只会删除一个元素,所以这很完美:) @Timo:其实这不是随机访问,根据文档,通过索引访问是一个O(1)操作,所以不管项目的数量,直接访问列表是完全可以的:msdn。 microsoft.com/en-us/library/0ebtbkkc.aspx【参考方案3】:

是的,你可以打破,如果那是你真正想要的。在for 循环尝试从列表中抓取下一项之前,不会引发异常。

但我发现创建和遍历列表副本是最简单的,因此您不必担心。

foreach(var item in list.ToList())

与更复杂代码的可维护性成本相比,额外的未触及列表所增加的性能开销通常可以忽略不计。

【讨论】:

这不是同样的问题吗?如果list 改变了,那么list.ToList() 也不会改变吗? @TheAdamGaskins: 不,list.ToList()for 循环开始时被评估一次,并且只是创建原始列表的直接副本。该副本用于迭代,而原始列表是添加和删除项目的列表。 @TheAdamGaskins:不,因为list.ToList() 将被评估并创建一个新列表。枚举器将枚举新列表,保留原始列表以供修改。 确实:ToList() 立即急切地求值,而不是懒惰地求值。一个很好的 Linq 方法及其评估样式表:visualcsharptutorials.com/linq/deferred-execution(向下滚动到底部)

以上是关于在 foreach 循环中修改列表的主要内容,如果未能解决你的问题,请参考以下文章

C# 使用IEnumerable,yield 返回结果,同时使用foreach时,在循环内修改变量的值无效

为啥在foreach循环中不能修改值类型实例

在 foreach 循环中修改数组值

在 C# 中,为啥我不能在 foreach 循环中修改值类型实例的成员?

在 laravel 的 foreach 循环中修改当前对象

关于PHP中foreach循环修改数组值失效的问题