继续执行循环

73
我有以下代码:
For x = LBound(arr) To UBound(arr)
  
    sname = arr(x)  
    If InStr(sname, "Configuration item") Then  
        '**(here I want to go to next x in loop and not complete the code below)**  
    End If

    '// other code to copy past and do various stuff

Next x  

我以为我可以简单地使用语句Then Next x,但是这会导致一个"没有声明for语句"的错误。
If InStr(sname, "Configuration item") Then之后,我应该放什么来使它继续到下一个x的值?

1
感谢那些纠正我的拼写错误的人,我知道我很糟糕,非常感激有人愿意花时间帮助我。干杯! - DevilWAH
9个回答

111

你可以使用 GoTo

Do

    '... do stuff your loop will be doing

    ' skip to the end of the loop if necessary:
    If <condition-to-go-to-next-iteration> Then GoTo ContinueLoop 

    '... do other stuff if the condition is not met

ContinueLoop:
Loop

13
尊崇的goto语句在逻辑和理性选择上值得肯定。http://www.drdobbs.com/cpp/what-dijkstra-said-was-harmful-about-got/228700940 - Richard Chambers

57

你考虑的是类似于JavaPython中的continue语句,但是VBA没有这样的本地语句,并且你不能像那样使用VBA的Next

你可以使用GoTo语句来实现你尝试做的事情,但实际上,GoTo应该保留用于替代方案不切实际和难以实现的情况。

对于单个“continue”条件的情况,有一种非常简单、清晰和易读的替代方法:

    If Not InStr(sname, "Configuration item") Then
        '// other code to copy paste and do various stuff
    End If

13
如果在循环中有多个条件,代码就会变得不够清晰易读。当代码嵌套层数增加时,对于阅读它的程序员来说需要更多的头脑空间。因此,在这种情况下使用GoTo可能更好,Arlen Beiler的回答也是另一个不错的解决方案。 - pettys
1
我同意,那些答案可能更好——但是不适用于这个问题。 - Jean-François Corbett
听起来我们都同意,对于那些寻求更一般的方法来解决VBA缺少“continue”语句的人来说,下面的替代答案有优势。我的意图只是通过权衡一般情况下的权衡来加深讨论。 - pettys
1
我有一个工作准则:“检查你的前置条件,如果它们失败了,那就退出”。方法的开始或循环的开始都是一样的。如果可以的话,我会使用continue,但是我使用Goto,因为它可以停止嵌套层级。 - Roger Willcocks
我不确定为什么这里的每个人都在关注“多层嵌套”,而问题明显只有一个?... - Jean-François Corbett
这个评论链可能有点老了,但我也想发表一下我的看法。人们之所以会在深度嵌套中感到沮丧,是因为他们发现没有 continue 几乎无法避免,只能通过 goto 来解救。在除了最琐碎的情况之外,goto 更实用和可维护。 - Gry-

29
For i=1 To 10
    Do 
        'Do everything in here and

        If I_Dont_Want_Finish_This_Loop Then
            Exit Do
        End If 

        'Of course, if I do want to finish it,
        'I put more stuff here, and then...

    Loop While False 'quit after one loop
Next i

这似乎是我见过的避免使用Goto继续For循环最简洁的方法。我想你也可以在其他情况下采用同样的方法来避免使用Goto... - tobriand
1
好的答案。Alfredo Yong的回答和这个想法一样,但是他的回答更加简洁,对我来说更易读。 - pettys
1
哇。女士们先生们,"Do Once or Exit Do to Continue" 成语。 - Jean-François Corbett
这类似于goto,但破坏了到达无法预测状态的能力,这是goto固有的。 - 仍然使代码阅读起来更加复杂,特别是如果想要做的事情很多。 - 我认为,当条件的逻辑反转变得太复杂而难以轻易理解或阅读时,这是有用的。 - I'm with Monica

21

很多年之后...我喜欢这个版本:

For x = LBound(arr) To UBound(arr): Do

    sname = arr(x)  
    If instr(sname, "Configuration item") Then Exit Do 

    '// other code to copy past and do various stuff

Loop While False: Next x

8
这基本上与Arlen Beiler早期的回答相同,只是少了几行间距... - Jean-François Corbett

