当前位置:  开发笔记 > 编程语言 > 正文

如何有条件地从.NET集合中删除项目

如何解决《如何有条件地从.NET集合中删除项目》经验,为你挑选了2个好方法。

我正在尝试在.NET中编写一个将在泛型集合上运行的扩展方法,并从集合中删除与给定条件匹配的所有项目.

这是我的第一次尝试:

public static void RemoveWhere(this ICollection Coll, Func Criteria){
    foreach (T obj in Coll.Where(Criteria))
        Coll.Remove(obj);
}

但是,这会抛出InvalidOperationException,"Collection已被修改;枚举操作可能无法执行".这是有意义的,所以我第二次尝试使用第二个集合变量来保存需要删除的项目并反复遍历:

public static void RemoveWhere(this ICollection Coll, Func Criteria){
    List forRemoval = Coll.Where(Criteria).ToList();

    foreach (T obj in forRemoval)
        Coll.Remove(obj);
}

这引发了同样的异常; 我不确定我真的理解为什么'Coll'不再是迭代的集合,为什么不能修改它?

如果有人对如何使这个工作有任何建议,或者更好的方法来实现同样的目标,那就太好了.

谢谢.



1> Marc Gravell..:

因为List,这已经存在了RemoveAll(Predicate).因此,我建议你保留名称(允许熟悉和优先).

基本上,迭代时无法删除.有两种常见的选择:

使用基于索引器的迭代(for)和删除

缓冲要删除的项目,并在foreach(如您已经完成)之后删除

所以也许:

public static void RemoveAll(this IList list, Func predicate) {
    for (int i = 0; i < list.Count; i++) {
        if (predicate(list[i])) {
            list.RemoveAt(i--);
        }
    }
}

或更普遍的任何ICollection:

public static void RemoveAll(this ICollection collection, Func predicate) {
    T element;

    for (int i = 0; i < collection.Count; i++) {
        element = collection.ElementAt(i);
        if (predicate(element)) {
            collection.Remove(element);
            i--;
        }
    }
}

这种方法的优点是可以避免大量额外的列表副本.


当我使用你的第一个常用选项从集合中删除时,我通常将Count存储在一个int n变量中,让循环从头到尾重新计数,不需要i--.在循环内保存Count()调用.(过早优化,我知道......)

2> Jon Skeet..:

正如Marc所说,List.RemoveAll()是列表的方法.

我很惊讶你的第二个版本没有用,因为你已经接到电话ToList()后的Where()电话.如果没有ToList()电话,它肯定会有意义(因为它会被懒惰地评估),但它应该没问题.你能否展示一下这个失败的简短但完整的例子?

编辑:关于你在问题中的评论,我仍然无法让它失败.这是一个简短但完整的例子,它有效:

using System;
using System.Collections.Generic;
using System.Linq;

public class Staff
{
    public int StaffId;
}

public static class Extensions
{
    public static void RemoveWhere(this ICollection Coll,
                                      Func Criteria)
    {
        List forRemoval = Coll.Where(Criteria).ToList();

        foreach (T obj in forRemoval)
        {
            Coll.Remove(obj);
        }
    }
}

class Test
{
    static void Main(string[] args)
    {
        List mockStaff = new List
        {
            new Staff { StaffId = 3 },
            new Staff { StaffId = 7 }
        };

       Staff newStaff = new Staff{StaffId = 5};
       mockStaff.Add(newStaff);
       mockStaff.RemoveWhere(s => s.StaffId == 5);

       Console.WriteLine(mockStaff.Count);
    }
}

如果您能提供一个类似的完整示例失败,我相信我们可以找出原因.

推荐阅读
sx-March23
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有