Introduction
An enumeration is a special type in the .NET Framework that is comprised of a number of named constants.
While you might not have created an enumeration type yourself, you have likely used enumerations many times
in day-to-day programming. For example, each item in an ASP.NET DataGrid has an ItemType property,
which is of type ListItemType. ListItemType is an enumeration with values like
Header, Item, AlternatingItem, Footer, and others. Too, if
you've created files with the System.IO.File class you've used the FileMode and FileAccess
enumerations; if you have worked with regular expressions, you've likely used the RegexOptions enumeration.
When creating your own classes and components, enumerations can be used to make your code strongly typed, which makes
the code more readable and less prone to errors. Creating and using enumerations is simple, so there's no reason not to use enumerations where
applicable. This article looks at how to create enumeration types, how enumerations differ from constants,
how to create enumerations whose members can be combined via bit-wise operations, and how to enumerate the members of
an enumeration.
Creating an Enumeration Type
An enumeration can be declared with the following syntax:
' -- VB.NET
Public Enum EnumName [As Type]
Carriage-return delimited list of Named Constants
End Enum
// -- C#
public enum EnumName [: Type]
{
Comma-delimited list of Named Constants
}
EnumName is the name of the enumeration type, Type is the underlying value of the enumeration, and
Named Constants is a set of names and optional values corresponding to the Type. All enumerations
have some core, underlying, integral value type, such as a Byte, Integer, or Long. The default underlying type is
Integer.
An example of creating an enumeration can be seen below:
' -- VB.NET
Public Enum MenuItemType
Header
Item
AlternatingItem
End Enum
// -- C#
public enum MenuItemType
{
Header,
Item,
AlternatingItem
}
Enumerations vs. Constants
A constant is single element whose value never changes, and is declared using the following syntax:
' -- VB.NET
Public Const ConstantName As Type = Value
// -- C#
public const TypeConstantName = Value
Where ConstantName is the constant's name, Type is the type, and Value is the constant's value,
which can never be changed. The benefit of using constants is that they give a name to some "magic number," which improves
code readability. Unfortunately, constants don't provide strong typing. For example, imagine we had the following constants
defined:
Public Const Header As Integer = 1
Public Const Item As Integer = 2
Public Const AlternatingItem As Integer = 3
Additionally, imagine that we have a method, CreateMenuItem(Integer), that takes in an Integer
specifying the type of the menu item to create. This method might be called from the code like:
'Create a Header menu item...
CreateMenuItem(Header)
'Create an AlternatingItem menu item...
CreateMenuItem(AlternatingItem)
While this approach is clearly more readable that calling CreateMenuItem(1) or CreateMenuItem(3),
the method allows any Integer to be passed in. That is, a call to CreateMenuItem(-1934) wouldn't
cause a compile-time error (although it would likely cause a runtime error of some sort).
An enumeration, on the other hand, is strongly typed. Rather than using constants, we could use an enumeration. The
CreateMenuItem() method, then, would expect an input parameter of type MenuItemType, and would
be invoked using code like CreateMenuItem(MenuItemType.Header) or CreateMenuItem(MenuItemType.AlternatingItem).
With this approach, a compile-time error would be noted if we attempted to call CreateMenuItem(-1934).
Enumerations Under the Covers
When compiling a project that includes enumerations, the enumeration is converted into a structure that turns each
of the enumeration's named constants into an actual constant. That is, an enumeration like:
public enum MenuItemType
{
Header,
Item,
AlternatingItem
}
Would get turned into the following at compile-time:
public struct MenuItemType : System.Enum
{
public const int Header = 0;
public const int Item = 1;
public const int AlternatingItem = 2;
}
Notice that this structure is derived from the System.Enum class. The System.Enum class
contains the base functionality for all enumerations and provides methods for accessing the individual items of
an enumeration (as we'll see shortly).
Creating Enumerations Whose Members Can Be Joined via Bit-Wise Operations
What happens if your function or class needs to be passed more than one or more of your enumeration's values?
For example, the RegexOptions class spells out a number of regular expression options: if the regular expression
should be compiled, whether it should ignore case, whether the regular expression should have the ^ and
$ symbols match on a single line, or on multiple lines, and so on. Since a regular expression might
need to be configured to support more than one of these options, the bit-wise OR operator can be used. For example,
to create a regular expression object that is both multiline and compiled, you'd use:
' -- VB.NET
Dim re as New Regex(pattern, _
RegexOptions.MultiLine OR RegexOptions.Compiled)
// -- C#
Regex re = new Regex(pattern,
RegexOptions.MultiLine | RegExOptions.Compiled);
Notice that in VB.NET the bit-wise OR operator is OR; in C# it's |.
By default, an enumeration's members cannot be bit-wise ORed together. In order to create an enumeration that supports
bit-wise operations you need to do two things:
Mark the enumeration with the Flags() attribute
Assign the members of the enumerations values corresponding to the powers of 2
Here's a sample of how to declare an enumeration that can support bit-wise operations:
' -- VB.NET
<Flags()> Public Enum Characteristics
None = 1
Tall = 2
Dark = 4
Handsome = 8
All = Tall Or Dark Or Handsome
End Enum
// -- C#
[Flags()]
public enum Characteristics
{
None = 1,
Tall = 2,
Dark = 4,
Handsome = 8,
All = Tall | Dark | Handsome
}
Notice that each enumeration member has an increasing power of 2 value: 1, 2, 4, and 8. To compute the next value
in the series, simply multiple the last value by 2. The reason we need the numbers to be powers of 2 is because the
way integers are represented by a computer. A thorough discussion of this is beyond the scope of this article.
Imagine we had a method called MakeIdealHusband(Characteristics). This method could be called
like: MakeIdealHusband(Characteristics.Tall | Characteristics.Handsome), which would create a Tall and
Handsome husband.
Enumerating the Members of an Enumeration
There may be times where you need to loop through an enumeration and display either the names within the enumeration
or the values. The System.Enum class contains a GetNames(Type) method that returns a
string array of the enumeration members.
Here's an example of how to loop through your enumeration's names:
'Loop through the enumeration members
Dim names As String() = System.Enum.GetNames(GetType(Characteristics))
For i as Integer = 0 To names.Length-1
Response.Write(names(i))
Next
The System.Enum class also contains a Parse(Type, String) method that can be
used to get a strongly typed enumeration instance back based on a specified enumeration name. For example, to
get an enumeration instance of Characteristics.Tall, we could use the following code:
'Convert string to enumeration type
Dim myMan As Characteristics = System.Enum.Parse(GetType(Characteristics), "Tall")
Conclusion
Enumerations are good for lists of things that never or very rarely change. They are not good for lists that have
frequent modifications since you would have to recompile your code every time something in your enumeration changed.
Your enumeration must have values with one of the following data types: Byte, Integer, Long, or Short. If you can
"translate" the underlying value of your enumeration to one of these data types then that list is a good candidate
for an enumeration. If your underlying values are Char or String data types, these lists are not good candidates
for enumerations.
Enumerations can make your coding a lot easier to read and understand by allowing you to refer to underlying lists
of constants with logical names.
Author Bio:
Rachael Schoenbaum is a developer specializing in ASP, ASP.NET, Visual Basic, SQL Server, XML, and related
technologies. She consults for Lucidea and has been programming since 1999.