foreach бросает исключение, почему? - PullRequest
3 голосов
/ 13 января 2010

Я получаю это исключение:

Коллекция была изменена; операция перечисления может не выполняться.

при выполнении этого раздела кода:

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

Я не понимаю, почему я получаю это исключение, поскольку я не изменяю содержимое списка items.

Ответы [ 4 ]

3 голосов
/ 13 января 2010

При этом мы должны спросить, как производится перечисление «предметов»? Это блок yield? Использование блоков доходности может пострадать из-за спектра отложенного выполнения (т. Е. Перечислимое не готовится до тех пор, пока не будет получен доступ к перечислителю), и вы можете получить неожиданные результаты от этого.

Если предположить, что исключение выдается из перечислителя элементов, единственная причина, по которой оно будет выброшено, заключается в том, что коллекция имеет , которая была изменена. Если это не собственный класс, реализующий свой собственный перечислитель, в этом случае в нем может быть просто ошибка.

Вы отказались от многопоточной среды, поэтому я проигнорирую эту возможность.

Вы все еще получаете ошибку, если вы "понимаете" перечислимое - то есть

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

Бьюсь об заклад, вы этого не сделаете, а если нет, то что-то определенно изменяет ваш список предметов.

Вы можете проверить, что items и результат вышеприведенного вызова .ToArray () (вам нужно было бы кешировать его в локальную переменную) все равно остаются в конце цикла foreach.

0 голосов
/ 13 января 2010

Обычно я рекомендую не задавать такие вопросы, но посмотрите на исключение трассировка стека . Обычно содержит исчерпывающий ответ.

Обновление: Мой ответ неверен, вы можете объявить items типа ObservableCollection<T> и подписаться на его событие CollectionChanged, чтобы определить, кто именно изменит вашу коллекцию (-:

0 голосов
/ 13 января 2010

Попробуй такой код :)

string text = searchText.ToLower();
results = items
  .Where(item => item.displayText.ToLower().Contains(text))
  .ToList();
0 голосов
/ 13 января 2010

Список может быть изменен другим потоком. Если это так, вы должны использовать правильную блокировку:

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; }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...