Alternatives to Magic
Magic Numbers vs. Enumerated Constants


Name: Enumerating Constants
Author: Mick Lambino
Date: 8/25/98

Description: This tutorial was graciously supplied by Mick Lambino and his excellent Visual Basic Workshop website. Be sure to give him a visit and check out the other items on his site. Re-printed by permission.
Controls needed: N/A
Level: All


In a small town in Saturdayville - there's a great little fruit shop called Frankie's Fruit and Veg. Frank was a pretty cool guy, open to technology, especially in the ways in which computer software could help him manage the ever fluctuating prices in his industry.

You see, in the old days Frank used to give his cashiers typed lists of his prices, so whenever the cashiers were ever unsure of the price of an item they could just look it up. This wasn't really cost effective as the lists had to be typed up from scratch early each morning before the shop had opened, photocopied and then handed out to each of Franks cashiers. Frank would try to save costs by using the previous day's pricelists as lining for his fruit trays, but that had to stop when the Department of Health received complaints that some of Franks fruit had photocopying toner over them and there was threat of legal action.

When Frank's crusty old photocopier kicked the bucket Frank invested in putting a computer network up and placed a terminal at each of his cashiers checkouts. He got a good payout when he sold his old cash registers to an antique dealer down the road and was able to buy old pinwriter printers to print customer receipts on. Frank was happy as he didn't have to buy photocopier toner anymore and only had to shell out a few bucks for old printer ribbon -- he looked at the modest upgrade and liked what he saw.

Frank got his neighbour Bill, who was a hobbiest programmer in VB, to write the software that would enable him to give his cashier's pricelists as before. The agreement between Frank and Bill was that Bill would come into the shop early in the morning and update the pricelists before the store opened. Frank knew nothing about programming, but was happy with Bill's program as it did the job and needless to say Franks business started to expand quickly. Frank's business grew so much so that he started selling exotic fruits and boasted no less than 150 types of fruit for sale each day!

Bill is a friend of mine who has generously provided the source to the application.

This is the (shortened - there is really at least 150 items in the list!!) source code to the simple application:

 
Option Explicit
 
Private Sub Form_Initialize()
    ' Form is initializing -- initialize the list.
    ' The first parameter specifies the text displayed
    ' in the listbox, and the 2nd parameter specifies
    ' the listIndex (including, the order in which the
    ' items will appear).
    With List1
        .AddItem "Apples", 0
        .AddItem "Oranges", 1
        .AddItem "Bananas", 2
        .AddItem "Grapes", 3
        .AddItem "Mangoes", 4
        .AddItem "Pears", 5
        .AddItem "Peaches", 6
        .AddItem "Lemons", 7
        .AddItem "Limes", 8
   End With
End Sub

Private Sub cmdQuit_Click()
    ' Invoke the form unload subroutine
    Call Unload(Me)
End Sub

 
Private Sub cmdSelect_Click()
    ' Passes the Index of the selected item to the
    ' Private subroutine.
    Call ListSelect(List1.ListIndex)
End Sub

Private Sub Form_Unload(Cancel As Integer)
    ' Free used resources.
    Set Form1.Icon = Nothing
    Set Form1 = Nothing
End Sub

' Custom Subroutine to handle which price to display depending upon
' which item in the listbox is selected.
Private Sub ListSelect(WhichFruit As Integer)
    Dim sPrice As String ' Storage for Price Check message.
 
    ' Store appropriate price of each fruit item.
    Select Case WhichFruit
        Case 0
            sPrice = "0.55"
        Case 1
            sPrice = "0.15"
        Case 2
            sPrice = "0.95"
        Case 3
            sPrice = "0.27"
        Case 4
            sPrice = "0.56"
        Case 5
            sPrice = "0.47"
        Case 6
            sPrice = "0.46"
        Case 7
            sPrice = "0.72"
        Case 8
            sPrice = "0.48"
   End Select
 
   ' Add the start of the message to save having to
   ' write the code in each fruit Selection
   sPrice = "Price = $" & sPrice
 
   ' Display the appropriate message for the list item.
   Me.Caption = sPrice
 
End Sub

Now, Frank's cashiers liked Bill's application but asked for a small Enhancement -- They wanted quick access to Apples, Oranges and Pears as they were common items sold and they were sick of scrolling down the list to find them. Bill understood that the Cashiers didn't want to memorize the list as their price changed daily and that this was the whole reason for the program anyway, so he added three more command buttons:

 and added the following code:

 
