【问题标题】:Create a new object using the text name of the class使用类的文本名称创建一个新对象
【发布时间】:2021-08-07 00:33:38
【问题描述】:

有没有办法通过使用类的文本名称将对象设置为类的新实例?

我将拥有一个类库,并且根据其他一些变量,我想在运行时获取其中一个类。

例如我有“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

【问题讨论】:

    标签: excel vba


    【解决方案1】:

    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 我为Spawn() 函数添加了标准模块位置。
    【解决方案2】:

    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 接口做一些事情。
    • 哦,好吧。我从上面的 Select Case 开始,但希望有更清洁的东西……而不是世界末日。谢谢
    • VBA 允许后期绑定,所以据我所知没有转换问题,您只需要小心避免运行时错误,因为编译器不会在方法名称等中提取任何拼写错误.
    • 只有这两种方法可以实现您的建议。 Concerned 的接口建议将简化一些实现问题(我经常为此目的使用接口),因此您可以以多态方式保留编译时类型检查。但是,您仍然可以使用 Object 实例对函数调用使用运行时绑定。
    • 谢谢。能够使用该界面真是太好了。不得不使用选择案例只是一种耻辱......
    【解决方案3】:

    您可以使用元编程来做到这一点,尽管它看起来确实是个 hack。 这是一个使用几个辅助函数的示例(为简洁起见,省略):

    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
    

    【讨论】:

      【解决方案4】:

      VB 类定义实际上是在幕后定义 COM 接口,因此可以使用 implements 关键字将数据类型定义为具有具体实现的抽象接口定义。

      要获得任何类型的多态性,您必须这样做,否则您将遇到转换问题。用 VB 做到这一点有点繁琐,但在技术上是可行的。如果您想深入了解它,请查找 Dan Appleman 或 Matthew Kurland 的一些高级 VB 书籍。我不确定它们是否仍在印刷中,但它们可能可以通过亚马逊商城购买。

      这适用于 VB6,我相当确定它适用于 VBA。

      【讨论】:

      • 非常好。它确实有效,谢谢。很高兴能够使用编译器而不必担心拼写错误等。
      • 实现只是强制您实现给定类的所有公共方法/属性。这对按名称创建类有何帮助?
      • 创建一个对象是轻而易举的事。问题不是创建类,而是随后使用它。您可以使用对象和后期绑定并希望最好。 Implements 为您提供类型安全的多态性。
      【解决方案5】:

      您也许可以使用集合类或对象数组来做到这一点。所有对象都在一个数组中。

      在您的类中有一个 .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)。

      【讨论】:

        猜你喜欢
        • 2020-02-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-06-08
        相关资源
        最近更新 更多