Collections of classes in VBA
Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
1
down vote
favorite
I have noticed this question https://stackoverflow.com/questions/49389392/nested-classes-and-collections-vba in StackOverflow, but it got closed faster than I could have answered. However, it was concerning a best practice for usage of collections of classes.
I usually use Array
and I add to it like this:
Public Sub AddToTeam(emp As Employee)
Dim cnt As Long
cnt = UBound(pTeam)
If Not pTeam(0) Is Nothing Then
ReDim Preserve pTeam(cnt + 1)
cnt = cnt + 1
End If
Set pTeam(cnt) = emp
End Sub
Furthermore, I initialize the Array like this in the class:
Private Sub Class_Initialize()
ReDim pTeam(0)
End Sub
This is the working code, feel free to give any ideas:
Main Module
Option Explicit
Public Sub TestMe()
Dim EmpA As New Employee
Dim EmpB As New Employee
Dim ManA As New Employee
Dim TeamA As New Team
ManA.Name = "John Doe Top Manager"
EmpA.Name = "Peter"
EmpB.Name = "George"
Set EmpB.Manager = ManA
TeamA.Name = "The best team!"
TeamA.AddToTeam ManA
TeamA.AddToTeam EmpA
TeamA.AddToTeam EmpB
TeamA.PrintNames
Debug.Print TeamA.Name
TeamA.PrintInfoForManagers
End Sub
Class Employee
Option Explicit
Private pName As String
Private pManager As Employee
Private pAge As Long
Private pTeam As String
Private pHasManager As Boolean
Public Property Get HasManager() As Boolean
HasManager = pHasManager
End Property
Public Property Let HasManager(Value As Boolean)
pHasManager = Value
End Property
Public Property Get Manager() As Employee
Set Manager = pManager
End Property
Public Property Set Manager(Value As Employee)
Set pManager = Value
HasManager = True
End Property
Public Property Get Name() As String
Name = pName
End Property
Public Property Let Name(Value As String)
pName = Value
End Property
Public Property Get Team() As Employee
Team = pTeam
End Property
Public Property Let Team(Value As Employee)
pTeam = Value
End Property
Class Team
Option Explicit
Private pTeam() As Employee
Private pName As String
Public Sub PrintInfoForManagers()
Dim emp As Variant
For Each emp In pTeam
If emp.HasManager Then
Debug.Print emp.Name & " is managed by " & emp.Manager.Name
Else
Debug.Print emp.Name & " has no manager."
End If
Next emp
End Sub
Public Sub PrintNames()
Dim emp As Variant
For Each emp In pTeam
Debug.Print emp.Name
Next emp
End Sub
Public Property Get Name() As String
Name = pName
End Property
Public Property Let Name(Value As String)
pName = Value
End Property
Public Sub AddToTeam(emp As Employee)
Dim cnt As Long
cnt = UBound(pTeam)
If Not pTeam(0) Is Nothing Then
ReDim Preserve pTeam(cnt + 1)
cnt = cnt + 1
End If
Set pTeam(cnt) = emp
End Sub
Private Sub Class_Initialize()
ReDim pTeam(0)
End Sub
So how would you review/improve it?
object-oriented vba
add a comment |Â
up vote
1
down vote
favorite
I have noticed this question https://stackoverflow.com/questions/49389392/nested-classes-and-collections-vba in StackOverflow, but it got closed faster than I could have answered. However, it was concerning a best practice for usage of collections of classes.
I usually use Array
and I add to it like this:
Public Sub AddToTeam(emp As Employee)
Dim cnt As Long
cnt = UBound(pTeam)
If Not pTeam(0) Is Nothing Then
ReDim Preserve pTeam(cnt + 1)
cnt = cnt + 1
End If
Set pTeam(cnt) = emp
End Sub
Furthermore, I initialize the Array like this in the class:
Private Sub Class_Initialize()
ReDim pTeam(0)
End Sub
This is the working code, feel free to give any ideas:
Main Module
Option Explicit
Public Sub TestMe()
Dim EmpA As New Employee
Dim EmpB As New Employee
Dim ManA As New Employee
Dim TeamA As New Team
ManA.Name = "John Doe Top Manager"
EmpA.Name = "Peter"
EmpB.Name = "George"
Set EmpB.Manager = ManA
TeamA.Name = "The best team!"
TeamA.AddToTeam ManA
TeamA.AddToTeam EmpA
TeamA.AddToTeam EmpB
TeamA.PrintNames
Debug.Print TeamA.Name
TeamA.PrintInfoForManagers
End Sub
Class Employee
Option Explicit
Private pName As String
Private pManager As Employee
Private pAge As Long
Private pTeam As String
Private pHasManager As Boolean
Public Property Get HasManager() As Boolean
HasManager = pHasManager
End Property
Public Property Let HasManager(Value As Boolean)
pHasManager = Value
End Property
Public Property Get Manager() As Employee
Set Manager = pManager
End Property
Public Property Set Manager(Value As Employee)
Set pManager = Value
HasManager = True
End Property
Public Property Get Name() As String
Name = pName
End Property
Public Property Let Name(Value As String)
pName = Value
End Property
Public Property Get Team() As Employee
Team = pTeam
End Property
Public Property Let Team(Value As Employee)
pTeam = Value
End Property
Class Team
Option Explicit
Private pTeam() As Employee
Private pName As String
Public Sub PrintInfoForManagers()
Dim emp As Variant
For Each emp In pTeam
If emp.HasManager Then
Debug.Print emp.Name & " is managed by " & emp.Manager.Name
Else
Debug.Print emp.Name & " has no manager."
End If
Next emp
End Sub
Public Sub PrintNames()
Dim emp As Variant
For Each emp In pTeam
Debug.Print emp.Name
Next emp
End Sub
Public Property Get Name() As String
Name = pName
End Property
Public Property Let Name(Value As String)
pName = Value
End Property
Public Sub AddToTeam(emp As Employee)
Dim cnt As Long
cnt = UBound(pTeam)
If Not pTeam(0) Is Nothing Then
ReDim Preserve pTeam(cnt + 1)
cnt = cnt + 1
End If
Set pTeam(cnt) = emp
End Sub
Private Sub Class_Initialize()
ReDim pTeam(0)
End Sub
So how would you review/improve it?
object-oriented vba
add a comment |Â
up vote
1
down vote
favorite
up vote
1
down vote
favorite
I have noticed this question https://stackoverflow.com/questions/49389392/nested-classes-and-collections-vba in StackOverflow, but it got closed faster than I could have answered. However, it was concerning a best practice for usage of collections of classes.
I usually use Array
and I add to it like this:
Public Sub AddToTeam(emp As Employee)
Dim cnt As Long
cnt = UBound(pTeam)
If Not pTeam(0) Is Nothing Then
ReDim Preserve pTeam(cnt + 1)
cnt = cnt + 1
End If
Set pTeam(cnt) = emp
End Sub
Furthermore, I initialize the Array like this in the class:
Private Sub Class_Initialize()
ReDim pTeam(0)
End Sub
This is the working code, feel free to give any ideas:
Main Module
Option Explicit
Public Sub TestMe()
Dim EmpA As New Employee
Dim EmpB As New Employee
Dim ManA As New Employee
Dim TeamA As New Team
ManA.Name = "John Doe Top Manager"
EmpA.Name = "Peter"
EmpB.Name = "George"
Set EmpB.Manager = ManA
TeamA.Name = "The best team!"
TeamA.AddToTeam ManA
TeamA.AddToTeam EmpA
TeamA.AddToTeam EmpB
TeamA.PrintNames
Debug.Print TeamA.Name
TeamA.PrintInfoForManagers
End Sub
Class Employee
Option Explicit
Private pName As String
Private pManager As Employee
Private pAge As Long
Private pTeam As String
Private pHasManager As Boolean
Public Property Get HasManager() As Boolean
HasManager = pHasManager
End Property
Public Property Let HasManager(Value As Boolean)
pHasManager = Value
End Property
Public Property Get Manager() As Employee
Set Manager = pManager
End Property
Public Property Set Manager(Value As Employee)
Set pManager = Value
HasManager = True
End Property
Public Property Get Name() As String
Name = pName
End Property
Public Property Let Name(Value As String)
pName = Value
End Property
Public Property Get Team() As Employee
Team = pTeam
End Property
Public Property Let Team(Value As Employee)
pTeam = Value
End Property
Class Team
Option Explicit
Private pTeam() As Employee
Private pName As String
Public Sub PrintInfoForManagers()
Dim emp As Variant
For Each emp In pTeam
If emp.HasManager Then
Debug.Print emp.Name & " is managed by " & emp.Manager.Name
Else
Debug.Print emp.Name & " has no manager."
End If
Next emp
End Sub
Public Sub PrintNames()
Dim emp As Variant
For Each emp In pTeam
Debug.Print emp.Name
Next emp
End Sub
Public Property Get Name() As String
Name = pName
End Property
Public Property Let Name(Value As String)
pName = Value
End Property
Public Sub AddToTeam(emp As Employee)
Dim cnt As Long
cnt = UBound(pTeam)
If Not pTeam(0) Is Nothing Then
ReDim Preserve pTeam(cnt + 1)
cnt = cnt + 1
End If
Set pTeam(cnt) = emp
End Sub
Private Sub Class_Initialize()
ReDim pTeam(0)
End Sub
So how would you review/improve it?
object-oriented vba
I have noticed this question https://stackoverflow.com/questions/49389392/nested-classes-and-collections-vba in StackOverflow, but it got closed faster than I could have answered. However, it was concerning a best practice for usage of collections of classes.
I usually use Array
and I add to it like this:
Public Sub AddToTeam(emp As Employee)
Dim cnt As Long
cnt = UBound(pTeam)
If Not pTeam(0) Is Nothing Then
ReDim Preserve pTeam(cnt + 1)
cnt = cnt + 1
End If
Set pTeam(cnt) = emp
End Sub
Furthermore, I initialize the Array like this in the class:
Private Sub Class_Initialize()
ReDim pTeam(0)
End Sub
This is the working code, feel free to give any ideas:
Main Module
Option Explicit
Public Sub TestMe()
Dim EmpA As New Employee
Dim EmpB As New Employee
Dim ManA As New Employee
Dim TeamA As New Team
ManA.Name = "John Doe Top Manager"
EmpA.Name = "Peter"
EmpB.Name = "George"
Set EmpB.Manager = ManA
TeamA.Name = "The best team!"
TeamA.AddToTeam ManA
TeamA.AddToTeam EmpA
TeamA.AddToTeam EmpB
TeamA.PrintNames
Debug.Print TeamA.Name
TeamA.PrintInfoForManagers
End Sub
Class Employee
Option Explicit
Private pName As String
Private pManager As Employee
Private pAge As Long
Private pTeam As String
Private pHasManager As Boolean
Public Property Get HasManager() As Boolean
HasManager = pHasManager
End Property
Public Property Let HasManager(Value As Boolean)
pHasManager = Value
End Property
Public Property Get Manager() As Employee
Set Manager = pManager
End Property
Public Property Set Manager(Value As Employee)
Set pManager = Value
HasManager = True
End Property
Public Property Get Name() As String
Name = pName
End Property
Public Property Let Name(Value As String)
pName = Value
End Property
Public Property Get Team() As Employee
Team = pTeam
End Property
Public Property Let Team(Value As Employee)
pTeam = Value
End Property
Class Team
Option Explicit
Private pTeam() As Employee
Private pName As String
Public Sub PrintInfoForManagers()
Dim emp As Variant
For Each emp In pTeam
If emp.HasManager Then
Debug.Print emp.Name & " is managed by " & emp.Manager.Name
Else
Debug.Print emp.Name & " has no manager."
End If
Next emp
End Sub
Public Sub PrintNames()
Dim emp As Variant
For Each emp In pTeam
Debug.Print emp.Name
Next emp
End Sub
Public Property Get Name() As String
Name = pName
End Property
Public Property Let Name(Value As String)
pName = Value
End Property
Public Sub AddToTeam(emp As Employee)
Dim cnt As Long
cnt = UBound(pTeam)
If Not pTeam(0) Is Nothing Then
ReDim Preserve pTeam(cnt + 1)
cnt = cnt + 1
End If
Set pTeam(cnt) = emp
End Sub
Private Sub Class_Initialize()
ReDim pTeam(0)
End Sub
So how would you review/improve it?
object-oriented vba
edited Mar 21 at 9:37
asked Mar 20 at 17:18
Vityata
167115
167115
add a comment |Â
add a comment |Â
2 Answers
2
active
oldest
votes
up vote
1
down vote
accepted
I prefer not to initiate arrays until they're needed. Automatically initiating your array can make it difficult to determine whether the array has been used. Using the Team
class as an example, calling PrintNames
before any names are added will throw an error.
Private Sub Class_Initialize()
ReDim pTeam(0)
End Sub
The main reason this is done is to avoid throwing an error when checking to whether or not an array has been initialized.
There is a trick to avoid the error 9, Subscript out of range
. Checking to see if an array IsEmpty()
on an uninitialized array will cause UBound()
to return -1.
By using Call IsEmpty(pTeam)
instead of ReDim pTeam(0)
we can easily check whether the array has actually been used using If UBound(pTeam) = -1 Then
.
Private Sub Class_Initialize()
Call IsEmpty(pTeam)
End Sub
Public Sub AddToTeam(emp As Employee)
If UBound(pTeam) = -1 Then
ReDim pTeam(0)
Else
ReDim Preserve pTeam(UBound(pTeam) + 1)
End If
Set pTeam(UBound(pTeam)) = emp
End Sub
Alternatively, we could write a function to check if the array has been used.
Public Function hasMembers()
Call IsEmpty(pTeam)
hasMembers = UBound(pTeam) > -1
End Function
Using a Private Type to Reference Class Members
Mathieu Guindon (formally known as Mat's Mug) likes to use a User Defined Type (UDT) named This
to reference class level variables. I have adopted the technique but name it vars
instead of This
. In this way you can avoid having to the class variable names different from the parameter names (see code below).
Refactored Code
Employee:Class
Option Explicit
Private Type Variables
Name As String
Manager As Employee
Age As Long
Team As String
End Type
Private vars As Variables
Public Function HasManager() As Boolean
HasManager = Not vars.Manager Is Nothing
End Function
Public Property Get Manager() As Employee
Set Manager = vars.Manager
End Property
Public Property Set Manager(Value As Employee)
Set vars.Manager = Value
End Property
Public Property Get Name() As String
Name = vars.Name
End Property
Public Property Let Name(Value As String)
vars.Name = Value
End Property
Public Property Get Team() As Employee
Team = vars.Team
End Property
Public Property Let Team(Value As Employee)
vars.Team = Value
End Property
Public Function This() As Team
Set This = Me
End Function
Team:Class
Option Explicit
Private Type Variables
Team() As Employee
Name As String
End Type
Private vars As Variables
Public Sub PrintInfoForManagers()
Dim emp As Variant
For Each emp In vars.Team
If emp.HasManager Then
Debug.Print emp.Name & " is managed by " & emp.Manager.Name
Else
Debug.Print emp.Name & " has no manager."
End If
Next emp
End Sub
Public Sub PrintNames()
Dim emp As Variant
If UBound(vars.Team) = -1 Then
Debug.Print "There are no Names to Print"
Else
For Each emp In vars.Team
Debug.Print emp.Name
Next emp
End If
End Sub
Public Property Get Name() As String
Name = vars.Name
End Property
Public Property Let Name(Value As String)
vars.Name = Value
End Property
Public Sub AddToTeam(emp As Employee)
If Not Me.hasMembers Then
ReDim vars.Team(0)
Else
ReDim Preserve vars.Team(UBound(vars.Team) + 1)
End If
Set vars.Team(UBound(vars.Team)) = emp
End Sub
Public Function hasMembers()
Call IsEmpty(vars.Team)
hasMembers = UBound(vars.Team) > -1
End Function
Public Function This() As Team
Set This = Me
End Function
Interesting ideas, thanks,Class_Initialize
really somehow smells. In general, why are you usingCall
? I thought this was one of the "forbidden" VBA features, just likeInteger
. A separate Thumbs Up for the Enums. It looks a bit clearer than the automaticpVariable
from MZ-Code.
â Vityata
Mar 21 at 9:29
1
Call IsEmpty (vars.Team)
looks more natural thenIsEmpty vars.Team
to me. Although there is no speed advantage to usingInteger
instead ofLong
, I would not callInteger
a "forbidden" fracture. You meanUser Defined Types
notEnums
but thanks!
â user109261
Mar 21 at 9:43
Yup, I meantUser Defined Types
. :)
â Vityata
Mar 21 at 9:46
add a comment |Â
up vote
2
down vote
I don't see the need for the HasManager
property. Either a Manager
is assigned, or not. It wouldn't make sense for you to set it to True when there isn't one, or False when there is.
Then just change your Print
function to check -
If Not emp.Manager Is Nothing Then
Debug.Print emp.Name & " is managed by " & emp.Manager.Name
Else
Debug.Print emp.Name & " has no manager."
If it was easy to overload the class initialization for employee, I'd say make a name value obligatory, but it's really not a simple task in VBA.
These two properties:
Public Property Get Team() As Employee
Team = pTeam
End Property
Public Property Let Team(Value As Employee)
pTeam = Value
End Property
Are passing Objects
- why not pass Strings
given the team information is handled by a string.
1
Both are good ideas, thanks. However, I like theHasManager
, it somehow gives me some kind of an interface to work with.Not Is Nothing
is a good way around.Team
is assigned as employee, because I can use some other properties later (probably).
â Vityata
Mar 21 at 9:23
1
Passing stringNames
for the managers instead ofEmployee
objects defeats the purpose of the post. Consider that you add and email field to theEmployee
class and needed to retrieve the Email address of an employees manager. Currently you could simply useEmployee.Manager.Email
. If you use string then you would have to iterate over all the managers to find the Email address.
â user109261
Mar 21 at 9:28
add a comment |Â
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
1
down vote
accepted
I prefer not to initiate arrays until they're needed. Automatically initiating your array can make it difficult to determine whether the array has been used. Using the Team
class as an example, calling PrintNames
before any names are added will throw an error.
Private Sub Class_Initialize()
ReDim pTeam(0)
End Sub
The main reason this is done is to avoid throwing an error when checking to whether or not an array has been initialized.
There is a trick to avoid the error 9, Subscript out of range
. Checking to see if an array IsEmpty()
on an uninitialized array will cause UBound()
to return -1.
By using Call IsEmpty(pTeam)
instead of ReDim pTeam(0)
we can easily check whether the array has actually been used using If UBound(pTeam) = -1 Then
.
Private Sub Class_Initialize()
Call IsEmpty(pTeam)
End Sub
Public Sub AddToTeam(emp As Employee)
If UBound(pTeam) = -1 Then
ReDim pTeam(0)
Else
ReDim Preserve pTeam(UBound(pTeam) + 1)
End If
Set pTeam(UBound(pTeam)) = emp
End Sub
Alternatively, we could write a function to check if the array has been used.
Public Function hasMembers()
Call IsEmpty(pTeam)
hasMembers = UBound(pTeam) > -1
End Function
Using a Private Type to Reference Class Members
Mathieu Guindon (formally known as Mat's Mug) likes to use a User Defined Type (UDT) named This
to reference class level variables. I have adopted the technique but name it vars
instead of This
. In this way you can avoid having to the class variable names different from the parameter names (see code below).
Refactored Code
Employee:Class
Option Explicit
Private Type Variables
Name As String
Manager As Employee
Age As Long
Team As String
End Type
Private vars As Variables
Public Function HasManager() As Boolean
HasManager = Not vars.Manager Is Nothing
End Function
Public Property Get Manager() As Employee
Set Manager = vars.Manager
End Property
Public Property Set Manager(Value As Employee)
Set vars.Manager = Value
End Property
Public Property Get Name() As String
Name = vars.Name
End Property
Public Property Let Name(Value As String)
vars.Name = Value
End Property
Public Property Get Team() As Employee
Team = vars.Team
End Property
Public Property Let Team(Value As Employee)
vars.Team = Value
End Property
Public Function This() As Team
Set This = Me
End Function
Team:Class
Option Explicit
Private Type Variables
Team() As Employee
Name As String
End Type
Private vars As Variables
Public Sub PrintInfoForManagers()
Dim emp As Variant
For Each emp In vars.Team
If emp.HasManager Then
Debug.Print emp.Name & " is managed by " & emp.Manager.Name
Else
Debug.Print emp.Name & " has no manager."
End If
Next emp
End Sub
Public Sub PrintNames()
Dim emp As Variant
If UBound(vars.Team) = -1 Then
Debug.Print "There are no Names to Print"
Else
For Each emp In vars.Team
Debug.Print emp.Name
Next emp
End If
End Sub
Public Property Get Name() As String
Name = vars.Name
End Property
Public Property Let Name(Value As String)
vars.Name = Value
End Property
Public Sub AddToTeam(emp As Employee)
If Not Me.hasMembers Then
ReDim vars.Team(0)
Else
ReDim Preserve vars.Team(UBound(vars.Team) + 1)
End If
Set vars.Team(UBound(vars.Team)) = emp
End Sub
Public Function hasMembers()
Call IsEmpty(vars.Team)
hasMembers = UBound(vars.Team) > -1
End Function
Public Function This() As Team
Set This = Me
End Function
Interesting ideas, thanks,Class_Initialize
really somehow smells. In general, why are you usingCall
? I thought this was one of the "forbidden" VBA features, just likeInteger
. A separate Thumbs Up for the Enums. It looks a bit clearer than the automaticpVariable
from MZ-Code.
â Vityata
Mar 21 at 9:29
1
Call IsEmpty (vars.Team)
looks more natural thenIsEmpty vars.Team
to me. Although there is no speed advantage to usingInteger
instead ofLong
, I would not callInteger
a "forbidden" fracture. You meanUser Defined Types
notEnums
but thanks!
â user109261
Mar 21 at 9:43
Yup, I meantUser Defined Types
. :)
â Vityata
Mar 21 at 9:46
add a comment |Â
up vote
1
down vote
accepted
I prefer not to initiate arrays until they're needed. Automatically initiating your array can make it difficult to determine whether the array has been used. Using the Team
class as an example, calling PrintNames
before any names are added will throw an error.
Private Sub Class_Initialize()
ReDim pTeam(0)
End Sub
The main reason this is done is to avoid throwing an error when checking to whether or not an array has been initialized.
There is a trick to avoid the error 9, Subscript out of range
. Checking to see if an array IsEmpty()
on an uninitialized array will cause UBound()
to return -1.
By using Call IsEmpty(pTeam)
instead of ReDim pTeam(0)
we can easily check whether the array has actually been used using If UBound(pTeam) = -1 Then
.
Private Sub Class_Initialize()
Call IsEmpty(pTeam)
End Sub
Public Sub AddToTeam(emp As Employee)
If UBound(pTeam) = -1 Then
ReDim pTeam(0)
Else
ReDim Preserve pTeam(UBound(pTeam) + 1)
End If
Set pTeam(UBound(pTeam)) = emp
End Sub
Alternatively, we could write a function to check if the array has been used.
Public Function hasMembers()
Call IsEmpty(pTeam)
hasMembers = UBound(pTeam) > -1
End Function
Using a Private Type to Reference Class Members
Mathieu Guindon (formally known as Mat's Mug) likes to use a User Defined Type (UDT) named This
to reference class level variables. I have adopted the technique but name it vars
instead of This
. In this way you can avoid having to the class variable names different from the parameter names (see code below).
Refactored Code
Employee:Class
Option Explicit
Private Type Variables
Name As String
Manager As Employee
Age As Long
Team As String
End Type
Private vars As Variables
Public Function HasManager() As Boolean
HasManager = Not vars.Manager Is Nothing
End Function
Public Property Get Manager() As Employee
Set Manager = vars.Manager
End Property
Public Property Set Manager(Value As Employee)
Set vars.Manager = Value
End Property
Public Property Get Name() As String
Name = vars.Name
End Property
Public Property Let Name(Value As String)
vars.Name = Value
End Property
Public Property Get Team() As Employee
Team = vars.Team
End Property
Public Property Let Team(Value As Employee)
vars.Team = Value
End Property
Public Function This() As Team
Set This = Me
End Function
Team:Class
Option Explicit
Private Type Variables
Team() As Employee
Name As String
End Type
Private vars As Variables
Public Sub PrintInfoForManagers()
Dim emp As Variant
For Each emp In vars.Team
If emp.HasManager Then
Debug.Print emp.Name & " is managed by " & emp.Manager.Name
Else
Debug.Print emp.Name & " has no manager."
End If
Next emp
End Sub
Public Sub PrintNames()
Dim emp As Variant
If UBound(vars.Team) = -1 Then
Debug.Print "There are no Names to Print"
Else
For Each emp In vars.Team
Debug.Print emp.Name
Next emp
End If
End Sub
Public Property Get Name() As String
Name = vars.Name
End Property
Public Property Let Name(Value As String)
vars.Name = Value
End Property
Public Sub AddToTeam(emp As Employee)
If Not Me.hasMembers Then
ReDim vars.Team(0)
Else
ReDim Preserve vars.Team(UBound(vars.Team) + 1)
End If
Set vars.Team(UBound(vars.Team)) = emp
End Sub
Public Function hasMembers()
Call IsEmpty(vars.Team)
hasMembers = UBound(vars.Team) > -1
End Function
Public Function This() As Team
Set This = Me
End Function
Interesting ideas, thanks,Class_Initialize
really somehow smells. In general, why are you usingCall
? I thought this was one of the "forbidden" VBA features, just likeInteger
. A separate Thumbs Up for the Enums. It looks a bit clearer than the automaticpVariable
from MZ-Code.
â Vityata
Mar 21 at 9:29
1
Call IsEmpty (vars.Team)
looks more natural thenIsEmpty vars.Team
to me. Although there is no speed advantage to usingInteger
instead ofLong
, I would not callInteger
a "forbidden" fracture. You meanUser Defined Types
notEnums
but thanks!
â user109261
Mar 21 at 9:43
Yup, I meantUser Defined Types
. :)
â Vityata
Mar 21 at 9:46
add a comment |Â
up vote
1
down vote
accepted
up vote
1
down vote
accepted
I prefer not to initiate arrays until they're needed. Automatically initiating your array can make it difficult to determine whether the array has been used. Using the Team
class as an example, calling PrintNames
before any names are added will throw an error.
Private Sub Class_Initialize()
ReDim pTeam(0)
End Sub
The main reason this is done is to avoid throwing an error when checking to whether or not an array has been initialized.
There is a trick to avoid the error 9, Subscript out of range
. Checking to see if an array IsEmpty()
on an uninitialized array will cause UBound()
to return -1.
By using Call IsEmpty(pTeam)
instead of ReDim pTeam(0)
we can easily check whether the array has actually been used using If UBound(pTeam) = -1 Then
.
Private Sub Class_Initialize()
Call IsEmpty(pTeam)
End Sub
Public Sub AddToTeam(emp As Employee)
If UBound(pTeam) = -1 Then
ReDim pTeam(0)
Else
ReDim Preserve pTeam(UBound(pTeam) + 1)
End If
Set pTeam(UBound(pTeam)) = emp
End Sub
Alternatively, we could write a function to check if the array has been used.
Public Function hasMembers()
Call IsEmpty(pTeam)
hasMembers = UBound(pTeam) > -1
End Function
Using a Private Type to Reference Class Members
Mathieu Guindon (formally known as Mat's Mug) likes to use a User Defined Type (UDT) named This
to reference class level variables. I have adopted the technique but name it vars
instead of This
. In this way you can avoid having to the class variable names different from the parameter names (see code below).
Refactored Code
Employee:Class
Option Explicit
Private Type Variables
Name As String
Manager As Employee
Age As Long
Team As String
End Type
Private vars As Variables
Public Function HasManager() As Boolean
HasManager = Not vars.Manager Is Nothing
End Function
Public Property Get Manager() As Employee
Set Manager = vars.Manager
End Property
Public Property Set Manager(Value As Employee)
Set vars.Manager = Value
End Property
Public Property Get Name() As String
Name = vars.Name
End Property
Public Property Let Name(Value As String)
vars.Name = Value
End Property
Public Property Get Team() As Employee
Team = vars.Team
End Property
Public Property Let Team(Value As Employee)
vars.Team = Value
End Property
Public Function This() As Team
Set This = Me
End Function
Team:Class
Option Explicit
Private Type Variables
Team() As Employee
Name As String
End Type
Private vars As Variables
Public Sub PrintInfoForManagers()
Dim emp As Variant
For Each emp In vars.Team
If emp.HasManager Then
Debug.Print emp.Name & " is managed by " & emp.Manager.Name
Else
Debug.Print emp.Name & " has no manager."
End If
Next emp
End Sub
Public Sub PrintNames()
Dim emp As Variant
If UBound(vars.Team) = -1 Then
Debug.Print "There are no Names to Print"
Else
For Each emp In vars.Team
Debug.Print emp.Name
Next emp
End If
End Sub
Public Property Get Name() As String
Name = vars.Name
End Property
Public Property Let Name(Value As String)
vars.Name = Value
End Property
Public Sub AddToTeam(emp As Employee)
If Not Me.hasMembers Then
ReDim vars.Team(0)
Else
ReDim Preserve vars.Team(UBound(vars.Team) + 1)
End If
Set vars.Team(UBound(vars.Team)) = emp
End Sub
Public Function hasMembers()
Call IsEmpty(vars.Team)
hasMembers = UBound(vars.Team) > -1
End Function
Public Function This() As Team
Set This = Me
End Function
I prefer not to initiate arrays until they're needed. Automatically initiating your array can make it difficult to determine whether the array has been used. Using the Team
class as an example, calling PrintNames
before any names are added will throw an error.
Private Sub Class_Initialize()
ReDim pTeam(0)
End Sub
The main reason this is done is to avoid throwing an error when checking to whether or not an array has been initialized.
There is a trick to avoid the error 9, Subscript out of range
. Checking to see if an array IsEmpty()
on an uninitialized array will cause UBound()
to return -1.
By using Call IsEmpty(pTeam)
instead of ReDim pTeam(0)
we can easily check whether the array has actually been used using If UBound(pTeam) = -1 Then
.
Private Sub Class_Initialize()
Call IsEmpty(pTeam)
End Sub
Public Sub AddToTeam(emp As Employee)
If UBound(pTeam) = -1 Then
ReDim pTeam(0)
Else
ReDim Preserve pTeam(UBound(pTeam) + 1)
End If
Set pTeam(UBound(pTeam)) = emp
End Sub
Alternatively, we could write a function to check if the array has been used.
Public Function hasMembers()
Call IsEmpty(pTeam)
hasMembers = UBound(pTeam) > -1
End Function
Using a Private Type to Reference Class Members
Mathieu Guindon (formally known as Mat's Mug) likes to use a User Defined Type (UDT) named This
to reference class level variables. I have adopted the technique but name it vars
instead of This
. In this way you can avoid having to the class variable names different from the parameter names (see code below).
Refactored Code
Employee:Class
Option Explicit
Private Type Variables
Name As String
Manager As Employee
Age As Long
Team As String
End Type
Private vars As Variables
Public Function HasManager() As Boolean
HasManager = Not vars.Manager Is Nothing
End Function
Public Property Get Manager() As Employee
Set Manager = vars.Manager
End Property
Public Property Set Manager(Value As Employee)
Set vars.Manager = Value
End Property
Public Property Get Name() As String
Name = vars.Name
End Property
Public Property Let Name(Value As String)
vars.Name = Value
End Property
Public Property Get Team() As Employee
Team = vars.Team
End Property
Public Property Let Team(Value As Employee)
vars.Team = Value
End Property
Public Function This() As Team
Set This = Me
End Function
Team:Class
Option Explicit
Private Type Variables
Team() As Employee
Name As String
End Type
Private vars As Variables
Public Sub PrintInfoForManagers()
Dim emp As Variant
For Each emp In vars.Team
If emp.HasManager Then
Debug.Print emp.Name & " is managed by " & emp.Manager.Name
Else
Debug.Print emp.Name & " has no manager."
End If
Next emp
End Sub
Public Sub PrintNames()
Dim emp As Variant
If UBound(vars.Team) = -1 Then
Debug.Print "There are no Names to Print"
Else
For Each emp In vars.Team
Debug.Print emp.Name
Next emp
End If
End Sub
Public Property Get Name() As String
Name = vars.Name
End Property
Public Property Let Name(Value As String)
vars.Name = Value
End Property
Public Sub AddToTeam(emp As Employee)
If Not Me.hasMembers Then
ReDim vars.Team(0)
Else
ReDim Preserve vars.Team(UBound(vars.Team) + 1)
End If
Set vars.Team(UBound(vars.Team)) = emp
End Sub
Public Function hasMembers()
Call IsEmpty(vars.Team)
hasMembers = UBound(vars.Team) > -1
End Function
Public Function This() As Team
Set This = Me
End Function
edited Mar 21 at 9:44
answered Mar 21 at 9:21
user109261
Interesting ideas, thanks,Class_Initialize
really somehow smells. In general, why are you usingCall
? I thought this was one of the "forbidden" VBA features, just likeInteger
. A separate Thumbs Up for the Enums. It looks a bit clearer than the automaticpVariable
from MZ-Code.
â Vityata
Mar 21 at 9:29
1
Call IsEmpty (vars.Team)
looks more natural thenIsEmpty vars.Team
to me. Although there is no speed advantage to usingInteger
instead ofLong
, I would not callInteger
a "forbidden" fracture. You meanUser Defined Types
notEnums
but thanks!
â user109261
Mar 21 at 9:43
Yup, I meantUser Defined Types
. :)
â Vityata
Mar 21 at 9:46
add a comment |Â
Interesting ideas, thanks,Class_Initialize
really somehow smells. In general, why are you usingCall
? I thought this was one of the "forbidden" VBA features, just likeInteger
. A separate Thumbs Up for the Enums. It looks a bit clearer than the automaticpVariable
from MZ-Code.
â Vityata
Mar 21 at 9:29
1
Call IsEmpty (vars.Team)
looks more natural thenIsEmpty vars.Team
to me. Although there is no speed advantage to usingInteger
instead ofLong
, I would not callInteger
a "forbidden" fracture. You meanUser Defined Types
notEnums
but thanks!
â user109261
Mar 21 at 9:43
Yup, I meantUser Defined Types
. :)
â Vityata
Mar 21 at 9:46
Interesting ideas, thanks,
Class_Initialize
really somehow smells. In general, why are you using Call
? I thought this was one of the "forbidden" VBA features, just like Integer
. A separate Thumbs Up for the Enums. It looks a bit clearer than the automatic pVariable
from MZ-Code.â Vityata
Mar 21 at 9:29
Interesting ideas, thanks,
Class_Initialize
really somehow smells. In general, why are you using Call
? I thought this was one of the "forbidden" VBA features, just like Integer
. A separate Thumbs Up for the Enums. It looks a bit clearer than the automatic pVariable
from MZ-Code.â Vityata
Mar 21 at 9:29
1
1
Call IsEmpty (vars.Team)
looks more natural then IsEmpty vars.Team
to me. Although there is no speed advantage to using Integer
instead of Long
, I would not call Integer
a "forbidden" fracture. You mean User Defined Types
not Enums
but thanks!â user109261
Mar 21 at 9:43
Call IsEmpty (vars.Team)
looks more natural then IsEmpty vars.Team
to me. Although there is no speed advantage to using Integer
instead of Long
, I would not call Integer
a "forbidden" fracture. You mean User Defined Types
not Enums
but thanks!â user109261
Mar 21 at 9:43
Yup, I meant
User Defined Types
. :)â Vityata
Mar 21 at 9:46
Yup, I meant
User Defined Types
. :)â Vityata
Mar 21 at 9:46
add a comment |Â
up vote
2
down vote
I don't see the need for the HasManager
property. Either a Manager
is assigned, or not. It wouldn't make sense for you to set it to True when there isn't one, or False when there is.
Then just change your Print
function to check -
If Not emp.Manager Is Nothing Then
Debug.Print emp.Name & " is managed by " & emp.Manager.Name
Else
Debug.Print emp.Name & " has no manager."
If it was easy to overload the class initialization for employee, I'd say make a name value obligatory, but it's really not a simple task in VBA.
These two properties:
Public Property Get Team() As Employee
Team = pTeam
End Property
Public Property Let Team(Value As Employee)
pTeam = Value
End Property
Are passing Objects
- why not pass Strings
given the team information is handled by a string.
1
Both are good ideas, thanks. However, I like theHasManager
, it somehow gives me some kind of an interface to work with.Not Is Nothing
is a good way around.Team
is assigned as employee, because I can use some other properties later (probably).
â Vityata
Mar 21 at 9:23
1
Passing stringNames
for the managers instead ofEmployee
objects defeats the purpose of the post. Consider that you add and email field to theEmployee
class and needed to retrieve the Email address of an employees manager. Currently you could simply useEmployee.Manager.Email
. If you use string then you would have to iterate over all the managers to find the Email address.
â user109261
Mar 21 at 9:28
add a comment |Â
up vote
2
down vote
I don't see the need for the HasManager
property. Either a Manager
is assigned, or not. It wouldn't make sense for you to set it to True when there isn't one, or False when there is.
Then just change your Print
function to check -
If Not emp.Manager Is Nothing Then
Debug.Print emp.Name & " is managed by " & emp.Manager.Name
Else
Debug.Print emp.Name & " has no manager."
If it was easy to overload the class initialization for employee, I'd say make a name value obligatory, but it's really not a simple task in VBA.
These two properties:
Public Property Get Team() As Employee
Team = pTeam
End Property
Public Property Let Team(Value As Employee)
pTeam = Value
End Property
Are passing Objects
- why not pass Strings
given the team information is handled by a string.
1
Both are good ideas, thanks. However, I like theHasManager
, it somehow gives me some kind of an interface to work with.Not Is Nothing
is a good way around.Team
is assigned as employee, because I can use some other properties later (probably).
â Vityata
Mar 21 at 9:23
1
Passing stringNames
for the managers instead ofEmployee
objects defeats the purpose of the post. Consider that you add and email field to theEmployee
class and needed to retrieve the Email address of an employees manager. Currently you could simply useEmployee.Manager.Email
. If you use string then you would have to iterate over all the managers to find the Email address.
â user109261
Mar 21 at 9:28
add a comment |Â
up vote
2
down vote
up vote
2
down vote
I don't see the need for the HasManager
property. Either a Manager
is assigned, or not. It wouldn't make sense for you to set it to True when there isn't one, or False when there is.
Then just change your Print
function to check -
If Not emp.Manager Is Nothing Then
Debug.Print emp.Name & " is managed by " & emp.Manager.Name
Else
Debug.Print emp.Name & " has no manager."
If it was easy to overload the class initialization for employee, I'd say make a name value obligatory, but it's really not a simple task in VBA.
These two properties:
Public Property Get Team() As Employee
Team = pTeam
End Property
Public Property Let Team(Value As Employee)
pTeam = Value
End Property
Are passing Objects
- why not pass Strings
given the team information is handled by a string.
I don't see the need for the HasManager
property. Either a Manager
is assigned, or not. It wouldn't make sense for you to set it to True when there isn't one, or False when there is.
Then just change your Print
function to check -
If Not emp.Manager Is Nothing Then
Debug.Print emp.Name & " is managed by " & emp.Manager.Name
Else
Debug.Print emp.Name & " has no manager."
If it was easy to overload the class initialization for employee, I'd say make a name value obligatory, but it's really not a simple task in VBA.
These two properties:
Public Property Get Team() As Employee
Team = pTeam
End Property
Public Property Let Team(Value As Employee)
pTeam = Value
End Property
Are passing Objects
- why not pass Strings
given the team information is handled by a string.
answered Mar 20 at 21:40
Raystafarian
5,4331046
5,4331046
1
Both are good ideas, thanks. However, I like theHasManager
, it somehow gives me some kind of an interface to work with.Not Is Nothing
is a good way around.Team
is assigned as employee, because I can use some other properties later (probably).
â Vityata
Mar 21 at 9:23
1
Passing stringNames
for the managers instead ofEmployee
objects defeats the purpose of the post. Consider that you add and email field to theEmployee
class and needed to retrieve the Email address of an employees manager. Currently you could simply useEmployee.Manager.Email
. If you use string then you would have to iterate over all the managers to find the Email address.
â user109261
Mar 21 at 9:28
add a comment |Â
1
Both are good ideas, thanks. However, I like theHasManager
, it somehow gives me some kind of an interface to work with.Not Is Nothing
is a good way around.Team
is assigned as employee, because I can use some other properties later (probably).
â Vityata
Mar 21 at 9:23
1
Passing stringNames
for the managers instead ofEmployee
objects defeats the purpose of the post. Consider that you add and email field to theEmployee
class and needed to retrieve the Email address of an employees manager. Currently you could simply useEmployee.Manager.Email
. If you use string then you would have to iterate over all the managers to find the Email address.
â user109261
Mar 21 at 9:28
1
1
Both are good ideas, thanks. However, I like the
HasManager
, it somehow gives me some kind of an interface to work with. Not Is Nothing
is a good way around. Team
is assigned as employee, because I can use some other properties later (probably).â Vityata
Mar 21 at 9:23
Both are good ideas, thanks. However, I like the
HasManager
, it somehow gives me some kind of an interface to work with. Not Is Nothing
is a good way around. Team
is assigned as employee, because I can use some other properties later (probably).â Vityata
Mar 21 at 9:23
1
1
Passing string
Names
for the managers instead of Employee
objects defeats the purpose of the post. Consider that you add and email field to the Employee
class and needed to retrieve the Email address of an employees manager. Currently you could simply use Employee.Manager.Email
. If you use string then you would have to iterate over all the managers to find the Email address.â user109261
Mar 21 at 9:28
Passing string
Names
for the managers instead of Employee
objects defeats the purpose of the post. Consider that you add and email field to the Employee
class and needed to retrieve the Email address of an employees manager. Currently you could simply use Employee.Manager.Email
. If you use string then you would have to iterate over all the managers to find the Email address.â user109261
Mar 21 at 9:28
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f190049%2fcollections-of-classes-in-vba%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password