Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/312.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何有条件地从.NET集合中删除项_C#_.net_Collections_Extension Methods - Fatal编程技术网

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

C# 如何有条件地从.NET集合中删除项,c#,.net,collections,extension-methods,C#,.net,Collections,Extension Methods,我正在尝试在.NET中编写一个扩展方法,该方法将对泛型集合进行操作,并从集合中删除符合给定条件的所有项 这是我第一次尝试: public static void RemoveWhere<T>(this ICollection<T> Coll, Func<T, bool> Criteria){ foreach (T obj in Coll.Where(Criteria)) Coll.Remove(obj); } public stati

我正在尝试在.NET中编写一个扩展方法,该方法将对泛型集合进行操作,并从集合中删除符合给定条件的所有项

这是我第一次尝试:

public static void RemoveWhere<T>(this ICollection<T> Coll, Func<T, bool> Criteria){
    foreach (T obj in Coll.Where(Criteria))
        Coll.Remove(obj);
}
public static void removehere(此ICollection Coll,Func标准){
foreach(在Coll.Where中的目标(标准))
Coll.移除(obj);
}
但是,这会引发InvalidOperationException,“集合已修改;枚举操作可能无法执行”。这是有意义的,因此我再次尝试使用第二个集合变量来保存需要删除的项,并对其进行迭代:

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

    foreach (T obj in forRemoval)
        Coll.Remove(obj);
}
public static void removehere(此ICollection Coll,Func标准){
移除列表=Coll.Where(标准).ToList();
foreach(用于拆卸的目标)
Coll.移除(obj);
}
这引发了同样的异常;我不确定我是否真的理解为什么“Coll”不再是被迭代的集合,那么为什么不能修改它呢

如果有人对我如何实现这一点有任何建议,或者有更好的方法来实现这一点,那就太好了

谢谢。

对于
列表
,它已经存在,因为
删除了所有(谓词)
。因此,我建议您保留名称(允许熟悉和优先)

基本上,您不能在迭代时删除。有两种常见的选择:

  • 使用基于索引器的迭代(
    用于
    )和删除
  • 缓冲要删除的项目,并在
    foreach
    之后删除(正如您已经做的那样)
所以也许:

public static void RemoveAll<T>(this IList<T> list, Func<T, bool> predicate) {
    for (int i = 0; i < list.Count; i++) {
        if (predicate(list[i])) {
            list.RemoveAt(i--);
        }
    }
}
publicstaticvoidremoveall(此IList列表,Func谓词){
for(int i=0;i
或更一般地,对于任何
i收集

publicstaticvoidremoveall(此ICollection集合,Func谓词){
T元素;
for(int i=0;i

这种方法的优点是避免了大量额外的列表副本。

我刚刚测试了它,您的第二种方法运行良好(应该如此)。一定是出了什么问题,你能提供一些示例代码来说明这个问题吗

List<int> ints = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

ints.RemoveWhere(i => i > 5);
foreach (int i in ints)
{
    Console.WriteLine(i);
}
正如Marc所说,
List.RemoveAll()
是处理列表的方法

但是,我很惊讶您的第二个版本没有工作,因为您在调用
Where()
之后调用了
ToList()
。如果没有
ToList()
调用,它肯定是有意义的(因为它会被惰性地计算),但它应该是正常的。你能举一个简短但完整的例子说明这个失败吗

编辑:关于你在问题中的评论,我仍然不能让它失败。下面是一个简短但完整的示例:

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

public class Staff
{
    public int StaffId;
}

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

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

class Test
{
    static void Main(string[] args)
    {
        List<Staff> mockStaff = new List<Staff>
        {
            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);
    }
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
公营班主任
{
公共国际办事处;
}
公共静态类扩展
{
公共静态无效删除位置(此ICollection Coll,
Func标准)
{
移除列表=Coll.Where(标准).ToList();
foreach(用于拆卸的目标)
{
Coll.移除(obj);
}
}
}
课堂测试
{
静态void Main(字符串[]参数)
{
List mockStaff=新列表
{
新工作人员{StaffId=3},
新工作人员{StaffId=7}
};
职员新闻TAFF=新职员{StaffId=5};
mockStaff.Add(newStaff);
mockStaff.removehere(s=>s.StaffId==5);
Console.WriteLine(mockStaff.Count);
}
}

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

我刚刚尝试了您的第二个示例,它似乎运行良好:

Collection<int> col = new Collection<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
col.RemoveWhere(x => x % 2 != 0);

foreach (var x in col)
    Console.WriteLine(x);
Console.ReadLine();
Collection col=new Collection(){1,2,3,4,5,6,7,8,9,10};
列RemoveWhere(x=>x%2!=0);
foreach(变量x列)
控制台写入线(x);
Console.ReadLine();

我没有遇到异常。

另一个版本的Marcs RemoveAll:

public static void RemoveAll<T>(this IList<T> list, Func<T, bool> predicate)
{
    int count = list.Count;
    for (int i = count-1; i > -1; i--)
    {
        if (predicate(list[i]))
        {
            list.RemoveAt(i);
        }
    }
}
publicstaticvoidremoveall(此IList列表,Func谓词)
{
int count=list.count;
对于(int i=count-1;i>-1;i--)
{
if(谓词(列表[i]))
{
列表。删除(i);
}
}
}

您要在哪个集合上测试这些?由于似乎只有您在第二个测试中得到异常,所以您的集合可能甚至不支持删除…下面是使用此方法时失败的单元测试的摘录,其中“mockStaff”是一个预先存在的列表。职员新闻TAFF=新职员{StaffId=5};mockStaff.Add(newStaff);mockStaff.removehere(s=>s.StaffId==5)//此处引发无效的操作异常Lee:一定发生了其他情况。请看我的答案中的一个例子。我将该方法还原为以前的方法,现在它似乎像每个人所说的那样工作。非常奇怪。也许我正在失去它。谢谢大家的帮助。当我使用您的第一个常用选项从集合中删除时,我通常将Count存储在int n变量中,让循环从头到尾计数,而不需要I--。在循环内保存Count()调用。(我知道,过早的优化…@rolls它将破坏foreach的
foreach
,但我们没有使用它。最常见的选项是“向后迭代”,但如果在删除时进行补偿,则向前也是有效的a)不应将计数存储在外部,如果这样做,编译器将无法删除边界检查。B)此重载几乎是无用的,在传入之前,您必须将谓词明确地保存为Func,否则它将满足原始RemoveAll(谓词),并且不会调用您的扩展。A)我知道,但是即使您删除循环中的项并依赖于
Collection<int> col = new Collection<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
col.RemoveWhere(x => x % 2 != 0);

foreach (var x in col)
    Console.WriteLine(x);
Console.ReadLine();
public static void RemoveAll<T>(this IList<T> list, Func<T, bool> predicate)
{
    int count = list.Count;
    for (int i = count-1; i > -1; i--)
    {
        if (predicate(list[i]))
        {
            list.RemoveAt(i);
        }
    }
}