Excel VBA:在For循环内部,On Error Goto语句不起作用

22

我试图循环遍历Excel中的一个表。此表的前三列具有文本标题,其余列具有日期作为标题。我想将这些日期按顺序分配给Date类型变量,然后根据日期执行某些操作。

为此,我正在使用foreach循环来遍历myTable.ListColumns。由于前三列没有日期标题,因此我尝试设置循环,以便如果将标题字符串分配给日期类型变量时出现错误,则循环直接进入下一列。

似乎这对第一列起作用。但是,当第二列的标题“分配”到日期类型变量时,即使它在错误处理块内,宏也会遇到错误。

Dim myCol As ListColumn
For Each myCol In myTable.ListColumns
    On Error GoTo NextCol

    Dim myDate As Date
    myDate = CDate(myCol.Name)

    On Error GoTo 0

    'MORE CODE HERE

NextCol:
    On Error GoTo 0
Next myCol

再强调一下,在循环的第二轮执行时,该语句抛出了错误。

myDate = CDate(myCol.Name)

有人能解释一下为什么On Error语句停止工作吗?


1
也许在这种情况下,使用IF和IsDate函数作为控制结构会更合适,而不是使用错误作为控制结构? - ray
1
如果你是“盲目地”处理错误 - 而不是针对错误类型采取具体行动 - 那么你应该在循环外使用一个 On Error Resume Next。目前,你正在每个列上重新使用错误处理。 - brettdj
1
@brettdj,我认为你不能只是简单地恢复下一个。整个想法是如果日期转换失败,则跳过“更多代码在这里”的代码。因此,您需要进入处理程序,以便可以恢复到特定行。此外,您只希望启用日期转换的处理程序,而不是整个循环体。 - paxdiablo
@paxdiablo 反思之后,我同意。我希望能看到更多的代码在这里。 - brettdj
6个回答

47

使用示例中的代码,在执行 next 语句时,你实际上仍然被视为处于错误处理程序内部

这意味着,在从当前错误处理程序恢复之前,不允许使用后续的错误处理程序。

更好的架构应该是:

    Dim myCol As ListColumn
    For Each myCol In myTable.ListColumns
        On Error GoTo ErrCol
        Dim myDate As Date
        myDate = CDate(myCol.Name)
        On Error GoTo 0
        ' MORE CODE HERE '
NextCol:
    Next myCol
    Exit Sub ' or something '

ErrCol:
    Resume NextCol

这明确区分了错误处理和正常代码,并确保当前执行的错误处理程序在您尝试设置另一个处理程序之前完成。

这个网站对问题有很好的描述:


错误处理块和On Error Goto

错误处理块,也称为错误处理程序,是一段通过On Error Goto <label>:语句转移执行的代码。该代码应旨在修复问题并在主代码块中继续执行,或终止过程的执行。您不能仅使用On Error Goto <label>:语句跳过行。例如,以下代码将无法正常工作:

    On Error GoTo Err1:
    Debug.Print 1 / 0
    ' more code
Err1:
    On Error GoTo Err2:
    Debug.Print 1 / 0
    ' more code
Err2:

第一个错误被触发时,执行转到Err1:后面的行。当第二个错误发生时,错误处理程序仍处于活动状态,因此第二个错误不会被On Error语句捕获。


1
“更好的架构”应该是……比跳过错误更好的错误处理技术 :) - enderland
6
@enderland:嗯,是的,那可能更好,但“当魔鬼吐在你的水壶里时就必须这样做” :-) 翻译:@enderland:虽然那可能更好,但“必要之事乃魔鬼吐在你的水壶里” :-) - paxdiablo
1
@brettdj,我不太确定。你只想在数据转换过程中启用错误处理程序,而不是在“更多代码这里”部分启用。 - paxdiablo
@paxdiablo 你说得很有道理。虽然我认为在整个过程中都开启错误处理会更有意义,然后测试触发的错误条件来判断如何处理它。顺便加一分。 - brettdj

8
你需要在错误处理代码中添加某种类型的resume,以表示错误处理已经结束。否则,第一个错误处理程序仍然处于活动状态,而你永远不会“解决”问题。
请参见http://www.cpearson.com/excel/errorhandling.htm(特别是“错误处理块和On Error Goto”和后面的部分)。

7

对paxdiablo所接受的答案进行后续跟进。这是可能的,在同一个子程序中允许两个错误陷阱,一个在另一个之后:

Public Sub test()
    On Error GoTo Err1:
    Debug.Print 1 / 0
    ' more code
Err1:
    On Error GoTo -1     ' clears the active error handler
    On Error GoTo Err2:  ' .. so we can set up another
    Debug.Print 1 / 0
    ' more code
Err2:
    MsgBox "Got here safely"
End Sub

使用 On Error GoTo -1 可以取消当前的错误处理程序,并允许设置另一个(而 err.clear 不能实现此功能!)。无论这是否是一个好主意,都留给读者思考,但它是有效的!


1
谢谢。非常有帮助。实际上,在循环中,您需要重置错误处理... - Mahhdy

1
将其改为"在错误发生时转到-1"。
Dim myCol As ListColumn
For Each myCol In myTable.ListColumns
    On Error GoTo NextCol

    Dim myDate As Date
    myDate = CDate(myCol.Name)

    On Error GoTo 0

    'MORE CODE HERE

NextCol:
    On Error GoTo -1
Next myCol

0
清除 Err 对象的所有属性设置并不等同于重置错误处理程序。
尝试这样做:
Sub TestErr()
Dim i As Integer
Dim x As Double
    On Error GoTo NextLoop
    For i = 1 To 2
10:     x = i / 0
NextLoop:
        If Err <> 0 Then
            Err.Clear
            Debug.Print "Cleared i=" & i
        End If
    Next
End Sub

你会注意到,就像原帖中一样,当i = 1时,它会正确捕获错误,但是当i = 2时,在第10行会失败,即使我们使用了Err.Clear


0

Dim ws As Worksheets

对于myTable.ListColumns中的每一列,使用以下代码:

On Error GoTo endbit

Dim myDate As Date

myDate = CDate(myCol.Name)

On Error GoTo 0
'MORE CODE HERE

endbit: 继续执行下一行

NextCol: ' 下一列 退出子程序 ' 或者其他操作 '


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