C# 将RemoveAll限制为一定数量的对象

C# 将RemoveAll限制为一定数量的对象,c#,lambda,removeall,C#,Lambda,Removeall,我正在使用一个包含父对象和子对象的列表。在此列表中,子对象知道其相关的父对象,反之亦然。使用这个列表,我试图实现一个业务规则,当其父对象属于某一类型时,将从列表中删除多达4个子对象。换句话说,如果此类型的父级有20个子级,则应从列表中删除其中的4个子级 var matches = myList.Where(item => item.Child && "Foo".Equals(item.Parent.SpecialType)).Take(someNumber).ToList(

我正在使用一个包含父对象和子对象的
列表
。在此列表中,子对象知道其相关的父对象,反之亦然。使用这个列表,我试图实现一个业务规则,当其父对象属于某一类型时,将从列表中删除多达4个子对象。换句话说,如果此类型的父级有20个子级,则应从列表中删除其中的4个子级

var matches = myList.Where(item => item.Child && "Foo".Equals(item.Parent.SpecialType)).Take(someNumber).ToList();
matches.ForEach(m => myList.Remove(m));
我在这里概述的代码将
删除满足条件的所有子对象。这是意料之中的,但我想做的是将
RemoveAll
限制为仅删除4个子项。是否有一种方法可以通过
RemoveAll
实现这一点,或者是否有其他方法可以使用

myList.RemoveaAll(item =>
  item.Child && "Foo".Equals(item.Parent.SpecialType));

为什么不使用
Take
函数?

使用Take扩展方法从IEnumerable中获取前n个匹配项。然后,您可以遍历匹配项并将其从列表中删除

var matches = myList.Where(item => item.Child && "Foo".Equals(item.Parent.SpecialType)).Take(someNumber).ToList();
matches.ForEach(m => myList.Remove(m));

哪4个重要吗?如果没有,您可以使用
.Take(4)
创建一个包含4个子项的列表,然后遍历并
删除
4…

尝试以下操作:

int i = 0;
myList.Removeall(item =>
  item.Child && "Foo".Equals(item.Parent.SpecialType) && i++ < 4);
inti=0;
myList.Removeall(项目=>
item.Child&“Foo.Equals(item.Parent.SpecialType)&&i++<4);

请注意,我还没有测试它,但它应该可以工作

您还可以编写一个扩展方法,在普通列表界面的基础上构建,如下所示:

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

namespace App
{

    public static class ListExtension
    {
        public static int RemoveAll<T>(this List<T> list, Predicate<T> match, uint maxcount)
        {
            uint removed = 0;
            Predicate<T> wrappingmatcher = (item) =>
            {
                if (match(item) && removed < maxcount)
                {
                    removed++;
                    return true;
                }
                else
                {
                    return false;
                }
            };
            return list.RemoveAll(wrappingmatcher);
        }
    }

    public interface IHero { }
    public class Batman : IHero { }

    public class HeroCompilation
    {
        public List<IHero> Herolist;

        public HeroCompilation()
        {
            Herolist = new List<IHero>();
        }

        public void AddBatmans(int count){
            for (int i = 1; i <= count; i++) Herolist.Add(new Batman());
        }
    }

    class Program
    {
        static void ConsoleWriteBatmanCount(List<IHero> hero)
        {
            Console.WriteLine("There are {0} Batmans", hero.Count);
        }


        static void Main(string[] args)
        {
            HeroCompilation tester = new HeroCompilation();
            ConsoleWriteBatmanCount(tester.Herolist);
            tester.AddBatmans(10);
            ConsoleWriteBatmanCount(tester.Herolist);
            tester.Herolist.RemoveAll((x) => { return true; }, 4);
            ConsoleWriteBatmanCount(tester.Herolist);
            tester.Herolist.RemoveAll((x) => { return true; }, 4);
            ConsoleWriteBatmanCount(tester.Herolist);
            tester.Herolist.RemoveAll((x) => { return true; }, 4);
            ConsoleWriteBatmanCount(tester.Herolist);
            while (true) ;
        }
    }
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用系统文本;
命名空间应用程序
{
公共静态类ListExtension
{
公共静态int-RemoveAll(此列表、谓词匹配、uint-maxcount)
{
移除的uint=0;
谓词包装器=(项)=>
{
if(匹配(项目)&删除{return true;},4);
控制台WriteBatmanCount(tester.Herolist);
tester.Herolist.RemoveAll((x)=>{return true;},4);
控制台WriteBatmanCount(tester.Herolist);
虽然(正确);
}
}
}

Take不会修改列表。他想删除项目。Take不会修改列表。为了澄清,Take不会删除任何项目。您正在使用它来查找随后要删除的项目。只有一个问题:
Take()
不迭代列表。因此,使用原样的代码,您可以在从列表中删除的同时迭代myList。这太糟糕了!我建议您在第一行中附加
.ToList()
.ToArray()
。事实上,我将继续为您添加它,如果您不同意,请随意回滚。通过@Randolpho的修改,这就像一个魅力。否则,在使用
ForEach
之前,必须将.ToList追加到匹配项中。我从未见过
Take
扩展方法。谢谢你指出这一点@Asteel:在调用
Take()
后添加
.ToList()
,或将其附加到
匹配项中
完成相同的任务;唯一改变的是,在这么短的时间内,匹配的类型实际上是什么。您也可以简单地在一行中完成它:
myList.Where(item=>item.Child&“Foo.Equals(item.Parent.SpecialType)).Take(someNumber.ToList().ForEach(m=>myList.Remove(m))不进行测试,
i
是否会在每次迭代时重置为0?第一次发现它似乎有用,但之后就不行了。