Enumerated Properties
An enumeration allows you to define your own set of named constants. A named
constant is an item that preserve a constant value throughout the execution of a
program and can be used in place of literal values. In other words, an Enum
is nothing but a group of related constant values that
automatically take different values. You can use named constants as
property values, method arguments, and as a function's return values. By using a named
constant it makes your code easier to read and maintain. For example, some
properties are intended to return a well-defined subset of integer numbers. In
our Student class, we can implement a YearLevel property that can
be assigned the values 1 (Freshmen), 2 (Sophomore), 3 (Junior), and 4
(Senior).
' In the declaration section of the class
Enum YearLevelEnum
Freshmen = 1
Sophomore
Junior
Senior
End Enum
|
You don't need to assign an explicit value to all the items in the Enum
structure, Visual Basic increments the preceding value automatically by 1. But because 0 is the
default value for any Integer property when the class is created, Visual Basic
starts at 0. It is a good programming style that you should always stay clear in
assigning value to the enum list, so that you can later trap any value that
hasn't been properly initialized . But Enum values don't need to be
in an increasing sequence. In fact, you can provide special values for Enum
constant list, as shown below:
' In the declaration section of the class
Enum GradeEnum
Falling = 50
Passing = 75
Probationary = 84
Except = 90
End Enum
|
After you define an Enum structure, you can create a Public or
Private
property
of the corresponding type:
Private m_YearLevel As YearLevelEnum
Property Get YearLevel() As YearLevelEnum
YearLevel= m_YearLevel
End Property
Property Let YearLevel(ByVal enumValue As YearLevelEnum)
' Refuse invalid assignments.
If enumValue <= 0 Or enumValue > Senior Then Err.Raise 5
m_YearLevel= enumValue
End Property
|
You should never forget that Enums are just shortcuts
for creating constants. This means that all the enumerated constants defined
within an Enum block should have unique names in their scope. Typically Enums
type are made by programmer to be Public, so their scope is often the
entire application.
It is a good programming practice, that you should devise a method for
generating unique names for all your enumerated constants. If you fail to do
that, the compiler refuses to compile your application and raises the
"Ambiguous name detected: <itemname>" error. The easy way to
avoid this problem is to add to all the enumerated constants a unique 2 or 3
letter prefix, for example:
' In the declaration section of the class
Enum YearLevelEnum
lvlFreshmen = 1
lvlSophomore
lvlJunior
lvlSenior
End Enum
|
Another way to avoid ambiguous name problem is use the complete enumname.constantname
syntax whenever you refer to an ambiguous Enum member, as in the
following code:
Student.YearLevel = YearLevelEnum.lvlSenior
|
While enumerated properties are very useful and allow you to store some
descriptive information in just 4 bytes of memory, sooner or later you will have to extract and
decode this information and
sometimes even show it to your users. For this reason, It is a good programming
practice to add a read-only property that returns the
description of an enumerated property:
Property Get YearLevelDescription() As String
Select Case m_YearLevel
Case lvlFreshmen : YearLevelDescription = "Freshmen"
Case lvlSophomore: YearLevelDescription = "Sophomore"
Case lvlJunior: YearLevelDescription = "Junior"
Case lvlSenior: YearLevelDescription = "Senior"
Case Else: Err.Raise 5
End Select
End Property
|
Another issue that you should never forget that your class are often change its structure if
you are still in developing stage. So it is possible that your validation code
can become outdated. For example, what happens if you later add a fifth YearLevel
constant such Graduate or Masteral? For this reason, you should always add
new constants safely without modifying the validation code in the
corresponding Property Let procedure, one way to do this is create a enum constant as the highest value in that block and assign
the value that you wanted to be the last enum constant, as shown below:
' In the declaration section of the class
Enum YearLevelEnum
lvlFreshmen = 1
lvlSophomore
lvlJunior
lvlSenior
lvlGraduate ' newly added enum constant
lvlMasteral ' newly added enum constant
YEAR_LEVEL_MAX = lvlMasteral ' make lvlMasteral the last enum constant
End Enum
Property Let YearLevel(ByVal enumValue As YearLevelEnum)
' Refuse invalid assignments
If enumValue <= 0 Or enumValue > YEAR_LEVEL_MAX Then Err.Raise 5
m_YearLevel= enumValue
End Property
|
As you can see we can safely add a new enum constant without worrying
about the validation code in our Property Let to become obsolete. And
making our maximum value in uppercase and putting a comment, we
can easily spot it in our source code. Of course, you should account your
read-only property description, because adding new enum constant in
our enumblock without adding appropriate description our YearLevelDescription
property will result to an error, but at least we are safely notified.
' Modified version
Property Get YearLevelDescription() As String
Select Case m_YearLevel
Case lvlFreshmen: YearLevelDescription = "Freshmen"
Case lvlSophomore: YearLevelDescription = "Sophomore"
Case lvlJunior: YearLevelDescription = "Junior"
Case lvlSenior: YearLevelDescription = "Senior"
Case lvlGraduate: YearLevelDescription = "Graduate"
Case lvlMasteral: YearLevelDescription = "Masteral"
Case Else: Err.Raise 5
End Select
End Property
|
The addition of highest value for your enum list might confuse your
user. You might want to hide this or decided not to show it from your user
when they started to use your class. I common technique that you can use
is by placing an underscore at the start of the enum list identifier as
shown below:
Enum GenderEnum
Male = 1
Female
[_GENDER MAX] = Female
End Enum
|
The square brackets [] are necessary because, Visual Basic will complain by raising
a compile error: Invalid character. Adding square brackets permit
us to add an underscore ( _ )at
the beginning of the enum list identifier and brought
us another useful technique, you can now add space to your enum list
identifier as shown above. Unfortunately, even you, the author of the
class cannot see this! So you must remember this enum list
identifier when you use your class, especially in the Property procedures
validation code.
One last thing that I can add pertaining to enumerated type. Sometimes, you need this description to populate a control in you
client form, such as ComboBox and ListBox control. One technique that
I used
frequently, I usually change the implementation of description property by
adding an optionalByVal parameter and then use the textual description
to populate the control:
' In the Student class module
Public Sub LoadYearLevelDescriptionTo(ctrl As Control)
Dim i As Integer
ctrl.Clear
For i = Freshmen To [_YEAR LEVEL MAX]
ctrl.AddItem YearLevelDescription(i)
Next i
End Sub
Property Get YearLevelDescription(Optional ByVal level As YearLevelEnum) As String
Dim tempLevel As Long
' If argument level contain a value, use it in the Select Case,
' otherwise, use the Private m_YearLevel variable
tempLevel = IIf(level = 0, m_YearLevel, level)
Select Case tempLevel
Case Freshmen: YearLevelDescription = "Freshmen"
Case Sophomore: YearLevelDescription = "Sophomore"
Case Junior: YearLevelDescription = "Junior"
Case Senior: YearLevelDescription = "Senior"
Case Graduate: YearLevelDescription = "Graduate"
Case Masteral: YearLevelDescription = "Masteral"
Case Else: Err.Raise 5
End Select
End Property
' In your client form
' Load Year level description in ListBox Control
Student.LoadYearLevelDescriptionTo List1
or
' Load Year level description in ListBox Control
Student.LoadYearLevelDescriptionTo Combo1
|
See, how easy it would be in the client, I don't have to populate the List
property of the Combo or ListBox control in the client form, all I
have to do is to call LoadYearLevelDescriptionTo to do the work.
Note, you can still use the Property YearLevelDescription without an argument:
' Return the Year Level Description of this particular Student
Debug.Print Student.YearLevelDescription
|