Почему foreach работает при удалении элементов из ListView и не работает из ListBox? - PullRequest
4 голосов
/ 05 декабря 2011

Я начал изучать C # и немного запутался в поведении, которое обнаружил.Я пытаюсь выяснить, почему в одном случае код работает, а в другом - нет:

foreach (ListViewItem l in listView1.SelectedItems) l.Remove();
foreach (object l in listBox1.SelectedItems) listBox1.Items.Remove(l);

Первый работает нормально, ошибок нет, но второй выдает исключение с информацией о том, что коллекция былаизменилось.

Может ли кто-нибудь объяснить мне это?

PS.В случае ListView я отлаживал код, а коллекция SelectedItems менялась, но даже при том, что она работала хорошо.

Ответы [ 3 ]

5 голосов
/ 05 декабря 2011

Когда я читаю код внутри .NET, более конкретно ListBox.cs и ListView.cs, у них есть два разных класса для хранения их коллекций SelectedItems.

ListBox.cs имеет SelectedObjectCollection, чтоимеет следующие члены:

private ListBox owner;
private bool    stateDirty; 
private int     lastVersion; 
private int     count;

ListView.cs имеет SelectedListViewItemCollection, который имеет только эти члены:

private ListView owner;
private int lastAccessedIndex = -1;

Итак, глядя на это, я думаю, я могу вывести, что * 1016Коллекция * - это правильный перечислитель, который отслеживает любые изменения и количество элементов в списке.ListView, с другой стороны, кажется, не заботится об этом вообще, а только следит за текущим индексом перечислителя и просто делает шаг вперед.

Так что ListBox выдает исключение, поскольку оно сохраняетотслеживания изменений, ListView нет.

РЕДАКТИРОВАТЬ : ListBox.cs 's SelectecObjectCollection метод GetEnumerator выглядит следующим образом:

public IEnumerator GetEnumerator() {
    return InnerArray.GetEnumerator(SelectedObjectMask); 
}

ИМетод GetEnumerator ListView.cs s SelectedListViewItemCollection выглядит следующим образом:

public IEnumerator GetEnumerator() { 
    if (owner.VirtualMode) { 
        throw new InvalidOperationException(SR.GetString(SR.ListViewCantAccessSelectedItemsCollectionWhenInVirtualMode));
    } 

    ListViewItem[] items = SelectedItemArray;
    if (items != null) {
        return items.GetEnumerator(); 
    }
    else { 
        return new ListViewItem[0].GetEnumerator(); 
    }
} 

Таким образом, ListView возвращает перечислитель массива, который является константой, а ListBox возвращает фактическийперечислитель как фильтр его InnerArray элементов.

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

1 голос
/ 05 декабря 2011

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

Метод Remove в элементе ListView предназначен для исключения исключений в этой ситуации.

1 голос
/ 05 декабря 2011
while (myListBox.SelectedItems.Count > 0)
{
    myListBox.Items.Remove(myListBox.SelectedItems[0]);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...