【问题标题】:How to modify or delete items from an enumerable collection while iterating through it in C#如何在 C# 中迭代​​可枚举集合时修改或删除项目
【发布时间】:2010-09-23 10:26:24
【问题描述】:

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

【问题讨论】:

  • 我编辑了标题以便更容易找到这个问题。我之前关闭了一个骗局,但我可以看到用户可能错过了这个带有旧标题的问题。

标签: c# collections iteration


【解决方案1】:

如果您通过使用委托而不是 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));
}

【讨论】:

    【解决方案2】:

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

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

    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);
    }
    

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

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

    【讨论】:

    • +1 用于实现 LINQ 的强大功能。当你不必做事时,为什么要做很长的路
    • 为什么不将列表 l 定义为 List 而不是 var 如果你知道它会是一个 List? +1 虽然
    • + 1 表示 RemoveAll。注意 RemoveAll 不是严格意义上的 LINQ。它可用于 .NET 2.0 中没有 LINQ 支持的任何“列表”类。
    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

      通过列表向后迭代听起来是一种更好的方法,因为如果您删除一个元素而其他元素“落入间隙”,那并不重要,因为您已经看过这些了。此外,您不必担心您的计数器变量会变得大于 .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);
                  }
              }
      

      【讨论】:

        【解决方案5】:

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

        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();
            }
        }
        

        对行调用 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
            }
        }
        

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

        【讨论】:

          【解决方案6】:

          如您所说,在迭代列表时删除或添加列表可能会破坏它。

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

          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.
          

          【讨论】:

            【解决方案7】:

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

            看看这个例子:

                    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);
                    }
            

            【讨论】:

            • 这是有缺陷的,因为并非所有元素都会被检查。如果删除 i 处的元素,则 i + 1 处的元素变为 i。当 i 然后为下一个循环递增时,它将跳过刚刚替换已删除元素的元素(希望这是有道理的)。
            • 我更正了这个。谢谢安迪的耳光。但我的观点是,您可以使用 for 循环更改集合。
            • @Bruno - 绝对同意 for 循环可用于编辑集合,并且您的编辑已修复元素跳过问题。
            • 向后迭代不是更好吗? for(int i = l.count; i > 0; i--)。这样,如果您删除一个元素并且下一个元素“落在它的位置”,那没关系,因为您已经检查过了。还是我在这里遗漏了什么?
            • 最好使用内置的 RemoveAll 方法,该方法以谓词为参数。
            【解决方案8】:

            while 循环会处理这个问题:

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

            【讨论】:

            • 此解决方案面临与上述相同的问题,如果您删除项目,您的索引将关闭。
            • 不,它们不会因为索引仅在未删除元素时增加。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-05-15
            • 2017-01-07
            相关资源
            最近更新 更多