Reputation: 315
I was given an answer on how to make a general class module: Class "let" stuck in infinite loop
I'm trying to apply this to dictionaries inside my classes.
My class module:
Option Explicit
Private Type categories
Temp As scripting.Dictionary
Humid As scripting.Dictionary
Wind As scripting.Dictionary
End Type
Private this As categories
Public Sub Initialize()
Set this.Temp = New scripting.Dictionary
Set this.Humid = New scripting.Dictionary
Set this.Wind = New scripting.Dictionary
End Sub
Public Property Get Temp(ByVal HourIndex As Long) As Double
Temp = this.Temp(HourIndex)
End Property
Public Property Let Temp(ByVal HourIndex As Long, ByVal Value As Double)
this.Temp(HourIndex) = Value
End Property
Public Property Get Humid(ByVal HourIndex As Long) As Double
Humid = this.Humid(HourIndex)
End Property
Public Property Let Humid(ByVal HourIndex As Long, ByVal Value As Double)
this.Humid(HourIndex) = Value
End Property
Public Property Get Wind(ByVal HourIndex As Long) As Double
Wind = this.Wind(HourIndex)
End Property
Public Property Let Wind(ByVal HourIndex As Long, ByVal Value As Double)
this.Wind(HourIndex) = Value
End Property
I tried to test this in the immediate window with set tester = new WeatherData
(the name of the module) and Initialize
. That did not work.
I then modified Initialize:
Public Sub Initialize(ByVal variable As categories)
Set variable.Temp = New scripting.Dictionary
Set variable.Humid = New scripting.Dictionary
Set variable.Wind = New scripting.Dictionary
End Sub
and entered Initialize tester
, but this did not work either ("Compile Error: Sub or Function not defined").
How do I put three dictionaries in a class module?
The following doesn't solve the problem, but it did skirt around it to the point that I don't have to acknowledge it:
Option Explicit
Private Type categories
Temp(23) As Double
Humid(23) As Double
wind(23) As Double
End Type
Private this As categories
Public Property Get Temp(ByVal HourIndex As Long) As Double
Temp = this.Temp(HourIndex)
End Property
Public Property Let Temp(ByVal HourIndex As Long, ByVal Value As Double)
this.Temp(HourIndex) = Value
End Property
Public Property Get Humid(ByVal HourIndex As Long) As Double
Humid = this.Humid(HourIndex)
End Property
Public Property Let Humid(ByVal HourIndex As Long, ByVal Value As Double)
this.Humid(HourIndex) = Value
End Property
Public Property Get wind(ByVal HourIndex As Long) As Double
wind = this.WindChill(HourIndex)
End Property
Public Property Let wind(ByVal HourIndex As Long, ByVal Value As Double)
this.wind(HourIndex) = Value
End Property
tl;dr: make arrays instead of dictionaries, and cut out initialize entirely. Your "keys" have no choice but to be numbers, but it works. I would be interested in knowing an actual solution, but the specific issue is solved.
Upvotes: 7
Views: 3166
Reputation: 132
I've found Mathieu Guindon example very instructive but quite minimalist for beginners.
All credits for Mathieu Guindon, but let me share an extended version of his code, using late binding just to change little details.
Class code module named WeatherData:
'Mathieu Guindon,Feb 6 '17
'https://stackoverflow.com/a/43263480
Option Explicit
Private dTemp As Object
Private dHumid As Object
Private dWind As Object
Private Sub Class_Initialize()
Set dTemp = CreateObject("Scripting.Dictionary")
Set dHumid = CreateObject("Scripting.Dictionary")
Set dWind = CreateObject("Scripting.Dictionary")
End Sub
Public Property Get Temp(ByVal key As String) As Double
Temp = dTemp(key)
End Property
Public Property Let Temp(ByVal key As String, ByVal value As Double)
dTemp(key) = value
End Property
Public Property Get TempItemCount() As Long
TempItemCount = dTemp.Count
End Property
Public Property Get Humid(ByVal key As String) As Double
Humid = dHumid(key)
End Property
Public Property Let Humid(ByVal key As String, ByVal value As Double)
dHumid(key) = value
End Property
Public Property Get HumidItemCount() As Long
HumidItemCount = dHumid.Count
End Property
Public Property Get Wind(ByVal key As String) As Double
Wind = dWind(key)
End Property
Public Property Let Wind(ByVal key As String, ByVal value As Double)
dWind(key) = value
End Property
Public Property Get WindItemCount() As Long
WindItemCount = dWind.Count
End Property
Standar code module:
Sub test()
Set Data = New WeatherData
With Data
.Temp("day 1") = 76
.Temp("day 2") = 78
.Humid("day 1") = 0.55
.Humid("day 2") = 0.61
.Wind("day 1") = 0.92
.Wind("day 2") = 1.27
Debug.Print .Temp("day 2")
Debug.Print .Humid("day 2")
Debug.Print .Wind("day 2")
Debug.Print .Wind("day 2")
Debug.Print .TempItemCount
End With
End Sub
Upvotes: 1
Reputation: 71177
Seems you want to implement an indexed property.
Simplified to a bare minimum:
Option Explicit
Private values As Scripting.Dictionary
Private Sub Class_Initialize()
Set values = New Scripting.Dictionary
End Sub
Public Property Get Something(ByVal key As String) As Double
Something = values(key)
End Property
Public Property Let Something(ByVal key As String, ByVal value As Double)
values(key) = value
End Property
You keep the dictionaries safely encapsulated as an implementation detail of your class (external code cannot set them to Nothing
, for example), and expose an indexed Get
+Let
property for each encapsulated dictionary, that takes the index (/key) as a parameter.
In the case of your WeatherData
class, this means you can populate the data like this:
Set data = New WeatherData
With data
.Temp("day 1") = 76
.Temp("day 2") = 78
.Humid("day 1") = 0.55
.Humid("day 2") = 0.61
.Wind("day 1") = 0.92
.Wind("day 2") = 1.27
End With
And then retrieve the temperature of "day 1"
with data.Temp("day 1")
.
As for your initializer method, it needed to be called from an instance of the class - being an instance method.
So instead of Initialize tester
you should have done tester.Initialize
.
Whether you make the internal encapsulated storage an array, a Collection
or a Dictionary
makes no difference to the calling code - it's an encapsulated implementation detail: your class could just as well store the data in .csv files or into a database if it wanted.
Upvotes: 11
Reputation: 470
In this case you should use late binding as follows:
Private Type categories
Temp As Object
Humid As Object
Wind As Object
End Type
Private this As categories
Public Sub Initialize()
Set this.Temp = CreateObject("Scripting.Dictionary")
Set this.Humid = CreateObject("Scripting.Dictionary")
Set this.Wind = CreateObject("Scripting.Dictionary")
End Sub
Furthermore you can't use Let
with multiple arguments. You should use a function to do that:
Public Function SetTemp(ByVal HourIndex As Long, ByVal Value As Double)
this.Temp(HourIndex) = Value
End Function
To run this I used:
Sub test()
Dim multi As Dictionaries
Set multi = New Dictionaries
multi.Initialize
multi.SetTemp 13, 25.522
Debug.Print multi.Temp(13)
End Sub
Where my class module is named Dictionaries
. So basically use late binding and change all your multi argument let functions to simple functions.
Upvotes: -1