在C#的foreach循环中回到上一个迭代

3

我有一个foreach循环,用于在C#中迭代treeView的不同级别,其简化版本如下:

foreach (TreeNode childNode in currentNode.Nodes)
{
    if (!someCondition)
    {
        currentNode.Remove();                    
    }
}   

但问题在于,当一个节点(例如从节点列表中的节点1、节点2、节点3和节点4)被移除时,列表变短了,foreach循环会跳过一个迭代(例如,如果要删除节点2,则列表变为节点1、节点3和节点4,下一个foreach循环考虑的节点将是节点4而不是节点3)。这是因为框架将这些节点存储在数组/列表中,所以我想知道是否有可能使foreach循环在我想从树中删除一个节点时向后退一次迭代。
我对.NET框架还很陌生,所以非常感谢您的帮助。

2
首先,请注意,如果您调用 currentNode.Remove(),您的迭代器可能会完全中断:在迭代时更改序列并不保证能够继续迭代。为什么不在确定要删除的内容之后再执行删除操作呢? - Marc Gravell
1
在迭代集合时,您不能修改它,否则会导致应用程序崩溃。 - Anthony Raymond
为什么不把foreach改成for循环?在for循环中删除项目是完全合法的。 - Dmitry Bychenko
1
@DmitryBychenko 虽然如果你反向迭代 for 循环会更容易 :) - RB.
4个回答

5
可能可以使用Linq通过设置来实现所需的结果。
currentNode.Nodes = currentNode.Nodes.Where( n => SomeCondition( n ) ).ToList();

或类似的方法,因此不需要显式迭代。一种不太优雅的解决方案是使用一个显式的倒序 for 循环,这样循环索引就不会变得无效。然而,当有更结构化的方法可用时,我认为这是不良实践。


我对Linq几乎一无所知,如果我的条件是currentNode.Nodes.Count == 0 && !currentNode.Text.Contains(this.txtFilter.Text),那么代码应该怎么写呢? - Ali Kahaei
Where( n => n.Nodes.Count==0 && !n.Text.Contains(this.txtFilter.Text) ): - qqbenq

3

你可以在这里使用for循环:

// Pay attention to reversed order:
// each currentNode.Remove() changes currentNode.Nodes.Count 
for (int i = currentNode.Nodes.Count - 1; i >= 0; --i)  {
  TreeNode childNode = currentNode.Nodes[i];

  if (!someCondition) {
    currentNode.Remove();                    
  }
}

1
这个方法很有效,我过去也用过类似的方法,但是我认为这是不好的做法。 - Codor
@Codor:是的,Linq解决方案很优雅,但如果你有一个复杂的for-each循环(正如问题所建议的),将其转换为for循环通常更容易。 - Dmitry Bychenko
我正在尝试重建树,删除一些节点,所以我猜这种方法会使节点顺序错误。不是吗? - Ali Kahaei
@Mark 你展示的代码只是在删除节点,而不是添加节点,所以它们被删除的顺序不应该有影响。 - Servy
@Mark:不应该重新排列树;这只是更改删除节点/分支的顺序。 - Dmitry Bychenko

1
不可能,因为foreach循环的迭代在严格意义上并没有“索引”。 但是,for循环是有索引的,因为你可以自己提供计数机制。在那里你可以更改计数器。

0
通常在 foreach 循环中修改正在迭代的集合不是一个好主意。你应该考虑使用 for 循环,手动跟踪当前索引。

1
这总是一个不好的主意,因为你不能。 - Jonesopolis
@Jonesy 不,那不是真的。有些迭代器会专门检查底层集合的修改并抛出异常。有些则不会,它们会在跳过项目、显示两次项目、输出垃圾或任何其他可能未定义的选项时继续运行。 - Servy
很有趣。在foreach中,我可以修改哪种类型而不会抛出错误? - Jonesopolis
@Jonesy 嗯,根据OP的问题,TreeNode.Nodes在其迭代器中不检查此项,因为根据他的问题,删除项目会导致跳过项目,而不是异常,因此这样的集合在理论上是可能的,而且这就是其中之一。你必须费心让迭代器支持这个功能。广泛使用的通用集合显然花了时间来做到这一点(尽管您可以为它们编写自己的不支持此功能的迭代器),但这并不意味着每个迭代器都这样做。 - Servy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接