VBA中使用"With"语句的错误处理程序

3

没有设置标签,VBA错误处理程序是否可以在With语句的开头恢复?

例如:

Dim rst As DAO.Recordset
Set rst = Nothing

On Error GoTo ErrHand
With rst
    .AddNew
    !MyValue = 1
    .Update
    .Bookmark = .LastModified
End With

ErrHand:

If Err.Number <> 0 Then
    Call SetRST 'Assume this procedure opens the recordset appropriately
    Resume
End If
End Sub()

代码将在“.AddNew”行上引发错误,然后当它通过错误处理程序时,会设置记录集,但是它将在“.AddNew”行上恢复。问题是它仍然在CommentRST为Nothing的“With”语句中。是否有一种方法可以告诉错误处理程序在“With RST”行而不是“.AddNew”行处恢复,而无需在“With”语句之前创建标签或首先检查空记录集?
我知道有办法解决这个问题(因为我刚刚提出了2个),但是我很好奇是否可能实现。

为什么不在添加之前检查记录集? - Fionnuala
1
你是这样处理的,是因为当你调用 .AddNew 时你的顶层 rst 可能没有打开吗? - Brad
https://dev59.com/O2Mm5IYBdhLWcg3wburb - Brad
我可以检查记录集,而且我确实打算以这种方式来做,但正如我所提到的,我很好奇是否这是可能的。这更多是一个例子,我需要创建这个问题。关于ADO,真的没有理由使用ADO记录集(实际上,我的记录集是DAO,我不确定为什么在这篇文章中将它声明为ADO。我会更新的)。 - OpiesDad
1
你正在询问是否有类似于“恢复上一个”(没有)。代码按顺序执行,不会在没有明确告知要去哪里的情况下“倒退”或返回到某个先前的点。在这种情况下,编译器如何知道要在With块的开头恢复(而不是在其他某个点)? - David Zemens
显示剩余7条评论
3个回答

4

只需在SetRST函数中添加一个byRef参数。

例如:

Sub SetRST(byref myrs as recordset)
   'do stuff
   set myrs = ...

顺便说一下,你的错误处理示例很糟糕:在ErrHand:之前添加一个Exit Sub,这样你就不需要测试err.number<>0了,因为你知道它总是会发生。
在你的错误处理中,使用:
call SetRST rst

抱歉,我目前只能使用英语回答您的问题。如果您能提供原始中文内容,我可以尝试将其翻译成英文。
If rst Is Nothing Then
    Set rst = something
End if
With rst
   'continue here

1
我从不写“Call”,因为它没用,我只是在这里写下来以保持提问者的风格。你是正确的,它是没有用的,(因此)不太优雅。 - iDevlop
抱歉代码有些混乱。我知道按照现有的代码无法打开记录集。然而,这并不是我遇到的问题。即使我添加了ByRef参数,当错误处理程序恢复时,它仍将在引用空记录集的“.AddNew”行上恢复,因此仍无法正常工作。 - OpiesDad
@OpiesDad:是的,它会。 - iDevlop
@OpiesDad 传递参数而不是使用全局变量,你的大部分问题将开始消失... - Mathieu Guindon
5
我喜欢你。如果你对 Stack Overflow 不感兴趣了,我们在 Code Review 上可以随时使用更多的 VBA 代码审查者。 - RubberDuck

3
在这种情况下,编译器如何知道要在With块的开头恢复执行(而不是其他地方)?它不会知道,虽然它可能在逻辑上相关(即,它在With块内),但仍然没有理由假设按规则执行应该在该块的开头恢复执行,除非明确指定在该点恢复执行。
你所询问的本质上假设了错误的来源,然后期望VBA已经内置了这个假设,但对于所有或甚至大多数情况来说,这显然不适用。考虑以下情况:假设rst已经打开等等,实际的错误发生在Bookmark属性处,你的错误处理程序没有考虑到这一点,因此在块的开头继续执行将导致无限的失败循环!
On Error GoTo ErrHand
With rst
    .AddNew
    !MyValue = 1
    .Update
    .Bookmark = "George Washington"
End With

请参阅Resume语句的文档: https://msdn.microsoft.com/en-us/library/office/gg251630.aspx 以下是您可以使用的选项来使用Resume
如果错误发生在与错误处理程序相同的过程中,则执行将在导致错误的语句处恢复。如果错误发生在被调用的过程中,则执行将在包含错误处理程序的过程中最后调用的语句处恢复。
或者使用Resume Next
如果错误发生在与错误处理程序相同的过程中,则执行将在导致错误的语句之后立即执行。如果错误发生在被调用的过程中,则执行将在包含错误处理程序的过程中最后调用的语句之后立即执行(或On Error Resume Next语句)。
或者使用Resume <line>
执行将在所需行参数中指定的行处恢复。行参数是行标签或行号,必须与错误处理程序位于同一过程中。

3
一个With块保存一个对象的实例,并在End With时释放它。如果跳出With块,该对象将被删除。 所以答案是否定的,你不能在With块的中间部分使用Resume实际上你可以,但是会变得混乱难懂。 这里你所犯的一个常见错误就是滥用With语句——你只是因为懒惰(无意冒犯),不想在每个使用该对象的行前面输入rst.
正确的用法应该让With块本身持有引用,像这样:
With GetRecordset 'a function that returns a recordset
    .AddNew
    !MyValue = 1
    .Update
    .Bookmark = .LastModified
    .Close
End With

虽然我不建议像这样使用记录集,但你应该明白了;)


1
只要错误处理程序没有执行 End With,你就可以在一个 with 块中使用 Resume - David Zemens
1
@DavidZemens 哎呀!我宁愿保持我的清白并相信你做不到那个...那不会很臭吗? - Mathieu Guindon

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