Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Общие вопросы по .NET и C# > Проблема с обходом и удалением из коллекции


Автор: Stas123 29.7.2011, 13:39
Хочу удалить из ObservableCollection некоторые элементы, но такой код не работает
Код

foreach(Order o in this.Orders){
                               if(TraderHelper.IsMatched(o) == true){
                                   this.Orders.Remove(o);
                               }

Ошибка такая
System.Reflection.TargetInvocationException: Адресат вызова создал исключение. ---> System.InvalidOperationException: Коллекция была изменена; невозможно выполнить операцию перечисления.
   at System.RuntimeMethodHandle.InvokeMethodFast
Как лучше сделать?

Автор: Суровый 29.7.2011, 14:01
Цитата

Как лучше сделать?

Можно попробовать начинать каждый раз поиск заново:
Код

bool ok = true;
do
{
    foreach(Order o in this.Orders)
    {
        if(TraderHelper.IsMatched(o))
        {
            this.Orders.Remove(o);
            ok = false;
            break;
        }
    }
}
while(!ok);                          


Добавлено через 2 минуты и 37 секунд
Второй способ - копирование всех "подходящих элементов":
Код

Orders orders;
foreach(Order o in this.Orders)
{
    if(!TraderHelper.IsMatched(o))
        orders.Add(o);
}
this.Orders = orders;

Автор: Stas123 29.7.2011, 14:58
в первом случае ,если до первого объекта, который нужно удалить, много объектов, то эта чистка будет выполнятся медленно.

Я не уверен в работоспособности второго вариата. Если мы делаем Add(), то ведь НЕ создается комия добавляамеого объекта?
Если не создается, а лишь копируется ссылка, то после "копирования", у меня старая коллекция повиснет в памяти без имени, но чистильшик не удали её т.к. есть ссылки указывающие на неё. Получается памячть будет загромождатся.

Автор: AlexNagits 29.7.2011, 16:35
Суровый,  smile 

Stas123, обычно делают так:

Код

List<Order> ordersToRemove = new List<Order>();
foreach(Order o in this.Orders)
{
    if(TraderHelper.IsMatched(o))
        ordersToRemove.Add(o);
}

foreach(Order orderToRemove in ordersToRemove)
    this.Orders.Remove(orderToRemove);


Если выглядит громоздко, то можно использовать linq, например так:
Код

IEnumerable<Order> ordersToRemove = this.Orders.Where(o => TraderHelper.IsMatched(o));
foreach (Order orderToRemove in ordersToRemove)
    this.Orders.Remove(orderToRemove);

Автор: CYBERDREAM 1.8.2011, 12:05
Надеюсь ты понял, что в теле foreach коллецию нельзя изменять?
Можно сделать через цикл for. В этом случае можно обойтись без всяких лишних коллекций
Код

            List<Item> mylist =GetItems();

            for (Int32 i = 0; i < mylist.Count;i++ )
            {
                if(mylist[i].IsObsolete)
                {
                    mylist.RemoveAt(i);
                    i--;
                }
            }

//либо
            for(Int32 i=mylist.Count-1;i>=0;i--)
            {
                if (mylist[i].IsObsolete)
                    mylist.RemoveAt(i);
            }

Автор: Stas123 1.8.2011, 13:37
CYBERDREAM, сделал почти также как во втором варианте.
Код

int n = this.Orders.Count - 1;
                           for(int i = n; i >= 0; i--){
                               if(TraderHelper.IsMatched(this.Orders[i]) == true){
                                   this.Orders.Remove(this.Orders[i]);
                               }
                           }

Автор: CYBERDREAM 1.8.2011, 13:48
Код

                               if(TraderHelper.IsMatched(this.Orders[i]) == true){
                                   this.Orders.Remove(this.Orders[i]);
                               }

разве нельзя записать как
Код

                               if(TraderHelper.IsMatched(this.Orders[i])){
                                   this.Orders.RemoveAt(this.Orders[i]);
                               }

Orders какого типа?

Автор: Exception 2.8.2011, 00:09
Коллекцию нельзя менять, пока ты по ней бегаешь.
Используй LINQ.

Код

var matches = Orders.Where(TraderHelper.IsMatched).ToList();
foreach (var match in matches)
    Orders.Remove (match);


Обрати внимание на вызов ToList: список на удаление вычисляется отдельно, поэтому ошибки не возникает.

Добавлено @ 00:14
Цитата(CYBERDREAM @  1.8.2011,  14:48 Найти цитируемый пост)
Orders какого типа?


В оп-посте написано: Orders это ObservableCollection.

Цитата(AlexNagits @  29.7.2011,  17:35 Найти цитируемый пост)
Если выглядит громоздко, то можно использовать linq, например так:
Код

IEnumerable<Order> ordersToRemove = this.Orders.Where(o => TraderHelper.IsMatched(o));
foreach (Order orderToRemove in ordersToRemove)
    this.Orders.Remove(orderToRemove)


Этот вариант близок к правде, но от тоже меняет коллекцию на ходу. Ты как бы выдираешь почку из-под ног у Where.



Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)