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

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

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


当前回答

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

如果您不介意在项目的某个地方添加一些代码,您可以向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()的简单性,没有内存开销。

其他回答

假设predicate是一个元素的布尔属性,如果它为真,则该元素应该被移除:

        int i = 0;
        while (i < list.Count())
        {
            if (list[i].predicate == true)
            {
                list.RemoveAt(i);
                continue;
            }
            i++;
        }

选择您确实需要的元素,而不是试图删除您不想要的元素。这比删除元素要容易得多(通常也更有效)。

var newSequence = (from el in list
                   where el.Something || el.AnotherThing < 0
                   select el);

我想把这篇评论作为对Michael Dillon下面评论的回应,但它太长了,可能在我的回答中有用:

就我个人而言,我从来不会一个接一个地删除项,如果你确实需要删除,那么调用RemoveAll,它接受一个谓词,只重新排列一次内部数组,而remove则是一个数组。为您删除的每个元素复制操作。RemoveAll大大提高了效率。

当你在一个列表上向后迭代时,你已经有了你想要删除的元素的索引,所以调用RemoveAt会更有效,因为remove首先遍历列表来找到你想要删除的元素的索引,但你已经知道那个索引。

总而言之,我不认为有任何理由在for循环中调用Remove。理想情况下,如果可能的话,使用上面的代码根据需要从列表中输入元素,这样就根本不需要创建第二个数据结构。

使用.ToList()将创建一个列表的副本,正如这个问题中所解释的: ToList()—它是否创建一个新列表?

通过使用ToList(),可以从原始列表中删除,因为实际上是在迭代一个副本。

foreach (var item in listTracked.ToList()) {    

        if (DetermineIfRequiresRemoval(item)) {
            listTracked.Remove(item)
        }

     }

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

如果您不介意在项目的某个地方添加一些代码,您可以向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()的简单性,没有内存开销。

 foreach (var item in list.ToList()) {
     list.Remove(item);
 }

如果你将".ToList()"添加到列表(或LINQ查询的结果)中,你可以直接从"list"中删除"item",而不用担心"Collection was modified;枚举操作不能执行"错误。编译器会复制“list”,这样你就可以安全地删除数组了。

虽然这种模式不是超级高效,但它有一种自然的感觉,并且对于几乎任何情况都足够灵活。例如,当您想要将每个“项”保存到一个DB,并仅当DB保存成功时才将其从列表中删除。