如何破解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:使用辅助变量我知道怎么做。
有两个真正的选项。告诉你不要删除更多项目的变量:
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;
});