我正在寻找一种更好的模式,用于处理每个元素的列表,然后根据结果从列表中删除。

你不能在foreach (var element in X)中使用.Remove(element)(因为它会导致Collection被修改;枚举操作可能无法执行。例外)……你也不能使用for (int I = 0;i < elements.Count();i++)和. removeat (i),因为它破坏了你在集合中相对于i的当前位置。

有没有一种优雅的方式来做到这一点?


当前回答

我希望“模式”是这样的:

foreach( thing in thingpile )
{
    if( /* condition#1 */ )
    {
        foreach.markfordeleting( thing );
    }
    elseif( /* condition#2 */ )
    {
        foreach.markforkeeping( thing );
    }
} 
foreachcompleted
{
    // then the programmer's choices would be:

    // delete everything that was marked for deleting
    foreach.deletenow(thingpile); 

    // ...or... keep only things that were marked for keeping
    foreach.keepnow(thingpile);

    // ...or even... make a new list of the unmarked items
    others = foreach.unmarked(thingpile);   
}

这将使代码与程序员大脑中进行的过程保持一致。

其他回答

你不能使用foreach,但是当你删除一个项目时,你可以向前迭代并管理你的循环索引变量,如下所示:

for (int i = 0; i < elements.Count; i++)
{
    if (<condition>)
    {
        // Decrement the loop counter to iterate this index again, since later elements will get moved down during the remove operation.
        elements.RemoveAt(i--);
    }
}

注意,一般来说,所有这些技术都依赖于被迭代的集合的行为。这里显示的技术将与标准List(T)一起工作。(很有可能编写自己的集合类和迭代器,允许在foreach循环期间删除项。)

因为任何移除都是在你可以使用的条件下进行的

list.RemoveAll(item => item.Value == someValue);
foreach(var item in list.ToList())

{

if(item.Delete) list.Remove(item);

}

只需从第一个列表创建一个全新的列表。我说“简单”而不是“正确”,因为创建一个全新的列表可能比之前的方法具有更高的性能(我没有费心进行任何基准测试)。我通常更喜欢这种模式,它在克服Linq-To-Entities限制方面也很有用。

for(i = list.Count()-1;i>=0;i--)

{

item=list[i];

if (item.Delete) list.Remove(item);

}

这种方法使用普通的For循环向后遍历列表。如果集合的大小发生了变化,那么向前执行这个操作可能会有问题,但是向后执行应该总是安全的。

在遍历列表时从列表中删除项的最佳方法是使用RemoveAll()。但是人们编写的主要问题是他们必须在循环中做一些复杂的事情和/或有复杂的比较情况。

解决方案是仍然使用RemoveAll(),但使用以下符号:

var list = new List<int>(Enumerable.Range(1, 10));
list.RemoveAll(item => 
{
    // Do some complex operations here
    // Or even some operations on the items
    SomeFunction(item);
    // In the end return true if the item is to be removed. False otherwise
    return item > 5;
});

For循环是一个不好的构造。

使用时

var numbers = new List<int>(Enumerable.Range(1, 3));

while (numbers.Count > 0)
{
    numbers.RemoveAt(0);
}

但是,如果你一定要用for

var numbers = new List<int>(Enumerable.Range(1, 3));

for (; numbers.Count > 0;)
{
    numbers.RemoveAt(0);
}

或者,这个:

public static class Extensions
{

    public static IList<T> Remove<T>(
        this IList<T> numbers,
        Func<T, bool> predicate)
    {
        numbers.ForEachBackwards(predicate, (n, index) => numbers.RemoveAt(index));
        return numbers;
    }

    public static void ForEachBackwards<T>(
        this IList<T> numbers,
        Func<T, bool> predicate,
        Action<T, int> action)
    {
        for (var i = numbers.Count - 1; i >= 0; i--)
        {
            if (predicate(numbers[i]))
            {
                action(numbers[i], i);
            }
        }
    }
}

用法:

var numbers = new List<int>(Enumerable.Range(1, 10)).Remove((n) => n > 5);

然而,LINQ已经有RemoveAll()来做这件事

var numbers = new List<int>(Enumerable.Range(1, 10));
numbers.RemoveAll((n) => n > 5);

最后,你最好使用LINQ的Where()来过滤和创建一个新列表,而不是改变现有的列表。不变性通常是好的。

var numbers = new List<int>(Enumerable.Range(1, 10))
    .Where((n) => n <= 5)
    .ToList();