据我了解,在VBA中,类都公开了一个默认接口(interface)(它只是类模块的名称)。你也可以让它们实现
另一个自定义接口(interface);为类提供一些属性,这些属性从自定义界面的角度来看是可见的,但从默认界面的角度来看是不可见的。
我有一个方法需要实现某个接口(interface)的类
Public Sub doStuff(ByVal item As ICustomInterface)
像这样叫
Dim a As New Class1 'Implements ICustomInterface
Dim b As New Class2 'Implements ICustomInterface too
doStuff a
doStuff b
doStuff New Collection 'raises "runtime error 13 - type mismatch" as Collection doesn't implement ICustomInterface
如果我理解正确,当我向该方法提供对象实例时,通过传递对该对象默认接口(interface)的引用,VBA 查询该对象实例以生成对 ICustomInterface
的引用该对象,并将新引用存储在 item
变量中。我认为这个过程称为向下转型。
我的问题是 doStuff
调用的方法需要传递项目的默认接口(interface),不是自定义接口(interface)。
为了演示,我们可以使用ObjPtr
来识别指向哪个接口(interface):
Dim implementation As Object
Dim defaultCast As Class1 'implements ICustomInterface
Dim downCast As ICustomInterface
Set implementation = New Class1 'or Class2 - store reference to default interface in variable
'1) Check if implementation indeed points to default interface
Set defaultCast = implementation
Debug.Assert ObjPtr(defaultCast) = ObjPtr(implementation) 'fine
'2) Check if down-casting gives different interface
Set downCast = implementation
Debug.Assert ObjPtr(downCast) <> ObjPtr(implementation) 'fine
'4) Check if casting from ICustomInterface to Object reverts to default interface
Dim objectUpCast As Object
Set objectUpCast = downCast
Debug.Assert ObjPtr(objectUpCast) = ObjPtr(implementation) 'fails :(
Debug.Assert ObjPtr(objectUpCast) = ObjPtr(downCast) 'succeeds - not what I want
'3) Check if casting from ICustomInterface to Class1 reverts to Class1's default interface
Dim strictUpCast As Class1
Set strictUpCast = downCast
Debug.Assert ObjPtr(strictUpCast) = ObjPtr(implementation) 'fine - but won't work if I Set implementation = New Class2
'/some other implementation of the ICustomInterface
第三种选择;采用自定义界面并恢复到默认界面是我想要的
为什么?
我想在我的函数签名中使用类型安全。我可以明确地使用自定义界面 - 这是我当前的解决方法
Public Sub doStuff(ByVal item As Object) 'receive default interface - or at least whatever interface is provided
Dim downCast As ICustomInterface
Set downCast = item
'work with downCast as necessary
'... later pass default interface "item" to other sub
End Sub
但我更喜欢检查函数签名中的类型然后在需要时向上转换回默认接口(interface)的工作流程
最佳答案
首先转换为 IUnknown:
Dim objectUpCast As Object
Dim iUnk As IUnknown
Set iUnk = downCast
Set objectUpCast = iUnk
编辑
所有 VB 接口(interface)都是双重接口(interface),这意味着所有接口(interface)都派生自 IDispatch,后者又派生自 IUnknown。
请注意,Object 数据类型代表 IDispatch 接口(interface)。
在你的例子中:
Dim strictUpCast As Class1
Set strictUpCast = downCast
第二行调用 QueryInterface 请求 Class1 接口(interface),正如预期的那样。
在你的另一个例子中:
Dim objectUpCast As Object
Set objectUpCast = downCast
在第二行可能会发生两件事:
- 如果变量 (downCast) 指向双重接口(interface)(从 IDispatch 派生的接口(interface)),那么 VB 只会增加引用计数(因此返回与 downCast 相同的接口(interface))
- 如果变量指向自定义接口(interface)(派生自 IUnknown),则 VB 调用 QueryInterface 并请求 IDispatch 接口(interface)
当转换到 IUnknown 接口(interface)(OLE 自动化库的一部分)时:
Dim iUnk As IUnknown
Set iUnk = downCast
iUnk 变量指向自定义接口(interface)(仅派生自 IUnknown)。
这得到:
Dim objectUpCast As Object
Set objectUpCast = iUnk
其中,在第二行,VB 调用 QueryInterface 并请求 IDispatch 接口(interface),因为变量(右侧)指向自定义接口(interface)(IUnknown 本身不是从 IDispatch 派生的)。 IDispatch 接口(interface)知道默认接口(interface),这就是返回的内容
关于excel - 我可以转换为任何对象的默认接口(interface)吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57536331/