可以倒序进行 For...Each 循环吗?

40

我不相信常规方法可以实现这一点,但是类似于这样冗长的代码:

For Each s As String In myStringList Step -1
    //' Do stuff here
Next

在传统的 For..Each 循环之前,我可能需要反转 myString 对象,是吗?

14个回答

48

我认为在下面 Mike 的回答中提到的文档非常 误导人的。 For Each 的顺序是由它调用的集合(即其对 IEnumerable/IEnumerable<T> 接口的实现)定义的,但这并不意味着在需要有序时就不应使用它。许多集合(如数组、List<T> 等)总是按照“自然”顺序排列。

文档的一部分确实暗示了这一点:

遍历顺序。当您执行 For each...Next 循环时,集合的遍历受 GetEnumerator 方法返回的枚举器对象控制。遍历顺序不是由 Visual Basic 决定的,而是由枚举器对象的 MoveNext 方法决定的。这意味着您可能无法预测要在元素中返回的第一个元素或给定元素之后要返回的下一个元素。

这与说它不能被依赖是完全不同的 - 如果您知道正在迭代的集合将以所需顺序产生结果,则可以依赖它。它不会随机选择元素。在 IEnumerable/IEnumerable<T> 方面的行为在该页面上清楚定义。

最重要的有序例外是字典和集合,它们自然是无序的。

要反转一个 IEnumerable<T>,可以使用 Enumerable.Reverse 方法。但如果需要倒序遍历一个通过位置索引的集合(例如数组或 List<T>),那么更有效率的方法是使用从结尾开始并向前工作的 For 循环。

3
谢谢Jon,获得您的意见和洞察力总是一种乐趣:D - Anders
1
为什么这个答案没有被采纳?它提供了更深入的示例。 - DotNet Programmer
@DotNet程序员:这是一个旧帖子,我意识到很久以前我没有接受“真正的答案”:) 现在已经给出了适当的信用(不是Jon需要更多积分:P) - Anders

46

您需要执行类似以下的操作:

For i as integer = myStringList.Count-1 to 0 step -1
    dim s as string = myStringList.Item(i)
    ' Do your stuff with s
Next i

但据我所知,您不能倒序使用"For...Each",尽管在某些情况下这将是很好的。


没错。我只是想确认一下,我没有错过 VB 的某些酷炫的“隐藏”的功能,也就是那些文档不太完善的功能。 - Anders
这太完美了! - GunWanderer
如果你像我一样偶然来到这里,那么这个链接可能会对你有所帮助:https://dev59.com/gGAf5IYBdhLWcg3wskiQ#26911575 - SlowLearner

12

遗憾的是,MSDN关于For Each的文档指出,For Each结构明确用于迭代顺序不重要(无序集合等)的情况。因此,很遗憾没有反转For Each的概念,因为顺序本身就没有定义。

祝你好运!


1
我预料到这个结果,但我不是100%确定,所以谢谢你明确的否定,并附上参考作为额外奖励 :) - Anders
这是正确的 - 我总是假设“For Each”循环按顺序进行,虽然它们通常确实如此,但不能依赖它。如果您想要特定的顺序,则数组中一个索引上的“For...Next”是您唯一的选择。 - SqlRyan
7
如果你知道使用的集合按正确顺序迭代,那么它是可靠的。例如,数组将始终按自然顺序迭代。有关更多详细信息,请参见我的答案。 - Jon Skeet
4
(因为“订单无论如何都没有定义”这一说法是错误的 - 它由集合本身定义。)被投票反对。 - Jon Skeet
3
麦克提供的MSDN页面证实了Jon Skeet所说的[它不敢做出其他的事情] - "遍历顺序不是由Visual Basic确定,而是由枚举器对象的MoveNext方法确定"这一部分。因此,顺序被定义的,但是由正在被迭代的集合定义,而不是由For Each语句本身定义。 - MarkJ
我应该指出,如果您的IEnumerable实现没有定义顺序,则ForEach不会强制执行顺序。 - Mike

12

3
所有可遍历类型都实现了GetEnumerator()方法。一些枚举器具有可靠的顺序,而另一些则没有。由于我们正在讨论的是反向顺序,很可能我们正在处理的枚举器具有可靠的逆序顺序。当然,并非所有枚举器都满足此标准。对于数组的枚举器,肯定是可靠排序和可逆转的。 - Amy B
2
请提供需要翻译的具体编程内容,以便本AI模型为您提供准确的翻译。 - AStopher
1
@cybermonkey 这不仅仅是链接。它是链接+句子。有时候多的词语并不能改善答案。 - Amy B
@DavidB 您的答案需要访问链接才能知道如何调用reverse方法,因此它是仅链接的(两个点赞我的评论的用户似乎也同意)。请至少编辑您的问题以扩展您的答案。 - AStopher
@cybermonkey,我添加了足够的信息,以便可以在不访问文档链接的情况下通过互联网搜索到文档。希望这符合您的苛求标准。请先关注被接受的答案,它实际上是一个虚假陈述。然后再关注第二个答案,它主要是对第一个答案的反应,其次才是对问题的回答。最后看看第三个答案,它重复了第一个答案中的虚假陈述。 - Amy B
显示剩余2条评论

5

在Framework 4中进行测试,代码如下:

For Each s As String In myStringList.Reverse
    //' Do stuff here
Next

它之前无法工作,正确的做法是:

myStringList.Reverse() ' it is a method not a function
For Each s As String In myStringList
//' Do stuff here
Next

看看:MSDN:保留

3
For Each s As String In myStringList.Reverse
    //' Do stuff here
Next

1
这个可以工作:

Dim myArray() As String = {"1", "2", "3", "4", "5"}

For Each n As String In myArray.Reverse
    Debug.Print(n)
Next

输出:5 4 3 2 1

'

这也可以运行:

Dim myArray() As String = {"1", "2", "3", "4", "5"}

For i As Integer = myArray.Length - 1 To 0 Step -1
    Debug.Print(myArray(i))
Next

输出:5 4 3 2 1


1
无法完成的原因是,作为基本设计特性,For Each 可以在没有最后一个元素或尚未知道最后一个元素的可枚举对象上进行迭代。

0

你可以为你想要反转的类添加一个扩展函数

<Serializable()> Public Class SomeCollection
    Inherits CollectionBase
    Public Sub New()
    End Sub

    Public Sub Add(ByVal Value As Something)
        Me.List.Add(Value)
    End Sub

    Public Sub Remove(ByVal Value As Something)
        Me.List.Remove(Value)
    End Sub

    Public Function Contains(ByVal Value As Something) As Boolean
        Return Me.List.Contains(Value)
    End Function

    Public Function Item(ByVal Index As Integer) As Something
        Return DirectCast(Me.List.Item(Index), Something)
    End Function

    Public Function Reverse() As SomeCollection
        Dim revList As SomeCollection = New SomeCollection()
        For index As Integer = (Me.List.Count - 1) To 0 Step -1
             revList.List.Add(Me.List.Item(index))
        Next
        Return revList
    End Function
End Class

然后你会这样调用它

For Each s As Something In SomeCollection.Reverse

Next

0

创建一个列表,通过 For 循环收集

dim revList as New List (of ToolStripItem)  
For each objItem as ToolStripItem in Menu.DropDownItems
    revList.Add(objItem)
next
revList.reverse ' reverse the order of that list

逆序遍历列表

for each objItem as ToolStripItem in revList   
    ' do whatever (like removing your menu from an ArrayMenu)
next

请将ToolStripItem和Menu.DropDownItems替换为您需要的内容


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