Create a new object using the text name of the class
Asked Answered
A

5

10

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"

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
Absentminded answered 29/6, 2009 at 10:45 Comment(0)
J
5

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
Jordison answered 28/1, 2011 at 16:16 Comment(0)
S
5

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

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(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? Spawn function instantiates clsSpawner and calls the clsSpawner instance to return requested property, and actually clsSpawner instance creates a new instance of the target class due to declaration with New keyword and returns the reference.

Santo answered 21/1, 2018 at 22:34 Comment(2)
Thanks for the solution. Not completely clear about where to add Spawn() function, but I got the idea. I wonder why the answer is still without any response.Schramke
@Schramke I added standard module location for Spawn() function.Santo
T
4

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:

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.

Function GetTestClass(lngClassNo as long) as Object

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

End Function
Trinity answered 29/6, 2009 at 10:54 Comment(5)
I think you might run into casting issues if you tried to return an object. You would have to do something with a COM interface.Chlodwig
Oh well. I started of with a Select Case as above, but was hoping for something cleaner....not the end of the world. ThanksAbsentminded
VBA allows late binding, so there are no casting issues as far as I can see, you just need to be careful to avoid runtime errors as the compiler won't pick up any typos in method names etc.Trinity
These are the only two ways to pull off what you're suggesting. The interface suggestion from Concerned would simplify some of the implementation issues (I use interfaces frequently for this very purpose) so you can retain compile-time type checking in a polymorphic manner. However, you can still use runtime binding on the function calls with using an Object instance.Lassiter
Thanks for this. It's nice to be being able to use the interface. Just a shame about having to use the select case...Absentminded
C
2

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 implements keyword.

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.

This works with VB6 and I'm fairly sure it works with VBA.

Chlodwig answered 29/6, 2009 at 11:3 Comment(3)
Very nice. It does work thanks. Nice to be able to use the compiler and not have to worry about typos etc.Absentminded
Implements just forces you implement all the public methods/properties of a given class. How would that help with creating a class by name?Triumph
Creating an object is a no brainer. The problem is not creating the class but using it subequently. You can use Object and late binding and hope for the best. Implements gives you type-safe polymorphism.Chlodwig
M
0

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:

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).

Marquardt answered 1/7, 2009 at 18:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.