foreach和for循环之间的奇怪行为

时间:2010-12-08 09:41:40

标签: c# windows-phone-7

我的windows phone 7 silverlight应用程序在将图钉放在地图图层上之前会移除之前存在的任何内容。

我在foreach循环中这样做如下:

            //Clear previous pins
        try
        {
            foreach (UIElement p in PushPinLayer.Children)
            {
                if(p.GetType() == typeof(Pushpin))
                {
                    PushPinLayer.Children.Remove(p);
                }
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
            //TODO: For some reason the foreach loop above causes an invalid Operation exception.
            //Cathing the error here until I can work out why it is happening.
        }

此代码根据需要删除任何图钉但在最后一个循环之后会抛出异常“无效操作”我将其重新编写为for循环:

        for (int i = 0; i < PushPinLayer.Children.Count; i++)
        {
            if (PushPinLayer.Children[i].GetType() == typeof(Pushpin))
            {
                PushPinLayer.Children.RemoveAt(i);
            }
        }

哪个工作正常,但我不明白为什么foreach会抛出错误。

6 个答案:

答案 0 :(得分:7)

这很正常,

您无法从列表中删除仍在foreach列表中使用的项目。 然后删除该项将更好地创建一个新列表,并且每次它不是图钉类型时,将该对象添加到新列表。

这样原始列表不会改变,你也不会得到例外。

我觉得for循环有效,但如果确实如此,那就意味着它们的迭代方式不同了。 for循环将被复制到另一个内存位置并用于for循环,以便for循环中不再使用原始的for循环。 foreach循环将从列表中获取参数,您删除项目,因此列表和参数变为并发。

答案 1 :(得分:3)

您的foreach循环使用和Enumerator来迭代集合中的对象。从集合中删除对象时,Enumerator不再有效,因为它引用了不再存在的对象。这会导致InvalidOperationException

删除它们的最佳方法是使用for循环,甚至更好地反向执行。

    for (int i = PushPinLayer.Children.Count - 1; i >= 0 ; i--)
    {
        if (PushPinLayer.Children[i].GetType() == typeof(Pushpin))
        {
            PushPinLayer.Children.RemoveAt(i);
        }
    }

这样可以确保在删除商品时,Index i不会超过您收藏中的商品数量。

答案 2 :(得分:2)

更改集合的内容时,foreach循环中使用的枚举器将变为无效。枚举时无法更改集合。

这是一种解决方法:

        List<UIElement> toRemove = new List<UIElement>();
        foreach (UIElement p in PushPinLayer.Children)
        {
            if(p.GetType() == typeof(Pushpin))
            {
                toRemove.Add(p);
            }
        }
        foreach(UIElement p in toRemove)
        {
            PushPinLayer.Children.Remove(p);
        }

或者,您可以使用RemoveAll方法,该方法将谓词作为参数:

        PushPinLayer.Children.RemoveAll(p => p is Pushpin);

答案 3 :(得分:2)

其他人已经告诉你这个问题的原因所以我只是想发布一个LINQ版本的工作

var toRemove = PushPinLayer.Children.OfType<Pushpin>().ToList();
// since toRemove is a separate collection, it's safe to do this now:
foreach (var child in toRemove)
   PushPinLayer.Children.Remove(child)

答案 4 :(得分:2)

由于其他人已经回答了您的问题,我只会评论您对xxx.GetType() == typeof(Pushpin)的使用情况。您只需使用C#保留关键字 即可检查某些内容是否为图钉。 e.g:

if (p is Pushpin) {...}

答案 5 :(得分:1)

在进行预习时,不得更改集合。