.NET - 从列表中删除在“foreach”内 环形

发布于 2024-07-20 12:22:55 字数 536 浏览 8 评论 0原文

我希望代码看起来像这样:

List<Type> Os;

...

foreach (Type o in Os)
    if (o.cond)
        return;  // Quitting early is important for my case!
    else
        Os.Remove(o);

... // Other code

这不起作用,因为当您处于该列表的 foreach 循环内时,无法从列表中删除:

是有没有通用的方法来解决问题?

如果需要的话我可以切换到不同的类型。

选项2:

List<Type> Os;

...

while (Os.Count != 0)
     if (Os[0].cond)
         return;
     else
         Os.RemoveAt(0);

... // Other code

丑陋,但应该有用。

I have code that I want to look like this:

List<Type> Os;

...

foreach (Type o in Os)
    if (o.cond)
        return;  // Quitting early is important for my case!
    else
        Os.Remove(o);

... // Other code

This doesn't work, because you cannot remove from the list when you are inside a foreach loop over that list:

Is there a common way to solve the problem?

I can switch to a different type if needed.

Option 2:

List<Type> Os;

...

while (Os.Count != 0)
     if (Os[0].cond)
         return;
     else
         Os.RemoveAt(0);

... // Other code

Ugly, but it should work.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(17

暖心男生 2024-07-27 12:22:55

您可以向后迭代列表:

for (int i = myList.Count - 1; i >= 0; i--)
{
    if (whatever) myList.RemoveAt(i);
}

为了响应您关于在找到不删除的项目时想要退出的评论,那么仅使用 while 循环将是最好的解决方案。

You can iterate through the list backwards:

for (int i = myList.Count - 1; i >= 0; i--)
{
    if (whatever) myList.RemoveAt(i);
}

In response to your comment about wanting to quit when you find an item that you're NOT removing, then just using a while loop would be the best solution.

茶底世界 2024-07-27 12:22:55

在 foreach 循环内部时,永远不应该从正在迭代的集合中删除任何内容。 这基本上就像锯你坐的树枝一样。

使用您的 while 替代方案。 这是必经之路。

You should never remove anything from a collection you are iterating over while inside of a foreach loop. It's basically like sawing the branch you are sitting on.

Use your while alternative. It is the way to go.

多情癖 2024-07-27 12:22:55

您真的需要在 foreach 循环中执行此操作吗?

这将实现与示例相同的结果,即从列表中删除所有项目,直到第一个与条件匹配的项目为止(或者如果没有一个项目与条件匹配,则删除所有项目)。

int index = Os.FindIndex(x => x.cond);

if (index > 0)
    Os.RemoveRange(0, index);
else if (index == -1)
    Os.Clear();

Do you really need to do this within a foreach loop?

This will achieve the same results as your examples, ie, remove all items from the list up until the first item that matches the condition (or remove all items if none of them match the condition).

int index = Os.FindIndex(x => x.cond);

if (index > 0)
    Os.RemoveRange(0, index);
else if (index == -1)
    Os.Clear();
冬天旳寂寞 2024-07-27 12:22:55

我是一名 Java 程序员,但是这样的事情是有效的:

List<Type> Os;
List<Type> Temp;
...
foreach (Type o in Os)
    if (o.cond)
        Temp.add(o);
Os.removeAll(Temp);  

I am a Java programmer, but something like this works:

List<Type> Os;
List<Type> Temp;
...
foreach (Type o in Os)
    if (o.cond)
        Temp.add(o);
Os.removeAll(Temp);  
拿命拼未来 2024-07-27 12:22:55

我的分析库刚刚遇到了这个问题。 我尝试过:

for (int i = 0; i < list.Count; i++)
{                
   if (/*condition*/)
   {
       list.RemoveAt(i);
       i--;
   }
}

这很简单,但我没有想到任何突破点。

I just had that problem with my analysis library. I tried this:

for (int i = 0; i < list.Count; i++)
{                
   if (/*condition*/)
   {
       list.RemoveAt(i);
       i--;
   }
}

It's pretty simple but I haven't thought of any breaking point.

机场等船 2024-07-27 12:22:55

这是最简单的解决方案,具有最简单的原因

问题:

通常,我们从原始列表中删除,这会产生维护列表计数和迭代器位置的问题。

List<Type> Os = ....;
Os.ForEach(
    delegate(Type o) {
        if(!o.cond) Os.Remove(o);
    }
);

解决方案 - LINQ.ForEach

请注意,我添加的只是 ToList()。 这将创建一个新列表,您可以对其执行 ForEach,因此您可以删除原始列表,但继续迭代整个列表。

List<Type> Os = ....;
Os.ToList().ForEach(
    delegate(Type o) {
        if(!o.cond) Os.Remove(o);
    }
);

解决方案 - 常规 foreach

此技术也适用于常规 foreach 语句。

List<Type> Os = ....;
foreach(Type o in Os.ToList()) {
  if(!o.cond) Os.Remove(o);
}

请注意,如果您的原始列表包含 struct 元素,则此解决方案将不起作用。

Here is the EASIEST SOLUTION with the simpliest WHY

PROBLEM:

Typically, we are removing from the original list, this produces the problem of maintaining the list count and iterator location.

List<Type> Os = ....;
Os.ForEach(
    delegate(Type o) {
        if(!o.cond) Os.Remove(o);
    }
);

SOLUTION - LINQ.ForEach:

Note all I've added was ToList(). This creates a new list that you perform ForEach on, therefore you can remove your original list, yet keep iterating through the entire list.

List<Type> Os = ....;
Os.ToList().ForEach(
    delegate(Type o) {
        if(!o.cond) Os.Remove(o);
    }
);

SOLUTION - Regular foreach:

This technique also works for regular foreach statements.

List<Type> Os = ....;
foreach(Type o in Os.ToList()) {
  if(!o.cond) Os.Remove(o);
}

Please note, that this solution won't work if your original List contains struct element.

隐诗 2024-07-27 12:22:55

我知道您还要求其他东西,但如果您想有条件地删除一堆元素,您可以使用 lambda 表达式:

Os.RemoveAll(o => !o.cond);

I know you asked for something else, but if you want to conditionally remove a bunch of elements you can use lambda expression:

Os.RemoveAll(o => !o.cond);
童话里做英雄 2024-07-27 12:22:55
 Os.RemoveAll(delegate(int x) { return /// });
 Os.RemoveAll(delegate(int x) { return /// });
梨涡少年 2024-07-27 12:22:55

我会尝试找到不满足谓词的第一个项目的索引,并对其执行RemoveRange(0,index)。 如果不出意外,Remove 调用应该会减少。

I'd try finding the index of first item that does not satisfy the predicate and do RemoveRange(0, index) on it. If nothing else, there should be less Remove calls.

暗恋未遂 2024-07-27 12:22:55

更新:为了完整性而添加

正如一些人已经回答的那样,您不应在使用 GetEnumerator() 迭代集合时修改集合(例如 foreach)。 框架通过抛出异常来阻止您执行此操作。 对此的通用解决方案是使用 for “手动”迭代(请参阅其他答案)。 请小心索引,以免跳过项目或对同一项目重新计算两次(通过使用 i-- 或向后迭代)。

但是,对于您的具体情况,我们可以优化删除操作...下面的原始答案。


如果您想要删除所有项目,直到其中一个项目满足给定条件(这就是您的代码所做的),您可以执行以下操作:

bool exitCondition;

while(list.Count > 0 && !(exitCondition = list[0].Condition))
   list.RemoveAt(0);

或者如果您想使用单个删除操作:

SomeType exitCondition;
int index = list.FindIndex(i => i.Condition);

if(index < 0)
    list.Clear();
else
{
    exitCondition = list[0].State;
    list.RemoveRange(0, count);
}

注意:因为我假设 item.Condition 是 bool,我使用 item.State 来保存退出条件。

更新:为两个示例添加了边界检查和保存退出条件

Update: Added for completeness

As several have answered, you shouldn't modify a collection while iterating it with GetEnumerator() (example foreach). The framework prevent you from doing this by throwing an exception. The generic colution to this is to iterate "manually" with for (see other answers). Be careful with your index so you don't skip items or re-evaluate the same one twice (by using i-- or iterating backward).

However, for your specific case, we can optimize the remove operation(s)... original answer below.


If what you want is to remove all items until one meets a given condition (that's what your code does), you can do this:

bool exitCondition;

while(list.Count > 0 && !(exitCondition = list[0].Condition))
   list.RemoveAt(0);

Or if you want to use a single remove operation:

SomeType exitCondition;
int index = list.FindIndex(i => i.Condition);

if(index < 0)
    list.Clear();
else
{
    exitCondition = list[0].State;
    list.RemoveRange(0, count);
}

Note: since I'm assuming that item.Condition is bool, I'm using item.State to save the exit condition.

Update: added bounds checking and saving exit condition to both examples

白昼 2024-07-27 12:22:55

你可以用 linq 来做

MyList = MyList.Where(x=>(someCondition(x)==true)).ToList()

you can do it with linq

MyList = MyList.Where(x=>(someCondition(x)==true)).ToList()
素食主义者 2024-07-27 12:22:55

如果您知道您的列表不是很大,您可以使用

foreach (Type o in new List<Type>(Os))
    ....

它将创建列表的临时副本。 这样,您的remove() 调用将不会干扰迭代器。

If you know your list isn't very large you can use

foreach (Type o in new List<Type>(Os))
    ....

which will create a temporary duplicate of the list. Your remove() call will then not be interfering with the iterator.

故事还在继续 2024-07-27 12:22:55

看看Enumerable.SkipWhile()

Enumerable.SkipWhile( x => condition).ToList()

通常不会改变列表,使生活变得更容易。 :)

Look at Enumerable.SkipWhile()

Enumerable.SkipWhile( x => condition).ToList()

Generally not mutating a list, makes live a lot easier. :)

清醇 2024-07-27 12:22:55

在迭代列表时删除列表中的项目

他们建议:

for(int i = 0; i < count; i++)
{
    int elementToRemove = list.Find(<Predicate to find the element>);

    list.Remove(elementToRemove);
}

There is a good discussion of this in Removing items in a list while iterating through it .

They propose:

for(int i = 0; i < count; i++)
{
    int elementToRemove = list.Find(<Predicate to find the element>);

    list.Remove(elementToRemove);
}
弄潮 2024-07-27 12:22:55

Anzurio 的解决方案可能是最简单的,但如果您不介意向实用程序库添加一堆接口/类,这里还有另一个干净的解决方案。

您可以这样写,

List<Type> Os;
...
var en = Os.GetRemovableEnumerator();
while (en.MoveNext())
{
    if (en.Current.Cond)
        en.Remove();
}

放置以下基础设施,灵感来自 Java 的 Iterator.remove到您的实用程序库中:

static class Extensions
{
    public static IRemovableEnumerator<T> GetRemovableEnumerator<T>(this IList<T> l)
    {
        return new ListRemovableEnumerator<T>(l);
    }
}

interface IRemovableEnumerator<T> : IEnumerator<T>
{
    void Remove();
}

class ListRemovableEnumerator<T> : IRemovableEnumerator<T>
{
    private readonly IList<T> _list;
    private int _count;
    private int _index;
    public ListRemovableEnumerator(IList<T> list)
    {
        _list = list;
        _count = list.Count;
        _index = -1;
    }

    private void ThrowOnModification()
    {
        if (_list.Count != _count)
            throw new InvalidOperationException("List was modified after creation of enumerator");
    }
    public void Dispose()
    {
    }

    public bool MoveNext()
    {
        ThrowOnModification();
        if (_index + 1 == _count)
            return false;
        _index++;
        return true;
    }

    public void Reset()
    {
        ThrowOnModification();
        _index = -1;
    }

    object IEnumerator.Current
    {
        get { return Current; }
    }

    public T Current
    {
        get { return _list[_index]; }
    }

    public void Remove()
    {
        ThrowOnModification();
        _list.RemoveAt(_index);
        _index--;
        _count--;
    }
}

Anzurio's solution is probably the most straightforward, but here's another clean one, if you don't mind adding a bunch of interfaces/classes to your utilities library.

You can write it like this

List<Type> Os;
...
var en = Os.GetRemovableEnumerator();
while (en.MoveNext())
{
    if (en.Current.Cond)
        en.Remove();
}

Put the following infrastructure, inspired by Java's Iterator<T>.remove, into your utility library:

static class Extensions
{
    public static IRemovableEnumerator<T> GetRemovableEnumerator<T>(this IList<T> l)
    {
        return new ListRemovableEnumerator<T>(l);
    }
}

interface IRemovableEnumerator<T> : IEnumerator<T>
{
    void Remove();
}

class ListRemovableEnumerator<T> : IRemovableEnumerator<T>
{
    private readonly IList<T> _list;
    private int _count;
    private int _index;
    public ListRemovableEnumerator(IList<T> list)
    {
        _list = list;
        _count = list.Count;
        _index = -1;
    }

    private void ThrowOnModification()
    {
        if (_list.Count != _count)
            throw new InvalidOperationException("List was modified after creation of enumerator");
    }
    public void Dispose()
    {
    }

    public bool MoveNext()
    {
        ThrowOnModification();
        if (_index + 1 == _count)
            return false;
        _index++;
        return true;
    }

    public void Reset()
    {
        ThrowOnModification();
        _index = -1;
    }

    object IEnumerator.Current
    {
        get { return Current; }
    }

    public T Current
    {
        get { return _list[_index]; }
    }

    public void Remove()
    {
        ThrowOnModification();
        _list.RemoveAt(_index);
        _index--;
        _count--;
    }
}
稀香 2024-07-27 12:22:55

我刚刚遇到了同样的问题,并使用以下方法解决了它:

foreach (Type o in (new List(Os)))
{
如果(某事)
Os.Remove(o);
它迭代

列表的副本并从原始列表中删除。

I just had the same problem and solved it by using the following:

foreach (Type o in (new List(Os)))
{
if (something)
Os.Remove(o);
}

It iterates through a copy of the list and removes from the original list.

ι不睡觉的鱼゛ 2024-07-27 12:22:55

在列表中添加要删除的项目,然后使用 RemoveAll 删除这些项目:

List<Type> Os;
List<Type> OsToRemove=new List<Type>();
...
foreach (Type o in Os){
    if (o.cond)
        return;
    else
        OsToRemove.Add(o);
}
Os.RemoveAll(o => OsToRemove.Contains(o));

Add the item to remove in a list, and then remove these items by using RemoveAll:

List<Type> Os;
List<Type> OsToRemove=new List<Type>();
...
foreach (Type o in Os){
    if (o.cond)
        return;
    else
        OsToRemove.Add(o);
}
Os.RemoveAll(o => OsToRemove.Contains(o));
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文