Private Sub cmdApples_Click()
    ' Shortcut Price Check
    Call ListSelect(0)
End Sub

Private Sub cmdOranges_Click()
    ' Shortcut Price Check
    Call ListSelect(1)
End Sub

Private Sub cmdPears_Click()
    ' Shortcut Price Check
    Call ListSelect(2)  '<--- Whoops!!
End Sub

The problem here was that Bill had a latenight on the apple juice the night before implementing the changes. In his rush the next morning he incorrectly passed 2 to his custom subroutine instead of 5 in the cmdPears click event. The Cashiers that day were eager to try out their Enhanced Pricelist application and inadvertantly overcharged each of their customers for the price of pears. The damage was done. When half of Franks customers saw that the Fruit shop down the street were selling their pears at almost half what they had paid they decided to never go back to Frank's and shop else where.

Bill's mistake was an easy one to make, especially because 2 and 5 are so close on the number pad and by looking at Bill's commenting, it was pretty much just copy-and-paste all the way through the application. Bill hit his head against the nearest brick-wall and said to himself: "If only I used enumerated constants instead!!!".

 

Ok, so what are Magic Numbers?

Magic numbers are numbers used in expressions that are not expressed as

In Bill's application he used the hardcoded numeric values of the Listbox's Listindex property to pass an Item to his custom subroutine (ListSelect).

 
Private Sub cmdPears_Click()
    ' Shortcut Price Check
    Call ListSelect(2)  '<--- Whoops!!
End Sub

When someone unfamiliar reads the source code to Bill's application, because of very poor commenting, they may be unsure whether or not they are passing correct values to custom subroutines. They need to read the initialize event to be sure, and even then, the author in this case did not realize that he was passing incorrect values.

 
        Case 0
            sPrice = "0.55"
        Case 1
            sPrice = "0.15"
        Case 2
            sPrice = "0.95"
        Case 3
            sPrice = "0.27"
        Case 4
            sPrice = "0.56"
        Case 5
            sPrice = "0.47"

Further, when an outside programmer reads the custom subroutine without reading the code in the initialize event it would be fair to say that they would not have a clue as to which hardcoded value corresponds to which item in the listbox (see above). This is due to poor programming / documenting techniques implemented by Bill which he himself later realizes the hardway.

 

Ok, so what are Constants?

Constants are constants :o) .. they are fixed values that won't change throughout the lifetime of an application. You can hardcode the values (use Magic Numbers) in your program logic as Bill did, but an alternate method would be to associate the numeric values with a name. A constant used in the context of this tutorial, is a numeric value with an associated descriptive name you can refer to in its place.

Take this alternate method Bill may consider in enhancing his source. Here we define a seperate constant to represent each of the above Fruit items (their ListIndex Value). We place the following in the declarations section of his application.

 
Option Explicit
 
' Constants for Fruit Items.
' These values dictate the order in which the
' Fruit items appear in the list (Listindex).
Private Const Apples = 0
Private Const Oranges = 1
Private Const Bananas = 2
Private Const Grapes = 3
Private Const Mangoes = 4
Private Const Pears = 5
Private Const Peaches = 6
Private Const Lemons = 7
Private Const Limes = 8

In making this system effective, Bill will have to change all the hardcoded values to their corresponding constants names. This is shown below.

 
Private Sub Form_Initialize()
    ' Form is initializing -- initialize the list.
    ' The first parameter specifies the text displayed
    ' in the listbox, and the 2nd parameter specifies
    ' the listIndex (including, the order in which the
    ' items will appear).
    With List1
        .AddItem "Apples", Apples
        .AddItem "Oranges", Oranges
        .AddItem "Bananas", Bananas
        .AddItem "Grapes", Grapes
        .AddItem "Mangoes", Mangoes
        .AddItem "Pears", Pears
        .AddItem "Peaches", Peaches
        .AddItem "Lemons", Lemons
        .AddItem "Limes", Limes
   End With
End Sub

