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);
}
}
});
我只是希望有一种方法不必重写所有这些…所以是时候更新和改变我的编码方式了。谢谢!
使用列表的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
方法删除多个元素。
编辑
我还想补充一点,可能任何发布的替代方案都更好——但我认为这是可以做到的,这很有趣;它的性能较差,但可能更快地插入现有代码。