我正在使用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:使用辅助变量我知道该怎么做。
答案 0 :(得分:3)
有两个真正的选择。一个变量,告诉您不要删除更多项目:
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
移除多个项目的能力,而不会将所有项目移动到最后,除非您重新创建自己的项目基于整个列表的结构。
答案 1 :(得分:1)
使用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++;
对于未分类的列表,从上到下afaik更为理想。
答案 2 :(得分:1)
您可以使用扩展方法执行此操作:
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,break,s2,s3
答案 3 :(得分:1)
长评论:删除项目的近似代码,取消并避免多次复制尾部:
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);
}
答案 4 :(得分:0)
我认为这不能在RemoveAll()
。
但是,您可以使用TakeWhile()
“模拟”中断,然后使用Where()
过滤列表
var newList = x.TakeWhile(elem => elem != "your condition").Where(elem => elem == "abc");
答案 5 :(得分:-1)
你可以利用闭包:
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;
});