我们都知道 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
例如,但事实是,它是一个返回一个的函数 - 正如对象浏览器中所揭示的:
并且该功能以这样一种方式实现,即您始终获得相同的对象。
对象需要公开一个接口(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
, 因为 c
和 InternalCollection
(/封装的 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/