使用类名文本创建新对象

10
有没有一种方法可以使用类的文本名称将对象设置为类的新实例?
我将拥有一系列类,根据其他一些变量,我希望在运行时获取其中一个类。
例如,我有“CTest1”,“CTest2”,“CTest3”。
我将拥有类似以下函数:
Function GetTestClass(lngClassNo as long) as Object
  Dim strClassName as String    
  strClassName = "CTest" & CStr(lngClassNo)
  Set GetTestClass = New instance of class(strClassName)
End Function
5个回答

5

您可以使用元编程来实现这一点,虽然看起来有些像黑客行为。 这里是一个示例,使用了几个辅助函数(出于简洁而省略):

Public Function CreateInstance(typeName As String) As Object
    Dim module As VBComponent
    Set module = LazilyCreateMPCache()

    If Not FunctionExists(typeName, module) Then
        Call AddInstanceCreationHelper(typeName, module)
    End If

    Dim instanceCreationHelperName As String
    instanceCreationHelperName = module.name & ".GetInstanceOf" & typeName
    Set CreateInstance = Application.Run(instanceCreationHelperName)
End Function

Sub AddInstanceCreationHelper(typeName As String, module As VBComponent)
   Dim strCode As String
   strCode = _
   "Public Function GetInstanceOf" & typeName & "() As " & typeName & vbCrLf & _
       "Set GetInstanceOf" & typeName & " = New " & typeName & vbCrLf & _
   "End Function"
   Call AddFunction(strCode, module)
End Sub

5

CallByName函数可以帮助您。假设您的项目中有一些类模块:clsSample0clsSample1clsSample2。添加一个名为clsSpawner的新类模块,其中列出了所有目标类作为公共变量,这些变量具有相同的名称,并使用New关键字声明:

Public clsSample0 As New clsSample0
Public clsSample1 As New clsSample1
Public clsSample2 As New clsSample2

在标准模块中添加Function Spawn()代码:
Function Spawn(sClassName) As Object

    Set Spawn = CallByName(New clsSpawner, sClassName, VbGet)

End Function

使用以下代码进行测试:

Sub TestSpawn()

    Dim objSample0a As Object
    Dim objSample0b As Object
    Dim objSample1 As Object
    Dim objSample2 As Object

    Set objSample0a = Spawn("clsSample0")
    Set objSample0b = Spawn("clsSample0")
    Set objSample1 = Spawn("clsSample1")
    Set objSample2 = Spawn("clsSample2")

    Debug.Print TypeName(objSample0a)            ' clsSample0
    Debug.Print TypeName(objSample0b)            ' clsSample0
    Debug.Print objSample0a Is objSample0b       ' False
    Debug.Print TypeName(objSample1)             ' clsSample1
    Debug.Print TypeName(objSample2)             ' clsSample2

End Sub

它是如何工作的?Spawn 函数实例化 clsSpawner 并调用 clsSpawner 实例以返回所需的属性,实际上 clsSpawner 实例由于带有 New 关键字的声明而创建目标类的新实例并返回引用。


感谢您提供的解决方案。对于Spawn()函数应该添加在哪里还不是完全清楚,但我已经有了思路。不知道为什么这个答案还没有任何回复。 - AlexL
1
@AlexL,我为Spawn()函数添加了标准模块位置。 - omegastripes

4

VBA中没有反射,所以我认为这是不可能的。恐怕您需要做类似以下的操作:

Function GetTestClass(lngClassNo as long) as Object

    Select Case lngClassNo
    Case 1
        Set GetTestClass = New CTest1
    Case 2
        Set GetTestClass = New CTest2
    ...

    End Select

End Function

除非您的CTest类是在COM DLL中定义的,否则您可以使用CreateObject语句。 但是,您需要使用VB6来创建这样的DLL,不能在Excel、Access等中创建DLL。

Function GetTestClass(lngClassNo as long) as Object

    Set GetTestClass = CreateObject("MyDll.CTest" & lngClassNo)

End Function

如果您尝试返回一个对象,我认为您可能会遇到强制转换问题。您需要使用COM接口进行处理。 - ConcernedOfTunbridgeWells
噢,好吧。我一开始用了上面的选择语句,但是希望有更简洁的方法...这并不是世界末日。谢谢。 - MT.
2
VBA允许晚期绑定,就我所见,没有任何强制转换问题,您只需要小心避免运行时错误,因为编译器不会捕获方法名称等方面的任何拼写错误。 - Patrick McDonald
1
这是你所建议的唯二实现方式。Concerned提出的接口建议可以简化一些实现问题(我经常使用接口来达到这个目的),因此你可以以多态的方式保留编译时类型检查。但是,你仍然可以使用对象实例上的运行时绑定来调用函数。 - Joel Goodwin
谢谢这个。能够使用界面真是太好了。只是有点遗憾要使用选择语句... - MT.

2
VB类定义实际上是在幕后定义COM接口,因此可以使用implements关键字将数据类型定义为抽象接口定义,并使用具体实现。如果不这样做,就会遇到强制转换的问题,无法实现多态性。在VB中进行这种操作有点棘手,但从技术上讲是可能的。如果您想深入了解,请查找Dan Appleman或Matthew Kurland撰写的高级VB图书。我不确定它们是否仍在发行,但它们可能通过Amazon Marketplace获得。这适用于VB6,我相当确定它也适用于VBA。

非常好,它确实有效,谢谢。能够使用编译器而不必担心打字错误等问题真是太好了。 - MT.
实现接口只是强制你实现给定类的所有公共方法/属性。那么这如何帮助通过名称创建一个类呢? - Oorang
1
创建一个对象很简单。问题不在于创建类,而在于随后如何使用它。您可以使用Object和late binding,并希望一切顺利。实现提供了类型安全的多态性。 - ConcernedOfTunbridgeWells

0
您可以使用集合类或对象数组来完成它。所有对象都在一个数组中。
在您的类中有一个.Name属性,当您创建它的实例时,请执行以下操作:
Dim CTest() as New CTest
For n = 1 to 10
    Redim Preserve CTest(n)
    CTest(n).Name = "CTest" & CStr(n)
Next l

快速而简单。上面的例子将在一个对象数组中返回10个CTest对象。您也可以放弃.Name,直接使用CTest(n)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接