C#List <t> .ForEach(委托(类型t)以及如何在列表迭代时删除项目</t>

时间:2013-03-20 20:49:30

标签: c# list xna-4.0

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

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

现在,我正在更新一些旧项目,并且有一个 LOT 代码,因为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);
                    }
                }
            });

我只是希望有一种方法可以不必重写所有这些..所以,时间更新并改变我的代码显然方式。谢谢!

5 个答案:

答案 0 :(得分:9)

使用列表的RemoveAll方法。

您可以将代码重构为:

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

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

答案 1 :(得分:3)

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

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

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

答案 2 :(得分:2)

既然你说你做了很多,为什么不做这样的事情:

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)

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

答案 3 :(得分:1)

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

所以有一个内置的东西可以做到这一点。

更好 - 内置的人确切地知道如何避免在迭代时修改集合的问题。而且因为它可以访问私有内部,所以它也更有效。

因此,只需使用List类本身就可以编写如下代码:

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

答案 4 :(得分:1)

这可以通过小调整来实现。这是一个例子:

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方法删除多个元素。

修改

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