vba 使用类的文本名称创建一个新对象

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1057670/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-11 10:32:14  来源:igfitidea点击:

Create a new object using the text name of the class

excelvba

提问by MT.

Is there a way to set an object to the new instance of a class by using the text name of the class?

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

I will have a library of classes, and depending on some other variable, I want to get one of these classes at runtime.

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

E.g. I have "CTest1", "CTest2", "CTest3"

例如我有“CTest1”、“CTest2”、“CTest3”

I would have function similar to the below

我会有类似于下面的功能

Function GetTestClass(lngClassNo as long) as Object
  Dim strClassName as String    
  strClassName = "CTest" & CStr(lngClassNo)
  Set GetTestClass = New instance of class(strClassName)
End Function

回答by Patrick McDonald

There's no reflection in VBA, so I don't think this is possible. You'd have to do something like the following I'm afraid:

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

Unless that is your CTest classes are defined in a COM DLL, in which case you could use the CreateObject statement. You would need to use VB6 to create such a DLL though, you can't create DLLs in Excel, Access, etc.

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

Function GetTestClass(lngClassNo as long) as Object

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

End Function

回答by Jordan

You can use metaprogramming to do this, although it does seem like quite a hack. Here is an example that uses a couple of helper functions (omitted for brevity):

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

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

回答by omegastripes

CallByNamefunction can help you. Let's say there are some class modules in your project: clsSample0, clsSample1and clsSample2. Add a new class module named clsSpawner, which lists all target classes as public variables having the same names, and declared with Newkeyword:

CallByName功能可以帮助您。假设您的项目中有一些类模块:clsSample0,clsSample1clsSample2. 添加一个名为 的新类模块clsSpawner,它将所有目标类列为具有相同名称的公共变量,并用New关键字声明:

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

In a standard module add Function Spawn()code:

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

Function Spawn(sClassName) As Object

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

End Function

Test it with some code like this:

用一些这样的代码测试它:

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

How does it work? Spawnfunction instantiates clsSpawnerand calls the clsSpawnerinstance to return requested property, and actually clsSpawnerinstance creates a new instance of the target class due to declaration with Newkeyword and returns the reference.

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

回答by ConcernedOfTunbridgeWells

VB class definitions are really defining COM interfaces behind the scenes, so one can define data types as an abstract interface definition with concrete implementations using the implementskeyword.

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

To get any sort of polymorphism you have to do this, otherwise you will have problems with casting. It is somewhat fiddly but technically possible to do this with VB. If you want to dig into it find some of the advanced VB books by Dan Appleman or Matthew Kurland. I'm not sure if they're still in print but they're probably available through Amazon Marketplace.

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

This works with VB6 and I'm fairlysure it works with VBA.

这适用于VB6,我相当确信它的工作原理与VBA。

回答by mandroid

You might be able to do it with a collection class or object array. All the objects are in one array.

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

In your class have a .Name property and when you create an instance of it do this:

在您的类中有一个 .Name 属性,当您创建它的实例时,请执行以下操作:

Dim CTest() as New CTest
For n = 1 to 10
    Redim Preserve CTest(n)
    CTest(n).Name = "CTest" & CStr(n)
Next l

Quick and dirty. The above example would return 10 CTest objects in a single object array. You could also ditch the .Name and just use CTest(n).

又快又脏。上面的示例将在单个对象数组中返回 10 个 CTest 对象。您也可以放弃 .Name 并只使用 CTest(n)。