【问题标题】:foreach throws exception, why?foreach 抛出异常,为什么?
【发布时间】:2011-01-04 15:14:27
【问题描述】:

我遇到了这个异常:

集合已修改;枚举操作可能无法执行。

在执行这段代码时:

List<T> results = new List<T>();
foreach (T item in items)
    if (item.displayText.ToLower().Contains(searchText.ToLower()))
        results.Add(item);
return results;

我不明白为什么会出现此异常,因为我没有更改 items 列表的内容。

【问题讨论】:

  • 我们能看到更多你的方法吗?
  • 您是否在多线程环境中运行?
  • 方法就这些了,单线程环境
  • displayText getter 中是否有任何代码可能导致此问题?
  • 复制所有相关代码并将其粘贴到此处 - 我怀疑还有比上述示例说明的更多内容。

标签: c# exception list foreach


【解决方案1】:

有了这个,我们不得不问“项目”是如何产生的?是yield 块吗?使用 yield 块可能会受到延迟执行的影响(即,在访问枚举器之前无法准备好枚举),并且您可能会因此产生意想不到的结果。

假设异常是从项目枚举器中抛出的,那么抛出它的唯一原因是集合被修改。除非它是实现自己的枚举器的自定义类,在这种情况下,它可能只是一个错误。

您已经忽略了多线程环境,所以我将忽略这种可能性。

如果你“意识到”可枚举,你是否仍然会收到错误 - 即

foreach(var item in items.ToArray())
{
   //your code.
}

我打赌你不会,如果没有,那么 something 肯定会修改你的项目列表。

您可以检查 items 和上述 .ToArray() 调用的结果(您必须将其缓存到局部变量)在 foreach 循环结束时是否仍然相同。

【讨论】:

    【解决方案2】:

    试试这样的代码:)

    string text = searchText.ToLower();
    results = items
      .Where(item => item.displayText.ToLower().Contains(text))
      .ToList();
    

    【讨论】:

    • +1 进行优化,但我们仍然想知道为什么他/她的代码会抛出该异常:(
    • visual studio 不让我在列表上做 .Where()
    • 可能是因为您缺少using System.Linq
    • @harryovers:那么items 是什么?如果您定义了 System.Linq 命名空间,那么您应该获取 items 的所有扩展方法(如果它是 IEnumerable),除非您没有使用 .NET 3.5?
    【解决方案3】:

    列表可能被另一个线程修改。如果是这种情况,您必须使用适当的锁定:

    lock(items.SyncRoot)
    {
        foreach (T item in items)
            ...
    }
    

    此锁定还必须添加到您修改列表的所有其他位置。

    既然你说你的代码是单线程的,你能试试下面的简单示例程序是否适合你(它适合我)?

    using System.Collections.Generic;
    
    class Program
    {
        static void Main(string[] args)
        {
            var items = new List<Item>()
            { 
                new Item() { DisplayText = "A" },
                new Item() { DisplayText = "B" },
                new Item() { DisplayText = "AB" },
            };
    
            var res = filter(items, "A");
        }
    
        static List<T> filter<T>(List<T> items, string searchText) where T : Item
        {
            List<T> results = new List<T>();
            foreach (T item in items)
                if (item.DisplayText.ToLower().Contains(searchText.ToLower()))
                    results.Add(item);
            return results;
        }
    }
    
    class Item
    {
        public string DisplayText { get; set; }
    }
    

    【讨论】:

    • 我不确定您的代码是否能有效识别问题。问题的两个最可能的罪魁祸首是集合“项目”,您已将其换成列表(我们不知道在 OP 的情况下它是什么集合类型),以及您已换出的类型 T对于匿名类型。除非 OP 错误并且这是一个简单的线程问题,否则问题几乎可以肯定是您换出的两件事的副作用。
    • 我添加示例只是为了证明问题实际上不在 for 循环中,而是在代码的其他部分中,OP 没有显示(我选择了 List 用于项目,因为 OP 在评论中这么说,并且 T 的类型为 Item 并且不是匿名的)。
    【解决方案4】:

    一般我建议不要问这样的问题,但要查看异常堆栈跟踪。通常它包含详尽的答案。

    更新:我的回答不正确,您可以声明ObservableCollection&lt;T&gt; 类型的items 并订阅其CollectionChanged 事件以确定谁确切更改了您的收藏(-:

    【讨论】:

    • 堆栈跟踪几乎肯定会显示引发异常的 OP - 我们已经知道(迭代 foreach 中的枚举)。 OP 需要找出的是 修改 集合的内容。由于 something 正在默默地执行此操作,因此查看异常的堆栈跟踪不太可能有帮助。
    猜你喜欢
    • 2010-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-28
    • 2011-12-12
    • 1970-01-01
    • 2013-09-04
    • 1970-01-01
    相关资源
    最近更新 更多