30

假设我在r中有一些带有一些类型变量的模块Range。假设,在某个时候,我在那里存储了一个 Range 对象(例如活动单元格)。现在我的问题是:r如果用户删除单元格(单元格,而不仅仅是它的值),值会怎样?

我试图在 VBA 中解决这个问题,但没有成功。结果很奇怪。r不是Nothing, 的值r被报告为 type Range,但是如果我尝试在调试器窗口中查看它的属性,每个属性的值都报告为“需要对象”。

我如何以编程方式确定变量r是否处于这种状态?

我可以在不产生错误并捕获它的情况下执行此操作吗?

4

4 回答 4

22

好问题!我以前从未考虑过这一点,但我认为这个函数将识别一个已初始化的范围 - 不是 Nothing - 但现在处于“需要对象”状态,因为它的单元格已被删除:

Function RangeWasDeclaredAndEntirelyDeleted(r As Range) As Boolean
Dim TestAddress As String

If r Is Nothing Then
    Exit Function
End If
On Error Resume Next
TestAddress = r.Address
If Err.Number = 424 Then    'object required
    RangeWasDeclaredAndEntirelyDeleted = True
End If
End Function

你可以测试是这样的:

Sub test()
Dim r As Range

Debug.Print RangeWasDeclaredAndEntirelyDeleted(r)
Set r = ActiveSheet.Range("A1")
Debug.Print RangeWasDeclaredAndEntirelyDeleted(r)
r.EntireRow.Delete
Debug.Print RangeWasDeclaredAndEntirelyDeleted(r)
End Sub
于 2012-08-26T04:24:39.497 回答
6

我相信,当您在 VBA 中使用 Set 关键字时,它会在后台创建一个指向您指定的工作表中工作表的 Range 对象的指针(每个单元格都是给定 Range 的 Worksheet 的 Cells 集合中的一个对象)。当您仍在内存中引用范围时删除范围时,范围变量指向的对象的内存已被释放。

但是,您的 Range 变量很可能仍然包含指向最近删除的 Range 对象的指针,这就是为什么它不是什么都不是,但是它所指向的任何东西都不再存在,这会在您再次尝试使用该变量时导致问题.

查看此代码以了解我的意思:

Public Sub test2()
    Dim r As Excel.Range
    Debug.Print ObjPtr(r)           ' 0

    Set r = ActiveSheet.Range("A1")
    Debug.Print ObjPtr(r)           ' some address

    r.Value = "Hello"

    r.Delete
    Debug.Print ObjPtr(r)           ' same address as before
End Sub

查看这篇文章了解有关 ObjPtr() 的更多信息:http: //support.microsoft.com/kb/199 ​​824

因此,虽然您拥有对象的有效地址,但不幸的是,该对象已被删除,因此不再存在。似乎“什么都没有”只是检查指针中的地址(我认为 VBA 认为该变量是“设置”)。

至于如何解决这个问题,不幸的是我目前没有看到一种干净的方法(如果有人确实找到了一种优雅的方法来处理这个问题,请发布它!)。您可以像这样使用 On Error Resume Next:

Public Sub test3()
    Dim r As Excel.Range
    Debug.Print ObjPtr(r)           ' 0

    Set r = ActiveSheet.Range("A1")
    Debug.Print ObjPtr(r)           ' some address

    r.Value = "Hello"

    r.Delete
    Debug.Print ObjPtr(r)           ' same address as before

    On Error Resume Next
    Debug.Print r.Value
    If (Err.Number <> 0) Then
        Debug.Print "We have a problem here..."; Err.Number; Err.Description
    End If
    On Error GoTo 0
End Sub
于 2012-08-26T04:17:41.297 回答
4

我如何以编程方式确定变量 r 是否处于这种状态?

我可以在不产生错误并捕获它的情况下执行此操作吗?

不。

据我所知,您无法可靠地测试这种情况:除非引发并捕获错误。

您的问题已在其他地方引起注意和讨论:Excel/VBA 博客中的两个大人物(Dick Kusleika 和 Rob Bovey)已经对其进行了研究,您可能会在其中找到一些有用的信息。但答案是否定的。

总而言之,这是一个很好的问题,但答案相当令人担忧。

于 2012-08-31T12:53:25.023 回答
2

为了测试一个范围对象当前是否无效,我使用了这个函数:

Public Function InvalidRangeReference(r As Range) As Boolean    
    On Error Resume Next
    If r.Count = 0 Then
        InvalidRangeReference = Err
    End If    
End Function
于 2014-04-24T02:25:03.337 回答