在 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时,在循环内修改变量的值无效