2016-05-12 25 views
1

众所周知,在For Each循环中从IEnumerable中删除项目是一个非常糟糕的主意。我一直认为IEnumerable会在你尝试去做时抛出异常。
但是我注意到ListTreeNodeCollection行为不一样,即使它们都执行IList,ICollectionIEnumerable在For Each循环中从其中删除项目时,IEnumerables的行为会有所不同

List表现得如我所料。下面的代码抛出InvalidOperationException

Dim list As New List(Of String) 

list.Add("Entry 1") 
list.Add("Entry 2") 
list.Add("Entry 3") 
list.Add("Entry 4") 
list.Add("Entry 5") 

For Each entry As String In list 
    list.Remove(entry) 
Next 

TreeNodeCollection不会在.NET 2抛出一个异常,并删除了一些项目,在.NET 4.5.2片断抛出一个NullReferenceException

Dim tree As New TreeView 
Dim root As TreeNode = tree.Nodes.Add("Root") 
root.Nodes.Add("Node 1") 
root.Nodes.Add("Node 2") 
root.Nodes.Add("Node 3") 
root.Nodes.Add("Node 4") 
root.Nodes.Add("Node 5") 

For Each node As TreeNode In root.Nodes 
    root.Nodes.Remove(node) 
Next 

为什么两个IEnumerable之间会有这种不同的行为?

+0

我觉得你的问题有点奇怪,因为'IEnumerable'接口提供* no *能力来修改正在枚举的东西。因此,当你真正对修改感兴趣时,专注于该界面似乎很奇怪。 –

+0

@Damien_The_Unbeliever我真的不明白为什么它很奇怪。我会很高兴,如果你编辑它更好的措辞 – Breeze

+0

为什么不只是清除() – Paparazzi

回答

2

IEnumerable接口没有任何技术上或通过文档来说修改。没有什么合约要求实现接口的集合在枚举期间被修改时抛出异常。

这可能是一些集合完全可以继续枚举,即使修改后。即它们枚举的可能是“枚举开始时集合的内容”,或者它们可能会为枚举开始后添加或删除的项目提供一些其他保证。

0

作为一般规则,如果你想删除的项目,你应该做一个备份:

For Each entry As String In list.ToList() 
    list.Remove(entry) 
Next 

其他选项包括:

  • 使用向后For循环。 For i = list.Count-1 To 0 Step - 1
  • 使用LINQ创建一个新列表。 list = list.Where(...).ToList