如何破解C#中的RemoveAll()语句

本文关键字:语句 中的 RemoveAll 破解 何破解 | 更新日期: 2023-09-27 17:59:55

我使用的是RemoveAll()语句,它执行列表的foreach元素,并根据委托返回的条件,从列表中删除或不删除该元素。像这样:

x.RemoveAll(delegate(string y)
            {
               if (y == "abc")
                   return true;
               return false;
            });

我想从removeAll中断开foreach,这样在满足某些条件时,我甚至不再尝试删除元素。类似这样的东西:

x.RemoveAll(delegate(string y)
            {
               if (Foo() || Bar())
                   break; //stop trying to remove elements
               if (y == "abc")
                   return true;
               return false;
            });

有没有办法在没有辅助变量的情况下做到这一点?

p.S:使用辅助变量我知道怎么做。

如何破解C#中的RemoveAll()语句

有两个真正的选项。告诉你不要删除更多项目的变量:

var done = false;
list.RemoveAll(item => 
{
    if(done) return false;
    if(Foo() || Bar())
    {
        done = true;
        return false;
    }
    return item == "abc";
}

或者抛出异常(尽管对控制流使用异常是一种非常糟糕的做法)。

list.RemoveAll(item => 
{
    if(Foo() || Bar())
        throw new SomeTypeOfException()
    return item == "abc";
}

如果Foo或Bar是真的是例外/错误的情况,那么也许你可以证明这一点,但这看起来确实像是代码的味道。请注意,从技术上讲,这将是使用RemoveAll的唯一方式,而不会在任何后续项上实际调用委托。

从根本上说,问题是你试图执行的操作与RemoveAll的设计不一致。你真正想要的是一个支持取消的方法版本,或者足够访问列表的内部,以创建一个具有适当取消的可比较方法。遗憾的是,除非您重新创建自己的整个基于列表的结构,否则您无法访问底层阵列,从而无法复制RemoveAll删除多个项目而不向上移动所有项目直到最后的能力。

使用带有break的简单循环将比使用带有触发器的DeleteAll在某个点之后为所有元素生成false更有效。

为什么不先过滤:

foreach(var item in x.Where(o => Foo(o)).ToList())
    x.Remove(item);

如果你关心效率,那么

for(int i = 0; i++; i < x.Length)
    if(Foo(x[i]))
    {
        x.RemoveAt(i);
        break; // if list is sorted
    }
    else
        i++;

对于未排序的列表,最好从上到下排列。

您可以使用扩展方法:

public static IEnumerable<T> RemoveAllUntil<T>(
    this IEnumerable<T> input, 
    Predicate<T> match, 
    Predicate<T> until)
{
    bool untilFound = false;
    foreach (T element in input)
    {
        if(!untilFound) untilFound = until(element);
        if(untilFound || !match(element))
        {
            yield return element;
        }
    }
}

并像这样使用:

var strings = new List<string> { "s1", "s2", "s2", "break", "s2", "s3"};
strings = strings.RemoveAllUntil(
        s => s == "s2", 
        s => s == "break")
    .ToList();

这将给你:

s1,中断,s2,s3

长注释:删除取消项并避免尾部多次复制的近似代码:

void RemoveWithCancelation(this List<T> list, 
     Func<RemoveWithCancelationResult> predicate)
{
   var indexToKeep = -1;
   for (var i = 0; i < list.Count; i++)
   {
      var condition = predicate(list[i]);
      if (condition.Cancel)
          break;
      if (!condition.RemoveItem)
      {
          indexToKeep++;
          list[indexToKeep] = list[i];
      }   
   }
   if (indexToKeep+1 < list.Count)
       list.RemoveRange(indexToKeep+1, list.Count);
}

我不认为这可以在RemoveAll()中完成。

但是,您可以用TakeWhile()"模拟"中断,然后用Where() 过滤列表

 var newList = x.TakeWhile(elem => elem != "your condition").Where(elem => elem == "abc");

您可以利用闭包:

bool stop = false; // the delegate closes over 'stop'
x.RemoveAll(delegate(string y)
{
   if (!stop && y == "abc")
       return true;
   if (Foo() || Bar())
       stop = true;
   return false;
});