在foreach循环中删除XElements

23

所以,我需要修复一个bug

foreach (XElement x in items.Elements("x")) 
{
    XElement result = webservice.method(x);

    if (/*condition based on values in result*/) 
    {
        x.Remove();
    }
}

问题在于调用x.Remove()修改了foreach循环,因此如果有两个元素("x"),并且删除了第一个元素,则循环不会到达第二个x元素。

那么我应该如何循环呢?还是说应该以另一种方式重写它?


8
我只是将foreach修改为"foreach (XElement x in items.Elements("x").Reverse())",这似乎可以正常工作,因为之前的问题是foreach在移动索引时,Remove会使所有内容向下移动,导致某些项被跳过。反转顺序似乎是有意义的。但是,如果有更好的解决方案,我将保持问题开放。 - CaffGeek
我做了一个for循环,在其中如果实际上删除了一个项目来补偿索引,我必须执行i--。不过你用反向的方法似乎也不是一个坏选择,但我不是.NET专家,所以我对自己说的话有点怀疑,哈哈。 - Xaisoft
重新标记为C#3.0。没有C# 3.5版本(请参阅此帖子了解详情https://dev59.com/hnVC5IYBdhLWcg3wliKe)。 - Vaccano
4个回答

32

我怀疑Linq能够帮助你实现以下操作。

using System.Linq;

void foo()
{
    items.Elements("x")
         .Where(x => condition(webservice.method(x)))
         .Remove();
}

如果那样行不通(即内部枚举器仍然无效),请按以下方式制作所选元素的浅拷贝并将其删除。

using System.Linq;

void foo()
{
    List xElements = items.Elements("x")
                          .Where(x => condition(webservice.method(x)))
                          .ToList();

    for (int i = xElements.Count - 1; i > -1; i--)
    {
        xElements[i].Remove();
    }
}

请注意,每个Remove()都会从第一个子元素开始遍历内部链接列表,因此每个删除的计算复杂度为O(N)。是否有一种O(1)的方法来删除元素? - redcalx
我去!我自己写了几个foreach,花了大约15分钟才完成所有工作。但是用你的方法(解决方案1),只需要664毫秒! - Jordec

1
在循环逻辑之前创建一个集合,将要删除的元素添加到新集合中,然后对新集合中的每个元素调用items.Remove。

这应该可以工作。我记得做过这个。我的答案可能不太好。我记得有关于如果你对列表或其他东西进行更改,最好使用for循环,但是如果你只是循环而没有进行任何更改,那么foreach就可以了。这正确吗? - Xaisoft

1
尝试使用无需 foreach 的 for 进行操作。

0

现有的答案提供了一个很好的纯Linq替代方案,但对于迭代方法来说工作量太大。

在foreach中仍然可以循环Elements("x").ToList(),无需完全转换为传统的for循环。

using System.Linq;

foreach (XElement x in items.Elements("x").ToList()) 
{
    XElement result = webservice.method(x);

    if (/*condition based on values in result*/) 
    {
        x.Remove();
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接