我有一个经典的案例,尝试从集合中删除一个项目,同时在循环中枚举它:
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的功能来清理列表。
This would满足我所有的需求,但这意味着lot的代码必须更改,以便每次访问项目时检查非活动标志。对于我来说,这实在是太多的管理工作了。
-
以某种方式将模式 2 的思想合并到派生自的类中List<T>
。该超级列表将处理非活动标志、事后删除对象,并且不会向枚举使用者公开标记为非活动的项目。基本上,它只是封装了模式 2(以及随后的模式 1)的所有思想。
存在这样的类吗?有人有这方面的代码吗?或者,还有更好的方法?
-
有人告诉我访问myIntCollection.ToArray()
代替myIntCollection
将解决问题并允许我在循环内删除。
对我来说,这似乎是一个糟糕的设计模式,或者也许还不错?
Details:
最好的解决方案通常是使用RemoveAll() https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1.removeall method:
myList.RemoveAll(x => x.SomeProp == "SomeValue");
或者,如果您需要certain删除的元素:
MyListType[] elems = new[] { elem1, elem2 };
myList.RemoveAll(x => elems.Contains(x));
当然,这是假设您的循环仅用于删除目的。如果你do需要额外的处理,那么最好的方法通常是使用for
or while
循环,从那时起你就不再使用枚举器:
for (int i = myList.Count - 1; i >= 0; i--)
{
// Do processing here, then...
if (shouldRemoveCondition)
{
myList.RemoveAt(i);
}
}
向后移动可确保您不会跳过任何元素。
对编辑的回应:
如果您要删除看似任意的元素,最简单的方法可能是只跟踪要删除的元素,然后立即将它们全部删除。像这样的事情:
List<int> toRemove = new List<int>();
foreach (var elem in myList)
{
// Do some stuff
// Check for removal
if (needToRemoveAnElement)
{
toRemove.Add(elem);
}
}
// Remove everything here
myList.RemoveAll(x => toRemove.Contains(x));
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)