Коллекция была изменена; перечисление не может выполнить ошибку при удалении ListItem из LIstBox - PullRequest
8 голосов
/ 30 апреля 2009

У меня есть два списка, lstAvailableColors и lstSelectedColors. Между каждым списком есть две кнопки, Добавить и Удалить. Когда в lstAvailableColors выбран цвет или цвета и нажата кнопка «Добавить», я хочу удалить их из lstAvailableColors и отобразить их в lstSelectedColors. Также, если в lstSelectedColors выбраны цвета и нажата кнопка «Удалить», я хочу удалить цвета из lstSelectedColors и добавить их обратно в lstAvailableColors. Когда я делаю это, я получаю следующую ошибку при удалении элемента:

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

Вот код для кнопки «Добавить» и «Удалить»:

Добавить:

protected void btnAdd_Click(object sender, EventArgs e)
{
    foreach (ListItem item in lstAvailableColors.Items)
    {
        if (item.Selected)
        {
            lstSelectedColors.Items.Add(item);
            lstAvailableColors.Items.Remove(item);
        }
    }
}

Удалить:

protected void btnRemove_Click(object sender, EventArgs e)
{
    foreach (ListItem item in lstSelectedColors.Items)
    {
        if (item.Selected)
        {
            lstAvailableColors.Items.Add(item);
            lstSelectedColors.Items.Remove(item);
        }
    }
}

Ответы [ 9 ]

17 голосов
/ 30 апреля 2009

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

protected void btnAdd_Click(object sender, EventArgs e)
{
    var selected = new List<ListItem>();
    foreach (ListItem item in lstAvailableColors.Items)
    {
        if (item.Selected)
        {
            selected.Add(item);
            lstSelectedColors.Items.Add(item);
        }
    }
    foreach (ListItem item in selected)
    {
        lstAvailableColors.Items.Remove(item);
    }
}

А вот более краткая версия с использованием LINQ

var selected = lstAvailableColors.Cast<ListItem>().Where(i => i.Selected).ToList();
selected.ForEach( x => { lstSelectedColors.Items.Add(x); });
selected.ForEach( x => { lstAvailableColors.Items.Remove(x);});

EDIT

Версия LINQ работает в двух частях. Первая часть - это первая строка, которая находит выбранные в данный момент элементы и сохраняет значение в List<ListItem>. Очень важно, чтобы в строке содержался вызов .ToList (), потому что это заставляет запрос выполняться немедленно, а не с задержкой выполнения.

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

6 голосов
/ 30 апреля 2009

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

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

protected void btnAdd_Click(object sender, EventArgs e)
{
    for (Int32 i = lstAvailableColors.Items.Count; i >= 0; i--)
    {
        ListItem item = lstAvailableColors.Items[i];

        if (item.Selected)
        {
            lstSelectedColors.Items.Add(item);
            lstAvailableColors.Items.Remove(item);
        }
    }
}
4 голосов
/ 30 апреля 2009

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

3 голосов
/ 30 апреля 2009

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

var itemsToRemove =
lstAvailableColors.Items.Cast<ListItem>().Where(i => i.IsSelected).ToArray();

foreach(ListItem item in itemsToRemove) lstAvailableColors.Remove(item);
1 голос
/ 30 апреля 2009

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

0 голосов
/ 23 сентября 2015

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

protected void btnAdd_Click(object sender, EventArgs e)
{
    lstAvailableColors.Items.RemoveAll(ac => ac.Selected);
}
0 голосов
/ 01 февраля 2013

Это может помочь вам;

Удалить:

protected void btnRemove_Click(object sender, EventArgs e)
{
    {
        for (int i = 0; i < lstAvailableColors.Items.Count; i++)
        { 
            if(lstAvailableColors.Items[i].Selected)
                lstAvailableColors.Items.RemoveAt(i);
        }
    }
}
0 голосов
/ 05 июля 2012

Может быть, это то, что вам нужно

protected void btnAdd_Click(object sender, EventArgs e)
{
    while(listBox1.SelectedIndex!=-1)
    {
           listBox1.Items.Remove(listBox1.SelectedItem);
    }
}
0 голосов
/ 16 октября 2010

Пример того, как удалить выбранные элементы. Здесь только выбранные индексы взяты и удалены.

   public void RemoveSelectedItems(ListBox listbox)
   {
       List<ListItem> items = GetSelectedItems(listbox);
       foreach (var listItem in items)
       {
           listbox.Items.Remove(listItem);
       }
   }

  public List<ListItem> GetSelectedItems(ListBox listbox)
  {
     int[] selectedIndices = listbox.GetSelectedIndices();
     return selectedIndices.Select(index => listbox.Items[index]).ToList();
  }
...