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

标签 vba error-handling ms-office

我们都知道 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

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

如果我们创建一个新类并给它一些与 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 作为输出。

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

最佳答案

没有任何。
Err通常被视为某种全局 ErrObject例如,但事实是,它是一个返回一个的函数 - 正如对象浏览器中所揭示的:

enter image description here

并且该功能以这样一种方式实现,即您始终获得相同的对象。

对象需要公开一个接口(interface)才能使用,因此 Err 返回的对象函数公开 ErrObject 的函数类 - 这并不意味着 ErrObject类的存在是为了它可以被用户代码实例化或封装:它只是提供一个接口(interface)来访问当前运行时错误状态的属性。

当您封装 ErrObject就像你做的那样,你基本上只是给自己另一种方式(除了 Err 函数)来访问 ErrObject实例 - 但它仍然是保存当前运行时错误状态属性的完全相同的对象。

当一个对象的属性发生变化时,指向该对象的封装副本将开始报告新值,而您要“记住”的旧值将被覆盖。

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

假设我有一门课可以用 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

如果我创建该类的一个实例(我们称之为 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 如果在全局错误状态已经包含相同信息的时刻消耗状态,则无需这样做。

该类很容易被 [ab] 用作 Function 的返回类型。返回 Nothing表示成功,以及在失败时封装的错误状态 - 问题在于该语言是围绕引发错误而不是返回错误而设计的;在不验证其返回值的情况下“一劳永逸”这样的函数太容易了,因为在调用站点实际运行时错误状态不会触发 On Error声明,将错误状态作为程序数据携带不是惯用的,它产生了一个“令人惊讶的”API,很容易导致代码最终忽略所有错误。

惯用的错误处理会尽快处理全局运行时错误状态,并且要么在同一范围内恢复,要么让错误状态在调用堆栈中冒泡到可以处理的地方。在处理错误之前,ErrObject状态可通过全局 Err 访问功能。

关于vba - 如果只能存在一个错误对象,那么声明 ErrObject 变量有什么用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55066376/

相关文章:

excel - 灾难性故障 EXCEL VBA - 错误 '-2147418113 (8000ffff)'

javascript - 使用不带选项值的 Excel VBA 设置 javascript 输入值

c# - 在 ELMAH 获取异常之前捕获异常并清除它

error-handling - 使错误更常见于10月的cms

c# - 使用 Linq to XML、XMLNS、XDeclaration、单元格格式创建 Office Excel 文档(有效)

database - 微软 Access 问题

bash - 声明具有进程替换的变量时,能否捕获子外壳程序的退出代码?

vba - Word 2016 for mac - 任何类型的自定义控件

.net - 那么,.NET 没有内置的 Office 功能吗?

vba - Excel VBA从公式中删除等号