Private Sub ListSelect(WhichFruit As Integer)
    Dim sPrice As String ' Storage for Price Check message.
 
    ' Store appropriate price of the selected fruit item.
    Select Case WhichFruit
        Case Apples
            sPrice = "0.55"
        Case Oranges
            sPrice = "0.15"
        Case Bananas
            sPrice = "0.95"
        Case Grapes
            sPrice = "0.27"
        Case Mangoes
            sPrice = "0.56"
        Case Pears
            sPrice = "0.47"
        Case Peaches
            sPrice = "0.46"
        Case Lemons
            sPrice = "0.72"
        Case Limes
            sPrice = "0.48"
   End Select
 
   ' Store the start of the message to save having to
   ' write the code in each fruit Selection
   sPrice = "Price = $" & sPrice
 
   ' Display the appropriate message for the list item.
   Me.Caption = sPrice
 
End Sub

As you can see above, the code reads much more easily for future programmers - well, the decision logic anyways. But I guess the question, If these changes were in place, would Bill have made the same mistake?, should be answered. Let's see how the coding of the cmdPears Click event will look in the VB IDE (design-time programming environment).

 

Now, in my opinion the above is ambiguous. A programmer will still not be able to tell whether a constant or an Integer should be passed as a parameter. An outside programmer will still have to checkout some other portion (now either the general declarations or the form initialize event) to really confirm the value in which they should pass.

