为什么 foreach 在从列表视图中删除项目时有效,而在从列表框中不起作用

本文关键字:列表 不起作用 有效 foreach 视图 为什么 删除项目 | 更新日期: 2023-09-27 18:16:43

我已经开始学习C#,我对我发现的行为有点困惑。我试图弄清楚,为什么在一种情况下代码有效,而在另一种情况下则不工作:

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

第一个工作正常,没有错误,但第二个抛出异常,其中包含集合已更改的信息。

谁能向我解释一下?

附言。在ListView的情况下,我正在调试代码并且集合SelectedItems正在更改,但即使它运行良好。

为什么 foreach 在从列表视图中删除项目时有效,而在从列表框中不起作用

当我阅读 .NET 中的代码时,更具体地说是 ListBox.csListView.cs ,他们有两个不同的类来保存他们的SelectedItems集合。

ListBox.csSelectedObjectCollection ,它有以下成员:

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

ListView.csSelectedListViewItemCollection ,它只有这些成员:

private ListView owner;
private int lastAccessedIndex = -1;

因此,通过查看它,我想我可以推断出ListBox的集合是一个适当的枚举器,可以跟踪任何更改和列表中的项目数。 另一方面,ListView似乎根本不在乎这一点,只跟踪枚举器的当前索引并简单地向前迈进。

因此,ListBox会引发异常,因为它会跟踪修改,ListView不会。

编辑ListBox.csSelectecObjectCollection 的 GetEnumerator 方法如下所示:

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

ListView.csSelectedListViewItemCollection 的 GetEnumerator 方法看起来像这样:

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 项的筛选器。

我知道这不是你问的;但是在循环访问它以删除内容之前将所有项目添加到临时列表中总是有利的,因为您永远无法知道枚举器是如何在后端实现的,也不知道它们将来会如何变化。

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

不能修改要枚举的集合。这就是您在第二个示例中收到异常的原因。

ListView 项上的 Remove 方法旨在在此情况下不引发异常。