如何在 C# 中迭代​​时修改或删除可枚举集合中的项目

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/308466/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 22:35:23  来源:igfitidea点击:

How to modify or delete items from an enumerable collection while iterating through it in C#

c#collectionsiteration

提问by kjv

I have to delete some rows from a data table. I've heard that it is not ok to change a collection while iterating through it. So instead of a for loop in which I check if a row meets the demands for deletion and then mark it as deleted, I should first iterate through the data table and add all of the rows in a list, then iterate through the list and mark the rows for deletions. What are the reasons for this, and what alternatives do I have (instead of using the rows list I mean)?.

我必须从数据表中删除一些行。我听说在迭代时更改集合是不行的。因此,我应该先遍历数据表并添加列表中的所有行,然后遍历列表并标记用于删除的行。这是什么原因,我有什么替代方案(而不是使用我的意思是使用行列表)?。

采纳答案by bruno conde

You can remove elements from a collection if you use a simple forloop.

如果使用简单for循环,则可以从集合中删除元素。

Take a look at this example:

看看这个例子:

        var l = new List<int>();

        l.Add(0);
        l.Add(1);
        l.Add(2);
        l.Add(3);
        l.Add(4);
        l.Add(5);
        l.Add(6);

        for (int i = 0; i < l.Count; i++)
        {
            if (l[i] % 2 == 0)
            {
                l.RemoveAt(i);
                i--;
            }
        }

        foreach (var i in l)
        {
            Console.WriteLine(i);
        }

回答by Andy Rose

A while loop would handle this:

while 循环将处理此问题:

int i = 0;
while(i < list.Count)
{
    if(<codition for removing element met>)
    {
        list.RemoveAt(i);
    }
    else
    {
        i++;
    }
}

回答by Philluminati

Deleting or adding to the list whilst iterating through it can break it, like you said.

就像您说的那样,在遍历列表的同时删除或添加到列表可能会破坏它。

I often used a two list approach to solve the problem:

我经常使用两个列表的方法来解决问题:

ArrayList matches = new ArrayList();   //second list

for MyObject obj in my_list
{

    if (obj.property == value_i_care_about)
        matches.addLast(obj);
}

//now modify

for MyObject m in matches
{
    my_list.remove(m); //use second list to delete from first list
}

//finished.

回答by MusiGenesis

Since you're working with a DataTable and need to be able to persist any changes back to the server with a table adapter (see comments), here is an example of how you should delete rows:

由于您正在使用 DataTable 并且需要能够使用表适配器将任何更改保留回服务器(请参阅评论),因此以下是您应该如何删除行的示例:

DataTable dt;
// remove all rows where the last name starts with "B"
foreach (DataRow row in dt.Rows)
{
    if (row["LASTNAME"].ToString().StartsWith("B"))
    {
        // mark the row for deletion:
        row.Delete();
    }
}

Calling delete on the rows will change their RowState property to Deleted, but leave the deleted rows in the table. If you still need to work with this table before persisting changes back to the server (like if you want to display the table's contents minus the deleted rows), you need to check the RowState of each row as you're iterating through it like this:

对行调用 delete 会将其 RowState 属性更改为 Deleted,但将已删除的行保留在表中。如果您在将更改持久化回服务器之前仍然需要使用此表(例如,如果您想显示表的内容减去已删除的行),则需要在迭代时检查每一行的 RowState 像这样:

foreach (DataRow row in dt.Rows)
{
    if (row.RowState != DataRowState.Deleted)
    {
        // this row has not been deleted - go ahead and show it
    }
}

Removing rows from the collection (as in bruno's answer) will break the table adapter, and should generally not be done with a DataTable.

从集合中删除行(如布鲁诺的回答)会破坏表适配器,通常不应该使用 DataTable 来完成。

回答by Michael Stum

Iterating Backwards through the List sounds like a better approach, because if you remove an element and other elements "fall into the gap", that does not matter because you have already looked at those. Also, you do not have to worry about your counter variable becoming larger than the .Count.

通过 List 向后迭代听起来是一种更好的方法,因为如果您删除一个元素而其他元素“落入间隙”,这并不重要,因为您已经看过这些。此外,您不必担心您的计数器变量会大于 .Count。

        List<int> test = new List<int>();
        test.Add(1);
        test.Add(2);
        test.Add(3);
        test.Add(4);
        test.Add(5);
        test.Add(6);
        test.Add(7);
        test.Add(8);
        for (int i = test.Count-1; i > -1; i--)
        {
            if(someCondition){
                test.RemoveAt(i);
            }
        }

回答by Adrian

When I need to remove an item from a collection that I am enumerating I usually enumerate it in reverse.

当我需要从我正在枚举的集合中删除一个项目时,我通常会反向枚举它。

回答by chakrit

Taking @bruno code, I'd do it backwards.

使用@bruno 代码,我会倒着做。

Because when you move backwards, the missing array indices do not interfere with the order of your loop.

因为当您向后移动时,缺少的数组索引不会干扰循环的顺序。

var l = new List<int>(new int[] { 0, 1, 2, 3, 4, 5, 6 });

for (int i = l.Count - 1; i >= 0; i--)
    if (l[i] % 2 == 0)
        l.RemoveAt(i);

foreach (var i in l)
{
    Console.WriteLine(i);
}

But seriuosly, these days, I'd use LINQ:

但说真的,这些天,我会使用 LINQ:

var l = new List<int>(new int[] { 0, 1, 2, 3, 4, 5, 6 });

l.RemoveAll(n => n % 2 == 0);

回答by chakrit

chakrit's solution can also be used if you are targetting .NET 2.0 (no LINQ/lambda expressions) by using a delegate rather than a lambda expression:

如果您使用委托而不是 lambda 表达式以 .NET 2.0(无 LINQ/lambda 表达式)为目标,也可以使用 chakrit 的解决方案:

public bool IsMatch(int item) {
    return (item % 3 == 1); // put whatever condition you want here
}
public void RemoveMatching() {
    List<int> x = new List<int>();
    x.RemoveAll(new Predicate<int>(IsMatch));
}