如何正确地从列表中移除项目

6
可能有重复:
在迭代集合并从该集合中删除项时出现异常
如何在迭代列表时从通用列表中删除元素?
更好的方法从列表中删除匹配的项

// tmpClientList is List<Client> type

if (txtboxClientName.Text != "")
    foreach (Client cli in tmpClientList)
        if (cli.Name != txtboxClientName.Text)
            tmpClientList.Remove(cli);

错误:"集合已修改;枚举操作无法执行。"

有没有一种简单的方法从列表中删除项目,而不必将这些项目的索引保存在另一个列表或数组中,并在代码的另一个位置删除它们。尝试使用RemoveAt(index),但情况完全相同,循环运行时会进行修改。


同意确切的重复问题,请查看链接一:http://stackoverflow.com/questions/1154325/better-way-to-remove-matched-items-from-a-list 和链接二:https://dev59.com/hknSa4cB1Zd3GeqPQbhY - CrimsonX
7个回答

11

向列表后方移动.. 这样删除一个项目不会影响下一个项目。

for(var i=tmpClientList.Count-1;i>=0;i--)
{
   if (tmpClientList[i].Name != txtboxClientName.Text)
            tmpClientList.RemoveAt(i);

}

不错的简单解决方案,感谢分享。 - Krzysztof Szynter

11

List<T> 上,有一个 RemoveAll 方法,该方法接受一个委托以指示是否删除该项。您可以像这样使用它:

tmpCLientList.RemoveAll(cli => cli.Name != txtboxClientName.Text);

+1 需要注意的是,这仅适用于C# 3.0及以上版本。OP在问题中标记了2、3和4个版本 :-) - Jakob Christensen
1
@Jakob:如果OP没有使用C#3或更新版本,则可以使用老式委托语法调用RemoveAlltmpCLientList.RemoveAll(delegate(Client cli) { return cli.Name != txtboxClientName.Text; }); - LukeH
哦,对了 - 我以为RemoveAll是一个扩展方法。对此感到抱歉 :-) - Jakob Christensen

4

您可以使用for/while循环,或者tmpClientList.RemoveAll(a => a.Name == txtboxClientName.Text)。由于您没有指定使用的C#版本,因此可能会有所不同。


1
当限制在2.0时,它只是多了一点冗长:tmpClientList.RemoveAll(delegate(Client a) { return a.Name == txtboxClientName.Text; }); - Humberto

2

不要使用foreach。使用for循环并倒序遍历列表(即从结尾开始),使用RemoveAt方法。

所以,

// tmpClientList is List<Client> type

if (txtboxClientName.Text != "")
    foreach (int pos = tmpClientList.Length - 1; pos >= 0; pos--)
    {
        Client cli = tmpClientList[pos];
        if (cli.Name != txtboxClientName.Text)
            tmpClientList.RemoveAt(pos);
    }

1
问题在于您正在尝试在foreach迭代中修改列表。使用for替换它,应该就可以了。
此外,由于您似乎正在使用用户输入的名称,请考虑对输入进行一些清理,至少使用Trim()删除多余的空格。如果不这样做,'John '和'John'将是两个不同的东西。 初始的!=“”检查也是如此。

你的意思是说,""和" "是一样的,如果用户只输入了空格字符,那么我也应该去掉输入中的空格吗? - Krzysztof Szynter
@dygi:是的。去除空格后只剩下相关信息 - 在这种情况下是没有的。 至于可能发生这种情况的情形:用户开始输入名称,包括名称后面的一个空格,然后决定删除字符,但空格仍然存在,因为他无法“看到”它。 - Rox

1

您可以创建另一个列表,其中包含您想要删除的项目,并迭代新列表以从“txtboxClientName”列表中删除项目。


好的,我可以在迭代原始列表时,在副本列表上使用Contains()和Remove()方法。谢谢。 - Krzysztof Szynter

1

实际上,foreach使用枚举器来遍历给定的项目集合。更进一步,System.Collections.Generic.List<T>实现了IEnumarable-Interface提供一个类,该类知道如何迭代列表中的项目,即枚举器。现在,如果您使用foreach遍历该列表,则枚举器会跟踪当前位置、如何到达下一个位置以及其他一些内容。内部逻辑可能是将项目数量存储在变量n中,然后从0到n-1访问所有对象。正如您可能注意到的那样,如果在迭代步骤之间删除任何对象,当枚举器尝试传递列表的最后一个对象时,我们将遇到NullReferenceException。因此,为了防止任何迭代失败,不允许在枚举期间修改列表本身。

希望我至少能够比较全面地表达出来。:-)


感谢提供非常精确的信息,关于GTK内部工作原理。 - Krzysztof Szynter

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