The question is, do we stay with magic numbers or do we go a step further? Obviously, using hardcoded magic numbers would probably take less time to code (providing you don't make mistakes) than writing constants for each of the values. But on the other hand, using magic numbers can make it harder to spot typographical errors than using constants (providing you make use of Option Explicit statement). The answer may be in the following.

 

So, what the heck are Enumerated Constants already!?!

Enumerated Constants have the same functionality as the standard defined constants but they also provide a convenient way of grouping related constants. You create a set of enumerated constants by declaring an enumeration type with the Enum statement. Look at the following.

 
Option Explicit
 
' Enumerated Constants for Fruits Selection
' These values dictate the order in which they
' appear in the list (Listindex).
Private Enum Fruits
   Apples = 0
   Oranges = 1
   Bananas = 2
   Grapes = 3
   Mangoes = 4
   Pears = 5
   Peaches = 6
   Lemons = 7
   Limes = 8
End Enum

Replacing the old Constant's definitions with the above Enumeration declaration is all that is needed to make this application functional but let's go the step further...

Take a look at the current custom subroutine parameters.

 
Private Sub ListSelect(WhichFruit As Integer)

When writing a call to this subroutine we see the following:

 

The programmer is given no clue as to whether constants have already been defined or whether a magic number (Integer) should be passed to the subroutine. Even worse, the programmer is not given a hint in respect to the range of valid numbers which could be passed to the subroutine. Upon compiling this application an incorrect magic number may be passed without an error being raised.

As an alternative we state that the parameter to be passed should be a member of the enumeration type (Fruits) rather than the less descriptive integer.

 
Private Sub ListSelect(WhichFruit As Fruits)

Declaring a set of constants as an enumeration type provides more functionality than giving the set of constants a group name. What has in fact been created is a (an enumeration) type from which variables can be modelled. Now, when we call the subroutine, the parameter to be passed is presented to us in a dropdown list we can choose from as below.

 

We now present any programmer updating the application with a range of choices, clearly displaying the range of parameters which are valid (or should be valid) for the subroutine. Any ambiguity should be removed as with the need to have to scroll up and down lengthy code to confirm if a magic number correctly corresponds to an item.

 

Hey, so what happened to Bill and Frank's Fruit and Veg?

Well, the story doesn't end so bad. After Bill implemented the technique of using enumerated constants and finally assured Frank that the mistake would not happen again, Frank took a bold step to try an win back his customers. Instead of upping the price of his fruit to cover his losses he promoted a big sale. Frank also further expanded the business to stock no less than 200 different fruits and vegetables. Frank also promoted some bottled items that were grown by some of the locals to encourage the local folk to come back and promote their products.

The move was a success. In no time at all Frank's Fruit and Veg opened a chain of stores in neighbouring towns and both Frank and Bill retired young. Looking back at what had happened, the pair were almost happy that the mistake had occured as it both taught Bill to implement a more reliable way of maintaining his application and Frank the inspiration to expand his business!

 

Please Sir. I want some more ...

Ok, for those who need to know a little more about Enumeration Types checkout the Microsoft Visual Basic Programmers Guide that should have been shipped with your copy of Visual Basic (Enterprise anyway - Pages 332 - 335 give a consise insight in them). Other than that, a little practical experience in their implementation on your part will go a long way. They're not very difficult but if you run into any problems let me know.

A further enhancement that could be made to the above example is to change the enumeration declaration by removing the actual numeric values from them. Take a look at the following.

 
Option Explicit
 
' Enumerated Constants for Fruits Selection
' These values dictate the order in which they
' appear in the list (Listindex).
Private Enum Fruits
   Apples
   Oranges
   Bananas
   Grapes
   Mangoes
   Pears
   Peaches
   Lemons
   Limes
End Enum

By default, Visual Basic will associate the first member of an enumeration with the value of 0 (zero) and each following member 1, 2, 3 ... etc, respectively ... In the above example, Apples will have the value of zero, Oranges will have the value one .... Limes will have the value of eight.

The practical benefits of this implementation can be seen in the case if Bill wanted the order of the items to change. Instead of changing the hardcoded values (ie, Apples = 0, Oranges = 1), all he would have to do to the above is change the order in which they are declared in the enumeration type.

Also for further clarity the Name of the Enumeration should be added to the Members when they are used as in the modified custom subroutine as below.

 
Private Sub Form_Initialize()
    ' Form is initializing -- initialize the list.
    ' The first parameter specifies the text displayed
    ' in the listbox, and the 2nd parameter specifies
    ' the listIndex (including, the order in which the
    ' items will appear).
    With List1
        .AddItem "Apples", Fruits.Apples
        .AddItem "Oranges", Fruits.Oranges
        .AddItem "Bananas", Fruits.Bananas
        .AddItem "Grapes", Fruits.Grapes
        .AddItem "Mangoes", Fruits.Mangoes
        .AddItem "Pears", Fruits.Pears
        .AddItem "Peaches", Fruits.Peaches
        .AddItem "Lemons", Fruits.Lemons
        .AddItem "Limes", Fruits.Limes
   End With
End Sub

Private Sub ListSelect(WhichFruit As Fruits)
    Dim sPrice As String ' Storage for Price Check message.
 
    ' Store appropriate price of the selected fruit item.
    Select Case WhichFruit
        Case Fruits.Apples
            sPrice = "0.55"
        Case Fruits.Oranges
            sPrice = "0.15"
        Case Fruits.Bananas
            sPrice = "0.95"
        Case Fruits.Grapes
            sPrice = "0.27"
        Case Fruits.Mangoes
            sPrice = "0.56"
        Case Fruits.Pears
            sPrice = "0.47"
        Case Fruits.Peaches
            sPrice = "0.46"
        Case Fruits.Lemons
            sPrice = "0.72"
        Case Fruits.Limes
            sPrice = "0.48"
   End Select
 
   ' Store the start of the message to save having to
   ' write the code in each fruit Selection
   sPrice = "Price = $" & sPrice
 
   ' Display the appropriate message for the list item.
   Me.Caption = sPrice
 
End Sub

This will reduce any further ambiguity that a programmer may feel. Adding the enumeration name also allows you to be able to use the same member names in different enumeration types. For example, you can have Fruits.Apples and a seperate Bottled.Apples. You can associate these members with two different numeric values.

Some of the last points to raise include that enumerated constants can only be used for numerical values. So what should I do if I have a set of string values that I am currently using as standard constants? I hear someone say. That may have to be followed up in another tutorial, but the short answer is to create a Class Module which maybe as simple as containing only Property Get statements to return the constant values.

Another point I have ignored in this tutorial is the use of some notation standards when using enums. I did this on purpose to both avoid notation wars and in my opinion enhance the comprehension of the source code. My only opinion with notation prefixes or whatever, is to choose one style and stick to it throughout the life of the application. You can change your style later but within the life of an application's coding stage, be consistant.

Oh yeah ... the only other thing that some may be wondering is whether the saga of Frank and Bill is a true story or not. Sorry to disappoint but it is all fiction. By the way, if there is infact a Frank and Bill that closely match the story, that's just obviously a coincidence - they're common names :o).

Ok, that's it! I'm done. Drop me an e-mail for any suggestions, comments or what you may feel is inaccurate. And watchout for the next episode between Frank and Bill.

Happy Programming,

Michael Lambino
The VB Workshop
ProSoft@the18th.com