Create a new object using the text name of the class
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
Solution 1:
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.
Solution 2:
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
Solution 3:
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
Solution 4:
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.