Global
is an obsolete keyword; the only difference between Global
and Public
is that you can't use Global
in a class module. Prefer Public
for consistency.
Use the little-known Friend
access modifier to make a member accessible everywhere inside the project it's declared in, but only within that project.
The caveat is that it's only usable in a class module, and only for procedures - you can't make a Friend Const
.
A work-around could be to leverage the hidden VB_PredeclaredId
class attribute and set it to True
- create a new text file in Notepad, with this content:
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
END
Attribute VB_Name = "StaticClass1"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Option Explicit
Save the file with a .cls
extension, then import it into your project.
Then you can rename it to something more meaningful, and expose a Friend Property Get
member:
Friend Property Get R() As Single
R = 8.314
End Property
And now you can use that value, but only within the project that contains this class. Because of the PredeclaredId
attribute, you don't need to create a New
instance:
Debug.Print StaticClass1.R ' 8.314
Sadly (?) the VB_GlobalNamespace
attribute has no effect in VBA user classes, so the qualifying object (the predeclared class instance has the same name as the class module itself - that's exactly how UserForm1.Show
can work) is required.
Caution: it can be tempting to store instance state (e.g. private module-level variables) on a default instance (especially in forms), but it should be avoided whenever possible. If you need to store state, consider working with a New
instance of the class instead. Stateful default instances are more prone to bugs, notably because the object's lifetime is managed by the VBA run-time, not by user code. If the default instance is recycled/recreated, any state previously held is reset to whatever the design-time defaults are.
Note that class modules are private by default, which already makes them invisible to other referencing projects. You can change the Instancing property of the class to make it PublicNotCreatable
, which will change the value of it VB_Exposed
attribute to True
:
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = True
Public classes can be accessed from outside the project, but can't be created directly with the New
keyword (changing the value of the VB_Creatable
attribute has no effect in VBA). With a public/exposed class, referencing projects will be able to invoke the Public
members, but not the Friend
ones:
Public Property Get UseMeAnywhere() As Long
UseMeAnywhere = 42
End Property
Friend Property Get YouOnlySeeMeInThisProject() As Long
YouOnlySeeMeInThisProject = 42
End Property
Thank you, this works so well! I've set the scope of the new Class to "PublicNotCreatable", which I'm surmising negated the creation of the class from notepad. If I do want to use this constant outside of the current project, I still need to point the calling project to the appropriate project identifier and class name, e.g. "mjFunctions.Constants.R". I'm assuming there's no other way to access this class property from outside of the "mjFunctions" add-in. – Michael James – 2019-04-18T14:56:09.593
1In addition, even accessing this property from within "mjFunctions" requires me to specify the "Constants" class, which should greatly prevent against confusion. – Michael James – 2019-04-18T15:02:07.807
@MichaelJames Yes! Explicit qualifiers are always a very good thing! IMO the
– Mathieu Guindon – 2019-04-18T15:07:20.230VB_PredeclaredId
attribute is the single most useful hidden VBA gem. With it you can expose a public parameterized factory method that creates & returns an initialized new instance of the class, so you can then do e.g.Set coord = GridCoord.Create(4, 2)
. Note that Rubberduck surfaces these attributes with special annotation comments, and automatically synchronizes hidden attributes with the annotations, so'@PredeclaredId
at the top, synchronize, and done!Is there any way to access the Class Attributes without creating the class in notepad and importing? – Michael James – 2019-04-18T15:08:46.260
@MichaelJames yes, with the Rubberduck add-in (an open-source project I contribute to) you can just add
– Mathieu Guindon – 2019-04-18T15:11:08.200'@PredeclaredId
at the top, and/or'@ModuleDescription("This module contains important stuff")
, and a bunch of others. Desynchronized attributes/annotations show up under "Rubberduck Opportunities" in the inspection results toolwindow, and then a quick-fix can automatically synchronize them.