迭代集合的最佳方式,避免看到“集合已修改,无法继续枚举...”的错误提示。

3
最近我遇到了一个问题,我有一个集合在客户端应用程序中,我不断使用它来更新UI。这个集合可能会被服务器推送随时修改,导致有时会出现异常“无法继续枚举,因为集合已被修改”。我尝试了各种策略来解决这个问题,比如捕获错误并重新执行代码(感觉很糟糕),以及使用synclock来锁定集合(我认为这并不能完全锁定集合)。
我相信这不是一个新的问题,我想知道其他开发者是如何解决这个问题的。

1
你在枚举循环内做什么? - System Down
2
不要修改你正在枚举的集合。 - Arran
6
病人:“医生,我这样做会疼!” 医生:“别那样做。” - user541686
3个回答

1
如果数据不太大,我喜欢在枚举集合时冻结它们:
foreach(var item in new List<ItemType>(collection))
{
    ...
}

1
它仍将执行集合枚举以创建列表 :) - MarcinJuraszek
@MarcinJuraszek - 是的,但是这个枚举不会修改原始内容,而它确实会这样做。 - System Down
1
据我理解,问题是在foreach循环内部没有修改集合,而是来自其他来源。 - MarcinJuraszek
@Marcin 是的,但整个重点在于foreach()不再遍历原始集合,而是相同的对象。如果更改原始集合,则不会影响foreach()。 - Mattias Åslund
1
是的,但如果更改发生在创建列表时(new List <T>(IEnumerable <T> source)构造函数将执行枚举),仍然有可能出现相同的错误。尽管这种情况发生的时间可能比原始的foreach短,但仍然存在获得相同异常的风险。 - MarcinJuraszek

1

你的问题实际上有两个部分。一是对可变集合进行迭代,但隐藏的复杂性是保持UI与可变集合同步。对于小集合和性能不会影响的区域,我会在foreach循环的末尾添加.ToList()。

foreach(var item in collection.ToList())

那样做,它会遍历一个新创建的集合,而不是一直变化的基本集合。但是,对于UI同步,您需要检查在迭代之后是否有更改,这是另一个级别的问题...

0

由于您似乎无法在迭代集合时锁定服务器更改,因此有几个建议:

  • 使用for (...)while (...)而不是foreach (...),以避免枚举器的“集合已修改”异常。请注意,您需要处理对集合的更改,例如在每次迭代时重新检查集合大小。
  • 如果可能,请仅更新修改过的项目的UI。您所说的“服务器推送”是什么意思?如果您的客户端应用程序可以监听来自服务器的有关修改项目(包括其索引/ ID)的通知,则只需要使用上述方法一次迭代集合(初始UI加载)。

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