How to keep all hardcoded class values in one place
I have created a class in VBA that would like to have some preset values associated with it. I'm new to classes and wondering what is the best (/ good) way to structure my VBA code inside a class object so that I can easily access these defaults as I type. The answer should preferably:
- Require relatively few extra lines of code on top of the lines that I believe would be required to actually hard-code the values
- i.e. something like extra
Sub
for each hardcoded value would not be ideal - This means my class won't get too cluttered.
- i.e. something like extra
- Let me use intellisense somehow to access these hardcoded values.
It's worth noting that my main use for these hardcoded values is to set default values for my class variables (by looping in an event initialize
), but I can also access them in other parts of the code
What I have tried:
Declaration Enum
to keep my hardcoded values
'Declarations
Private Enum startingVals
Top = 10
Column_Count = 4
Left = 15
...
End Enum
Private topVal As Long 'variables which I assign default values to
Private colCnt As Long
Private leftVal As Long
Private Sub Class_Initialize()
topVal = startingVals.Top
colCnt = startingVals.Column_Count
'etc.
End Sub
This has 2 limitations;
- Enumerations can only store
Long
s- Work around this by using a load
Const
instead, but then you have to remember every persistent name, plus it looks cluttered in your code
- Work around this by using a load
- Although I am getting Intellisense for
.Top
and.Column_Count
, I still need to enter startVals completely- This is significantly better than memorizing all the hard-coded constant names, but
Ideally I could do this
Private Sub Class_Initialize()
With startingVals 'or Dim v As startingVals, With v
topVal = .Top
colCnt = .Column_Count
'etc.
End With
End Sub
But I can not
Another approach would be to use a function to store values, so that you could only declare different types as long.
'Declarations
Private Enum startingVals
Top = 1
Column_Count = 2
Left = 3
...
End Enum
Private topVal As Long 'variables which I assign default values to
Private colCnt As Long
Private leftVal As Long
Private Sub Class_Initialize()
topVal = getval(Top)
colCnt = getval(Column_Count)
'etc.
End Sub
Then, to access your hardcoded data, you have a function that takes enum input (allows intellisense)
Private Function getval(dataType As startVals) As String
Const savedData As String = "1,2,1.17171717,hey,me,you" 'save the return values for the index specified by dataType
getval = Split(savedData, ",")(dataType) 'use datatype as a direct index of the array
End Function
or another way to store values
Private Function getval(dataType As startVals) As String
Const colV As Long = 10 'index 1
Const topV As String = "This is the top" 'index 2
'...
If dataType = ColumnCount Then getval = colV 'use dataType to check what to return
If dataType = Top Then getval = colV 'could use a select case too
'etc
End Function
- But in any case, we still cannot access the constants if we do not enter the function name.
- Also this approach requires me to update the enum declaration in my class declaration part and the const declaration inside the function itself, making it difficult to work with the code.
TL; DR
What's the best way to store hardcoded values in a class object where it is best stated
- Uses VBA intellisense (autocomplete) so I can quickly select the value I want as I type
- Is neat, self contained and concise in my class module to avoid clutter
- Can contain any hardcoded value type (datatype) (although I only use it
Long
in the project I'm currently working on) - Can be accessed without having to enter the initialization part every time (like name
function
orEnum
)- Of course, the block or function equivalent
With
would be accurate, as it only requires one instance of specifying the enum / data collection name
- Of course, the block or function equivalent
source to share
... so my class doesn't get messed up
I would decouple the class from the initialization process by adding another class by calling it Initializer
. The initializer will know how to initialize my objects, contain the default values, and fill my object with those default values. But in the initializer you will have to write tasks, without intellisense magic, but just write m_
and select from the list. NTN
Foo class
Option Explicit
'variables which I assign default values to
Private m_topVal As Long
Private m_colCnt As Long
'Private m_leftVal As Long
Private Sub Class_Initialize()
Dim initializer As FooInitializer
Set initializer = New FooInitializer
initializer.Initialize Me
End Sub
Public Property Get TopVal() As Long
TopVal = m_topVal
End Property
Public Property Let TopVal(ByVal vNewValue As Long)
m_topVal = vNewValue
End Property
Public Property Get ColCnt() As Long
ColCnt = m_colCnt
End Property
Public Property Let ColCnt(ByVal vNewValue As Long)
m_colCnt = vNewValue
End Property
' Add Get/Let(Set) for other member variables as well
FooInitializer class
Option Explicit
' Default startingVals values
Private m_topValDefault As Integer
Private m_columnCountDefault As Integer
'etc.
Public Sub Initialize(ByRef fooInstance As Foo)
fooInstance.TopVal = m_topValDefault
fooInstance.ColCnt = m_columnCountDefault
'etc.
End Sub
Private Sub Class_Initialize()
m_topValDefault = 10
m_columnCountDefault = 4
'etc.
End Sub
Standard module
Option Explicit
Sub test()
Dim f As Foo
Set f = New Foo
' f is now initizlized via initializer with default values
Debug.Print f.TopVal
Debug.Print f.ColCnt
End Sub
source to share
You can use constants to define default values in one place.
Then you can easily access them with Ctrl + Space + Default ...
Const Default_Top = 10
Const Default_Text = "abcd"
Private m_topVal As Long
Private m_text As String
Private Sub Class_Initialize()
m_topVal = Default_Top
m_text = Default_Text
End Sub
Public Property Get TopVal() As Long
TopVal = m_topVal
End Property
source to share
I can't claim ownership of this solution, but when I ran into it over Code Review it was ingenious enough for me to include it in quite a lot of my code since then.
As used in some other object-oriented languages, accessing the internal variables of a class instance using a construct is this
very familiar. The concept is expanded in VBA using an example here.
I created a class module named CustomClass
and inside it a private custom type is created for use only within that class.
Option Explicit
Private Type CustomType
Top As Long
Name As String
Temperature As Double
anotherCustomObject As CustomClass
End Type
Private this As CustomType
By working this way, you can create any number of internal variables of any combination of types (including objects). Accessing and initializing each of these values is now as easy as using a structured variable this
. The tab Class_Initialize
shows how:
Private Sub Class_Initialize()
this.Top = 150
this.Name = "Wayne"
this.Temperature = 98.6
Set this.anotherCustomObject = New CustomClass
End Sub
Set and initialize all your values in your heart content.
Alternatively, you can set each one with property accessories if you like. Some of them may be read-only:
'--- Read Only Properties
Public Property Get Name() As String
Name = this.Name
End Property
Public Property Get Temperature() As Double
Temperature = this.Temperature
End Property
Public Property Get ContainedObject() As CustomClass
Set ContainedObject = this.anotherCustomObject
End Property
And you can create some that read / write:
'--- Read/Write Properties
Public Property Let Top(ByVal newValue As Long)
this.Top = newValue
End Property
Public Property Get Top() As Long
Top = this.Top
End Property
Also, you can still easily use properties in the class using the keyword Me
:
'--- Internal Private Methods
Private Sub TestThisClass()
Debug.Print "current temperature is " & Me.Temperature
Debug.Print "the Top value is " & Me.Top
End Sub
Of course, all this works when you declare an object CustomClass
in another module.
Hope this helps you a little to help you organize your code.
(For convenience, here's the whole class :)
Option Explicit
Private Type CustomType
Top As Long
Name As String
Temperature As Double
anotherCustomObject As CustomClass
End Type
Private this As CustomType
Private Sub Class_Initialize()
this.Top = 150
this.Name = "Wayne"
this.Temperature = 98.6
Set this.anotherCustomObject = New CustomClass
End Sub
'--- Read Only Properties
Public Property Get Name() As String
Name = this.Name
End Property
Public Property Get Temperature() As Double
Temperature = this.Temperature
End Property
Public Property Get ContainedObject() As CustomClass
Set ContainedObject = this.anotherCustomObject
End Property
'--- Read/Write Properties
Public Property Let Top(ByVal newValue As Long)
this.Top = newValue
End Property
Public Property Get Top() As Long
Top = this.Top
End Property
'--- Internal Private Methods
Private Sub TestThisClass()
Debug.Print "current temperature is " & Me.Temperature
Debug.Print "the Top value is " & Me.Top
End Sub
source to share