c# List< t>.ForEach(delegate(type t))以及如何在迭代列表时删除项

本文关键字:迭代 列表 删除 List ForEach delegate type | 更新日期: 2023-09-27 18:04:52

这可能被认为是糟糕的编程,但在。net 4之前,我曾经大量使用类似的代码:

    enemyList.ForEach(delegate(Enemy e)
    {
        e.Update();
        if (someCondition)
              enemyList.Remove(e);
    });

现在,我正在经历一个更新一些旧项目,有一个很多的代码将不得不改变,因为ForEach被删除。现在,我确实有一个扩展,允许我使用ForEach:

    public static void ForEach<T>(this IEnumerable<T> sequence, Action<T> action)
    {
        if (sequence == null) throw new ArgumentNullException("sequence");
        if (action == null) throw new ArgumentNullException("action");
        foreach (T item in sequence)
            action(item);
    }

我知道我可以这样做:

    var index = 0;
    while(index < enemyList.Count)
    {
        if(condition)
            enemyList.RemoveAt(index);
        else
            index++;
    }

但是像那样重写其中一些会很痛苦…有没有办法把这个功能添加回来,这样我就可以遍历这个列表,删除我需要的项目,而不必回去重写和编辑所有这些函数?我仍然认为自己是一个编码新手,我只是不明白这个…任何帮助将不胜感激!

* * * * * * * * * 编辑 * * * * * * * * *

所以我想它归结为重写大量的代码…我有很多这样的代码,我刚刚从一个项目中取出:

            GameplayScreen.gameWorld.shipList.ForEach(delegate(Ship s)
            {
                if (eS.originalShipsID == s.shipID)
                {
                    if (!eS.Alive || eS.health <= 0)
                    {
                        // this one sunk...
                        string log = "0" + s.shipName + " was sunk in battle.. All crew and cargo were lost.";
                        AddLogEntry(log);
                        totalCrewLost += s.currentCrew;
                        GameplayScreen.gameWorld.shipList.Remove(s);
                    }
                }
            });

我只是希望有一种方法不必重写所有这些…所以是时候更新和改变我的编码方式了。谢谢!

c# List< t>.ForEach(delegate(type t))以及如何在迭代列表时删除项

使用列表的RemoveAll方法

您可以将代码重构为:

enemyList.RemoveAll(enemy => enemy.SomeCondition);

它不仅比while循环好,我认为它比Foreach方法好得多。

你不能。唯一的方法是将要删除的项添加到另一个列表中,然后遍历该列表,并在初始迭代后删除它们。

更好的选择是使用反向for循环来遍历这些值。然后,您可以在初始迭代期间安全地删除这些项:

for (var i = enemyList.Count() - 1; i >= 0; i--) {
{
    if(condition) enemyList.RemoveAt(i);
}

既然你说你经常这样做,为什么不这样做呢:

public static void RemoveIfTrue<T>(this ICollection<T> list, Func<T, bool> condition)
{
    List<T> itemsToRemove = list.Where(condition).ToList();
    foreach (var item in itemsToRemove)
    {
        list.Remove(item);
    }
}

那么你可以这样使用:

myList.RemoveIfTrue(x => YourConditionIsTrue)

这样你就不会有一堆重复的逻辑。

如果您使用的是List<T>,则可以使用List<T>.RemoveAll(Predicate<T> match)

已经有一个内置的东西来做这个了

甚至更好——内置的函数知道如何避免在迭代集合时修改集合的问题。因为它可以访问私有内部,所以效率也更高。

所以,只要使用List类本身你就可以写这样的代码:

enemies.RemoveAll(enemy => (enemy.Health <= 0));

只要稍微调整一下就可以了。下面是一个例子:

public static class Extensions
{
    public static void ForEach<T>(this IList<T> list, Action<T> action)
    {
        for (int i = 0; i < list.Count; i++)
        {
            action(list[i]);
        }
    }
}
class Program
{
    static void Main(string[] args)
    {
        List<string> vals = new List<string>(new string[] { "a", "bc", "de", "f", "gh", "i", "jk" });
        vals.ToList().ForEach<string>(delegate(string value)
        {
            if (value.Length > 1)
            {
                vals.Remove(value);
            }
        });
        vals.ToList().ForEach<string>(delegate(string value)
        {
            Console.WriteLine(value);
        });
        Console.ReadKey();
    }
}

现在,这里有几件事值得一提:首先,通常元素会被跳过。但是,通过调用ToList()来生成列表的单独副本。其次,您应该小心地只对引用类型执行此操作,即不要对基本类型执行此操作,否则您将使用remove方法删除多个元素。

编辑

我还想补充一点,可能任何发布的替代方案都更好——但我认为这是可以做到的,这很有趣;它的性能较差,但可能更快地插入现有代码。