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

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

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


当前回答

myList.RemoveAt(i--);

simples;

其他回答

你不能使用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循环期间删除项。)

复制您正在迭代的列表。然后从副本中删除并与原件相互作用。倒退是令人困惑的,并且在并行循环时不能很好地工作。

var ids = new List<int> { 1, 2, 3, 4 };
var iterableIds = ids.ToList();

Parallel.ForEach(iterableIds, id =>
{
    ids.Remove(id);
});

我将从一个过滤掉您不想保留的元素的LINQ查询中重新分配列表。

list = list.Where(item => ...).ToList();

除非列表非常大,否则这样做应该不会有明显的性能问题。

当您希望在迭代Collection时从其中删除元素时,首先想到的应该是反向迭代。

幸运的是,有一个比编写for循环更优雅的解决方案,因为for循环涉及不必要的输入,而且容易出错。

ICollection<int> test = new List<int>(new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10});

foreach (int myInt in test.Reverse<int>())
{
    if (myInt % 2 == 0)
    {
        test.Remove(myInt);
    }
}

这里还有一个没有提到的选项。

如果您不介意在项目的某个地方添加一些代码,您可以向List添加和扩展,以返回反向遍历列表的类的实例。

你可以这样使用它:

foreach (var elem in list.AsReverse())
{
    //Do stuff with elem
    //list.Remove(elem); //Delete it if you want
}

这是什么扩展看起来像:

public static class ReverseListExtension
{
    public static ReverseList<T> AsReverse<T>(this List<T> list) => new ReverseList<T>(list);

    public class ReverseList<T> : IEnumerable
    {
        List<T> list;
        public ReverseList(List<T> list){ this.list = list; }

        public IEnumerator GetEnumerator()
        {
            for (int i = list.Count - 1; i >= 0; i--)
                yield return list[i];
            yield break;
        }
    }
}

这基本上就是没有分配的list.Reverse()。

就像一些人提到的那样,你仍然会有一个一个地删除元素的缺点,如果你的列表非常长,这里的一些选项会更好。但我认为有些人希望list.Reverse()的简单性,没有内存开销。