8
几年晚了,但这里有另一种选择。
For x = LBound(arr) To UBound(arr)
    sname = arr(x)  
    If InStr(sname, "Configuration item") Then  
        'Do nothing here, which automatically go to the next iteration
    Else
        'Code to perform the required action
    End If
Next x

2
是的,但如果有多个“continue”条件,这是解决问题的标准选择,会导致丑陋的嵌套。 - Andreas Covidiot

2

许多年后 :D 我使用了一个 "select" 语句来举一个简单的例子:

  For Each zThisRow In zRowRange
    zRowNum = zThisRow.Row
    Select Case zRowNum
      Case 1 '- Skip header row and any other rows to skip -----
             '- no need to put anything here -----

      Case Else '- Rows to process -----
             '- Process for stuff to do something here -----
    End Select
  Next zThisRow

你可以通过将每个“if”结果转换为一个值(也许过于复杂的代码会有所帮助:D),使其变得更加复杂:
zSkip = 0
If 'condition1 = skip' Then zSkip = zSkip + 1
If 'condition2 = skip' Then zSkip = zSkip + 1
If 'condition3 = skip' Then zSkip = zSkip + 1
Select Case zRowNum
  Case 0 '- Stuff to do -----
  Case Else '- Stuff to skip -----
End Select

这只是一个建议;祝大家圣诞快乐!

(该段内容已经是中文,无需翻译)

0

这个问题也可以使用布尔值来解决。

For Each rngCol In rngAll.Columns
    doCol = False '<==== Resets to False at top of each column
    For Each cell In Selection
        If cell.row = 1 Then
            If thisColumnShouldBeProcessed Then doCol = True
        End If
        If doCol Then
            'Do what you want to do to each cell in this column
        End If
    Next cell
Next rngCol

例如,这里是完整的示例:
(1)识别工作表上使用的单元格范围
(2)循环遍历每一列
(3)如果列标题是接受的标题,则循环遍历该列中的所有单元格
Sub HowToSkipForLoopIfConditionNotMet()
    Dim rngCol, rngAll, cell As Range, cnt As Long, doCol, cellValType As Boolean
    Set rngAll = Range("A1").CurrentRegion
    'MsgBox R.Address(0, 0), , "All data"
    cnt = 0
    For Each rngCol In rngAll.Columns
        rngCol.Select
        doCol = False
        For Each cell In Selection
            If cell.row = 1 Then
                If cell.Value = "AnAllowedColumnTitle" Then doCol = True
            End If
            If doCol Then '<============== THIS LINE ==========
                cnt = cnt + 1
                Debug.Print ("[" & cell.Value & "]" & " / " & cell.Address & " / " & cell.Column & " / " & cell.row)
                If cnt > 5 Then End '<=== NOT NEEDED. Just prevents too much demo output.
            End If
        Next cell
    Next rngCol
End Sub

注意:如果您没有立即捕捉到它,那么行If docol Then是您的反向CONTINUE。也就是说,如果doCol保持为False,则脚本将继续到下一个单元格并且不执行任何操作。
当然,这不像一个适当的continuenext for语句那样快速/高效,但最终结果是我能够得到的最接近的。

0

你可以通过简单的方法实现,只需将for循环中使用的变量值更改为示例中的结束值即可。

Sub TEST_ONLY()
    For i = 1 To 10
        ActiveSheet.Cells(i, 1).Value = i
        If i = 5 Then
            i = 10
        End If
    Next i
End Sub

你的提议将完全结束循环(对于“i>=6”不执行任何指令),同时也会执行在“End If”和“Next i”之间可能存在的指令;问题是如何获得相反的效果:跳过“End If”后面的指令,同时继续循环下一个值。另外,与其写成“i = 10”,更清晰的做法是写成“Exit For”。 - AntoineL

-1

有时我会使用双重循环:

Do

    Do

        If I_Don't_Want_to_Finish_This_Loop Then Exit Do

        Exit Do

    Loop

Loop Until Done

这样可以避免出现"goto spaghetti"的情况。

这与Arlen Beiler早期的回答基本相同。 - Jean-François Corbett

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