【发布时间】:2023-03-07 00:19:01
【问题描述】:
我有一个经典案例,即尝试从集合中删除一个项目,同时在循环中枚举它:
List<int> myIntCollection = new List<int>();
myIntCollection.Add(42);
myIntCollection.Add(12);
myIntCollection.Add(96);
myIntCollection.Add(25);
foreach (int i in myIntCollection)
{
if (i == 42)
myIntCollection.Remove(96); // The error is here.
if (i == 25)
myIntCollection.Remove(42); // The error is here.
}
在发生更改后的迭代开始时,会抛出InvalidOperationException,因为枚举器不喜欢底层集合发生更改。
我需要在迭代时对集合进行更改。有很多模式可以用来避免这种情况,但似乎没有一个有很好的解决方案:
-
不要在这个循环中删除,而是保留一个单独的“删除列表”,你在主循环之后处理。
这通常是一个很好的解决方案,但在我的情况下,我需要将该项目作为“等待”立即消失,直到之后 真正删除项目的主循环改变了我的代码的逻辑流程。
-
无需删除该项目,只需在该项目上设置一个标志并将其标记为非活动状态。然后添加模式 1 的功能来清理列表。
这将满足我的所有需求,但这意味着必须更改大量代码才能在每次项目被检查时检查非活动标志访问。这对我来说太过繁琐了。
-
以某种方式将模式 2 的思想融入到派生自
List<T>的类中。此 Superlist 将处理非活动标志,事后删除对象,并且不会将标记为非活动的项目暴露给枚举消费者。基本上,它只是封装了模式 2(以及随后的模式 1)的所有想法。这样的类存在吗?有人有这方面的代码吗?还是有更好的办法?
-
有人告诉我,访问
myIntCollection.ToArray()而不是myIntCollection将解决问题并允许我在循环内删除。对我来说,这似乎是一个糟糕的设计模式,还是没问题?
详情:
该列表将包含许多项目,我将只删除其中的一些。
在循环内部,我将执行各种流程,添加、删除等,因此解决方案需要相当通用。
我需要删除的项目可能不是循环中的当前项目。例如,我可能在 30 项循环中的第 10 项上,需要删除第 6 项或第 26 项。因此,向后遍历数组将不再起作用。 ;o(
【问题讨论】:
-
对其他人可能有用的信息:Avoid Collection has been modified error(模式 1 的封装)
-
附注:列表有很多时间(通常为 O(N),其中 N 是列表的长度)移动值。如果确实需要有效的随机访问,则可以在 O(log N) 内实现删除,使用平衡二叉树来保存其根所在的子树中的节点数。它是一个隐含键(序列中的索引)的 BST。
标签: c# list foreach enumeration