从 List<T> 中删除项目的智能方式在 C# 中枚举时 [英] Intelligent way of removing items from a List&lt;T&gt; while enumerating in C#

查看:25
本文介绍了从 List<T> 中删除项目的智能方式在 C# 中枚举时的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个经典案例,就是在循环中枚举集合时尝试从集合中删除它:

I have the classic case of trying to remove an item from a collection while enumerating it in a loop:

List<int> myIntCollection = new List<int>();
myIntCollection.Add(42);
myIntCollection.Add(12);
myIntCollection.Add(96);
myIntCollection.Add(25);

foreach (int i in myIntCollection)
{
    if (i == 42)
        myIntCollection.Remove(96);    // The error is here.
    if (i == 25)
        myIntCollection.Remove(42);    // The error is here.
}

在发生更改后的迭代开始时,会抛出 InvalidOperationException,因为枚举器不喜欢底层集合发生更改.

At the beginning of the iteration after a change takes place, an InvalidOperationException is thrown, because enumerators don’t like when the underlying collection changes.

我需要在迭代时对集合进行更改.有许多模式可以用来避免这种情况,但似乎没有一个很好的解决方案:

I need to make changes to the collection while iterating. There are many patterns that can be used to avoid this, but none of them seems to have a good solution:

  1. 不要在这个循环内删除,而是保留一个单独的删除列表",在主循环之后处理.

  1. Do not delete inside this loop, instead keep a separate "Delete List", that you process after the main loop.

这通常是一个很好的解决方案,但在我的情况下,我需要项目立即消失,因为等待"直到之后真正删除项目的主循环改变了我代码的逻辑流程.

This is normally a good solution, but in my case, I need the item to be gone instantly as "waiting" till after the main loop to really delete the item changes the logic flow of my code.

不要删除该项目,只需在该项目上设置一个标志并将其标记为非活动.然后添加模式1的功能来清理列表.

Instead of deleting the item, simply set a flag on the item and mark it as inactive. Then add the functionality of pattern 1 to clean up the list.

满足我的所有需求,但这意味着必须更改很多的代码,以便在每次出现项目时检查非活动标志访问.这对我来说太过分了.

This would work for all of my needs, but it means that a lot of code will have to change in order to check the inactive flag every time an item is accessed. This is far too much administration for my liking.

以某种方式将模式 2 的想法合并到一个派生自 List 的类中.这个超级列表将处理非活动标志,事后删除对象,并且不会向枚举消费者公开标记为非活动的项目.基本上,它只是封装了模式 2(以及随后的模式 1)的所有想法.

Somehow incorporate the ideas of pattern 2 in a class that derives from List<T>. This Superlist will handle the inactive flag, the deletion of objects after the fact and also will not expose items marked as inactive to enumeration consumers. Basically, it just encapsulates all the ideas of pattern 2 (and subsequently pattern 1).

有这样的类吗?有没有人有这方面的代码?或者有更好的方法吗?

Does a class like this exist? Does anyone have code for this? Or is there a better way?

有人告诉我,访问 myIntCollection.ToArray() 而不是 myIntCollection 将解决问题并允许我在循环内删除.

I’ve been told that accessing myIntCollection.ToArray() instead of myIntCollection will solve the problem and allow me to delete inside the loop.

对我来说,这似乎是一个糟糕的设计模式,或者它很好?

This seems like a bad design pattern to me, or maybe it’s fine?

详情:

  • 该列表将包含许多项目,我将仅删除其中的一些.

  • The list will contain many items and I will be removing only some of them.

在循环内部,我将进行各种处理,添加、删除等,因此解决方案需要相当通用.

Inside the loop, I will be doing all sorts of processes, adding, removing etc., so the solution needs to be fairly generic.

我需要删除的项目可能不是循环中的当前项目.例如,我可能在 30 项循环的第 10 项上并且需要删除项 6 或项 26.因此,向后遍历数组将不再有效.;o(

The item that I need to delete may not be the current item in the loop. For example, I may be on item 10 of a 30 item loop and need to remove item 6 or item 26. Walking backwards through the array will no longer work because of this. ;o(

推荐答案

最好的解决方案通常是使用 RemoveAll() 方法:

The best solution is usually to use the RemoveAll() method:

myList.RemoveAll(x => x.SomeProp == "SomeValue");

或者,如果您需要删除某些元素:

Or, if you need certain elements removed:

MyListType[] elems = new[] { elem1, elem2 };
myList.RemoveAll(x => elems.Contains(x));

当然,这假设您的循环仅用于删除目的.如果您确实需要额外的处理,那么最好的方法通常是使用 forwhile 循环,因为那时您不会使用一个枚举器:

This assume that your loop is solely intended for removal purposes, of course. If you do need to additional processing, then the best method is usually to use a for or while loop, since then you're not using an enumerator:

for (int i = myList.Count - 1; i >= 0; i--)
{
    // Do processing here, then...
    if (shouldRemoveCondition)
    {
        myList.RemoveAt(i);
    }
}

后退确保您不会跳过任何元素.

Going backwards ensures that you don't skip any elements.

对编辑的回应:

如果您要删除看似随意的元素,最简单的方法可能是跟踪要删除的元素,然后将它们全部删除.像这样:

If you're going to have seemingly arbitrary elements removed, the easiest method might be to just keep track of the elements you want to remove, and then remove them all at once after. Something like this:

List<int> toRemove = new List<int>();
foreach (var elem in myList)
{
    // Do some stuff

    // Check for removal
    if (needToRemoveAnElement)
    {
        toRemove.Add(elem);
    }
}

// Remove everything here
myList.RemoveAll(x => toRemove.Contains(x));

这篇关于从 List<T> 中删除项目的智能方式在 C# 中枚举时的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