Click here to Skip to main content
15,867,308 members
Articles / VBA

Reflection in VBA (a CreateObject() function for VBA classes)

Rate me:
Please Sign up or sign in to vote.
4.80/5 (5 votes)
2 Mar 2011Ms-PL3 min read 81.5K   638   11   10
A method for instantiating classes using Reflection.

Introduction

I've often thought about how one could instantiate a class in VBA without specifically declaring its type beforehand. Of course, my conclusion was inevitable: it can't be done. VBA does not support Reflection[^], an OOP concept which has many facets, including the ability to instantiate a class without knowing its type at design time. Reflection offers great flexibility that is agonizingly absent from VBA (at least for those of us who like to use VBA for more than it is typically intended).

Using the code

Truth be told, Reflection can occur in VBA, but only with the CreateObject() function, and that only applies to ActiveX objects.

For example, if I wanted to create an instance of an Excel Application object without having to declare the type explicitly in code (using Set xlApp = New Excel.Application), I can do so using the following code:

VBScript
Dim xlApp As Excel.Application 

'reflection-level object creation 
Set xlApp = CreateObject(,"Excel.Application") 

'equivalent, type specific statement 
Set xlApp = New Excel.Application

So how can we solve this problem? I've seen a range of solutions, from enumerating a series of possible classes in a Select...Case...End Select logic block, to a hackish (but effective) idea of writing function code on-the-fly that instantiates the specific class type.

The approach I prefer falls somewhere in between. Basically, the goal is to be able to instantiate a class of any given type by providing a string representation of the type name. In other words, an equivalent CreateObject() function that works on user-defined VBA classes.

Let's start with an example class, cMyClass:

VBScript
Option Explicit 

Public Sub Initialize() 
    MsgBox "This class has been initialized."
End Sub

Simple enough. To use it normally, we may create a public function in a standard module called MakeMyClass() with the following code:

VBScript
Option Explicit 

Public Sub MakeMyClass() 
 Dim x As cMyClass 
 Set x = New cMyClass
End Sub

But, what we'd like to do is this:

VBScript
Option Explicit 

Public Sub MakeMyClass(strMyClassName) 
   Dim x As strMyClassName 
   Set x = New strMyClassName 
End Sub

Which, of course, we can't do. At least not like this. Somehow, somewhere, we need to lay down type-specific code that does the actual object creation. The trick is to use a sort of Reflection VBA does support: the Application.Run() method. This method allows us to execute a public function stored in a standard module by simply passing its name as a string - just like we want to do with class instancing. So, we could add another standard module and call it Support_cMyClass. Then add code like this:

VB
Option Explicit 
Public Const ccMyClass = "MyClass" 

Public Function MyClass() As cMyClass 
  Set MyClass = New cMyClass 
  MyClass.Initialize 
End Function

What we're doing is creating a sort of "external constructor" (to borrow an idea from C++). Finally, in a "main" standard module, we add our VBA class version of the ActiveX CreateObject() function:

VBScript
Option Explicit

Public Function CreateVBAClass(ClassName As String) As Object 
    Set CreateVBAClass = Application.Run(ClassName) 
End Function

To use our function, we merely need to invoke it with the name of our class (less the "c" prefix) using either the name in a string or the public constant defined in the class' support module:

VBScript
Public Sub MakeMyClass() 
 Dim x As Object 
 Set x = CreateVBAClass("MyClass") 
 Set x = CreateVBAClass(ccMyClass) 
End Sub

In either case, when the code runs, a message box will pop up, letting us know that an instance of cMyClass has been created and initialized. Note that the use of Public Const acts as a sort of "dynamic enumerant". That is, it provides a sort of enumeration of strings which follows a consistent naming convention. While it is not intelli-sense friendly, it does provide us the greatest flexibility and modularity, enabling a more "plug-and-play" approach to our Reflection technique rather than requiring added classes to be enumerated in a global Enum or function with an extensive Select...Case...End Select logic.

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 5 Pin
MikeGTi15-Mar-21 1:51
MikeGTi15-Mar-21 1:51 
QuestionIf I understand correctly Pin
MikeGTi4-Jun-20 23:32
MikeGTi4-Jun-20 23:32 
BugError in download file "Main.bas" Pin
Stef Heyenrath13-Oct-13 1:37
Stef Heyenrath13-Oct-13 1:37 
GeneralMy vote of 4 Pin
Steve Russo14-Mar-12 7:59
Steve Russo14-Mar-12 7:59 
GeneralInteresting but... Pin
vbfengshui2-Mar-11 11:27
vbfengshui2-Mar-11 11:27 
GeneralRe: Interesting but... Pin
Joel C Graff2-Mar-11 12:14
Joel C Graff2-Mar-11 12:14 
Ha! And I thought you were going to say, "Interesting, but I have no idea what 'Refelction' is..." I noticed the misspelling in the article title only *after* it was posted... And by the time this comment gets read, I'm sure the mistake will be corrected. Smile | :)

Anyway, why use this technique over a Select...Case...End Select logic? In most cases you probably wouldn't want to. And you're right, it is more work. In the end, it's really no more reflection than the Select...Case...End Select method because you're still laying down type-specific code.

But let's say I have an object model that I use to manage widgets. The object model provides widget creation / destruction / reference management code, but apart from the widgets, doesn't really do anything.

Then, let's say we have a library of thousands of widgets, any of which I may choose to use in a project that implements my object model. I want to be able to use them in a sort of "plug-in" manner - that is, I add / remove widgets as I need from the project.

In the end, I'd like my core object model to be unaffected. Whether or not the widget is present should have no effect on the core object model code, but I still want the object model to use a widget class that I've added to my project. How do I do that? With the Select...Case...End Select code, I could end up managing a *very* large logic construct, esp. if I want to use a hundred or so different widgets.

Using this reflection technique, my core object model doesn't require any hard coding of the widget types. All it needs is the widget type passed as a string, and it uses Application.Run() to instance the class, with (sort-of) no idea of what kind of class it's implementing.

I don't know if that helps, but that is exactly the problem I'm having with achieving a more independent "modularity" with my VBA Composite Controls Object Model[^]. As it stands, I have to modify code in the object model core every time I want to add / remove a composite control class from a project. Using this technique, I won't have to worry about it again. On a small scale, it's no real advantage. On a larger scale, it simplifies development and limits the places where I have to use type-specific code.

My object model currently uses the Select...Case...End Select method you propose and I have to maintain a rather extensive enumeration to manage all the different types of controls that I've (so far) managed to come up with. It works fine, but if I want to dream big, this isn't going to work... But then, I'm also coding this entirely in Office VBA, so one has to take the phrase "dream big" with a grain of salt. Smile | :)
GeneralRe: Interesting but... Pin
vbfengshui2-Mar-11 14:30
vbfengshui2-Mar-11 14:30 
GeneralRe: Interesting but... Pin
Joel C Graff2-Mar-11 15:28
Joel C Graff2-Mar-11 15:28 
GeneralRe: Interesting but... Pin
oharab200023-Mar-11 23:51
oharab200023-Mar-11 23:51 
GeneralRe: Interesting but... Pin
Joel C Graff24-Mar-11 10:30
Joel C Graff24-Mar-11 10:30 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.