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

你不能在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()的简单性,没有内存开销。

其他回答

在泛型列表上使用ToArray()可以在泛型列表上执行Remove(item):

        List<String> strings = new List<string>() { "a", "b", "c", "d" };
        foreach (string s in strings.ToArray())
        {
            if (s == "b")
                strings.Remove(s);
        }

当您希望在迭代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);
    }
}

我会这样做

using System.IO;
using System;
using System.Collections.Generic;

class Author
    {
        public string Firstname;
        public string Lastname;
        public int no;
    }

class Program
{
    private static bool isEven(int i) 
    { 
        return ((i % 2) == 0); 
    } 

    static void Main()
    {    
        var authorsList = new List<Author>()
        {
            new Author{ Firstname = "Bob", Lastname = "Smith", no = 2 },
            new Author{ Firstname = "Fred", Lastname = "Jones", no = 3 },
            new Author{ Firstname = "Brian", Lastname = "Brains", no = 4 },
            new Author{ Firstname = "Billy", Lastname = "TheKid", no = 1 }
        };

        authorsList.RemoveAll(item => isEven(item.no));

        foreach(var auth in authorsList)
        {
            Console.WriteLine(auth.Firstname + " " + auth.Lastname);
        }
    }
}

输出

Fred Jones
Billy TheKid

使用属性跟踪要删除的元素,并在处理后将它们全部删除。

using System.Linq;

List<MyProperty> _Group = new List<MyProperty>();
// ... add elements

bool cond = false;
foreach (MyProperty currObj in _Group)
{
    // here it is supposed that you decide the "remove conditions"...
    cond = true; // set true or false...
    if (cond) 
    {
        // SET - element can be deleted
        currObj.REMOVE_ME = true;
    }
}
// RESET
_Group.RemoveAll(r => r.REMOVE_ME);

如果决定删除哪些项的函数没有副作用,也没有改变项(这是一个纯函数),一个简单有效的(线性时间)解决方案是:

list.RemoveAll(condition);

如果有副作用,我会使用如下方法:

var toRemove = new HashSet<T>();
foreach(var item in items)
{
     ...
     if(condition)
          toRemove.Add(item);
}
items.RemoveAll(toRemove.Contains);

这仍然是线性时间,假设哈希是好的。但是由于hashset,它增加了内存使用。

最后,如果你的列表只是一个IList<T>,而不是一个list <T>,我建议我的答案,我怎么能做这个特殊的foreach迭代器?与许多其他答案的二次运行时相比,给出IList<T>的典型实现,它将具有线性运行时。