【问题标题】:how properly remove item from list [duplicate]如何正确地从列表中删除项目[重复]
【发布时间】:2011-03-01 15:46:41
【问题描述】:

可能的重复:
Exception during iteration on collection and remove items from that collection
How to remove elements from a generic list while iterating around it?
Better way to remove matched items from a list

// tmpClientList is List<Client> type

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

错误:“集合已修改;枚举操作可能无法执行。”

如何以某种简单的方式从列表中删除项目,而无需将这些项目的索引保存在另一个列表或数组中,并将它们删除到代码中的另一个位置。也试过 RemoveAt(index) 但情况完全相同,在循环运行时进行修改。

【问题讨论】:

标签: c#


【解决方案1】:

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

希望我能够至少稍微全面地说明这一点。 :-)

【讨论】:

  • 感谢您提供非常准确的信息,GTK 内部是如何工作的
【解决方案2】:

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

【讨论】:

  • 对,我可以在复制列表上使用 Contains() 和 Remove() 方法,同时迭代原始列表。谢谢。
【解决方案3】:

List&lt;T&gt; 上,有一个RemoveAll 方法接受一个委托来指示是否删除该项目。你可以这样使用它:

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

【讨论】:

  • +1 请注意,这只适用于 C# 3.0 及更高版本。 OP 用版本 2、3 和 4 标记了问题 :-)
  • @Jakob:如果 OP 不使用 C#3 或更新版本,那么他们可以使用 old-skool 委托语法调用 RemoveAlltmpCLientList.RemoveAll(delegate(Client cli) { return cli.Name != txtboxClientName.Text; });
  • 哦,对了 - 认为 RemoveAll 是一种扩展方法。对此感到抱歉:-)
【解决方案4】:

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

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

}

【讨论】:

  • 不错的简单解决方案,感谢分享
【解决方案5】:

要么使用 for/while 循环,要么使用tmpClientList.RemoveAll(a =&gt; a.Name == txtboxClientName.Text)。由于您没有指定您使用的是哪个 c# 版本,ymmw。

【讨论】:

  • 当仅限于 2.0 时,它只是有点冗长:tmpClientList.RemoveAll(delegate(Client a) { return a.Name == txtboxClientName.Text; });
【解决方案6】:

不要使用 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);
    }

【讨论】:

    【解决方案7】:

    问题是您正在尝试在 foreach 迭代中修改列表。将其替换为 for 就可以了。

    此外,由于您似乎使用用户输入作为名称,请考虑稍微清理一下输入,至少使用 Trim() 来删除多余的空格。如果你不这样做,“John”和“John”将是两个不同的东西。 相同的初始 != "" 检查。

    【讨论】:

    • 你的意思是,“”和“”是一样的,在那种情况下我也应该修剪输入(当用户只输入白色字符作为输入时)?
    • @dygi:是的。删除空格只留下相关信息 - 在这种情况下没有。至于可能发生这种情况的情况:用户开始输入名称,包括其后的空格,然后决定删除字符并保留空格,因为他无法“看到”它。
    猜你喜欢
    • 1970-01-01
    • 2020-12-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-15
    • 2011-09-29
    • 2013-08-03
    • 1970-01-01
    相关资源
    最近更新 更多