Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using predeclared classes in VBA as namespaces

Tags:

excel

vba

One of my biggest issues with coding in VBA is a total lack of namespaces making it difficult to ensure things like scoping and selecting the right function when every function of the same name is in the global namcespace. I know you can prefix your function calls with the module name it is in, but it seems to me that this is also possible by replacing all your modules by predeclared classes. A namespace would look something like this:

VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
END
Attribute VB_Name = "MyNamespace"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
'@Folder("Project.Namespace")
Option Explicit

Public Function Foo() As String
    Foo = "bar"
End Function

' Opionally make sure users do not use this as a class
Private Sub Class_Initialize()
    If Not Me Is MyNamespace Then
        Err.Raise 512, _
            Source:="MyNamespace.Initialize", _
            Description:="Cannot create an instance of a namespace."
    End If
End Sub

Thus if you ever want to call Foo in this namespace you have to write

MyNamespace.Foo()

While simply calling Foo() will not work.

My question is this:

Is there any reason you wouldn't want to do this? As far as I can see the class' constructor is only called the first time you call any of its functions after you open your project, so I can't see any overhead there, but there could be something sneaky I am not aware of. Of course this is in no way an ideal way of addressing this lack of functionality, but it is not as if VBA programmers aren't already using the language in roundabout ways to provide lacking functionality.

Also, I see that this is basically the same as this question, however I'd further want to know whether there are any specific issues with having a ton of predeclared "empty" classes in your code. Say you replace 100 modules with 100 predeclared classes, will that have any significant impact on e.g. performance/stability/etc...?

like image 338
Jonas Glesaaen Avatar asked Mar 20 '19 10:03

Jonas Glesaaen


1 Answers

It costs you an object pointer, is all. I wouldn't call it a namespace though.

Every single UserForm module you've ever worked with has a VB_PredeclaredId attribute set to True. This instance is automatically created, as you noticed, and while you can explicitly destroy it, the next time it's referenced it'll be automatically (silently) re-created again... with whatever the original state is/was... pretty much like an auto-instantiated object, aka an As New declaration.

So what you have there is more like an object hierarchy than a namespace structure - very, very similar to how you can drill down the Excel object model starting with Application, and go do this:

Set someCell = Excel.Application.Workbooks(1).Worksheets(1).Range("A1")

Here the "namespace" is the Excel library, Application is the root object, and the rest of that expression is all member access - property getters, named after classes.

But the classes aren't in "namespaces"... the Worksheets collection type (class) exists on its own, under the Excel library: there being a Worksheets property on the Application class does not in any way, shape, or form, shield the class from a "name collision": if your user code has a class module named Worksheets (and it very well can), then as per how VBA resolves identifier references, Dim foo As Worksheet is going to be an instance of that custom class: the only valid qualifier is the library name (Excel, or MyVBAProject).

So, what you have there is a reasonable approach for building a relatively complex object model structure - but it won't (can't) replace or simulate a namespace. If you find yourself making "empty" classes, reconsider your design.

As for the last question, I don't see how having 100 predeclared custom objects might be any different than a project with 100 userforms: in both cases I'd wonder if there'd be a way to trim that down and generalize/reuse components, whether they be forms or classes.

What you want to avoid, is a predeclared object that holds state - because that state is effectively global now, and you have no control over what gets to change that state, where, and when: Application.Calculation is a prime example of this.

like image 189
Mathieu Guindon Avatar answered Oct 30 '22 11:10

Mathieu Guindon