【发布时间】:2011-04-02 12:33:56
【问题描述】:
我有一个循环遍历列表中的元素。我需要根据某些条件从循环内的此列表中删除元素。当我尝试在 C# 中执行此操作时,出现异常。显然,不允许从正在迭代的列表中删除元素。使用 foreach 循环观察到该问题。有没有解决这个问题的标准方法?
注意:我能想到的一种解决方案是仅出于迭代目的创建列表的副本,并从循环内的原始列表中删除元素。我正在寻找一种更好的方法来解决这个问题。
【问题讨论】:
我有一个循环遍历列表中的元素。我需要根据某些条件从循环内的此列表中删除元素。当我尝试在 C# 中执行此操作时,出现异常。显然,不允许从正在迭代的列表中删除元素。使用 foreach 循环观察到该问题。有没有解决这个问题的标准方法?
注意:我能想到的一种解决方案是仅出于迭代目的创建列表的副本,并从循环内的原始列表中删除元素。我正在寻找一种更好的方法来解决这个问题。
【问题讨论】:
当使用List<T> 时,ToArray() 方法在这种情况下有很大帮助:
List<MyClass> items = new List<MyClass>();
foreach (MyClass item in items.ToArray())
{
if (/* condition */) items.Remove(item);
}
另一种方法是使用 for 循环而不是 foreach,但是每次删除元素时都必须减少索引变量,即
List<MyClass> items = new List<MyClass>();
for (int i = 0; i < items.Count; i++)
{
if (/* condition */)
{
items.RemoveAt(i);
i--;
}
}
【讨论】:
List<T>,那么为什么不在这种情况下使用内置的RemoveAll 方法呢?
如果您的列表是实际的List<T>,那么您可以使用内置的RemoveAll 方法根据谓词删除项目:
int numberOfItemsRemoved = yourList.RemoveAll(x => ShouldThisItemBeDeleted(x));
【讨论】:
ShouldThisItemBeDeleted 函数将是这些条件的表达式。
您可以使用整数索引来删除项目:
List<int> xs = new List<int> { 1, 2, 3, 4 };
for (int i = 0; i < xs.Count; ++i)
{
// Remove even numbers.
if (xs[i] % 2 == 0)
{
xs.RemoveAt(i);
--i;
}
}
不过,这可能读起来很奇怪,也很难维护,尤其是在循环中的逻辑变得更加复杂的情况下。
【讨论】:
您可以使用 LINQ 通过过滤掉项目来用新列表替换初始列表:
IEnumerable<Foo> initialList = FetchList();
initialList = initialList.Where(x => SomeFilteringConditionOnElement(x));
// Now initialList will be filtered according to the condition
// The filtered elements will be subject to garbage collection
这样你就不用担心循环了。
【讨论】:
List<T>,那么您可以使用RemoveAll 方法。
另一个技巧是向后循环列表。删除一个项目不会影响您将在循环的其余部分遇到的任何项目。
我不推荐这个或其他任何东西。您需要的所有内容都可以使用 LINQ 语句根据您的要求过滤列表来完成。
【讨论】:
您可以通过这种方式使用 foreach 进行迭代:
List<Customer> custList = Customer.Populate();
foreach (var cust in custList.ToList())
{
custList.Remove(cust);
}
注意:ToList 上的变量列表,这会遍历由 ToList 创建的列表,但会从原始列表中删除项目。
希望这会有所帮助。
【讨论】:
推荐的解决方案是将您要删除的所有元素放在一个单独的列表中,并在第一个循环之后,放置第二个循环,在其中迭代删除列表并从第一个列表中删除这些元素。
【讨论】:
您收到错误的原因是您使用了 foreach 循环。如果您考虑一下 foreach 循环的工作原理,这是有道理的。 foreach 循环调用 List 上的 GetEnumerator 方法。如果您在哪里更改列表中的元素数量,则 foreach 循环所包含的枚举器将没有正确数量的元素。如果您删除了一个元素,则会引发空异常错误,如果您添加了一个元素,则循环将错过一个项目。
如果你喜欢 Linq 和 Lamda 表达式,我会推荐 Darin Dimitrov 解决方案,否则我会使用 Chris Schmich 提供的解决方案。
【讨论】: