Why does foreach work when deleting items from a ListView and not working from a ListBox?

I started learning C # and I got a little confused about the behavior I discovered. I am trying to find out why in one case the code works, and in the other not:

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

The first works fine, and there is no error, but the second throws an exception with information that the collection has been modified.

Can anyone explain this to me?

PS. In the case of ListView, I debugged the code and the SelectedItems collection changed, but even though it worked well.

+4
source share
3 answers

When I read the code inside .NET, more specifically ListBox.cs and ListView.cs , they have two different classes for storing their SelectedItems collections.

ListBox.cs has a SelectedObjectCollection , which has the following elements:

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

ListView.cs has a SelectedListViewItemCollection , which only has these members:

 private ListView owner; private int lastAccessedIndex = -1; 

Therefore, looking at this, I assume that we can conclude that the ListBox collection is a valid enumerator that tracks any changes and the number of items in the list. ListView , on the other hand, does not seem to care about this at all and only tracks the current index of the enumerator and just takes a step forward.

So the ListBox throws an exception, since it tracks changes, the ListView does not.

ListBox.cs : ListBox.cs SelectecObjectCollection The GetEnumerator method looks like this:

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

And ListView.cs SelectedListViewItemCollection The GetEnumerator method is as follows:

 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(); } } 

So, it looks like the ListView returns an array enumerator, which is constant, and the ListBox returns the actual enumerator as a filter of its InnerArray elements.

I know that this is not what you asked for; but it’s always useful to add all elements to a temporary list before iterating through it to remove things, since you may never know how enumerated elements are implemented on the backend, and how they may change in the future.

+5
source
 while (myListBox.SelectedItems.Count > 0) { myListBox.Items.Remove(myListBox.SelectedItems[0]); } 
+1
source

You cannot change the collection by which you list. That is why you get an exception in the second example.

The Remove method in the ListView element is designed to throw an exception in this situation.

+1
source

Source: https://habr.com/ru/post/1384617/


All Articles