2

这可能被认为是糟糕的编程,但在 .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);
                    }
                }
            });

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

4

5 回答 5

9

使用列表的RemoveAll方法。

您可以将代码重构为:

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

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

于 2013-03-20T21:02:25.850 回答
3

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

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

for (var i = enemyList.Count() - 1; i >= 0; i--) {
{
    if(condition) enemyList.RemoveAt(i);
}
于 2013-03-20T20:50:31.350 回答
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)

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

于 2013-03-20T20:55:49.080 回答
1

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

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

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

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

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

于 2013-03-20T21:01:21.287 回答
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其次,您应该小心仅对引用类型执行此操作——即不使用原始类型——否则您将使用该方法删除多个元素。

编辑

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

于 2013-03-20T21:02:16.363 回答