Enumerations (Syntax 2 and 3)

Constant Blocks can also declare enumerated constant symbols. Enumerations are logically grouped symbols which have incrementing integer constant values assigned to them that are each unique for the group. For example, an object may have the need for certain modes of operation. Each of these modes can be identified by a number, 0, 1, 2 and 3, for example. The numbers themselves don't really matter for our purposes; they just need to be unique within the context of the operation mode. Since the numbers themselves are not descriptive, it may be difficult to remember what mode 3 does, but it is a lot easier to remember what the mode means if it had a descriptive name instead. Look at the following example.

CON
  'Declare modes of operation
  RunTest = 0
  RunVerbose = 1
  RunBrief = 2
  RunFull = 3

The above example would suffice for our purposes; now users of our object can indicate "RunFull" instead of "3" to specify the desired mode of operation. The problem is, defining a logical group of items this way may cause bugs and maintenance problems because if any value was changed (on purpose or by accident) without changing the rest accordingly, it may cause the program to fail. Also, imagine a case where there were 20 modes of operation. That would be a much longer set of constants and even more opportunities for maintenance issues.

Enumerations solve these problems by automatically incrementing values for symbols. We can rewrite the above example with enumeration syntax as follows:

CON 'Declare modes of operation
  #0, RunTest, RunVerbose, RunBrief, RunFull 

Here, #0, tells the compiler to start counting from the number 0 and it sets the next symbol equal to that value. Then, any additional symbols that do not specify their own value (via an '= expression') are automatically assigned the previous value plus 1. The result is that RunTest equals 0, RunVerbose equals 1, RunBrief equals 2 and RunFull equals 3. For most cases, the values themselves don't usually matter; all that matters is that they are each assigned a unique number. Defining enumerated values like this has the advantages of insuring that the assigned values are unique and contiguous within the group.

Using the example above, the methods that use them can do things like the following (assume Mode is a symbol set by a calling object):

case Mode
  RunTest : <test code here>
  RunVerbose : <verbose code here>
  RunBrief : <brief code here>
  RunFull : <full code here>

—or—

if Mode > RunVerbose
  <brief and run mode code here>

Notice that these routines do not rely on the exact value of the mode, but rather they rely on the enumerated mode symbol itself for comparisons as well as the position of the symbol in relation to other symbols in the same enumeration. It is important to write code this way to decrease potentials for bugs introduced by future changes.

Enumerations don't have to consist of comma-separated items either. The following also works and leaves room for right-side comments about each mode.

CON 'Declare modes of operation
  #0
  RunTest 'Run in test mode
  RunVerbose 'Run in verbose mode
  RunBrief 'Run with brief prompts
  RunFull 'Run in full production mode 

The above example does the same thing as the previous in-line example, but now we have convenient room to describe the purpose of each mode without losing the automatic incrementing advantage. Later on, if there's a need to add a fifth mode, simply add it to the list in whatever position is necessary. If there is a need for the list to begin at a certain value, simply change the #0 to whatever you need: #1, #20, etc.

It is even possible to modify the enumerated value in the middle of the list.

CON
  'Declare modes of operation
  #1, RunTest, RunVerbose, #5, RunBrief, RunFull

Here, RunTest and RunVerbose are 1 and 2, respectively, and RunBrief and RunFull are 5 and 6, respectively. While this feature may be handy, to maintain good programming practices it should only be used in rare cases.

A more recommended way to achieve the previous example's result is to include the optional Offset field. The previous code could have been written as follows:

CON
  'Declare modes of operation
  #1, RunTest, RunVerbose[3], RunBrief, RunFull 

Just as before, RunTest and RunVerbose are 1 and 2, respectively. The [3] immediately following RunVerbose causes the current enumeration value (2) to be incremented by 3 before the next enumerated symbol. The effect of this is also like before, RunBrief and RunFull are 5 and 6, respectively. The advantage of this technique, however, is that the enumerated symbols are all set relative to each other. Changing the line's starting value causes them all to change relatively. For example, changing the #1, to #4 causes RunTest and RunVerbose to be 4 and 5, respectively, and RunBrief and RunFull to be 8 and 9, respectively. In contrast, if the original example's #1 were changed to #4, both RunVerbose and RunBrief would be set to 5, possibly causing the code that relies on those symbols to misbehave.

The Offset value may be any signed value, but only affects the value immediately following it; the enumerated value is always incremented by 1 after a Symbol that doesn't specify an Offset. If overlapping values are desired, specifying an Offset of 0 or less can achieve that effect.

Syntax 3 is a variation of the enumeration syntax. It doesn't specify any starting value. Anything defined this way will always start with the first symbol equal to either 0 (for new CON blocks) or to the next enumerated value relative to the previous one (within the same CON block).

Unless otherwise noted, content on this site is licensed under the
Creative Commons Attribution-ShareAlike 4.0 International License.