从 VB.NET 的 List(Of T) 中删除项目失败

5
我有一个通用列表,我使用List.Remove(Object)删除其中的项目。 我已经删除了一些项目,但每当我尝试删除第五个项目时,它都会失败并且无法将其从列表中删除。似乎无论我要删除什么,每次尝试删除五个项目时都会在第五个项目上失败。
可能的原因是什么?查看List(Of T).Remove的文档,它没有指定他们用于删除项目的算法。

你能给我们展示一些代码吗?比如列表中对象的类型、大小等。 - jose
1
你要添加到列表中的具体类型是什么?它们是.NET内置类型,还是你创建的自定义类型的对象? - Justin Niessner
它们是我创建的自定义对象。我通过将要从.remove方法中删除的对象传递给该方法来将它们从列表中移除。我没有在循环中执行此操作。 - Cody C
我应该在说“失败”时更清楚。它只是不能将其从列表中移除。没有任何异常。 - Cody C
你是否正在使用 FOR EACH 循环时尝试删除一个项目?如果是这样,你应该考虑将你的 FOR EACH ... IN [list].ToArray(),然后从那里进行删除。 - SQLMason
显示剩余3条评论
5个回答

13

Remove方法会通过调用对象的.Equals来进行匹配。默认情况下,对于给定的对象,它只会匹配相同的对象。如果您希望将具有相同属性的两个对象视为相等,即使它们不是同一个对象,您需要重写Equals方法并在其中放置逻辑。

另一种很好的选择是使用RemoveAll,并传入一个匿名委托或lambda表达式,包含您要查找的条件。例如:

customers.RemoveAll(customer => customer.LastName.Equals(myCustomer.LastName));

当然,这仅在您确实希望删除 所有 匹配项时起作用,或者您确定只有一个匹配项。


2

如果您正在使用基于索引的方法从列表中删除项目,并记住在删除它之前的项时,其后面的项的索引将减少1。


1
+1 - 对于这个问题中缺乏的细节,我敢打赌这是正确答案。 - Joe
这就是为什么,如果他正在使用基于索引的方法,他应该从列表末尾开始枚举。 - Dan Tao
在每一步中,他可以要么增加循环变量要么删除一个项目,但不能两者兼而有之。 - Ryan Lundy

0
你的列表里有多少项?你是如何删除它们的,只是通过循环吗?请记住这些是基于0的列表。如果你正在使用任何带有整数的For循环,则可能无法正常工作,因为你正在删除项目。

0

通常情况下,您不应修改正在循环的集合,而只能修改该集合中的项。在 for each 循环内部删除项的问题在于它会更改正在被循环的集合,并干扰列表计数和迭代位置。

常见解决方案包括:

  1. 反向循环集合,以便迭代器的干扰不会影响执行
  2. 创建一个新集合,这样您就可以修改一个集合并循环另一个集合。

反向循环:

这是一个扩展方法,它接受一个 Collection(Of T)(您可以使用 List(Of T),但是列表类已经公开了 RemoveAll 方法,基本上做了相同的事情)。
我们将反向循环并根据传入的 lambda 函数检查集合中每个项的有效性。如果匹配,则将其删除。

<Extension()>
Public Sub RemoveEach(Of T)(ByRef col As Collection(Of T),
                            ByVal match As Func(Of T, Boolean))
    For i = col.Count - 1 To 0 Step -1
        If match(col(i)) Then col.RemoveAt(i)
    Next
End Sub

然后像这样使用:

Dim col = New Collection(Of Integer)({1, 2, 3, 4}.ToList)
col.RemoveEach(Function(i) (i Mod 2) = 0)
'Produces list of 1 & 3

创建新集合:

跟踪集合索引并使用RemoveAt删除项比使用Remove更有效,后者需要一个对象,然后检查其余的集合是否与该对象匹配。但是,如果您真的想要一种在For循环中处理删除项的方法,可以在原始集合上调用ToList,从而创建一个新列表。然后,您可以循环遍历新列表以查找需要从原始列表中删除的项。每当您找到一个对象时,都可以安全地从原始集合中删除它,因为它当前没有被枚举。

<Extension()>
Public Sub RemoveEachObject(Of T)(ByRef col As Collection(Of T), 
                                  ByVal match As Func(Of T, Boolean))
    For Each o As T In col.ToList()
        If match(o) Then col.Remove(o)
    Next
End Sub

更多信息,请查看这个很棒的在'foreach'循环中从列表中删除的答案


-1
如果您正在使用for循环来删除元素,那么您应该考虑使用foreach,它更适用于集合、列表和可枚举对象。

3
如果他在 foreach 循环中从列表中删除一个元素,由于集合已被修改,他将收到一个“InvalidOperationException”异常。 - Dan Tao

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