VBA中的反射(VBA类的CreateObject()函数)






4.80/5 (5投票s)
使用反射实例化类的方法。
引言
我经常思考如何在VBA中实例化一个类,而无需事先明确声明其类型。当然,我的结论是不可避免的:这是不可能的。VBA不支持反射[^],这是一个OOP概念,它具有许多方面,包括能够在设计时不了解其类型的情况下实例化一个类。反射提供了巨大的灵活性,而VBA却令人痛苦地缺乏这种灵活性(至少对于那些喜欢将VBA用于其通常用途之外的人来说是这样)。
使用代码
说实话,反射可以在VBA中发生,但仅限于CreateObject()
函数,并且仅适用于ActiveX对象。
例如,如果我想创建一个Excel应用程序对象的实例,而无需在代码中显式声明类型(使用Set xlApp = New Excel.Application
),我可以使用以下代码
Dim xlApp As Excel.Application
'reflection-level object creation
Set xlApp = CreateObject(,"Excel.Application")
'equivalent, type specific statement
Set xlApp = New Excel.Application
那么我们如何解决这个问题呢?我见过一系列的解决方案,从在Select...Case...End Select
逻辑块中枚举一系列可能的类,到一种hackish(但有效)的想法,即动态编写实例化特定类类型的函数代码。
我更喜欢的方法介于两者之间。基本上,目标是通过提供类型名称的字符串表示形式来实例化任何给定类型的类。换句话说,一个等效的CreateObject()
函数,它适用于用户定义的VBA类。
让我们从一个示例类cMyClass
开始
Option Explicit
Public Sub Initialize()
MsgBox "This class has been initialized."
End Sub
很简单。要正常使用它,我们可以在一个标准模块中创建一个名为MakeMyClass()
的公共函数,其代码如下
Option Explicit
Public Sub MakeMyClass()
Dim x As cMyClass
Set x = New cMyClass
End Sub
但是,我们想做的是这样的
Option Explicit
Public Sub MakeMyClass(strMyClassName)
Dim x As strMyClassName
Set x = New strMyClassName
End Sub
当然,我们不能这样做。至少不能像这样。在某个地方,我们需要建立特定于类型的代码,该代码执行实际的对象创建。诀窍是使用VBA支持的一种反射:Application.Run()
方法。此方法允许我们通过仅将函数名作为字符串传递来执行存储在标准模块中的公共函数 - 就像我们想对类实例化所做的那样。因此,我们可以添加另一个标准模块并将其命名为Support_cMyClass
。然后添加类似这样的代码
Option Explicit
Public Const ccMyClass = "MyClass"
Public Function MyClass() As cMyClass
Set MyClass = New cMyClass
MyClass.Initialize
End Function
我们正在做的是创建一个“外部构造函数”(借用C++中的一个想法)。最后,在一个“main”标准模块中,我们添加我们的VBA类版本的ActiveX CreateObject()
函数
Option Explicit
Public Function CreateVBAClass(ClassName As String) As Object
Set CreateVBAClass = Application.Run(ClassName)
End Function
要使用我们的函数,我们只需要使用我们的类的名称(减去“c”前缀)来调用它,可以使用字符串中的名称或在类的支持模块中定义的公共常量
Public Sub MakeMyClass()
Dim x As Object
Set x = CreateVBAClass("MyClass")
Set x = CreateVBAClass(ccMyClass)
End Sub
在任何一种情况下,当代码运行时,将弹出一个消息框,让我们知道已创建并初始化了cMyClass
的一个实例。请注意,使用Public Const
充当一种“动态枚举”。也就是说,它提供了一种遵循一致命名约定的字符串枚举。虽然它不友好于智能感知,但它确实为我们提供了最大的灵活性和模块化,从而能够使我们的反射技术采用更“即插即用”的方式,而不是要求在全局Enum
或具有扩展的Select...Case...End Select
逻辑的函数中枚举添加的类。