如果永远只能存在一个错误对象,那么声明一个 ErrObject 变量有什么用呢?

What is the use of declaring an ErrObject variable if there can only ever exist one error object?

我们都知道 VBA.
中永远只能有一个错误对象 在帮助同事处理错误以及为什么他不应该使用 On Error Resume Next 时,我有一个想法:
将错误对象存储在某处以供以后引用。

考虑这段测试代码:

Sub Test()
    Dim t As ErrObject
    On Error Resume Next
    Err.Raise 1
    Set t = Err
    On Error GoTo 0
    Debug.Print t.Number
    On Error Resume Next
    Err.Raise 1
    Debug.Print t.Number
End Sub

它将打印 0 到立即数 window 因为 On Error GoTo 0 重置错误对象然后打印 1 因为它仍然持有对唯一错误对象 (?) 的引用。

如果我们创建一个新的 class 并为其提供一些与 ErrObject 相关的属性,如下所示:

(TestClass)
Option Explicit
Public oError As ErrObject
Private Sub Class_Initialize(): End Sub
Private Sub Class_Terminate()
    If Not oError Is Nothing Then Set oError = Nothing
End Sub
Public Property Get Error()
    Error = oError
End Property
Public Property Set Error(ByVal ErrorObject As ErrObject)
    Set oError = ErrorObject
End Property

并像这样创建我们的实例:

Sub Test2()
    Dim t As TestClass
    On Error Resume Next
    Set t = New TestClass
    Err.Raise 1
    Set t.Error = Err
    On Error GoTo 0
    Debug.Print t.oError.Number
    On Error Resume Next
    Err.Raise 1
    Debug.Print t.oError.Number
End Sub

我们仍然分别得到 0 和 1 作为输出。

这让我想到了我的问题:当我们无法创建一个新对象本身但它只是成为指向 [=34 中唯一错误对象的另一个指针时,将变量声明为 ErrObject 有什么用=]?

None随便。

Err 通常被视为某种全局 ErrObject 实例,但事实是,它是 returns 的函数一个 - 如对象浏览器中所示:

并且该功能的实现方式始终是相同的对象。

对象需要公开一个可用的接口,因此由 Err 函数编辑的对象 return 公开了 ErrObject class 的对象 - 它不会这并不意味着 ErrObject class 存在,因此它可以被用户代码实例化或封装:它只是提供一个接口来访问当前 运行-time 错误状态的属性。

当你像你那样封装一个 ErrObject 时,你基本上只是给自己另一种方式(除了 Err 函数)来访问 ErrObject 实例 - 但它仍然是保存 当前 运行 时间错误状态 .

属性的完全相同的对象

当一个对象的属性发生变化时,指向该对象的封装副本将开始报告新值,而您打算 "remember" 的旧值将被覆盖。

请注意,这适用于任何对象,而不仅仅是 ErrObject

假设我有一个 class 可以执行您对 ErrObject 引用所做的操作,但具有 Collection:

Private coll As Collection

Public Property Set InternalCollection(ByVal c As Collection)
    Set coll = c
End Property

Public Property Get InternalCollection() As Collection
    Set InternalCollection = coll
End Property

如果我创建一个 class 的实例(我们称它为 Class1)并将 c 分配给它的 InternalCollection,然后将项目添加到 c...

Dim c As Collection
Set c = New Collection
With New Class1
    Set .InternalCollection = c

    c.Add 42
    .InternalCollection.Add 42

    Debug.Print .InternalCollection.Count
End With

输出是 2,因为 cInternalCollection(/封装的 coll 引用)是完全相同的对象,这就是封装的情况ErrObject.

解决方案是 封装 ErrObject 本身,而是将其值拉入封装 的仅获取属性的支持字段ErrObject 的状态:

Private errNumber As Long
Private errDescription As String
'...

Public Sub SetErrorInfo() 'note: an ErrObject argument would be redundant!
    With Err
        errNumber = .Number
        errDescription = .Description
        '...
    End With
End Sub

Public Property Get Number() As Long
    Number = errNumber 
End Property

Public Property Get Description() As String
    Description = errDescription
End Property

'...

现在,这是否有用还有待商榷 - IMO 如果在全局错误状态已包含相同信息的时刻消耗状态,则无需执行此操作。

class 很容易被 [ab] 用作 Function 的 return 类型,returns Nothing 表示成功,以及失败时封装的错误状态——问题是语言是围绕 raising 错误而不是 returning 错误设计的;在不验证其 return 值的情况下 "fire-and-forget" 这样的函数太容易了,并且由于在调用站点 actual 运行time 错误状态是'不会触发 On Error 语句,携带错误状态作为 程序数据 不是惯用的,它使 "surprising" API 很容易导致在最终忽略所有错误的代码中。

惯用错误处理会尽快处理全局 运行time 错误状态,并在同一范围内恢复,或者让错误状态在调用堆栈中冒泡到可以处理的位置。在处理错误之前,可以通过全局 Err 函数访问 ErrObject 状态。